vitest-auto-spy 5.53.0 → 5.55.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 +30 -1
- package/agent-docs/checklist.md +5 -2
- package/dist/cli.js +139 -30
- package/dist/perf-reporter.d.ts +10 -0
- package/dist/perf-reporter.js +10 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,33 @@ The latest released version here must always match the one published on
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [5.55.0] - 2026-09-30
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **`perf-import` no longer names a barrel the spec cannot avoid.** A spec whose other imports —
|
|
18
|
+
usually its subject — already load the same barrel is skipped, since importing the module directly
|
|
19
|
+
would leave the graph as it is. So is a barrel that a non-wildcard tsconfig `paths` alias points at,
|
|
20
|
+
when the spec lives outside the barrel's directory: that is another package's public entry, and in
|
|
21
|
+
an Nx workspace `@nx/enforce-module-boundaries` rejects the relative import that would bypass it.
|
|
22
|
+
On a consumer Nx workspace this took the report from 7 barrel findings to 0.
|
|
23
|
+
|
|
24
|
+
## [5.54.0] - 2026-09-30
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **`perf --json <reports> --gate` re-measures its suspects without `--command`.** The perf reporter
|
|
29
|
+
now records the Vitest config file the suite ran with (`configFile`, relative to `root`), and the
|
|
30
|
+
confirmation pass re-runs each suspect with `vitest run --config <that file>` over the suspects
|
|
31
|
+
only: one run per config when the reports came from several (`vitest.config.ts` and
|
|
32
|
+
`libs/tooling/vitest.config.ts`, say), read back as one second reading. A single slow reading on a
|
|
33
|
+
loaded runner now comes back as not reproduced instead of needing `--no-confirm` to fail anything.
|
|
34
|
+
The re-run has coverage off (`--coverage.enabled=false`), even when the first reading had it on:
|
|
35
|
+
coverage thresholds fail a run of a few files, and instrumentation is the harness's cost rather
|
|
36
|
+
than the test's, so a body over budget only because of instrumentation is not reproduced.
|
|
37
|
+
`--command` still takes precedence, and a report without `configFile` is not re-measured, as
|
|
38
|
+
before. The perf report format is version 6; versions 1–6 are read.
|
|
39
|
+
|
|
13
40
|
## [5.53.0] - 2026-09-29
|
|
14
41
|
|
|
15
42
|
### Added
|
|
@@ -6657,7 +6684,9 @@ moved under 1 %: `/angular` +192 B (+1.0 %) for the override queue and the HTTP
|
|
|
6657
6684
|
This copy ships with the package and carries 5.x only. Releases before 5.0.0 are in the
|
|
6658
6685
|
[full changelog](https://github.com/ASDAlexey/vitest-auto-spy/blob/master/CHANGELOG.md).
|
|
6659
6686
|
|
|
6660
|
-
[Unreleased]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.
|
|
6687
|
+
[Unreleased]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.55.0...HEAD
|
|
6688
|
+
[5.55.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.54.0...v5.55.0
|
|
6689
|
+
[5.54.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.53.0...v5.54.0
|
|
6661
6690
|
[5.53.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.52.0...v5.53.0
|
|
6662
6691
|
[5.52.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.51.0...v5.52.0
|
|
6663
6692
|
[5.51.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.50.0...v5.51.0
|
package/agent-docs/checklist.md
CHANGED
|
@@ -254,8 +254,11 @@ test bodies, and the gate prints the slowest tests, the share in hooks against b
|
|
|
254
254
|
time went in the spec, in your code and by package. The report ends in two tables of what is over
|
|
255
255
|
budget and nothing else. And every candidate is re-measured on its own before it may fail anything: a
|
|
256
256
|
file that is fast when it has the machine to itself is reported as _not reproduced_, an `info` rather
|
|
257
|
-
than a finding.
|
|
258
|
-
|
|
257
|
+
than a finding. `--json` without `--command` re-measures too: the report records its Vitest config
|
|
258
|
+
file, and the gate runs `vitest run --config <it>` over the suspects, one run per config when the
|
|
259
|
+
reports came from several, with coverage off. Without a way to re-measure (a `--command` without
|
|
260
|
+
`{paths}`, a report from before the config was recorded), findings are warnings that fail nothing
|
|
261
|
+
unless `--no-confirm` says one reading is enough. Exit `1` is "over budget", exit `2` is "there was nothing
|
|
259
262
|
to judge" — including a red or unfinished (`partial`) suite, which the gate will not judge at all, since a failed test is
|
|
260
263
|
measured until its timeout and 30 s of timeout looks exactly like 30 s of slow code.
|
|
261
264
|
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { ownVersion, isDirectory, readProfile, formatFindings, outputWidth, painterFor, summarize, DOCTOR_CHECKS, writeTextFile, REPORT_SCHEMA, sortFindings, findingJson, tallyOf,
|
|
2
|
+
import { ownVersion, isDirectory, readProfile, formatFindings, outputWidth, painterFor, summarize, ownPackageRoot, DOCTOR_CHECKS, writeTextFile, REPORT_SCHEMA, sortFindings, findingJson, tallyOf, readTextFile, SCAN_CAP_ENV, scanCap, COVERAGE_MATCHING_DOCS, VITEST_5_PERF_DOCS, filterBySeverity, isSymlink, skillFrontmatter, toPosix, pathExists, NOTHING_TO_READ_DOCS, removeFile, parseJsonc, isRecord, extractSetupFiles, captures, gitignoreFilter, BARE_RUN_DOCS, MONOCHROME, findStringEnd, listRepositoryFiles, wrapText, isSkippedDirectory, versionFrom, stripColor, padStart, padEnd } from './cli-SELXGLDQ.js';
|
|
3
3
|
import { resolve, join, posix, relative, isAbsolute, dirname } from 'node:path';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
@@ -1654,7 +1654,7 @@ function foreignPragmaPass() {
|
|
|
1654
1654
|
}
|
|
1655
1655
|
|
|
1656
1656
|
// src/cli/checks/export-map.generated.ts
|
|
1657
|
-
var EXPORT_MAP_VERSION = "5.
|
|
1657
|
+
var EXPORT_MAP_VERSION = "5.54.0";
|
|
1658
1658
|
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";
|
|
1659
1659
|
var EXPORTED_BY = {
|
|
1660
1660
|
AccessorImplementations: "0 1 2 3 4 12 20 21 22",
|
|
@@ -4614,8 +4614,6 @@ function runNgTest(options, tools, io) {
|
|
|
4614
4614
|
}
|
|
4615
4615
|
return tools.spawn({ command: process.execPath, args: run, cwd: options.cwd, env: {}, shell: false }).status;
|
|
4616
4616
|
}
|
|
4617
|
-
|
|
4618
|
-
// src/cli/checks/barrels.ts
|
|
4619
4617
|
var BARREL_NAME = /(?:^|\/)(?:index|public[_-]api)\.[cm]?[jt]sx?$/;
|
|
4620
4618
|
var RE_EXPORT = /\bexport\s+(?:type\s+)?(?:\*|{[^}]*})[^;]*?\bfrom\s*["'][^"']+["']/g;
|
|
4621
4619
|
var OWN_DECLARATION = /(?:^|\n)\s*export\s+(?:abstract\s+)?(?:default|class|function|const|let|var|enum|namespace|interface|type\s+\w+\s*=)\b/;
|
|
@@ -4625,9 +4623,9 @@ function isBarrel(file, text) {
|
|
|
4625
4623
|
}
|
|
4626
4624
|
return (text.match(RE_EXPORT) ?? []).length >= 2;
|
|
4627
4625
|
}
|
|
4628
|
-
function
|
|
4629
|
-
const seen =
|
|
4630
|
-
const order = [
|
|
4626
|
+
function reachableFrom(starts, graph) {
|
|
4627
|
+
const seen = new Set(starts);
|
|
4628
|
+
const order = [...starts];
|
|
4631
4629
|
for (const current of order) {
|
|
4632
4630
|
for (const edge of graph.imports.get(current) ?? []) {
|
|
4633
4631
|
if (!seen.has(edge)) {
|
|
@@ -4636,9 +4634,33 @@ function reachOf(file, graph) {
|
|
|
4636
4634
|
}
|
|
4637
4635
|
}
|
|
4638
4636
|
}
|
|
4639
|
-
return seen
|
|
4637
|
+
return seen;
|
|
4638
|
+
}
|
|
4639
|
+
function reachOf(file, graph) {
|
|
4640
|
+
return reachableFrom([file], graph).size - 1;
|
|
4641
|
+
}
|
|
4642
|
+
function packageEntries(cwd, files) {
|
|
4643
|
+
const entries = /* @__PURE__ */ new Set();
|
|
4644
|
+
for (const alias of readCompilerPaths2(cwd).aliases) {
|
|
4645
|
+
for (const target of alias.wildcard ? [] : alias.targets) {
|
|
4646
|
+
const resolved = resolveRelative("package.json", `./${target}`, files);
|
|
4647
|
+
if (resolved !== void 0) {
|
|
4648
|
+
entries.add(resolved);
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
return entries;
|
|
4653
|
+
}
|
|
4654
|
+
function loadedAnyway(barrel, imported, graph) {
|
|
4655
|
+
return reachableFrom(
|
|
4656
|
+
imported.filter((other) => other !== barrel),
|
|
4657
|
+
graph
|
|
4658
|
+
).has(barrel);
|
|
4659
|
+
}
|
|
4660
|
+
function outsidePackage(spec, barrel, entries) {
|
|
4661
|
+
return entries.has(barrel) && !spec.startsWith(`${posix.dirname(barrel)}/`);
|
|
4640
4662
|
}
|
|
4641
|
-
function findBarrelImports(graph) {
|
|
4663
|
+
function findBarrelImports(graph, entries = /* @__PURE__ */ new Set()) {
|
|
4642
4664
|
const barrels = /* @__PURE__ */ new Map();
|
|
4643
4665
|
const found = [];
|
|
4644
4666
|
for (const [file, text] of graph.texts) {
|
|
@@ -4652,7 +4674,7 @@ function findBarrelImports(graph) {
|
|
|
4652
4674
|
}
|
|
4653
4675
|
for (const target of imported) {
|
|
4654
4676
|
const reach = barrels.get(target);
|
|
4655
|
-
if (reach !== void 0) {
|
|
4677
|
+
if (reach !== void 0 && !outsidePackage(spec, target, entries) && !loadedAnyway(target, imported, graph)) {
|
|
4656
4678
|
found.push({ spec, barrel: target, reach });
|
|
4657
4679
|
}
|
|
4658
4680
|
}
|
|
@@ -4665,8 +4687,8 @@ var PERF_OUTPUT_ENV = "VITEST_AUTO_SPY_PERF_OUT";
|
|
|
4665
4687
|
var PERF_REPORTER_ENV = "VITEST_AUTO_SPY_PERF_REPORTER";
|
|
4666
4688
|
var PERF_PROFILE_ENV = "VITEST_AUTO_SPY_PERF_PROFILE";
|
|
4667
4689
|
var PERF_ISOLATE_ENV = "VITEST_AUTO_SPY_PERF_ISOLATE";
|
|
4668
|
-
var PERF_FORMAT_VERSION =
|
|
4669
|
-
var READABLE_VERSIONS = [1, 2, 3, 4, 5];
|
|
4690
|
+
var PERF_FORMAT_VERSION = 6;
|
|
4691
|
+
var READABLE_VERSIONS = [1, 2, 3, 4, 5, 6];
|
|
4670
4692
|
var CASE_FLOOR_MS = 100;
|
|
4671
4693
|
var CASES_PER_FILE = 5;
|
|
4672
4694
|
var OPTIONAL_FILE_NUMBERS = ["workerId", "lane", "start", "fetch", "setupFetch", "retries", "autoSpy"];
|
|
@@ -4811,7 +4833,8 @@ function parsePerfRun(text) {
|
|
|
4811
4833
|
...parsed["partial"] === true ? { partial: true } : {},
|
|
4812
4834
|
...end === "failed" || end === "interrupted" || end === "passed" ? { end } : {},
|
|
4813
4835
|
...parsed["hung"] === true ? { hung: true } : {},
|
|
4814
|
-
...coverage === void 0 ? {} : { coverage }
|
|
4836
|
+
...coverage === void 0 ? {} : { coverage },
|
|
4837
|
+
...typeof parsed["configFile"] === "string" && parsed["configFile"] !== "" ? { configFile: parsed["configFile"] } : {}
|
|
4815
4838
|
};
|
|
4816
4839
|
}
|
|
4817
4840
|
function sumOf(run, key) {
|
|
@@ -5385,7 +5408,8 @@ function finding3(candidate, severity, tail) {
|
|
|
5385
5408
|
};
|
|
5386
5409
|
}
|
|
5387
5410
|
var UNCONFIRMED = [
|
|
5388
|
-
"Measured once and never confirmed, so it fails nothing. Run the gate where the files can be re-measured
|
|
5411
|
+
"Measured once and never confirmed, so it fails nothing. Run the gate where the files can be re-measured \u2014",
|
|
5412
|
+
"a --command with {paths}, or a report whose perf reporter recorded the Vitest config \u2014",
|
|
5389
5413
|
"or pass --no-confirm to gate on a single reading."
|
|
5390
5414
|
].join(" ");
|
|
5391
5415
|
function rowOf(candidate, outcome, again) {
|
|
@@ -7070,9 +7094,9 @@ function perfMarkdown(document) {
|
|
|
7070
7094
|
|
|
7071
7095
|
// src/cli/perf.ts
|
|
7072
7096
|
var QUIET_MS = 5e3;
|
|
7073
|
-
function barrelCandidates(graph) {
|
|
7097
|
+
function barrelCandidates(graph, entries) {
|
|
7074
7098
|
const widest = /* @__PURE__ */ new Map();
|
|
7075
|
-
for (const use of findBarrelImports(graph)) {
|
|
7099
|
+
for (const use of findBarrelImports(graph, entries)) {
|
|
7076
7100
|
if (!widest.has(use.spec)) {
|
|
7077
7101
|
widest.set(use.spec, use);
|
|
7078
7102
|
}
|
|
@@ -7082,11 +7106,11 @@ function barrelCandidates(graph) {
|
|
|
7082
7106
|
function importPhase(run) {
|
|
7083
7107
|
return run.files.length > 0 && run.files.every((file) => file.fetch !== void 0) ? "Evaluating imported modules \u2014 the wait for their transforms is counted under `transform` \u2014" : "Importing modules";
|
|
7084
7108
|
}
|
|
7085
|
-
function importFindings(phases, graph, run, context) {
|
|
7109
|
+
function importFindings(phases, graph, profile, run, context) {
|
|
7086
7110
|
if (shareOf(phases, "import") < DOMINATES || context.builder !== void 0) {
|
|
7087
7111
|
return [];
|
|
7088
7112
|
}
|
|
7089
|
-
const ranked = barrelCandidates(graph);
|
|
7113
|
+
const ranked = barrelCandidates(graph, packageEntries(profile.cwd, new Set(profile.files)));
|
|
7090
7114
|
if (ranked.length === 0) {
|
|
7091
7115
|
return [];
|
|
7092
7116
|
}
|
|
@@ -7127,7 +7151,7 @@ function analysePerf(run, profile, failOnFlaky = false, context = perfContext(pr
|
|
|
7127
7151
|
...environmentFindings(phases, profile, graph, measured),
|
|
7128
7152
|
...domEngineFindings(phases, graph, run, context),
|
|
7129
7153
|
...transformFindings(phases, graph, run, context),
|
|
7130
|
-
...importFindings(phases, graph, run, context),
|
|
7154
|
+
...importFindings(phases, graph, profile, run, context),
|
|
7131
7155
|
...isolationFindings(phases, graph, profile, run, void 0, context),
|
|
7132
7156
|
...poolFindings(phases, graph, run, context),
|
|
7133
7157
|
...workerFindings(total, graph, run, void 0, context)
|
|
@@ -7450,6 +7474,11 @@ function reroot(path, from, to) {
|
|
|
7450
7474
|
return rest.startsWith("/") || rest.startsWith("\\") ? `${to}${rest}` : path;
|
|
7451
7475
|
}
|
|
7452
7476
|
var END_ORDER = ["interrupted", "failed", "passed"];
|
|
7477
|
+
function sharedConfigFile(inputs) {
|
|
7478
|
+
const configFiles = new Set(inputs.map((input) => input.run.configFile));
|
|
7479
|
+
const [configFile] = configFiles;
|
|
7480
|
+
return { shared: configFiles.size <= 1, configFile };
|
|
7481
|
+
}
|
|
7453
7482
|
function mergedRun(inputs) {
|
|
7454
7483
|
const runs2 = inputs.map((input) => input.run);
|
|
7455
7484
|
const vitest = runs2.find((run) => run.vitest !== void 0)?.vitest;
|
|
@@ -7457,6 +7486,7 @@ function mergedRun(inputs) {
|
|
|
7457
7486
|
const startups = runs2.flatMap((run) => run.startup === void 0 ? [] : [run.startup]);
|
|
7458
7487
|
const end = END_ORDER.find((reason) => runs2.some((run) => run.end === reason));
|
|
7459
7488
|
const coverages = runs2.flatMap((run) => run.coverage === void 0 ? [] : [run.coverage]);
|
|
7489
|
+
const configFile = sharedConfigFile(inputs);
|
|
7460
7490
|
const startup = startups.reduce((total, each) => ({ ms: total.ms + each.ms, workers: total.workers + each.workers }), {
|
|
7461
7491
|
ms: 0,
|
|
7462
7492
|
workers: 0
|
|
@@ -7474,7 +7504,8 @@ function mergedRun(inputs) {
|
|
|
7474
7504
|
...runs2.some((run) => run.partial === true) ? { partial: true } : {},
|
|
7475
7505
|
...end === void 0 ? {} : { end },
|
|
7476
7506
|
...runs2.some((run) => run.hung === true) ? { hung: true } : {},
|
|
7477
|
-
...coverages.length === 0 ? {} : { coverage: Math.max(...coverages) }
|
|
7507
|
+
...coverages.length === 0 ? {} : { coverage: Math.max(...coverages) },
|
|
7508
|
+
...configFile.shared && configFile.configFile !== void 0 ? { configFile: configFile.configFile } : {}
|
|
7478
7509
|
};
|
|
7479
7510
|
}
|
|
7480
7511
|
function mergeRuns(inputs) {
|
|
@@ -7483,15 +7514,18 @@ function mergeRuns(inputs) {
|
|
|
7483
7514
|
const carriedBy = /* @__PURE__ */ new Map();
|
|
7484
7515
|
const empty = [];
|
|
7485
7516
|
const offset = { workerId: 0, lane: 0 };
|
|
7517
|
+
const perFile = !sharedConfigFile(inputs).shared;
|
|
7486
7518
|
for (const input of inputs) {
|
|
7487
7519
|
const from = trimRoot(input.run.root);
|
|
7488
7520
|
const shift = { ...offset };
|
|
7521
|
+
const configFile = perFile ? input.run.configFile : void 0;
|
|
7489
7522
|
if (input.run.files.length === 0) {
|
|
7490
7523
|
empty.push(input.path);
|
|
7491
7524
|
}
|
|
7492
7525
|
for (const file of input.run.files) {
|
|
7493
7526
|
const path = reroot(file.file, from, root);
|
|
7494
|
-
const
|
|
7527
|
+
const placed2 = { ...file, file: path, ...configFile === void 0 ? {} : { configFile } };
|
|
7528
|
+
const moved = shifted(placed2, shift, offset);
|
|
7495
7529
|
const previous = kept.get(path);
|
|
7496
7530
|
carriedBy.set(path, [...carriedBy.get(path) ?? [], input.path]);
|
|
7497
7531
|
if (previous === void 0 || fileTotal(moved) > fileTotal(previous)) {
|
|
@@ -7726,13 +7760,22 @@ function vitestMajor3(cwd) {
|
|
|
7726
7760
|
function versionFlags(cwd) {
|
|
7727
7761
|
return (vitestMajor3(cwd) ?? 0) >= 5 ? ["--experimental.diagnostics=false"] : [];
|
|
7728
7762
|
}
|
|
7763
|
+
function configArgs(config) {
|
|
7764
|
+
return config === void 0 ? [] : ["--config", config, "--coverage.enabled=false"];
|
|
7765
|
+
}
|
|
7729
7766
|
function fromRun(options, spawn, packageRoot) {
|
|
7730
7767
|
const entry = join(options.cwd, "node_modules", "vitest", "vitest.mjs");
|
|
7731
7768
|
const reporter = reporterPath(packageRoot);
|
|
7732
|
-
const mismatch = bareRunWouldMeasureSomethingElse(options.profile);
|
|
7769
|
+
const mismatch = options.config === void 0 ? bareRunWouldMeasureSomethingElse(options.profile) : void 0;
|
|
7733
7770
|
if (mismatch !== void 0) {
|
|
7734
7771
|
return failed(mismatch);
|
|
7735
7772
|
}
|
|
7773
|
+
if (options.config !== void 0 && !pathExists(resolve(options.cwd, options.config))) {
|
|
7774
|
+
return failed(
|
|
7775
|
+
`The report was measured with the Vitest config ${options.config}, and there is no such file in ${options.cwd}. Run the gate in the checkout the report was written for, or pass --command.
|
|
7776
|
+
Docs: ${NOTHING_TO_READ_DOCS}`
|
|
7777
|
+
);
|
|
7778
|
+
}
|
|
7736
7779
|
if (!pathExists(entry)) {
|
|
7737
7780
|
return failed(
|
|
7738
7781
|
`No Vitest is installed in ${options.cwd}, so there is nothing to run. Install vitest there, then measure again.
|
|
@@ -7749,7 +7792,16 @@ Docs: ${NOTHING_TO_READ_DOCS}`
|
|
|
7749
7792
|
removeFile(target);
|
|
7750
7793
|
const spawned = timed(spawn, {
|
|
7751
7794
|
command: process.execPath,
|
|
7752
|
-
args: [
|
|
7795
|
+
args: [
|
|
7796
|
+
entry,
|
|
7797
|
+
"run",
|
|
7798
|
+
...configArgs(options.config),
|
|
7799
|
+
"--reporter=default",
|
|
7800
|
+
`--reporter=${reporter}`,
|
|
7801
|
+
"--logHeapUsage",
|
|
7802
|
+
...versionFlags(options.cwd),
|
|
7803
|
+
...options.paths
|
|
7804
|
+
],
|
|
7753
7805
|
cwd: options.cwd,
|
|
7754
7806
|
env: runEnv(options, target, reporter),
|
|
7755
7807
|
shell: false
|
|
@@ -7809,15 +7861,66 @@ function readPerfRun(options, spawn = spawnProcess, packageRoot = ownPackageRoot
|
|
|
7809
7861
|
const source = options.json === void 0 ? fromCommand(options, command, spawn, packageRoot) : fromFile(options.json, options.cwd);
|
|
7810
7862
|
return source.ok ? { ...source, command } : source;
|
|
7811
7863
|
}
|
|
7812
|
-
function
|
|
7864
|
+
function localRoot(root, cwd) {
|
|
7865
|
+
const here = relative(cwd, root);
|
|
7866
|
+
return root !== "" && !here.startsWith("..") && !isAbsolute(here) ? root : cwd;
|
|
7867
|
+
}
|
|
7868
|
+
function recordedConfigs(recorded, cwd) {
|
|
7869
|
+
const root = localRoot(recorded.root, cwd);
|
|
7870
|
+
const configs = /* @__PURE__ */ new Map();
|
|
7871
|
+
for (const [path, file] of measuredFiles(recorded, cwd)) {
|
|
7872
|
+
const configFile = file.configFile ?? recorded.configFile;
|
|
7873
|
+
if (configFile !== void 0) {
|
|
7874
|
+
configs.set(path, toPosix(relative(cwd, resolve(root, configFile))));
|
|
7875
|
+
}
|
|
7876
|
+
}
|
|
7877
|
+
return configs;
|
|
7878
|
+
}
|
|
7879
|
+
function mergeMeasured(measured) {
|
|
7880
|
+
return {
|
|
7881
|
+
ok: true,
|
|
7882
|
+
run: mergeRuns(measured.map((each, index) => ({ path: String(index), run: each.run }))).run,
|
|
7883
|
+
runFailed: measured.some((each) => each.runFailed),
|
|
7884
|
+
profiles: new Map(measured.flatMap((each) => [...new Map(each.profiles)]))
|
|
7885
|
+
};
|
|
7886
|
+
}
|
|
7887
|
+
function underRecordedConfigs(configs, remeasure) {
|
|
7888
|
+
return (paths) => {
|
|
7889
|
+
const groups = /* @__PURE__ */ new Map();
|
|
7890
|
+
for (const path of paths) {
|
|
7891
|
+
const config = configs.get(path);
|
|
7892
|
+
if (config !== void 0) {
|
|
7893
|
+
groups.set(config, [...groups.get(config) ?? [], path]);
|
|
7894
|
+
}
|
|
7895
|
+
}
|
|
7896
|
+
if (groups.size === 0) {
|
|
7897
|
+
return failed(
|
|
7898
|
+
`None of the files to re-measure is in a report that recorded its Vitest config. Pass --command to re-measure them.
|
|
7899
|
+
Docs: ${NOTHING_TO_READ_DOCS}`
|
|
7900
|
+
);
|
|
7901
|
+
}
|
|
7902
|
+
const measured = [];
|
|
7903
|
+
for (const [config, group] of groups) {
|
|
7904
|
+
const source = remeasure(group, config);
|
|
7905
|
+
if (!source.ok) {
|
|
7906
|
+
return source;
|
|
7907
|
+
}
|
|
7908
|
+
measured.push(source);
|
|
7909
|
+
}
|
|
7910
|
+
return mergeMeasured(measured);
|
|
7911
|
+
};
|
|
7912
|
+
}
|
|
7913
|
+
function perfRemeasure(options, spawn = spawnProcess, packageRoot = ownPackageRoot(), recorded) {
|
|
7813
7914
|
if (options.command !== void 0 && !commandTakesPaths(options.command)) {
|
|
7814
7915
|
return void 0;
|
|
7815
7916
|
}
|
|
7917
|
+
const profileDir = join(options.cwd, "node_modules", ".cache", "vitest-auto-spy", `profiles-${process.pid}`);
|
|
7918
|
+
const scoped = { ...options, json: void 0, out: void 0, profileDir };
|
|
7816
7919
|
if (options.command === void 0 && options.json !== void 0) {
|
|
7817
|
-
|
|
7920
|
+
const configs = recorded === void 0 ? /* @__PURE__ */ new Map() : recordedConfigs(recorded, options.cwd);
|
|
7921
|
+
return configs.size === 0 ? void 0 : underRecordedConfigs(configs, (paths, config) => readPerfRun({ ...scoped, paths, config }, spawn, packageRoot));
|
|
7818
7922
|
}
|
|
7819
|
-
|
|
7820
|
-
return (paths) => readPerfRun({ ...options, json: void 0, out: void 0, paths, profileDir }, spawn, packageRoot);
|
|
7923
|
+
return (paths) => readPerfRun({ ...scoped, paths }, spawn, packageRoot);
|
|
7821
7924
|
}
|
|
7822
7925
|
function perfAbIsolate(options, spawn, packageRoot = ownPackageRoot()) {
|
|
7823
7926
|
if (options.command === void 0 && options.json !== void 0) {
|
|
@@ -7901,7 +8004,7 @@ function baselineRequest(args, cwd) {
|
|
|
7901
8004
|
}
|
|
7902
8005
|
};
|
|
7903
8006
|
}
|
|
7904
|
-
var PROFILE_DIR_UNUSED = "warning --profile-dir keeps the profiles the gate's confirmation pass records, and this run has no such pass: add --gate, without --no-confirm, on a source it can run again.\n";
|
|
8007
|
+
var PROFILE_DIR_UNUSED = "warning --profile-dir keeps the profiles the gate's confirmation pass records, and this run has no such pass: add --gate, without --no-confirm, on a source it can run again \u2014 a run, a --command with {paths}, or a report that recorded its Vitest config.\n";
|
|
7905
8008
|
function perfCommand(cwd, argv, io) {
|
|
7906
8009
|
const args = parseArgs(argv);
|
|
7907
8010
|
const format = formatOf(args);
|
|
@@ -7918,7 +8021,13 @@ function perfCommand(cwd, argv, io) {
|
|
|
7918
8021
|
...keepProfiles === void 0 ? {} : { keepProfiles: resolve(cwd, keepProfiles) }
|
|
7919
8022
|
};
|
|
7920
8023
|
const trustSingle = flagEnabled(args, "no-confirm");
|
|
7921
|
-
const
|
|
8024
|
+
const handed = options.json === void 0 ? void 0 : readPerfRun(options, spawn);
|
|
8025
|
+
const recorded = handed?.ok === true ? handed.run : void 0;
|
|
8026
|
+
const gate = flagEnabled(args, "gate") ? {
|
|
8027
|
+
options: gateOptions(args),
|
|
8028
|
+
remeasure: trustSingle ? void 0 : perfRemeasure(options, spawn, ownPackageRoot(), recorded),
|
|
8029
|
+
trustSingle
|
|
8030
|
+
} : void 0;
|
|
7922
8031
|
if (keepProfiles !== void 0 && gate?.remeasure === void 0) {
|
|
7923
8032
|
io.err(PROFILE_DIR_UNUSED);
|
|
7924
8033
|
}
|
|
@@ -7927,7 +8036,7 @@ function perfCommand(cwd, argv, io) {
|
|
|
7927
8036
|
const minSeverity = minSeverityOf(args);
|
|
7928
8037
|
const failOn = failOnOf(args);
|
|
7929
8038
|
const codeQuality = flagValue(args, "code-quality");
|
|
7930
|
-
return renderPerf(readPerfRun(options, spawn), profile, io, {
|
|
8039
|
+
return renderPerf(handed ?? readPerfRun(options, spawn), profile, io, {
|
|
7931
8040
|
budgets: gateOptions(args),
|
|
7932
8041
|
...format === "json" || format === "markdown" ? { format } : {},
|
|
7933
8042
|
...gate === void 0 ? {} : { gate },
|
package/dist/perf-reporter.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ interface PerfFile {
|
|
|
64
64
|
readonly setupFetch?: number;
|
|
65
65
|
/** Failed attempts before a pass, summed over the file's tests. */
|
|
66
66
|
readonly retries?: number;
|
|
67
|
+
/** Set only by `mergeRuns`, when the merged reports ran different configs: this file's, as `PerfRun.configFile`. */
|
|
68
|
+
readonly configFile?: string;
|
|
67
69
|
}
|
|
68
70
|
/** What the run was configured with, read from Vitest's resolved config rather than the config text. */
|
|
69
71
|
interface PerfConfig {
|
|
@@ -108,6 +110,8 @@ interface PerfRun {
|
|
|
108
110
|
readonly hung?: boolean;
|
|
109
111
|
/** Milliseconds coverage took after the last file: generating the map, then writing the reports. */
|
|
110
112
|
readonly coverage?: number;
|
|
113
|
+
/** The Vitest config file the run loaded, relative to `root` with `/` separators; absent without one. */
|
|
114
|
+
readonly configFile?: string;
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
interface PerfDiagnostic {
|
|
@@ -208,6 +212,12 @@ interface PerfVitest {
|
|
|
208
212
|
readonly workersSpawned?: number;
|
|
209
213
|
};
|
|
210
214
|
readonly projects?: readonly PerfProject[];
|
|
215
|
+
/** Vite's resolved config holds the file Vitest loaded, on Vitest 4 and 5 alike; `configFile` is unset without one. */
|
|
216
|
+
readonly vite?: {
|
|
217
|
+
readonly config?: {
|
|
218
|
+
readonly configFile?: string | undefined;
|
|
219
|
+
};
|
|
220
|
+
};
|
|
211
221
|
}
|
|
212
222
|
declare class PerfReporter {
|
|
213
223
|
#private;
|
package/dist/perf-reporter.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { join, dirname, sep, basename } from 'node:path';
|
|
1
|
+
import { join, dirname, relative, sep, basename } from 'node:path';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { mkdirSync, writeFileSync, renameSync, rmSync, realpathSync, statSync } from 'node:fs';
|
|
4
4
|
|
|
@@ -39,7 +39,7 @@ var PERF_OUTPUT_ENV = "VITEST_AUTO_SPY_PERF_OUT";
|
|
|
39
39
|
var PERF_PROFILE_ENV = "VITEST_AUTO_SPY_PERF_PROFILE";
|
|
40
40
|
var PERF_ISOLATE_ENV = "VITEST_AUTO_SPY_PERF_ISOLATE";
|
|
41
41
|
var AUTO_SPY_META_KEY = "autoSpyMs";
|
|
42
|
-
var PERF_FORMAT_VERSION =
|
|
42
|
+
var PERF_FORMAT_VERSION = 6;
|
|
43
43
|
var CASE_FLOOR_MS = 100;
|
|
44
44
|
var CASES_PER_FILE = 5;
|
|
45
45
|
|
|
@@ -208,6 +208,10 @@ function configOf(vitest) {
|
|
|
208
208
|
...provided === void 0 ? {} : { provided: Object.keys(provided).filter((key) => provided[key] === true) }
|
|
209
209
|
};
|
|
210
210
|
}
|
|
211
|
+
function configFileOf(vitest) {
|
|
212
|
+
const file = vitest.vite?.config?.configFile;
|
|
213
|
+
return file === void 0 ? void 0 : toPosix(relative(vitest.config.root, file));
|
|
214
|
+
}
|
|
211
215
|
function startupOf(vitest) {
|
|
212
216
|
const { startupTime, workersSpawned } = vitest.state;
|
|
213
217
|
return startupTime === void 0 || workersSpawned === void 0 ? void 0 : { ms: startupTime, workers: workersSpawned };
|
|
@@ -232,9 +236,11 @@ var PerfReporter = class {
|
|
|
232
236
|
#ended;
|
|
233
237
|
#coverage;
|
|
234
238
|
#hung = false;
|
|
239
|
+
#configFile;
|
|
235
240
|
onInit(vitest) {
|
|
236
241
|
this.#vitest = vitest;
|
|
237
242
|
this.#start = Date.now();
|
|
243
|
+
this.#configFile = configFileOf(vitest);
|
|
238
244
|
if (target() === void 0 && !profiling()) {
|
|
239
245
|
return;
|
|
240
246
|
}
|
|
@@ -349,7 +355,8 @@ var PerfReporter = class {
|
|
|
349
355
|
...partial ? { partial: true } : {},
|
|
350
356
|
...end === void 0 ? {} : { end },
|
|
351
357
|
...this.#hung ? { hung: true } : {},
|
|
352
|
-
...this.#coverage === void 0 ? {} : { coverage: this.#coverage }
|
|
358
|
+
...this.#coverage === void 0 ? {} : { coverage: this.#coverage },
|
|
359
|
+
...this.#configFile === void 0 ? {} : { configFile: this.#configFile }
|
|
353
360
|
};
|
|
354
361
|
}
|
|
355
362
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitest-auto-spy",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.55.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",
|