|
1 | | -import process from 'node:process' |
2 | | -import path from 'node:path' |
3 | | -// @ts-expect-error: hush |
4 | | -import gitDiffTree from 'git-diff-tree' |
5 | | -import {findUp} from 'vfile-find-up' |
6 | | - |
7 | | -const own = {}.hasOwnProperty |
8 | | - |
9 | | -/** @type {string} */ |
10 | | -let previousRange |
11 | | - |
12 | | -/** @type {import('unified').Plugin<[]>} */ |
13 | | -export default function diff() { |
14 | | - /** @type {Record<string, string>} */ |
15 | | - let cache = {} |
16 | | - |
17 | | - return function (_, file, next) { |
18 | | - const base = file.dirname |
19 | | - /** @type {string|undefined} */ |
20 | | - let commitRange |
21 | | - /** @type {Array<string>|undefined} */ |
22 | | - let range |
23 | | - |
24 | | - // Looks like Travis. |
25 | | - if (process.env.TRAVIS_COMMIT_RANGE) { |
26 | | - commitRange = process.env.TRAVIS_COMMIT_RANGE |
27 | | - range = commitRange.split(/\.{3}/) |
28 | | - } |
29 | | - // Looks like GH Actions. |
30 | | - 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 |
42 | | - commitRange = range.join('...') |
43 | | - } |
44 | | - |
45 | | - if ( |
46 | | - !base || |
47 | | - !commitRange || |
48 | | - !range || |
49 | | - !file.dirname || |
50 | | - range.length !== 2 |
51 | | - ) { |
52 | | - return next() |
53 | | - } |
54 | | - |
55 | | - if (commitRange !== previousRange) { |
56 | | - cache = {} |
57 | | - previousRange = commitRange |
58 | | - } |
59 | | - |
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 | | - } |
74 | | - |
75 | | - cache[base] = git.dirname |
76 | | - tick(git.dirname) |
77 | | - }) |
78 | | - } |
79 | | - |
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] |
92 | | - }) |
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 | | - } |
130 | | - } else { |
131 | | - position = undefined |
132 | | - } |
133 | | - } |
134 | | - |
135 | | - const fp = path.resolve(root, data.bPath) |
136 | | - |
137 | | - // Long diffs. |
138 | | - /* c8 ignore next */ |
139 | | - if (!(fp in diffs)) diffs[fp] = [] |
140 | | - |
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() |
152 | | - } |
153 | | - |
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 | | - } |
167 | | -} |
| 1 | +export {default} from './lib/index.js' |
0 commit comments