@descent-vtt/spec-guard 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +150 -0
- package/README.md +262 -36
- package/dist/cli.d.ts +3 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +27 -3
- package/dist/cli.js.map +1 -1
- package/dist/comments.d.ts +78 -0
- package/dist/comments.d.ts.map +1 -0
- package/dist/comments.js +241 -0
- package/dist/comments.js.map +1 -0
- package/dist/engine.d.ts +62 -40
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +236 -186
- package/dist/engine.js.map +1 -1
- package/dist/glob.d.ts +34 -6
- package/dist/glob.d.ts.map +1 -1
- package/dist/glob.js +50 -28
- package/dist/glob.js.map +1 -1
- package/dist/imports.d.ts +94 -0
- package/dist/imports.d.ts.map +1 -0
- package/dist/imports.js +492 -0
- package/dist/imports.js.map +1 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +24 -2
- package/dist/parser.js.map +1 -1
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +67 -2
- package/dist/reporter.js.map +1 -1
- package/dist/runner.d.ts +23 -3
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +274 -73
- package/dist/runner.js.map +1 -1
- package/dist/scope.d.ts +108 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +115 -0
- package/dist/scope.js.map +1 -0
- package/dist/types.d.ts +47 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -6
- package/dist/types.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACzF,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGtF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC;AACzB,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAC7B,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comment classification.
|
|
3
|
+
*
|
|
4
|
+
* A documented codebase defeats a plain text search in the most ironic way
|
|
5
|
+
* available: the comment recording that a symbol was deleted is itself a match
|
|
6
|
+
* for that symbol. `@assert-absence symbol="LegacyThing"` then fails on
|
|
7
|
+
*
|
|
8
|
+
* // LegacyThing was removed in ADR-398
|
|
9
|
+
*
|
|
10
|
+
* which is the sentence proving the assertion true. So matches are classified
|
|
11
|
+
* and, by default, those inside comments do not count.
|
|
12
|
+
*
|
|
13
|
+
* The scanner tracks strings as well as comments, and it has to: a `//` inside
|
|
14
|
+
* a string literal is not a comment, and misreading one is the dangerous
|
|
15
|
+
* direction. Consider
|
|
16
|
+
*
|
|
17
|
+
* const url = "http://example.com"; const x = LegacyThing;
|
|
18
|
+
*
|
|
19
|
+
* Treating `//` in that URL as a comment start would hide a real use of
|
|
20
|
+
* LegacyThing and turn a violation into a silent pass. Every uncertainty here
|
|
21
|
+
* therefore resolves the same way: **when in doubt, it is code**. A match
|
|
22
|
+
* wrongly kept is a visible failure someone can argue with; a match wrongly
|
|
23
|
+
* dropped is a lie.
|
|
24
|
+
*
|
|
25
|
+
* Languages it does not recognise get no comment ranges at all, so their
|
|
26
|
+
* matches all count - and the result says so rather than implying the file was
|
|
27
|
+
* understood.
|
|
28
|
+
*/
|
|
29
|
+
interface StringRule {
|
|
30
|
+
open: string;
|
|
31
|
+
close: string;
|
|
32
|
+
/** Backslash escapes apply inside. */
|
|
33
|
+
escape: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface CommentSyntax {
|
|
36
|
+
name: string;
|
|
37
|
+
/** Prefixes that comment out the rest of the line. */
|
|
38
|
+
line: readonly string[];
|
|
39
|
+
/** Delimiter pairs for block comments. */
|
|
40
|
+
block: ReadonlyArray<readonly [string, string]>;
|
|
41
|
+
/** Block comments nest, as in Rust. */
|
|
42
|
+
nested: boolean;
|
|
43
|
+
/** Literal forms that may contain comment-looking text. */
|
|
44
|
+
strings: readonly StringRule[];
|
|
45
|
+
}
|
|
46
|
+
/** The comment syntax for a path, or null when the language is unknown. */
|
|
47
|
+
export declare function syntaxFor(filePath: string): CommentSyntax | null;
|
|
48
|
+
/** Half-open [start, end) offsets that are comment text. */
|
|
49
|
+
export type CommentRange = readonly [number, number];
|
|
50
|
+
/**
|
|
51
|
+
* Finds every comment in the source.
|
|
52
|
+
*
|
|
53
|
+
* One pass, tracking strings so that comment markers inside them are ignored.
|
|
54
|
+
* An unterminated comment or string runs to the end of the file, which matches
|
|
55
|
+
* how a compiler would read it.
|
|
56
|
+
*
|
|
57
|
+
* The scan is written so that it cannot stand still: whatever the branches
|
|
58
|
+
* below decide, the loop advances by at least one character. That guarantee is
|
|
59
|
+
* the difference between a wrong answer and a hang, and this loop appends as it
|
|
60
|
+
* goes - standing still here would not spin, it would eat memory until the
|
|
61
|
+
* process died, on somebody's file, in somebody's CI.
|
|
62
|
+
*/
|
|
63
|
+
export declare function commentRanges(source: string, syntax: CommentSyntax): CommentRange[];
|
|
64
|
+
/**
|
|
65
|
+
* Decides whether a byte offset falls inside a comment.
|
|
66
|
+
*
|
|
67
|
+
* `classified` is false when the language is unknown, in which case nothing is
|
|
68
|
+
* treated as a comment. Callers report that rather than implying the file was
|
|
69
|
+
* understood.
|
|
70
|
+
*/
|
|
71
|
+
export interface CommentMask {
|
|
72
|
+
readonly classified: boolean;
|
|
73
|
+
readonly syntax: string | null;
|
|
74
|
+
isComment(offset: number): boolean;
|
|
75
|
+
}
|
|
76
|
+
export declare function createCommentMask(source: string, filePath: string): CommentMask;
|
|
77
|
+
export {};
|
|
78
|
+
//# sourceMappingURL=comments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../src/comments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,0CAA0C;IAC1C,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,uCAAuC;IACvC,MAAM,EAAE,OAAO,CAAC;IAChB,2DAA2D;IAC3D,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;CAChC;AAuHD,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAEhE;AAED,4DAA4D;AAC5D,MAAM,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAqCrD;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,YAAY,EAAE,CAkCnF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;CACpC;AAQD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAqB/E"}
|
package/dist/comments.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comment classification.
|
|
3
|
+
*
|
|
4
|
+
* A documented codebase defeats a plain text search in the most ironic way
|
|
5
|
+
* available: the comment recording that a symbol was deleted is itself a match
|
|
6
|
+
* for that symbol. `@assert-absence symbol="LegacyThing"` then fails on
|
|
7
|
+
*
|
|
8
|
+
* // LegacyThing was removed in ADR-398
|
|
9
|
+
*
|
|
10
|
+
* which is the sentence proving the assertion true. So matches are classified
|
|
11
|
+
* and, by default, those inside comments do not count.
|
|
12
|
+
*
|
|
13
|
+
* The scanner tracks strings as well as comments, and it has to: a `//` inside
|
|
14
|
+
* a string literal is not a comment, and misreading one is the dangerous
|
|
15
|
+
* direction. Consider
|
|
16
|
+
*
|
|
17
|
+
* const url = "http://example.com"; const x = LegacyThing;
|
|
18
|
+
*
|
|
19
|
+
* Treating `//` in that URL as a comment start would hide a real use of
|
|
20
|
+
* LegacyThing and turn a violation into a silent pass. Every uncertainty here
|
|
21
|
+
* therefore resolves the same way: **when in doubt, it is code**. A match
|
|
22
|
+
* wrongly kept is a visible failure someone can argue with; a match wrongly
|
|
23
|
+
* dropped is a lie.
|
|
24
|
+
*
|
|
25
|
+
* Languages it does not recognise get no comment ranges at all, so their
|
|
26
|
+
* matches all count - and the result says so rather than implying the file was
|
|
27
|
+
* understood.
|
|
28
|
+
*/
|
|
29
|
+
import path from 'node:path';
|
|
30
|
+
const QUOTES = [
|
|
31
|
+
{ open: '"', close: '"', escape: true },
|
|
32
|
+
{ open: "'", close: "'", escape: true },
|
|
33
|
+
];
|
|
34
|
+
const C_LIKE = {
|
|
35
|
+
name: 'c-like',
|
|
36
|
+
line: ['//'],
|
|
37
|
+
block: [['/*', '*/']],
|
|
38
|
+
nested: false,
|
|
39
|
+
strings: QUOTES,
|
|
40
|
+
};
|
|
41
|
+
const JS_LIKE = {
|
|
42
|
+
...C_LIKE,
|
|
43
|
+
name: 'javascript',
|
|
44
|
+
strings: [...QUOTES, { open: '`', close: '`', escape: true }],
|
|
45
|
+
};
|
|
46
|
+
const C_SHARP = {
|
|
47
|
+
...C_LIKE,
|
|
48
|
+
name: 'c#',
|
|
49
|
+
// Verbatim strings come first so @" wins over ".
|
|
50
|
+
strings: [{ open: '@"', close: '"', escape: false }, ...QUOTES],
|
|
51
|
+
};
|
|
52
|
+
const RUST = {
|
|
53
|
+
name: 'rust',
|
|
54
|
+
line: ['//'],
|
|
55
|
+
block: [['/*', '*/']],
|
|
56
|
+
// Rust block comments nest, so /* /* */ */ is one comment, not one and a half.
|
|
57
|
+
nested: true,
|
|
58
|
+
strings: [
|
|
59
|
+
{ open: 'r#"', close: '"#', escape: false },
|
|
60
|
+
{ open: 'r"', close: '"', escape: false },
|
|
61
|
+
...QUOTES,
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
const GO = {
|
|
65
|
+
...C_LIKE,
|
|
66
|
+
name: 'go',
|
|
67
|
+
strings: [...QUOTES, { open: '`', close: '`', escape: false }],
|
|
68
|
+
};
|
|
69
|
+
const HASH = {
|
|
70
|
+
name: 'hash',
|
|
71
|
+
line: ['#'],
|
|
72
|
+
block: [],
|
|
73
|
+
nested: false,
|
|
74
|
+
// Triple quotes first: a Python docstring is a string, not a comment, and
|
|
75
|
+
// treating it as code is the conservative reading.
|
|
76
|
+
strings: [
|
|
77
|
+
{ open: '"""', close: '"""', escape: true },
|
|
78
|
+
{ open: "'''", close: "'''", escape: true },
|
|
79
|
+
...QUOTES,
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
const SQL_LIKE = {
|
|
83
|
+
name: 'sql-like',
|
|
84
|
+
line: ['--'],
|
|
85
|
+
block: [['/*', '*/']],
|
|
86
|
+
nested: false,
|
|
87
|
+
strings: QUOTES,
|
|
88
|
+
};
|
|
89
|
+
const MARKUP = {
|
|
90
|
+
name: 'markup',
|
|
91
|
+
line: [],
|
|
92
|
+
block: [['<!--', '-->']],
|
|
93
|
+
nested: false,
|
|
94
|
+
strings: [],
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Formats with no comments at all.
|
|
98
|
+
*
|
|
99
|
+
* Worth naming rather than leaving unknown: "this file has no comments" is
|
|
100
|
+
* something we know about JSON, and saying "I could not read it" instead would
|
|
101
|
+
* attach a caveat to every match in a package.json. Dialects that do allow
|
|
102
|
+
* comments in practice - tsconfig.json and its `//` lines - simply have them
|
|
103
|
+
* counted as code, which is the direction that fails loudly.
|
|
104
|
+
*/
|
|
105
|
+
const NO_COMMENTS = {
|
|
106
|
+
name: 'none',
|
|
107
|
+
line: [],
|
|
108
|
+
block: [],
|
|
109
|
+
nested: false,
|
|
110
|
+
strings: [],
|
|
111
|
+
};
|
|
112
|
+
/** Extension to comment syntax. Anything absent is left unclassified. */
|
|
113
|
+
const BY_EXTENSION = new Map([
|
|
114
|
+
...['.js', '.mjs', '.cjs', '.jsx', '.ts', '.mts', '.cts', '.tsx'].map((extension) => [extension, JS_LIKE]),
|
|
115
|
+
...['.c', '.h', '.cc', '.cpp', '.cxx', '.hpp', '.java', '.kt', '.kts', '.scala', '.swift', '.dart', '.php', '.m', '.mm', '.zig'].map((extension) => [extension, C_LIKE]),
|
|
116
|
+
...['.cs', '.csx'].map((extension) => [extension, C_SHARP]),
|
|
117
|
+
...['.rs'].map((extension) => [extension, RUST]),
|
|
118
|
+
...['.go'].map((extension) => [extension, GO]),
|
|
119
|
+
...['.py', '.pyi', '.rb', '.sh', '.bash', '.zsh', '.yaml', '.yml', '.toml', '.tf', '.pl', '.r'].map((extension) => [extension, HASH]),
|
|
120
|
+
...['.sql', '.lua', '.hs', '.elm'].map((extension) => [extension, SQL_LIKE]),
|
|
121
|
+
...['.html', '.htm', '.xml', '.svg', '.vue', '.svelte', '.md', '.markdown'].map((extension) => [extension, MARKUP]),
|
|
122
|
+
// .jsonc is named for the comments it allows, so it gets the C-style reader.
|
|
123
|
+
...['.jsonc'].map((extension) => [extension, C_LIKE]),
|
|
124
|
+
...['.json', '.txt', '.csv', '.tsv', '.lock', '.log'].map((extension) => [extension, NO_COMMENTS]),
|
|
125
|
+
]);
|
|
126
|
+
/** The comment syntax for a path, or null when the language is unknown. */
|
|
127
|
+
export function syntaxFor(filePath) {
|
|
128
|
+
return BY_EXTENSION.get(path.extname(filePath).toLowerCase()) ?? null;
|
|
129
|
+
}
|
|
130
|
+
/** Where the literal opened at `at` ends, or the end of the file. */
|
|
131
|
+
function endOfString(source, at, rule) {
|
|
132
|
+
let index = at + rule.open.length;
|
|
133
|
+
while (index < source.length) {
|
|
134
|
+
if (rule.escape && source[index] === '\\') {
|
|
135
|
+
index += 2;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (source.startsWith(rule.close, index))
|
|
139
|
+
return index + rule.close.length;
|
|
140
|
+
index += 1;
|
|
141
|
+
}
|
|
142
|
+
return source.length;
|
|
143
|
+
}
|
|
144
|
+
/** Where the block comment opened at `at` ends, or the end of the file. */
|
|
145
|
+
function endOfBlock(source, at, pair, nested) {
|
|
146
|
+
const [open, close] = pair;
|
|
147
|
+
let index = at + open.length;
|
|
148
|
+
let depth = 1;
|
|
149
|
+
while (index < source.length && depth > 0) {
|
|
150
|
+
if (nested && source.startsWith(open, index)) {
|
|
151
|
+
depth += 1;
|
|
152
|
+
index += open.length;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (source.startsWith(close, index)) {
|
|
156
|
+
depth -= 1;
|
|
157
|
+
index += close.length;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
index += 1;
|
|
161
|
+
}
|
|
162
|
+
return index;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Finds every comment in the source.
|
|
166
|
+
*
|
|
167
|
+
* One pass, tracking strings so that comment markers inside them are ignored.
|
|
168
|
+
* An unterminated comment or string runs to the end of the file, which matches
|
|
169
|
+
* how a compiler would read it.
|
|
170
|
+
*
|
|
171
|
+
* The scan is written so that it cannot stand still: whatever the branches
|
|
172
|
+
* below decide, the loop advances by at least one character. That guarantee is
|
|
173
|
+
* the difference between a wrong answer and a hang, and this loop appends as it
|
|
174
|
+
* goes - standing still here would not spin, it would eat memory until the
|
|
175
|
+
* process died, on somebody's file, in somebody's CI.
|
|
176
|
+
*/
|
|
177
|
+
export function commentRanges(source, syntax) {
|
|
178
|
+
const ranges = [];
|
|
179
|
+
let index = 0;
|
|
180
|
+
while (index < source.length) {
|
|
181
|
+
const start = index;
|
|
182
|
+
const startsWith = (token) => source.startsWith(token, start);
|
|
183
|
+
// First match wins, in this order: a literal hides comment markers inside
|
|
184
|
+
// it, and a line comment hides a block opener on the same line. Each lookup
|
|
185
|
+
// sits in the branch that needs it, so none of them runs speculatively.
|
|
186
|
+
const stringRule = syntax.strings.find((rule) => startsWith(rule.open));
|
|
187
|
+
if (stringRule) {
|
|
188
|
+
index = endOfString(source, start, stringRule);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
const lineToken = syntax.line.find((token) => startsWith(token));
|
|
192
|
+
if (lineToken !== undefined) {
|
|
193
|
+
const newline = source.indexOf('\n', start);
|
|
194
|
+
index = newline === -1 ? source.length : newline;
|
|
195
|
+
ranges.push([start, index]);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
const blockPair = syntax.block.find(([open]) => startsWith(open));
|
|
199
|
+
if (blockPair) {
|
|
200
|
+
index = endOfBlock(source, start, blockPair, syntax.nested);
|
|
201
|
+
ranges.push([start, index]);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// The single advance point, and the only place termination depends on.
|
|
206
|
+
if (index <= start)
|
|
207
|
+
index = start + 1;
|
|
208
|
+
}
|
|
209
|
+
return ranges;
|
|
210
|
+
}
|
|
211
|
+
const UNCLASSIFIED = {
|
|
212
|
+
classified: false,
|
|
213
|
+
syntax: null,
|
|
214
|
+
isComment: () => false,
|
|
215
|
+
};
|
|
216
|
+
export function createCommentMask(source, filePath) {
|
|
217
|
+
const syntax = syntaxFor(filePath);
|
|
218
|
+
if (!syntax)
|
|
219
|
+
return UNCLASSIFIED;
|
|
220
|
+
const ranges = commentRanges(source, syntax);
|
|
221
|
+
return {
|
|
222
|
+
classified: true,
|
|
223
|
+
syntax: syntax.name,
|
|
224
|
+
isComment(offset) {
|
|
225
|
+
let low = 0;
|
|
226
|
+
let high = ranges.length - 1;
|
|
227
|
+
while (low <= high) {
|
|
228
|
+
const mid = (low + high) >> 1;
|
|
229
|
+
const [start, end] = ranges[mid];
|
|
230
|
+
if (offset < start)
|
|
231
|
+
high = mid - 1;
|
|
232
|
+
else if (offset >= end)
|
|
233
|
+
low = mid + 1;
|
|
234
|
+
else
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
return false;
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comments.js","sourceRoot":"","sources":["../src/comments.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.d.ts
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Search engines.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* There is one set of semantics here, not two. The scanner reads files, counts
|
|
5
|
+
* matches, classifies comments, decides what is binary and keeps the ledger of
|
|
6
|
+
* what it could not inspect. ripgrep answers one question - which files under
|
|
7
|
+
* these targets contain this text at all - and everything downstream of that is
|
|
8
|
+
* the scanner's work, for both engines.
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
10
|
+
* It was not always so, and the reason for the rewrite is worth keeping: the
|
|
11
|
+
* two used to be independent implementations, and they drifted. On a tree with
|
|
12
|
+
* eight copies of one symbol the scanner found two and ripgrep found four,
|
|
13
|
+
* because one honoured .gitignore and the other did not, one skipped a
|
|
14
|
+
* hardcoded list of directory names and the other did not, and they disagreed
|
|
15
|
+
* about binary files. Nothing in the output said so. See ADR-0007.
|
|
11
16
|
*
|
|
12
17
|
* Both engines expose a batch API. Scanning a tree costs the same whether you
|
|
13
18
|
* look for one symbol or twenty, so assertions that share a target set and
|
|
14
|
-
* flags are answered by a single pass.
|
|
15
|
-
* eight assertions cost ~280ms as eight ripgrep passes and ~90ms as one.
|
|
19
|
+
* flags are answered by a single pass.
|
|
16
20
|
*/
|
|
21
|
+
import { type CommentMask } from './comments.js';
|
|
22
|
+
import { type SkippedPath } from './scope.js';
|
|
17
23
|
import type { EngineName, MatchLocation, SearchOptions, SearchResult } from './types.js';
|
|
18
24
|
/** Files larger than this are skipped by both engines, keeping them in sync. */
|
|
19
25
|
export declare const MAX_FILE_SIZE: number;
|
|
@@ -37,52 +43,56 @@ export interface Engine {
|
|
|
37
43
|
}
|
|
38
44
|
export type EnginePreference = 'auto' | 'ripgrep' | 'javascript';
|
|
39
45
|
export declare function escapeRegExp(value: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Orders matches by path, then by line. ripgrep searches in parallel and emits
|
|
48
|
+
* files in no fixed order, so this is what makes snippet output stable.
|
|
49
|
+
*/
|
|
50
|
+
export declare function sortLocations(locations: MatchLocation[]): MatchLocation[];
|
|
40
51
|
/** Per-pattern accumulator used by both engines. */
|
|
41
52
|
interface Tally {
|
|
42
53
|
count: number;
|
|
43
54
|
locations: MatchLocation[];
|
|
55
|
+
/** Matches skipped because they were inside a comment. */
|
|
56
|
+
commentCount: number;
|
|
44
57
|
}
|
|
45
|
-
/**
|
|
46
|
-
* True when no two of these literals can ever match overlapping text, which is
|
|
47
|
-
* exactly when merging them into one ripgrep alternation is safe.
|
|
48
|
-
*
|
|
49
|
-
* Two ways an alternation loses a match that separate passes would find:
|
|
50
|
-
* containment - ["Primary", "PrimaryButton"] on "PrimaryButton"; and
|
|
51
|
-
* dovetailing - ["abc", "cd"] on "abcd", where the scan resumes past "cd".
|
|
52
|
-
* Rejecting both leaves batching indistinguishable from separate passes.
|
|
53
|
-
*/
|
|
54
|
-
export declare function canBatchLiterals(patterns: readonly string[]): boolean;
|
|
55
58
|
/** Runs requests through the batch API when the engine has one. */
|
|
56
59
|
export declare function runSearches(engine: Engine, requests: SearchRequest[]): Promise<SearchResult[]>;
|
|
57
60
|
export declare function resetRipgrepProbe(): void;
|
|
58
61
|
/** Locates a usable `rg` binary. `SPEC_GUARD_RG` overrides PATH lookup. */
|
|
59
62
|
export declare function findRipgrep(): Promise<string | null>;
|
|
60
|
-
/** Builds the argv for one ripgrep pass over `patterns`. */
|
|
61
|
-
export declare function buildRipgrepArgs(request: SearchRequest, patterns?: readonly string[]): string[];
|
|
62
63
|
/**
|
|
63
|
-
*
|
|
64
|
+
* Builds the argv that makes ripgrep walk exactly what the scanner walks.
|
|
65
|
+
*
|
|
66
|
+
* Almost every flag here switches off an opinion. ripgrep's defaults are
|
|
67
|
+
* excellent for a developer grepping their own checkout and wrong for a rule
|
|
68
|
+
* about a repository:
|
|
64
69
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
70
|
+
* --hidden `.github`, `.husky` and `.claude-rules` hold real code, and
|
|
71
|
+
* leaving them out let an absence assertion pass while the
|
|
72
|
+
* forbidden thing sat in a workflow file.
|
|
73
|
+
* --no-ignore .gitignore says what git should carry, not what a rule
|
|
74
|
+
* covers - and it applies only inside a git repository, so the
|
|
75
|
+
* same tree answered differently depending on whether a .git
|
|
76
|
+
* directory happened to exist above it.
|
|
77
|
+
*
|
|
78
|
+
* What remains is spec-guard's own scope policy, passed as exclusions so that
|
|
79
|
+
* both engines skip the same four names for the same reasons. Those come last
|
|
80
|
+
* because ripgrep lets a later glob override an earlier one, and a policy skip
|
|
81
|
+
* must not be undone by a user's `glob="*.ts"`.
|
|
69
82
|
*/
|
|
70
|
-
export declare function
|
|
71
|
-
export interface RipgrepSink {
|
|
72
|
-
/** Feeds one line of ripgrep --json output. */
|
|
73
|
-
line(text: string): void;
|
|
74
|
-
readonly tallies: Map<string, Tally>;
|
|
75
|
-
readonly failure: Error | null;
|
|
76
|
-
}
|
|
83
|
+
export declare function buildRipgrepArgs(request: SearchRequest, patterns?: readonly string[]): string[];
|
|
77
84
|
/**
|
|
78
|
-
*
|
|
85
|
+
* Pulls the paths out of ripgrep's stderr.
|
|
79
86
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
87
|
+
* `--no-messages` used to be passed, which threw these away: a file ripgrep
|
|
88
|
+
* could not open produced no match, no error and no difference from a file that
|
|
89
|
+
* was searched and found clean. ripgrep writes one line per failure, as
|
|
90
|
+
* `path: reason`, and the path is what the report needs.
|
|
91
|
+
*
|
|
92
|
+
* Anything that does not parse is returned as-is rather than dropped, on the
|
|
93
|
+
* principle that an unexplained line from a subprocess is still information.
|
|
84
94
|
*/
|
|
85
|
-
export declare function
|
|
95
|
+
export declare function parseRipgrepErrors(stderr: string): string[];
|
|
86
96
|
/** Builds the matching RegExp shared by the JS engine. */
|
|
87
97
|
export declare function buildJsRegExp(symbol: string, options: SearchOptions): RegExp;
|
|
88
98
|
interface CandidateFile {
|
|
@@ -94,6 +104,8 @@ export interface Enumeration {
|
|
|
94
104
|
/** True when the walk stopped early because the budget was reached. */
|
|
95
105
|
exceeded: boolean;
|
|
96
106
|
bytes: number;
|
|
107
|
+
/** Paths the walk declined to inspect, with the reason for each. */
|
|
108
|
+
skipped: SkippedPath[];
|
|
97
109
|
}
|
|
98
110
|
export interface EnumerationBudget {
|
|
99
111
|
maxFiles: number;
|
|
@@ -109,11 +121,18 @@ export interface EnumerationBudget {
|
|
|
109
121
|
*/
|
|
110
122
|
export declare function enumerateCandidates(request: SearchRequest, budget?: EnumerationBudget): Promise<Enumeration>;
|
|
111
123
|
/** Counts matches in one file and records per-line snippets. */
|
|
112
|
-
export declare function scanContent(content: string, relativePath: string, regexp: RegExp
|
|
124
|
+
export declare function scanContent(content: string, relativePath: string, regexp: RegExp,
|
|
125
|
+
/**
|
|
126
|
+
* Resolved lazily, and only once the file has produced a match. Classifying
|
|
127
|
+
* every file in scope would mean a comment scan of the whole tree; this way
|
|
128
|
+
* the cost is proportional to matches, which for an architecture assertion is
|
|
129
|
+
* usually zero.
|
|
130
|
+
*/
|
|
131
|
+
getMask?: () => CommentMask): Tally;
|
|
113
132
|
/** An engine that is guaranteed to implement the batch API. */
|
|
114
133
|
export type BatchEngine = Engine & Required<Pick<Engine, 'searchBatch'>> & {
|
|
115
134
|
/** Scans a file list that the caller already enumerated. */
|
|
116
|
-
searchFiles(files: readonly CandidateFile[], requests: SearchRequest[]): Promise<SearchResult[]>;
|
|
135
|
+
searchFiles(files: readonly CandidateFile[], requests: SearchRequest[], unreadable?: readonly string[], skipped?: readonly SkippedPath[]): Promise<SearchResult[]>;
|
|
117
136
|
};
|
|
118
137
|
export declare const javascriptEngine: BatchEngine;
|
|
119
138
|
/**
|
|
@@ -133,6 +152,9 @@ export declare const javascriptEngine: BatchEngine;
|
|
|
133
152
|
* Windows avoids a spawn that would cost ten times the whole search.
|
|
134
153
|
*/
|
|
135
154
|
export declare const SMALL_TREE_BUDGET: EnumerationBudget;
|
|
155
|
+
/** Exported so the code list can be asserted; EACCES and friends are not
|
|
156
|
+
* reproducible on demand from a real spawn. */
|
|
157
|
+
export declare function isMissingBinary(error: unknown): boolean;
|
|
136
158
|
/** Resolves the engine to use, honouring an explicit preference. */
|
|
137
159
|
export declare function resolveEngine(preference?: EnginePreference): Promise<Engine>;
|
|
138
160
|
export type CachedEngine = Engine & {
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|