Skip to content

Commit c13a8a6

Browse files
committed
Refactor code-style
1 parent 3158ff5 commit c13a8a6

File tree

5 files changed

+279
-207
lines changed

5 files changed

+279
-207
lines changed

lib/index.js

Lines changed: 168 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,85 @@
1-
import process from 'node:process'
1+
/**
2+
* @typedef {import('vfile').VFile} VFile
3+
*/
4+
5+
/**
6+
* @typedef {'patch' | 'stats' | 'raw'} DiffType
7+
*
8+
* @typedef PatchData
9+
* Data of a patch.
10+
* @property {string} aPath
11+
* From.
12+
* @property {string} bPath
13+
* To.
14+
* @property {Array<string>} lines
15+
* Changes.
16+
* @property {boolean} isBlacklisted
17+
* No idea.
18+
*
19+
* @typedef {[originalRev: string, rev: string]} Range
20+
* Range of two refs (such as commits).
21+
*
22+
* @typedef {[from: number, to: number]} Diff
23+
* Diff range, two line numbers between which there’s been a change.
24+
*/
25+
226
import path from 'node:path'
3-
// @ts-expect-error: hush
27+
import process from 'node:process'
28+
import {ok as assert} from 'devlop'
29+
// @ts-expect-error: not typed.
430
import gitDiffTree from 'git-diff-tree'
531
import {findUp} from 'vfile-find-up'
632

7-
const own = {}.hasOwnProperty
8-
33+
// This is mostly to enable the tests to mimick different CIs.
34+
// Normally, a Node process exits between CI runs.
935
/** @type {string} */
1036
let previousRange
1137

