vitest-auto-spy 5.26.0 → 5.27.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 +28 -1
- package/dist/cli.js +144 -45
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,32 @@ The latest released version here must always match the one published on
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [5.27.0] - 2026-09-23
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **The `doctor` and `codemod` repository scan honours the root `.gitignore` for directories.** The
|
|
18
|
+
hardcoded skip list only knew the usual build and store names, so a repository's own generated or
|
|
19
|
+
local output (`src/generated/`, `/reports`, `tmp-*/`) was read as source: counted in the report,
|
|
20
|
+
walked by the import graph and offered to `codemod --write`. The file is parsed directly, with no
|
|
21
|
+
`git` process and no dependency: comments, `!` negations with the last match winning, anchored and
|
|
22
|
+
unanchored patterns, `*`, `?`, `**` and bracket classes. Only the root file and only directories
|
|
23
|
+
are honoured — an ignored file is still listed. A pattern with a `\` escape or a POSIX class is
|
|
24
|
+
dropped, which only means scanning more, and a `!` rule that cannot be read disables the whole
|
|
25
|
+
file rather than prune a directory git keeps. `perf-merge`'s report listing is unaffected.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- **The `tsconfig` glob check no longer reports a pattern it could never see matched.** A pattern
|
|
30
|
+
inside a skipped directory below the root (`libs/app/out-tsc/**/*.ts`) or inside a directory the
|
|
31
|
+
root `.gitignore` excludes is exempt, and so is a `files` entry there — both were reported as
|
|
32
|
+
matching nothing or missing, although the scan had simply not descended into them.
|
|
33
|
+
- **The `prefer-create-spy-from-class` reference lists every factory whose arguments are exempt.**
|
|
34
|
+
The built-in doubles and stubs (`createComponentStub`, `createDirectiveHost`, `createWindowDouble`,
|
|
35
|
+
`provideRouterDouble` and the rest) were exempt in the rule but missing from the docs, which also
|
|
36
|
+
still said "four shapes" over a list of eleven. The Russian page gained the four exemptions it was
|
|
37
|
+
missing (`vi.hoisted`, the options bag, `createFixture`, an RxJS observer).
|
|
38
|
+
|
|
13
39
|
## [5.26.0] - 2026-09-23
|
|
14
40
|
|
|
15
41
|
### Changed
|
|
@@ -7770,7 +7796,8 @@ by hand there, in more than one place, by more than one person.
|
|
|
7770
7796
|
`mockAccessorsProp`.
|
|
7771
7797
|
- Dual ESM + CJS build with type declarations; 100% test coverage.
|
|
7772
7798
|
|
|
7773
|
-
[Unreleased]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.
|
|
7799
|
+
[Unreleased]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.27.0...HEAD
|
|
7800
|
+
[5.27.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.26.0...v5.27.0
|
|
7774
7801
|
[5.26.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.25.0...v5.26.0
|
|
7775
7802
|
[5.25.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.24.0...v5.25.0
|
|
7776
7803
|
[5.24.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.23.0...v5.24.0
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { resolve, join, dirname,
|
|
3
|
-
import { readFileSync, statSync, mkdirSync, writeFileSync,
|
|
2
|
+
import { resolve, join, dirname, posix, isAbsolute, relative, sep } from 'node:path';
|
|
3
|
+
import { readFileSync, statSync, mkdirSync, writeFileSync, existsSync, lstatSync, rmSync, readdirSync } from 'node:fs';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { createRequire } from 'node:module';
|
|
@@ -98,6 +98,84 @@ function flagEnabled(args, name) {
|
|
|
98
98
|
}
|
|
99
99
|
return value !== "false";
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
// src/cli/gitignore.ts
|
|
103
|
+
function parseGitignore(text) {
|
|
104
|
+
const rules = [];
|
|
105
|
+
for (const raw of text.split("\n")) {
|
|
106
|
+
const line = raw.trimEnd();
|
|
107
|
+
if (line === "" || line.startsWith("#")) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const negated = line.startsWith("!");
|
|
111
|
+
const pattern = toRegExp(negated ? line.slice(1) : line);
|
|
112
|
+
if (pattern !== void 0) {
|
|
113
|
+
rules.push({ negated, pattern });
|
|
114
|
+
} else if (negated) {
|
|
115
|
+
return void 0;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return rules;
|
|
119
|
+
}
|
|
120
|
+
function isIgnoredDirectory(rules, path) {
|
|
121
|
+
let ignored = false;
|
|
122
|
+
for (const rule of rules) {
|
|
123
|
+
if (rule.pattern.test(path)) {
|
|
124
|
+
ignored = !rule.negated;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return ignored;
|
|
128
|
+
}
|
|
129
|
+
function toRegExp(line) {
|
|
130
|
+
const body = line.replace(/\/+$/, "");
|
|
131
|
+
if (body === "" || line.includes("\\")) {
|
|
132
|
+
return void 0;
|
|
133
|
+
}
|
|
134
|
+
const anchored = body.includes("/");
|
|
135
|
+
const segments = (body.startsWith("/") ? body.slice(1) : body).split("/");
|
|
136
|
+
let source = "";
|
|
137
|
+
for (const [index, segment] of segments.entries()) {
|
|
138
|
+
const last = index === segments.length - 1;
|
|
139
|
+
if (segment === "**") {
|
|
140
|
+
source += last ? ".*" : "(?:[^/]+/)*";
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const converted = segmentSource(segment);
|
|
144
|
+
if (converted === void 0) {
|
|
145
|
+
return void 0;
|
|
146
|
+
}
|
|
147
|
+
source += last ? converted : `${converted}/`;
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
return new RegExp(`^${anchored ? "" : "(?:.*/)?"}${source}$`);
|
|
151
|
+
} catch {
|
|
152
|
+
return void 0;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function segmentSource(segment) {
|
|
156
|
+
let source = "";
|
|
157
|
+
let index = 0;
|
|
158
|
+
while (index < segment.length) {
|
|
159
|
+
const char = segment.charAt(index);
|
|
160
|
+
if (char === "[") {
|
|
161
|
+
const negated = /^[!^]/.test(segment.slice(index + 1));
|
|
162
|
+
const start = index + (negated ? 2 : 1);
|
|
163
|
+
const end = segment.indexOf("]", start + 1);
|
|
164
|
+
const members = segment.slice(start, end);
|
|
165
|
+
if (end === -1 || members.includes("[")) {
|
|
166
|
+
return void 0;
|
|
167
|
+
}
|
|
168
|
+
source += `[${negated ? "^" : ""}${members.replace(/[\]^]/g, "\\$&")}]`;
|
|
169
|
+
index = end + 1;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
source += char === "*" ? "[^/]*" : char === "?" ? "[^/]" : char.replace(/[$()+.^{|}]/, "\\$&");
|
|
173
|
+
index += 1;
|
|
174
|
+
}
|
|
175
|
+
return source;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/cli/fs-scan.ts
|
|
101
179
|
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
102
180
|
".angular",
|
|
103
181
|
".bun",
|
|
@@ -174,18 +252,25 @@ function removeFile(path) {
|
|
|
174
252
|
rmSync(path, { force: true });
|
|
175
253
|
}
|
|
176
254
|
function listRepositoryFiles(root, limit = scanCap()) {
|
|
177
|
-
return
|
|
255
|
+
return scan(root, limit, []).files;
|
|
178
256
|
}
|
|
179
257
|
function scanRepository(root, limit = scanCap()) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
258
|
+
return scan(root, limit, readGitignoreRules(root));
|
|
259
|
+
}
|
|
260
|
+
function scan(root, limit, rules) {
|
|
261
|
+
const walker = { root, rules, found: [], ignored: [], limit };
|
|
262
|
+
const truncated = walk(walker, root);
|
|
263
|
+
return { files: walker.found.sort(), ignored: walker.ignored.sort(), truncated };
|
|
264
|
+
}
|
|
265
|
+
function readGitignoreRules(root) {
|
|
266
|
+
const text = readTextFile(join(root, ".gitignore"));
|
|
267
|
+
return (text === void 0 ? void 0 : parseGitignore(text)) ?? [];
|
|
183
268
|
}
|
|
184
269
|
function isRepositoryRoot(directory) {
|
|
185
270
|
return existsSync(join(directory, ".git"));
|
|
186
271
|
}
|
|
187
|
-
function walk(
|
|
188
|
-
if (found.length >= limit) {
|
|
272
|
+
function walk(walker, directory) {
|
|
273
|
+
if (walker.found.length >= walker.limit) {
|
|
189
274
|
return true;
|
|
190
275
|
}
|
|
191
276
|
let entries;
|
|
@@ -195,22 +280,30 @@ function walk(root, directory, found, limit) {
|
|
|
195
280
|
return false;
|
|
196
281
|
}
|
|
197
282
|
for (const entry of entries) {
|
|
198
|
-
if (found.length >= limit) {
|
|
283
|
+
if (walker.found.length >= walker.limit) {
|
|
199
284
|
return true;
|
|
200
285
|
}
|
|
201
286
|
const full = join(directory, entry.name);
|
|
202
287
|
if (entry.isDirectory()) {
|
|
203
|
-
if (!isSkippedDirectory(entry.name) && !isRepositoryRoot(full) &&
|
|
288
|
+
if (!isSkippedDirectory(entry.name) && !isRepositoryRoot(full) && !isGitignored(walker, full) && walk(walker, full)) {
|
|
204
289
|
return true;
|
|
205
290
|
}
|
|
206
291
|
continue;
|
|
207
292
|
}
|
|
208
293
|
if (entry.isFile()) {
|
|
209
|
-
found.push(toPosix(relative(root, full)));
|
|
294
|
+
walker.found.push(toPosix(relative(walker.root, full)));
|
|
210
295
|
}
|
|
211
296
|
}
|
|
212
297
|
return false;
|
|
213
298
|
}
|
|
299
|
+
function isGitignored(walker, directory) {
|
|
300
|
+
const path = toPosix(relative(walker.root, directory));
|
|
301
|
+
if (!isIgnoredDirectory(walker.rules, path)) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
walker.ignored.push(path);
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
214
307
|
function parseJsonc(text) {
|
|
215
308
|
try {
|
|
216
309
|
return JSON.parse(stripJsonComments(text));
|
|
@@ -640,7 +733,7 @@ function readProfile(cwd) {
|
|
|
640
733
|
const scripts = stringMap(packageJson["scripts"]);
|
|
641
734
|
const runner = detectRunner(dependencies, scripts);
|
|
642
735
|
const framework = detectFramework(dependencies);
|
|
643
|
-
const
|
|
736
|
+
const scan3 = scanRepository(cwd);
|
|
644
737
|
return {
|
|
645
738
|
cwd,
|
|
646
739
|
runner,
|
|
@@ -651,8 +744,9 @@ function readProfile(cwd) {
|
|
|
651
744
|
setupFiles: detectSetupFiles(cwd),
|
|
652
745
|
dependencies,
|
|
653
746
|
scripts,
|
|
654
|
-
files:
|
|
655
|
-
filesTruncated:
|
|
747
|
+
files: scan3.files,
|
|
748
|
+
filesTruncated: scan3.truncated,
|
|
749
|
+
ignoredDirectories: scan3.ignored
|
|
656
750
|
};
|
|
657
751
|
}
|
|
658
752
|
var PACKAGE_NAME = "vitest-auto-spy";
|
|
@@ -1059,7 +1153,7 @@ function textOf(context, range) {
|
|
|
1059
1153
|
function group(groups, position) {
|
|
1060
1154
|
return groups.slice(position, position + 1).join("");
|
|
1061
1155
|
}
|
|
1062
|
-
function
|
|
1156
|
+
function scan2(masked, pattern) {
|
|
1063
1157
|
return [...masked.matchAll(pattern)].map((match) => ({ index: match.index, whole: group(match, 0), groups: [...match] }));
|
|
1064
1158
|
}
|
|
1065
1159
|
|
|
@@ -1139,7 +1233,7 @@ function replacement(start, end, text) {
|
|
|
1139
1233
|
function replacements(context, pattern, text) {
|
|
1140
1234
|
return {
|
|
1141
1235
|
...EMPTY_OUTPUT,
|
|
1142
|
-
edits:
|
|
1236
|
+
edits: scan2(context.masked, pattern).map((match) => ({
|
|
1143
1237
|
start: match.index,
|
|
1144
1238
|
end: match.index + match.whole.length,
|
|
1145
1239
|
text: text(match)
|
|
@@ -1182,7 +1276,7 @@ var jasmineStrategies = {
|
|
|
1182
1276
|
function withArgsOutput(context) {
|
|
1183
1277
|
const closes = spyOnCloses(context);
|
|
1184
1278
|
return mergeOutputs(
|
|
1185
|
-
|
|
1279
|
+
scan2(context.masked, WITH_ARGS).map(
|
|
1186
1280
|
(match) => closes.has(lastCodeBefore(context.masked, match.index)) ? { ...EMPTY_OUTPUT, notes: [withArgsOnSpyOnNote(context, match.index)] } : replacement(match.index, match.index + match.whole.length, ".calledWith(")
|
|
1187
1281
|
)
|
|
1188
1282
|
);
|
|
@@ -1205,7 +1299,7 @@ function lastCodeBefore(masked, index) {
|
|
|
1205
1299
|
}
|
|
1206
1300
|
function strategyOutputs(context) {
|
|
1207
1301
|
return mergeOutputs(
|
|
1208
|
-
|
|
1302
|
+
scan2(context.masked, AND_STRATEGY).flatMap((match) => {
|
|
1209
1303
|
const name = group(match.groups, 1);
|
|
1210
1304
|
return HELPER_NAMES.has(name) ? [] : [strategyOutput(context, match, name)];
|
|
1211
1305
|
})
|
|
@@ -1279,12 +1373,12 @@ var jasmineSpyOn = {
|
|
|
1279
1373
|
residue: /(?:^|[^\w$.])spyOn(?:Property)?\s*\(/,
|
|
1280
1374
|
run: (context) => ({
|
|
1281
1375
|
...EMPTY_OUTPUT,
|
|
1282
|
-
edits:
|
|
1376
|
+
edits: scan2(context.masked, SPY_ON).flatMap((match) => spyOnEdits(context, match))
|
|
1283
1377
|
})
|
|
1284
1378
|
};
|
|
1285
1379
|
function spyOnCloses(context) {
|
|
1286
1380
|
return new Set(
|
|
1287
|
-
|
|
1381
|
+
scan2(context.masked, SPY_ON).flatMap((match) => {
|
|
1288
1382
|
const range = spyOnRange(context, match);
|
|
1289
1383
|
return range === void 0 ? [] : [range[1]];
|
|
1290
1384
|
})
|
|
@@ -1517,7 +1611,7 @@ var jasmineGlobals = {
|
|
|
1517
1611
|
// leftover `jasmine.SpyObj` reported twice would name a transform that never intended to touch it.
|
|
1518
1612
|
residue: /\bjasmine\s*\.\s*(?!Spy(?:Obj)?\b)/,
|
|
1519
1613
|
run: (context) => mergeOutputs(
|
|
1520
|
-
|
|
1614
|
+
scan2(context.masked, MEMBER).flatMap((match) => {
|
|
1521
1615
|
const name = group(match.groups, 1);
|
|
1522
1616
|
return TYPE_TARGETS[name] === void 0 ? [globalOutput(context, match, name)] : [];
|
|
1523
1617
|
})
|
|
@@ -1592,7 +1686,7 @@ var jasmineTypes = {
|
|
|
1592
1686
|
summary: "jasmine.Spy \u2192 Mock from vitest, and jasmine.SpyObj<T> \u2192 Spy<T> from this package.",
|
|
1593
1687
|
residue: /\bjasmine\s*\.\s*Spy(?:Obj)?\b/,
|
|
1594
1688
|
run: (context) => mergeOutputs(
|
|
1595
|
-
|
|
1689
|
+
scan2(context.masked, MEMBER).flatMap((match) => {
|
|
1596
1690
|
const target = TYPE_TARGETS[group(match.groups, 1)];
|
|
1597
1691
|
return target === void 0 ? [] : [typeOutput(context, match, target)];
|
|
1598
1692
|
})
|
|
@@ -1630,11 +1724,11 @@ var jasmineMatchers = {
|
|
|
1630
1724
|
([from, to]) => replacements(context, new RegExp(String.raw`\.\s*${from}\s*\(`, "g"), () => `.${to}(`)
|
|
1631
1725
|
),
|
|
1632
1726
|
{ ...EMPTY_OUTPUT, edits: failEdits(context) },
|
|
1633
|
-
{ ...EMPTY_OUTPUT, edits:
|
|
1727
|
+
{ ...EMPTY_OUTPUT, edits: scan2(context.masked, EXPECT).flatMap((match) => withContextEdits(context, match)) }
|
|
1634
1728
|
])
|
|
1635
1729
|
};
|
|
1636
1730
|
function failEdits(context) {
|
|
1637
|
-
return
|
|
1731
|
+
return scan2(context.masked, FAIL).filter((match) => isCallPosition(context.masked, match.index + match.whole.length - 1)).map((match) => ({
|
|
1638
1732
|
start: match.index,
|
|
1639
1733
|
end: match.index + match.whole.length,
|
|
1640
1734
|
text: `${group(match.groups, 1)}expect.fail(`
|
|
@@ -1775,7 +1869,7 @@ var jestNamespace = {
|
|
|
1775
1869
|
summary: "jest.<member> \u2192 vi.<member>, reporting the members with no vi twin instead of renaming them.",
|
|
1776
1870
|
residue: /\bjest\s*\./,
|
|
1777
1871
|
run: (context) => collect(
|
|
1778
|
-
|
|
1872
|
+
scan2(context.masked, MEMBER2).flatMap((match) => {
|
|
1779
1873
|
const name = group(match.groups, 1);
|
|
1780
1874
|
return TYPE_NAMES[name] === void 0 ? [memberOutput(context, match, name)] : [];
|
|
1781
1875
|
})
|
|
@@ -1847,7 +1941,7 @@ var jestTypes = {
|
|
|
1847
1941
|
run: (context) => {
|
|
1848
1942
|
const functionFirst = importsJestGlobals(context);
|
|
1849
1943
|
return collect([
|
|
1850
|
-
...
|
|
1944
|
+
...scan2(context.masked, MEMBER2).flatMap((match) => {
|
|
1851
1945
|
const name = group(match.groups, 1);
|
|
1852
1946
|
const target = TYPE_NAMES[name];
|
|
1853
1947
|
return target === void 0 ? [] : [typeOutput2(context, match, name, target, functionFirst)];
|
|
@@ -1863,7 +1957,7 @@ var GENERIC_CALL = /\bjest\s*\.\s*(fn|spyOn)\s*</g;
|
|
|
1863
1957
|
function genericCallNotes(context) {
|
|
1864
1958
|
return {
|
|
1865
1959
|
...EMPTY_OUTPUT,
|
|
1866
|
-
notes:
|
|
1960
|
+
notes: scan2(context.masked, GENERIC_CALL).map((match) => unreadableTypeArguments(context, match.index, group(match.groups, 1)))
|
|
1867
1961
|
};
|
|
1868
1962
|
}
|
|
1869
1963
|
var NO_ARGUMENT = /\.\s*(mockImplementation(?:Once)?)\s*\(\s*\)/g;
|
|
@@ -1874,7 +1968,7 @@ var mockImplementationArity = {
|
|
|
1874
1968
|
residue: /\.\s*mockImplementation(?:Once)?\s*\(\s*\)/,
|
|
1875
1969
|
run: (context) => ({
|
|
1876
1970
|
...EMPTY_OUTPUT,
|
|
1877
|
-
edits:
|
|
1971
|
+
edits: scan2(context.masked, NO_ARGUMENT).map((match) => ({
|
|
1878
1972
|
start: match.index,
|
|
1879
1973
|
end: match.index + match.whole.length,
|
|
1880
1974
|
text: `.${group(match.groups, 1)}(() => undefined)`
|
|
@@ -1890,7 +1984,7 @@ var jasmineAliases = {
|
|
|
1890
1984
|
run: (context) => ({
|
|
1891
1985
|
...EMPTY_OUTPUT,
|
|
1892
1986
|
edits: Object.entries(JASMINE_ALIASES).flatMap(
|
|
1893
|
-
([alias, replacement2]) =>
|
|
1987
|
+
([alias, replacement2]) => scan2(context.masked, new RegExp(`(^|[^\\w$.])${alias}\\s*\\(`, "g")).filter((match) => isCallPosition(context.masked, match.index + match.whole.length - 1)).map((match) => {
|
|
1894
1988
|
const start = match.index + group(match.groups, 1).length;
|
|
1895
1989
|
return { start, end: start + alias.length, text: replacement2 };
|
|
1896
1990
|
})
|
|
@@ -1911,7 +2005,7 @@ var jestGlobalsImport = {
|
|
|
1911
2005
|
function globalsEdits(context, statement) {
|
|
1912
2006
|
const specifierAt = context.source.indexOf(JEST_GLOBALS, statement.start);
|
|
1913
2007
|
const [open, close] = statement.braces ?? [statement.start, statement.start];
|
|
1914
|
-
const renames =
|
|
2008
|
+
const renames = scan2(context.masked.slice(open, close), /\bjest\b/g).map((match) => ({
|
|
1915
2009
|
start: open + match.index,
|
|
1916
2010
|
end: open + match.index + "jest".length,
|
|
1917
2011
|
text: "vi"
|
|
@@ -2112,7 +2206,7 @@ var injectCast = {
|
|
|
2112
2206
|
residue: /\bas\s+Spy\s*</,
|
|
2113
2207
|
run: (context) => {
|
|
2114
2208
|
const helper = context.entries === void 0 ? void 0 : entryFor(context.entries, "asSpy", context.preferredEntry);
|
|
2115
|
-
const casts =
|
|
2209
|
+
const casts = scan2(context.masked, INJECT).flatMap((match) => {
|
|
2116
2210
|
const cast = castAt(context, match.index, match.whole.length);
|
|
2117
2211
|
return cast === void 0 ? [] : [cast];
|
|
2118
2212
|
});
|
|
@@ -2124,7 +2218,7 @@ var injectCast = {
|
|
|
2124
2218
|
};
|
|
2125
2219
|
function withHelper(context, casts, need) {
|
|
2126
2220
|
const covered = new Set(casts.map((cast) => cast.angle));
|
|
2127
|
-
const strays =
|
|
2221
|
+
const strays = scan2(context.masked, ANY_SPY_CAST).filter((match) => !covered.has(match.index + match.whole.length - 1)).map((match) => castNote(context, match.index));
|
|
2128
2222
|
return {
|
|
2129
2223
|
edits: casts.map((cast) => cast.edit),
|
|
2130
2224
|
needs: casts.length === 0 ? [] : [need],
|
|
@@ -2152,7 +2246,7 @@ var TRANSFORMS = [
|
|
|
2152
2246
|
function residueOf(file, text, transforms) {
|
|
2153
2247
|
const masked = maskComments(text);
|
|
2154
2248
|
return transforms.flatMap(
|
|
2155
|
-
(transform) =>
|
|
2249
|
+
(transform) => scan2(masked, new RegExp(transform.residue.source, `${transform.residue.flags.replace("g", "")}g`)).filter((match) => transform.residueIgnores?.(masked, match) !== true).map((match) => residueNote(file, text, match, transform))
|
|
2156
2250
|
);
|
|
2157
2251
|
}
|
|
2158
2252
|
function residueNote(file, text, match, transform) {
|
|
@@ -2911,7 +3005,7 @@ function checkForeignPragma(graph) {
|
|
|
2911
3005
|
}
|
|
2912
3006
|
|
|
2913
3007
|
// src/cli/checks/export-map.generated.ts
|
|
2914
|
-
var EXPORT_MAP_VERSION = "5.
|
|
3008
|
+
var EXPORT_MAP_VERSION = "5.26.0";
|
|
2915
3009
|
var ENTRY_SPECIFIERS = "vitest-auto-spy vitest-auto-spy/bun vitest-auto-spy/bun-angular vitest-auto-spy/node vitest-auto-spy/rstest vitest-auto-spy/rxjs vitest-auto-spy/console vitest-auto-spy/dom-stubs vitest-auto-spy/diagnostics vitest-auto-spy/jasmine vitest-auto-spy/jasmine-compat vitest-auto-spy/observer-spy vitest-auto-spy/angular vitest-auto-spy/angular/diagnostics vitest-auto-spy/angular/doubles vitest-auto-spy/angular/matchers vitest-auto-spy/angular-http vitest-auto-spy/angular-router vitest-auto-spy/signal-forms vitest-auto-spy/nestjs vitest-auto-spy/react vitest-auto-spy/vue vitest-auto-spy/svelte vitest-auto-spy/setup vitest-auto-spy/zone vitest-auto-spy/eslint-plugin vitest-auto-spy/perf-reporter";
|
|
2916
3010
|
var EXPORTED_BY = {
|
|
2917
3011
|
AccessorImplementations: "0 1 2 3 4 12 20 21 22",
|
|
@@ -3787,10 +3881,14 @@ function expandInclude(pattern) {
|
|
|
3787
3881
|
const base = last === "*" ? trimmed2 : `${trimmed2}${last === "**" ? "/*" : "/**/*"}`;
|
|
3788
3882
|
return IMPLIED_EXTENSIONS.map((extension) => `${base}${extension}`);
|
|
3789
3883
|
}
|
|
3790
|
-
function isExemptPattern(pattern) {
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3884
|
+
function isExemptPattern(pattern, ignoredDirectories = []) {
|
|
3885
|
+
return /\.d\.[cm]?ts$/.test(pattern) || isOutsideScan(pattern, ignoredDirectories);
|
|
3886
|
+
}
|
|
3887
|
+
function isOutsideScan(path, ignoredDirectories) {
|
|
3888
|
+
const segments = path.split("/");
|
|
3889
|
+
const wildcard = segments.findIndex((segment) => /[*?[]/.test(segment));
|
|
3890
|
+
const literal = wildcard === -1 ? segments : segments.slice(0, wildcard);
|
|
3891
|
+
return literal.some(isSkippedDirectory) || ignoredDirectories.some((directory) => `${path}/`.startsWith(`${directory}/`));
|
|
3794
3892
|
}
|
|
3795
3893
|
function literalTail(pattern) {
|
|
3796
3894
|
const last = pattern.slice(pattern.lastIndexOf("/") + 1);
|
|
@@ -3816,12 +3914,13 @@ function stringList(value) {
|
|
|
3816
3914
|
function resolveFromConfig(configPath, entry) {
|
|
3817
3915
|
return posix.normalize(posix.join(posix.dirname(configPath), entry)).replace(/^\.\//, "");
|
|
3818
3916
|
}
|
|
3819
|
-
function checkInclude(configPath, entry,
|
|
3917
|
+
function checkInclude(configPath, entry, profile) {
|
|
3918
|
+
const files = profile.files;
|
|
3820
3919
|
if (entry.includes("${") || entry.startsWith("/")) {
|
|
3821
3920
|
return void 0;
|
|
3822
3921
|
}
|
|
3823
3922
|
const resolved = resolveFromConfig(configPath, entry);
|
|
3824
|
-
if (isExemptPattern(resolved) || matchesAnyFile(resolved, files)) {
|
|
3923
|
+
if (isExemptPattern(resolved, profile.ignoredDirectories) || matchesAnyFile(resolved, files)) {
|
|
3825
3924
|
return void 0;
|
|
3826
3925
|
}
|
|
3827
3926
|
const tail = literalTail(resolved);
|
|
@@ -3842,16 +3941,16 @@ function checkInclude(configPath, entry, files) {
|
|
|
3842
3941
|
fix: "A pattern that matches nothing type-checks nothing, and `tsc --noEmit` still reports zero errors. Fix the glob or delete the entry."
|
|
3843
3942
|
};
|
|
3844
3943
|
}
|
|
3845
|
-
function checkOne(
|
|
3846
|
-
const text = readTextFile(join(cwd, configPath));
|
|
3944
|
+
function checkOne(profile, configPath) {
|
|
3945
|
+
const text = readTextFile(join(profile.cwd, configPath));
|
|
3847
3946
|
const parsed = text === void 0 ? void 0 : parseJsonc(text);
|
|
3848
3947
|
if (!isRecord(parsed)) {
|
|
3849
3948
|
return [];
|
|
3850
3949
|
}
|
|
3851
|
-
const findings = stringList(parsed["include"]).flatMap((entry) => checkInclude(configPath, entry,
|
|
3950
|
+
const findings = stringList(parsed["include"]).flatMap((entry) => checkInclude(configPath, entry, profile) ?? []);
|
|
3852
3951
|
for (const entry of stringList(parsed["files"])) {
|
|
3853
3952
|
const resolved = resolveFromConfig(configPath, entry);
|
|
3854
|
-
if (!files.includes(resolved)) {
|
|
3953
|
+
if (!profile.files.includes(resolved) && !isOutsideScan(resolved, profile.ignoredDirectories)) {
|
|
3855
3954
|
findings.push({
|
|
3856
3955
|
check: "tsconfig-file-missing",
|
|
3857
3956
|
severity: "error",
|
|
@@ -3865,7 +3964,7 @@ function checkOne(cwd, configPath, files) {
|
|
|
3865
3964
|
}
|
|
3866
3965
|
function checkTsconfigGlobs(profile) {
|
|
3867
3966
|
const configs = profile.files.filter((file) => TSCONFIG_NAME2.test(file));
|
|
3868
|
-
return configs.flatMap((config) => checkOne(profile
|
|
3967
|
+
return configs.flatMap((config) => checkOne(profile, config));
|
|
3869
3968
|
}
|
|
3870
3969
|
|
|
3871
3970
|
// src/cli/checks/unawaited-helper.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitest-auto-spy",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.27.0",
|
|
4
4
|
"description": "Auto-generate fully-typed test spies from a class — across Vitest, Bun, node:test and Rstest, with Angular/NestJS/React/Vue/Svelte recipes. Drop-in replacement for jest-auto-spies and jasmine-auto-spies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auto-mock",
|