12-
/** @type {import('unified').Plugin<[]>} */
13-
export default function diff() {
14-
/** @type {Record<string, string>} */
15-
let cache = {}
16-
17-
return function (_, file, next) {
38+
/**
39+
* @returns
40+
* Transform.
41+
*/
42+
export default function unifiedDiff() {
43+
/** @type {Map<string, string>} */
44+
let cache = new Map()
45+
46+
/**
47+
* @param {unknown} _
48+
* Tree.
49+
* @param {VFile} file
50+
* File.
51+
* @returns {Promise<undefined>}
52+
* Promise to nothing.
53+
*/
54+
return async function (_, file) {
1855
const base = file.dirname
19-
/** @type {string|undefined} */
56+
/** @type {string | undefined} */
2057
let commitRange
21-
/** @type {Array<string>|undefined} */
58+
/** @type {Range | undefined} */
2259
let range
2360

2461
// Looks like Travis.
2562
if (process.env.TRAVIS_COMMIT_RANGE) {
2663
commitRange = process.env.TRAVIS_COMMIT_RANGE
27-
range = commitRange.split(/\.{3}/)
64+
// Cast because we check `length` later.
65+
range = /** @type {Range} */ (commitRange.split(/\.{3}/))
2866
}
2967
// Looks like GH Actions.
3068
else if (process.env.GITHUB_SHA) {
31-
// @ts-expect-error: fine.
32-
range =
33-
// This is a PR: check the whole PR.
34-
// Refs take the form `refs/heads/main`.
35-
process.env.GITHUB_BASE_REF && process.env.GITHUB_HEAD_REF
36-
? [
37-
process.env.GITHUB_BASE_REF.split('/').pop(),
38-
process.env.GITHUB_HEAD_REF.split('/').pop()
39-
]
40-
: [process.env.GITHUB_SHA + '^1', process.env.GITHUB_SHA]
41-
// @ts-expect-error: We definitely just defined this
69+
const sha = process.env.GITHUB_SHA
70+
const base = process.env.GITHUB_BASE_REF
71+
const head = process.env.GITHUB_HEAD_REF
72+
73+
if (base && head) {
74+
const baseTail = base.split('/').pop()
75+
const headTail = head.split('/').pop()
76+
assert(baseTail)
77+
assert(headTail)
78+
range = [baseTail, headTail]
79+
} else {
80+
range = [sha + '^1', sha]
81+
}
82+
4283
commitRange = range.join('...')
4384
}
4485

@@ -49,119 +90,124 @@ export default function diff() {
4990
!file.dirname ||
5091
range.length !== 2
5192
) {
52-
return next()
93+
return
5394
}
5495

55-
if (commitRange !== previousRange) {
56-
cache = {}
96+
// Reset cache.
97+
if (previousRange !== commitRange) {
98+
cache = new Map()
5799
previousRange = commitRange
58100
}
59101

60-
/* c8 ignore next 3 */
61-
if (own.call(cache, base)) {
62-
tick(cache[base])
63-
} else {
64-
findUp('.git', file.dirname, (error, git) => {
65-
// Never happens.
66-
/* c8 ignore next */
67-
if (error) return next(error)
68-
69-
// Not testable in a Git repo…
70-
/* c8 ignore next 3 */
71-
if (!git || !git.dirname) {
72-
return next(new Error('Not in a git repository'))
73-
}
102+
let gitFolder = cache.get(base)
74103

75-
cache[base] = git.dirname
76-
tick(git.dirname)
77-
})
104+
if (!gitFolder) {
105+
const gitFolderFile = await findUp('.git', file.dirname)
106+
107+
/* c8 ignore next 3 -- not testable in a Git repo… */
108+
if (!gitFolderFile || !gitFolderFile.dirname) {
109+
throw new Error('Not in a git repository')
110+
}
111+
112+
cache.set(base, gitFolderFile.dirname)
113+
gitFolder = gitFolderFile.dirname
114+
}
115+
116+
const diffs = await checkGit(gitFolder, range)
117+
const ranges = diffs.get(path.resolve(file.cwd, file.path))
118+
119+
// Unchanged file: drop all messages.
120+
if (!ranges || ranges.length === 0) {
121+
file.messages.length = 0
122+
return
78123
}
79124

80-
/**
81-
* @param {string} root
82-
*/
83-
function tick(root) {
84-
/** @type {Record<string, Array<[number, number]>>} */
85-
const diffs = {}
86-
87-
gitDiffTree(path.join(root, '.git'), {
88-
// @ts-expect-error: fine.
89-
originalRev: range[0],
90-
// @ts-expect-error: fine.
91-
rev: range[1]
125+
file.messages = file.messages.filter(function (message) {
126+
return ranges.some(function (range) {
127+
return (
128+
message.line && message.line >= range[0] && message.line <= range[1]
129+
)
92130
})
93-
.on('error', next)
94-
.on(
95-
'data',
96-
/**
97-
* @param {string} type
98-
* @param {{lines: string, aPath: string, bPath: string}} data
99-
*/
100-
(type, data) => {
101-
if (type !== 'patch') return
102-
103-
const lines = data.lines
104-
const re = /^@@ -(\d+),?(\d+)? \+(\d+),?(\d+)? @@/
105-
const match = lines[0].match(re)
106-
107-
// Should not happen, maybe if Git returns weird diffs?
108-
/* c8 ignore next */
109-
if (!match) return
110-
111-
/** @type {Array<[number, number]>} */
112-
const ranges = []
113-
const start = Number.parseInt(match[3], 10) - 1
114-
let index = 0
115-
/** @type {number|undefined} */
116-
let position
117-
118-
while (++index < lines.length) {
119-
const line = lines[index]
120-
121-
if (line.charAt(0) === '+') {
122-
const no = start + index
123-
124-
if (position === undefined) {
125-
position = ranges.length
126-
ranges.push([no, no])
127-
} else {
128-
ranges[position][1] = no
129-
}
131+
})
132+
}
133+
}
134+
135+
/**
136+
* Check a folder.
137+
*
138+
* @param {string} root
139+
* Folder.
140+
* @param {Range} range
141+
* Range.
142+
* @returns {Promise<Map<string, Array<Diff>>>}
143+
* Nothing.
144+
*/
145+
function checkGit(root, range) {
146+
return new Promise(function (resolve, reject) {
147+
/** @type {Map<string, Array<Diff>>} */
148+
const diffs = new Map()
149+
const [originalRev, rev] = range
150+
151+
gitDiffTree(path.join(root, '.git'), {originalRev, rev})
152+
.on('error', reject)
153+
.on(
154+
'data',
155+
/**
156+
* @param {DiffType} type
157+
* Data type.
158+
* @param {PatchData} data
159+
* Data.
160+
* @returns {undefined}
161+
* Nothing.
162+
*/
163+
function (type, data) {
164+
if (type !== 'patch') return
165+
166+
const lines = data.lines
167+
const re = /^@@ -(\d+),?(\d+)? \+(\d+),?(\d+)? @@/
168+
const match = lines[0].match(re)
169+
170+
/* c8 ignore next -- should not happen, maybe if Git returns weird diffs? */
171+
if (!match) return
172+
173+
/** @type {Array<Diff>} */
174+
const ranges = []
175+
const start = Number.parseInt(match[3], 10) - 1
176+
let index = 0
177+
/** @type {number | undefined} */
178+
let position
179+
180+
while (++index < lines.length) {
181+
const line = lines[index]
182+
183+
if (line.charAt(0) === '+') {
184+
const no = start + index
185+
186+
if (position === undefined) {
187+
position = ranges.length
188+
ranges.push([no, no])
130189
} else {
131-
position = undefined
190+
ranges[position][1] = no
132191
}
192+
} else {
193+
position = undefined
133194
}
195+
}
134196

135-
const fp = path.resolve(root, data.bPath)
197+
const fp = path.resolve(root, data.bPath)
136198

137-
// Long diffs.
138-
/* c8 ignore next */
139-
if (!(fp in diffs)) diffs[fp] = []
199+
let list = diffs.get(fp)
140200

141-
diffs[fp].push(...ranges)
142-
}
143-
)
144-
.on('end', () => {
145-
const fp = path.resolve(file.cwd, file.path)
146-
const ranges = diffs[fp]
147-
148-
// Unchanged file.
149-
if (!ranges || ranges.length === 0) {
150-
file.messages = []
151-
return next()
201+
if (!list) {
202+
list = []
203+
diffs.set(fp, list)
152204
}
153205

154-
file.messages = file.messages.filter((message) =>
155-
ranges.some(
156-
(range) =>
157-
message.line &&
158-
message.line >= range[0] &&
159-
message.line <= range[1]
160-
)
161-
)
162-
163-
next()
164-
})
165-
}
166-
}
206+
list.push(...ranges)
207+
}
208+
)
209+
.on('end', function () {
210+
resolve(diffs)
211+
})
212+
})
167213
}

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,20 @@
3535
"index.js"
3636
],
3737
"dependencies": {
38+
"devlop": "^1.0.0",
3839
"git-diff-tree": "^1.0.0",
40+
"vfile": "^6.0.0",
3941
"vfile-find-up": "^7.0.0"
4042
},
4143
"devDependencies": {
4244
"@types/nlcst": "^2.0.0",
43-
"@types/tape": "^5.0.0",
45+
"@types/node": "^20.0.0",
4446
"c8": "^8.0.0",
4547
"nlcst-to-string": "^4.0.0",
4648
"parse-english": "^7.0.0",
4749
"prettier": "^3.0.0",
4850
"remark-cli": "^11.0.0",
4951
"remark-preset-wooorm": "^9.0.0",
50-
"tape": "^5.0.0",
5152
"to-vfile": "^8.0.0",
5253
"type-coverage": "^2.0.0",
5354
"typescript": "^5.0.0",

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ const file = unified()
104104
.process(await read('example.md'))
105105

106106
console.error(reporter(file))
107-
process.exit(file.messages.length > 0 ? 1 : 0)
107+
process.exitCode = file.messages.length > 0 ? 1 : 0
108108
```
109109

110110
…and our Travis configuration `.travis.yml` contains:

0 commit comments

Comments
 (0)