@hublo/sentinel 1.4.0-alpha.48 → 1.4.0-alpha.49

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.
@@ -5,13 +5,14 @@ import {
5
5
  parseJsonc
6
6
  } from "./chunk-QXFCZON7.js";
7
7
  import {
8
+ DEFAULT_MAX_DIAGNOSTICS,
8
9
  WORKSPACE_ROOT_MARKER,
9
10
  findWorkspaceRoot,
10
11
  moduleName,
11
12
  readNxProjectName,
12
13
  readOwnPackage,
13
14
  readProjectPackageJson
14
- } from "./chunk-NW6UHNYX.js";
15
+ } from "./chunk-KMKQDGI6.js";
15
16
 
16
17
  // src/core/base-adapter.ts
17
18
  var BaseAdapter = class {
@@ -367,9 +368,9 @@ function binFromOwnInstall(packageName, binName) {
367
368
  try {
368
369
  const manifest = require2.resolve(`${packageName}/package.json`);
369
370
  const bin = require2(manifest).bin;
370
- const relative7 = typeof bin === "string" ? bin : bin?.[binName];
371
- if (!relative7) return void 0;
372
- const executable = join5(dirname(manifest), relative7);
371
+ const relative8 = typeof bin === "string" ? bin : bin?.[binName];
372
+ if (!relative8) return void 0;
373
+ const executable = join5(dirname(manifest), relative8);
373
374
  return existsSync4(executable) ? executable : void 0;
374
375
  } catch {
375
376
  return void 0;
@@ -747,8 +748,9 @@ function resolveVitest(cwd) {
747
748
 
748
749
  // src/roles/test/adapters/vitest/vitest.adapter.ts
749
750
  import { spawnSync } from "node:child_process";
750
- import { readFileSync as readFileSync24, writeFileSync as writeFileSync2 } from "node:fs";
751
- import { isAbsolute as isAbsolute2, join as join30, relative as relative6, resolve as resolve9 } from "node:path";
751
+ import { existsSync as existsSync25, mkdtempSync as mkdtempSync3, readFileSync as readFileSync25, writeFileSync as writeFileSync2 } from "node:fs";
752
+ import { tmpdir as tmpdir3 } from "node:os";
753
+ import { isAbsolute as isAbsolute2, join as join31, relative as relative7, resolve as resolve10 } from "node:path";
752
754
 
753
755
  // src/core/config/tool-args.ts
754
756
  import { existsSync as existsSync6 } from "node:fs";
@@ -920,14 +922,14 @@ function rewriteToolchainImports(toolchain, source, fileName) {
920
922
  }
921
923
 
922
924
  // src/roles/test/jest-migration.ts
923
- import { existsSync as existsSync19, readFileSync as readFileSync20, readdirSync as readdirSync6 } from "node:fs";
925
+ import { existsSync as existsSync19, readFileSync as readFileSync21, readdirSync as readdirSync6 } from "node:fs";
924
926
  import { createRequire as createRequire5 } from "node:module";
925
- import { dirname as dirname11, join as join24, relative as relative5 } from "node:path";
927
+ import { dirname as dirname12, join as join25, relative as relative6 } from "node:path";
926
928
 
927
929
  // src/roles/test/codemod/migrate-module.ts
928
- import { readdirSync as readdirSync4, readFileSync as readFileSync15, statSync as statSync4 } from "node:fs";
929
- import { join as join17 } from "node:path";
930
- import { parseSync as parseSync8 } from "oxc-parser";
930
+ import { readdirSync as readdirSync4, readFileSync as readFileSync16, statSync as statSync5 } from "node:fs";
931
+ import { join as join18 } from "node:path";
932
+ import { parseSync as parseSync9 } from "oxc-parser";
931
933
 
932
934
  // src/roles/test/msw-lifecycle.ts
933
935
  import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
@@ -950,11 +952,11 @@ function ownsMswLifecycle(cwd, setupFiles) {
950
952
  // src/roles/test/support-file-name.ts
951
953
  var JEST_PREFIX = "jest.";
952
954
  var VITEST_PREFIX = "vitest.";
953
- function vitestSupportName(relative7) {
954
- const cut = relative7.lastIndexOf("/");
955
- const directory = cut === -1 ? "" : relative7.slice(0, cut + 1);
956
- const base = relative7.slice(cut + 1);
957
- if (!base.startsWith(JEST_PREFIX)) return relative7;
955
+ function vitestSupportName(relative8) {
956
+ const cut = relative8.lastIndexOf("/");
957
+ const directory = cut === -1 ? "" : relative8.slice(0, cut + 1);
958
+ const base = relative8.slice(cut + 1);
959
+ if (!base.startsWith(JEST_PREFIX)) return relative8;
958
960
  return `${directory}${VITEST_PREFIX}${base.slice(JEST_PREFIX.length)}`;
959
961
  }
960
962
 
@@ -1007,7 +1009,7 @@ function bareModuleMocks(program) {
1007
1009
  });
1008
1010
  return found;
1009
1011
  }
1010
- function inheritedAutomocks(cwd, setupFiles, parse4) {
1012
+ function inheritedAutomocks(cwd, setupFiles, parse5) {
1011
1013
  const found = /* @__PURE__ */ new Set();
1012
1014
  for (const named of setupFiles) {
1013
1015
  const path = resolve3(cwd, named.replace("<rootDir>/", "./"));
@@ -1019,7 +1021,7 @@ function inheritedAutomocks(cwd, setupFiles, parse4) {
1019
1021
  } catch {
1020
1022
  continue;
1021
1023
  }
1022
- const program = parse4(path, source);
1024
+ const program = parse5(path, source);
1023
1025
  if (program === void 0) continue;
1024
1026
  for (const specifier of bareModuleMocks(program)) found.add(specifier);
1025
1027
  }
@@ -1243,8 +1245,8 @@ ${source}`;
1243
1245
  }
1244
1246
 
1245
1247
  // src/roles/test/codemod/migrate-file.ts
1246
- import { dirname as dirname7 } from "node:path";
1247
- import { parseSync as parseSync7 } from "oxc-parser";
1248
+ import { dirname as dirname8 } from "node:path";
1249
+ import { parseSync as parseSync8 } from "oxc-parser";
1248
1250
 
1249
1251
  // src/roles/test/codemod/assertion-return-value.ts
1250
1252
  function rootedAtExpect(node) {
@@ -1856,9 +1858,9 @@ function hoistable(name, bindings, imported, seen = /* @__PURE__ */ new Set()) {
1856
1858
  seen.add(name);
1857
1859
  const init = bindings.get(name);
1858
1860
  if (init === void 0) return false;
1859
- for (const read3 of readIdentifiers(init)) {
1860
- if (imported.has(read3)) return false;
1861
- if (bindings.has(read3) && !hoistable(read3, bindings, imported, seen)) return false;
1861
+ for (const read4 of readIdentifiers(init)) {
1862
+ if (imported.has(read4)) return false;
1863
+ if (bindings.has(read4) && !hoistable(read4, bindings, imported, seen)) return false;
1862
1864
  }
1863
1865
  return true;
1864
1866
  }
@@ -1885,8 +1887,8 @@ function simpleDeclarations(program) {
1885
1887
  }
1886
1888
  function interdependent(names, bindings) {
1887
1889
  for (const name of names) {
1888
- for (const read3 of readIdentifiers(bindings.get(name))) {
1889
- if (read3 !== name && names.includes(read3)) return `${name} reads ${read3}`;
1890
+ for (const read4 of readIdentifiers(bindings.get(name))) {
1891
+ if (read4 !== name && names.includes(read4)) return `${name} reads ${read4}`;
1890
1892
  }
1891
1893
  }
1892
1894
  return void 0;
@@ -2140,8 +2142,8 @@ function reachesBack(fromFile, specifier) {
2140
2142
  } catch {
2141
2143
  continue;
2142
2144
  }
2143
- for (const relative7 of relativeSpecifiers(source)) {
2144
- const next = resolveRelative(file, relative7);
2145
+ for (const relative8 of relativeSpecifiers(source)) {
2146
+ const next = resolveRelative(file, relative8);
2145
2147
  if (next !== void 0) stack.push(next);
2146
2148
  }
2147
2149
  }
@@ -2151,9 +2153,9 @@ function namedRequireDeclarations(program) {
2151
2153
  const found = /* @__PURE__ */ new Map();
2152
2154
  walk(program, (node) => {
2153
2155
  if (node.type !== "VariableDeclaration") return;
2154
- const declarators = node.declarations ?? [];
2155
- if (declarators.length !== 1) return;
2156
- const declarator = declarators[0];
2156
+ const declarators2 = node.declarations ?? [];
2157
+ if (declarators2.length !== 1) return;
2158
+ const declarator = declarators2[0];
2157
2159
  const init = declarator.init;
2158
2160
  if (init?.type !== "CallExpression") return;
2159
2161
  const callee = init.callee;
@@ -2446,9 +2448,9 @@ function applyNamedRules(source, program, fileName = "") {
2446
2448
  const refusals = [];
2447
2449
  const staticImports = [];
2448
2450
  const namespaces = namespaceBindings(program);
2449
- const castIfTestOnly = (specifier, read3) => {
2451
+ const castIfTestOnly = (specifier, read4) => {
2450
2452
  const keys = testOnly.get(specifier);
2451
- return keys === void 0 ? read3 : `(${read3}${testOnlyExportCast(keys)})`;
2453
+ return keys === void 0 ? read4 : `(${read4}${testOnlyExportCast(keys)})`;
2452
2454
  };
2453
2455
  const mocked = mockedSpecifiers(program);
2454
2456
  const testOnly = testOnlyExports(program);
@@ -3625,6 +3627,367 @@ function rewriteMockFactoryGenerics(source, program) {
3625
3627
  return { source: result, rewritten: edits.length, typeImports: [] };
3626
3628
  }
3627
3629
 
3630
+ // src/roles/test/codemod/spy-on-internal-call.ts
3631
+ import { readFileSync as readFileSync14, statSync as statSync3 } from "node:fs";
3632
+ import { dirname as dirname7, join as join16, relative as relative3, resolve as resolve8 } from "node:path";
3633
+ import { parseSync as parseSync7 } from "oxc-parser";
3634
+ var EXTENSIONS3 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
3635
+ function resolveRelative2(fromFile, specifier) {
3636
+ if (!specifier.startsWith(".")) return void 0;
3637
+ const base = resolve8(dirname7(fromFile), specifier);
3638
+ const candidates = [
3639
+ base,
3640
+ ...EXTENSIONS3.map((extension) => `${base}${extension}`),
3641
+ ...EXTENSIONS3.map((extension) => join16(base, `index${extension}`))
3642
+ ];
3643
+ for (const candidate of candidates) {
3644
+ try {
3645
+ if (statSync3(candidate).isFile()) return candidate;
3646
+ } catch {
3647
+ }
3648
+ }
3649
+ return void 0;
3650
+ }
3651
+ function read(path) {
3652
+ try {
3653
+ return readFileSync14(path, "utf8");
3654
+ } catch {
3655
+ return void 0;
3656
+ }
3657
+ }
3658
+ function parse2(fileName, source) {
3659
+ const { program, errors } = parseSync7(fileName, source, { sourceType: "module" });
3660
+ return errors.length > 0 ? void 0 : program;
3661
+ }
3662
+ function unwrap(node) {
3663
+ let current = node;
3664
+ while (current?.type === "ParenthesizedExpression") current = current.expression;
3665
+ return current;
3666
+ }
3667
+ function usesSemicolons2(source) {
3668
+ const first = /^import\s[^\n]*$/m.exec(source);
3669
+ return first !== null && first[0].trimEnd().endsWith(";");
3670
+ }
3671
+ function lineOf2(source, offset) {
3672
+ let line = 1;
3673
+ for (let index = 0; index < offset && index < source.length; index += 1) {
3674
+ if (source[index] === "\n") line += 1;
3675
+ }
3676
+ return line;
3677
+ }
3678
+ function namespaceImports(program) {
3679
+ const found = /* @__PURE__ */ new Map();
3680
+ for (const statement2 of program.body ?? []) {
3681
+ if (statement2.type !== "ImportDeclaration") continue;
3682
+ const source = statement2.source;
3683
+ if (source === void 0) continue;
3684
+ for (const clause of statement2.specifiers ?? []) {
3685
+ if (clause.type !== "ImportNamespaceSpecifier") continue;
3686
+ const local = clause.local;
3687
+ if (local?.type === "Identifier") found.set(local.name, String(source.value));
3688
+ }
3689
+ }
3690
+ return found;
3691
+ }
3692
+ function stringValue(node) {
3693
+ if (node?.type !== "Literal" && node?.type !== "StringLiteral") return void 0;
3694
+ return typeof node.value === "string" ? node.value : void 0;
3695
+ }
3696
+ function namespacedCall(node, member) {
3697
+ if (node.type !== "CallExpression") return void 0;
3698
+ const callee = node.callee;
3699
+ if (callee === void 0 || !MEMBER_TYPES.has(callee.type)) return void 0;
3700
+ const object = callee.object;
3701
+ const property = callee.property;
3702
+ if (object?.type !== "Identifier" || object.name !== "vi" && object.name !== "jest") {
3703
+ return void 0;
3704
+ }
3705
+ if (property?.type !== "Identifier" || property.name !== member) return void 0;
3706
+ return node.arguments ?? [];
3707
+ }
3708
+ function declarationOf(program, exported) {
3709
+ const body = program.body ?? [];
3710
+ const direct = (declaration, name) => {
3711
+ if (declaration === void 0) return void 0;
3712
+ if (declaration.type === "VariableDeclaration") {
3713
+ for (const declarator of declaration.declarations ?? []) {
3714
+ const id2 = declarator.id;
3715
+ if (id2?.type === "Identifier" && id2.name === name) return declarator;
3716
+ }
3717
+ return void 0;
3718
+ }
3719
+ const id = declaration.id;
3720
+ return id?.type === "Identifier" && id.name === name ? declaration : void 0;
3721
+ };
3722
+ for (const statement2 of body) {
3723
+ if (statement2.type !== "ExportNamedDeclaration") continue;
3724
+ const found = direct(statement2.declaration, exported);
3725
+ if (found !== void 0) return { node: found, local: exported };
3726
+ }
3727
+ for (const statement2 of body) {
3728
+ if (statement2.type !== "ExportNamedDeclaration") continue;
3729
+ if (statement2.source !== void 0 && statement2.source !== null) continue;
3730
+ for (const clause of statement2.specifiers ?? []) {
3731
+ const outward = clause.exported;
3732
+ const local = clause.local;
3733
+ const outwardName = outward?.type === "Identifier" ? outward.name : void 0;
3734
+ if (outwardName !== exported || local?.type !== "Identifier") continue;
3735
+ const localName = local.name;
3736
+ for (const candidate of body) {
3737
+ const declaration = candidate.type === "ExportNamedDeclaration" ? candidate.declaration : candidate;
3738
+ const found = direct(declaration, localName);
3739
+ if (found !== void 0) return { node: found, local: localName };
3740
+ }
3741
+ }
3742
+ }
3743
+ return void 0;
3744
+ }
3745
+ function internalCallLines(source, program, local) {
3746
+ const lines = [];
3747
+ walk(program, (node) => {
3748
+ if (node.type !== "CallExpression") return;
3749
+ const callee = node.callee;
3750
+ if (callee?.type !== "Identifier" || callee.name !== local) return;
3751
+ lines.push(lineOf2(source, node.start));
3752
+ });
3753
+ return [...new Set(lines)].sort((left, right) => left - right);
3754
+ }
3755
+ function importOf(program, local) {
3756
+ for (const statement2 of program.body ?? []) {
3757
+ if (statement2.type !== "ImportDeclaration") continue;
3758
+ if (statement2.importKind === "type") continue;
3759
+ const source = statement2.source;
3760
+ if (source === void 0) continue;
3761
+ for (const clause of statement2.specifiers ?? []) {
3762
+ const bound = clause.local;
3763
+ if (bound?.type !== "Identifier" || bound.name !== local) continue;
3764
+ if (clause.type !== "ImportSpecifier" || clause.importKind === "type") return void 0;
3765
+ const imported = clause.imported;
3766
+ const name = imported?.type === "Identifier" ? imported.name : stringValue(imported);
3767
+ if (name === void 0) return void 0;
3768
+ return { imported: name, from: String(source.value) };
3769
+ }
3770
+ }
3771
+ return void 0;
3772
+ }
3773
+ function respell(fromFile, target) {
3774
+ const path = relative3(dirname7(fromFile), target).replaceAll("\\", "/").replace(/\.(?:m|c)?[jt]sx?$/, "").replace(/\/index$/, "");
3775
+ return path.startsWith(".") ? path : `./${path}`;
3776
+ }
3777
+ function namesInUse2(program) {
3778
+ const used = /* @__PURE__ */ new Set();
3779
+ walk(program, (node) => {
3780
+ if (node.type === "Identifier") used.add(node.name);
3781
+ });
3782
+ return used;
3783
+ }
3784
+ function declarators(program) {
3785
+ const found = /* @__PURE__ */ new Map();
3786
+ walk(program, (node) => {
3787
+ if (node.type !== "VariableDeclaration") return;
3788
+ const declaration = node.declarations ?? [];
3789
+ if (declaration.length !== 1) return;
3790
+ const declarator = declaration[0];
3791
+ const id = declarator.id;
3792
+ const init = unwrap(declarator.init);
3793
+ if (id?.type !== "Identifier" || init === void 0) return;
3794
+ found.set(init, { name: id.name, id, statement: node });
3795
+ });
3796
+ return found;
3797
+ }
3798
+ function statementSpan2(source, statement2) {
3799
+ let end = statement2.end;
3800
+ if (source[end] === "\r") end++;
3801
+ if (source[end] === "\n") end++;
3802
+ return { start: statement2.start, end };
3803
+ }
3804
+ function existingMocks(program) {
3805
+ const found = /* @__PURE__ */ new Map();
3806
+ walk(program, (node) => {
3807
+ const args = namespacedCall(node, "mock");
3808
+ if (args === void 0) return;
3809
+ const specifier = stringValue(args[0]);
3810
+ if (specifier === void 0) return;
3811
+ const getters = found.get(specifier) ?? /* @__PURE__ */ new Set();
3812
+ walk(args[1], (inner) => {
3813
+ if (inner.type !== "Property" && inner.type !== "ObjectProperty") return;
3814
+ if (inner.kind !== "get") return;
3815
+ const key = inner.key;
3816
+ if (key?.type === "Identifier") getters.add(key.name);
3817
+ });
3818
+ found.set(specifier, getters);
3819
+ });
3820
+ return found;
3821
+ }
3822
+ function drivenValues(source, program, binding) {
3823
+ const statementOf = /* @__PURE__ */ new Map();
3824
+ walk(program, (node) => {
3825
+ if (node.type !== "ExpressionStatement") return;
3826
+ const expression = unwrap(node.expression);
3827
+ if (expression !== void 0) statementOf.set(expression, node);
3828
+ });
3829
+ const values = [];
3830
+ const used = /* @__PURE__ */ new Set();
3831
+ let chained = false;
3832
+ walk(program, (node) => {
3833
+ if (node.type !== "CallExpression") return;
3834
+ const callee = node.callee;
3835
+ if (callee === void 0 || !MEMBER_TYPES.has(callee.type)) return;
3836
+ const object = unwrap(callee.object);
3837
+ const property = callee.property;
3838
+ if (object?.type !== "Identifier" || object.name !== binding) return;
3839
+ if (property?.type !== "Identifier" || property.name !== "mockReturnValue") return;
3840
+ const args = node.arguments ?? [];
3841
+ const statement2 = statementOf.get(node);
3842
+ if (args.length !== 1 || statement2 === void 0) {
3843
+ chained = true;
3844
+ return;
3845
+ }
3846
+ const argument = args[0];
3847
+ values.push({
3848
+ start: statement2.start,
3849
+ end: statement2.end,
3850
+ value: source.slice(argument.start, argument.end)
3851
+ });
3852
+ used.add(object);
3853
+ });
3854
+ return { values, chained, used };
3855
+ }
3856
+ function remainingUses(program, binding, spent) {
3857
+ let count = 0;
3858
+ walk(program, (node) => {
3859
+ if (node.type !== "Identifier" || node.name !== binding) return;
3860
+ if (spent.has(node)) return;
3861
+ count += 1;
3862
+ });
3863
+ return count;
3864
+ }
3865
+ function rewriteInternalCallSpies(fileName, source, program) {
3866
+ const unchanged = { source, rewritten: [], refusals: [] };
3867
+ if (!source.includes("spyOn")) return unchanged;
3868
+ const namespaces = namespaceImports(program);
3869
+ if (namespaces.size === 0) return unchanged;
3870
+ const bindings = declarators(program);
3871
+ const mocked = existingMocks(program);
3872
+ const taken = namesInUse2(program);
3873
+ const refusals = [];
3874
+ const rewritten = [];
3875
+ const edits = [];
3876
+ const flagLines = [];
3877
+ const getters = /* @__PURE__ */ new Map();
3878
+ walk(program, (node) => {
3879
+ const args = namespacedCall(node, "spyOn");
3880
+ if (args === void 0) return;
3881
+ const target = unwrap(args[0]);
3882
+ const exported = stringValue(args[1]);
3883
+ if (target?.type !== "Identifier" || exported === void 0) return;
3884
+ const specifier = namespaces.get(target.name);
3885
+ if (specifier === void 0) return;
3886
+ const modulePath = resolveRelative2(fileName, specifier);
3887
+ if (modulePath === void 0) return;
3888
+ const moduleSource = read(modulePath);
3889
+ if (moduleSource === void 0) return;
3890
+ const moduleProgram = parse2(modulePath, moduleSource);
3891
+ if (moduleProgram === void 0) return;
3892
+ const declared = declarationOf(moduleProgram, exported);
3893
+ if (declared === void 0) return;
3894
+ const internal = internalCallLines(moduleSource, moduleProgram, declared.local);
3895
+ if (internal.length === 0) return;
3896
+ const where = `\`${target.name}.${exported}\`, spied in this file, is called ${internal.length} time(s) by \`${respell(fileName, modulePath)}\` itself (line${internal.length > 1 ? "s" : ""} ${internal.join(", ")}). Under ESM a spy on the namespace does not reach those calls`;
3897
+ const refuse = (because) => {
3898
+ refusals.push({ binding: exported, reason: `${where}, and ${because}` });
3899
+ };
3900
+ const derived = deriveRemedy(fileName, modulePath, moduleProgram, declared);
3901
+ if (typeof derived === "string") return refuse(derived);
3902
+ if (mocked.get(derived.specifier)?.has(derived.imported) === true) return;
3903
+ if (mocked.has(derived.specifier)) {
3904
+ return refuse(
3905
+ `this file already mocks \`${derived.specifier}\`. Merging a getter into a factory somebody wrote is guessing at what it is for: add \`get ${derived.imported}()\` to it by hand, or drive the spy from there`
3906
+ );
3907
+ }
3908
+ const declaration = bindings.get(node);
3909
+ if (declaration === void 0) {
3910
+ return refuse(
3911
+ `the spy is not bound to a variable here, so there is nothing whose \`mockReturnValue\` can be transposed onto the value \`${exported}\` reads`
3912
+ );
3913
+ }
3914
+ const driven = drivenValues(source, program, declaration.name);
3915
+ if (driven.chained) {
3916
+ return refuse(
3917
+ `\`${declaration.name}.mockReturnValue(...)\` is used as an expression rather than as a statement of its own, and an assignment cannot be chained onto`
3918
+ );
3919
+ }
3920
+ let flag = `${exported}Flag`;
3921
+ for (let suffix = 2; taken.has(flag); suffix += 1) flag = `${exported}Flag${suffix}`;
3922
+ taken.add(flag);
3923
+ flagLines.push(
3924
+ `const { ${flag} } = vi.hoisted(() => ({
3925
+ ${flag}: { value: undefined as ReturnType<typeof ${target.name}.${exported}> | undefined },
3926
+ }))`
3927
+ );
3928
+ getters.set(derived.specifier, [
3929
+ ...getters.get(derived.specifier) ?? [],
3930
+ { imported: derived.imported, flag }
3931
+ ]);
3932
+ for (const value of driven.values) {
3933
+ edits.push({ start: value.start, end: value.end, text: `${flag}.value = ${value.value}` });
3934
+ }
3935
+ const spent = /* @__PURE__ */ new Set([...driven.used, declaration.id]);
3936
+ if (remainingUses(program, declaration.name, spent) === 0) {
3937
+ edits.push({ ...statementSpan2(source, declaration.statement), text: "" });
3938
+ }
3939
+ rewritten.push(exported);
3940
+ });
3941
+ if (flagLines.length === 0) return { source, rewritten: [], refusals };
3942
+ const semi = usesSemicolons2(source) ? ";" : "";
3943
+ const blocks = [
3944
+ ...flagLines.map((line) => `${line}${semi}`),
3945
+ ...[...getters].map(([specifier, entries]) => renderMock(specifier, entries, semi))
3946
+ ].join("\n\n");
3947
+ const imports = (program.body ?? []).filter(
3948
+ (statement2) => statement2.type === "ImportDeclaration"
3949
+ );
3950
+ const at = imports[imports.length - 1].end;
3951
+ edits.push({ start: at, end: at, text: `
3952
+
3953
+ ${blocks}` });
3954
+ return { source: applyEdits(source, edits), rewritten, refusals };
3955
+ }
3956
+ function deriveRemedy(fileName, modulePath, moduleProgram, declared) {
3957
+ if (declared.node.type !== "VariableDeclarator") {
3958
+ return `\`${declared.local}\` is declared as a \`${declared.node.type}\`, not as \`export const ${declared.local} = () => IDENT\`, so there is no single value behind it to drive instead`;
3959
+ }
3960
+ const init = unwrap(declared.node.init);
3961
+ const body = unwrap(init?.body);
3962
+ const parameters = init?.params ?? [];
3963
+ if (init?.type !== "ArrowFunctionExpression" || init.async === true || parameters.length > 0 || body?.type !== "Identifier") {
3964
+ return `\`${declared.local}\` is not \`export const ${declared.local} = () => IDENT\`: only a parameterless arrow whose whole body is one identifier has a value underneath it that a mock can drive`;
3965
+ }
3966
+ const read4 = importOf(moduleProgram, body.name);
3967
+ if (read4 === void 0) {
3968
+ return `\`${declared.local}\` returns \`${body.name}\`, which that module does not import as a named value, so there is no other module to mock in its place`;
3969
+ }
3970
+ const target = resolveRelative2(modulePath, read4.from);
3971
+ if (target === void 0) {
3972
+ return `\`${body.name}\` comes from \`${read4.from}\`, which is not a relative module of that project. Mocking it from the spec would reach past this module, so this one needs a person`;
3973
+ }
3974
+ return { specifier: respell(fileName, target), imported: read4.imported };
3975
+ }
3976
+ function renderMock(specifier, entries, semi) {
3977
+ const body = entries.map(
3978
+ ({ imported, flag }) => ` get ${imported}() {
3979
+ return ${flag}.value ?? actual.${imported}
3980
+ },
3981
+ `
3982
+ ).join("");
3983
+ return `vi.mock('${specifier}', async () => {
3984
+ const actual = await vi.importActual<Record<string, unknown>>('${specifier}')${semi}
3985
+ return {
3986
+ ...actual,
3987
+ ${body} }${semi}
3988
+ })${semi}`;
3989
+ }
3990
+
3628
3991
  // src/roles/test/codemod/typed-matcher.ts
3629
3992
  var SINGLE_VALUE_MATCHERS = /* @__PURE__ */ new Set([
3630
3993
  "toBe",
@@ -3690,8 +4053,8 @@ function mockFactories(program) {
3690
4053
  });
3691
4054
  return factories;
3692
4055
  }
3693
- function parse2(fileName, source) {
3694
- const { program, errors } = parseSync7(fileName, source, { sourceType: "module" });
4056
+ function parse3(fileName, source) {
4057
+ const { program, errors } = parseSync8(fileName, source, { sourceType: "module" });
3695
4058
  return errors.length > 0 ? void 0 : program;
3696
4059
  }
3697
4060
  function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new Set(), asyncHelpers = /* @__PURE__ */ new Set(), modulesOwnMsw = false) {
@@ -3715,72 +4078,72 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
3715
4078
  }
3716
4079
  ]
3717
4080
  });
3718
- const beforeStatic = parse2(fileName, source);
4081
+ const beforeStatic = parse3(fileName, source);
3719
4082
  if (beforeStatic === void 0) return unparsable("reading the file");
3720
4083
  const statics = rewriteSetupStaticImports(
3721
4084
  fileName,
3722
4085
  source,
3723
4086
  beforeStatic,
3724
- findWorkspaceRoot(dirname7(fileName))
4087
+ findWorkspaceRoot(dirname8(fileName))
3725
4088
  );
3726
4089
  source = statics.source;
3727
- const original = statics.rewritten === 0 ? beforeStatic : parse2(fileName, source);
4090
+ const original = statics.rewritten === 0 ? beforeStatic : parse3(fileName, source);
3728
4091
  if (original === void 0) return unparsable("hoisting the setup requires");
3729
4092
  const titles = rewriteEachTitles(source, original);
3730
4093
  const tables = rewriteEachTableRows(titles.source, original);
3731
- const afterTables = parse2(fileName, tables.source);
4094
+ const afterTables = parse3(fileName, tables.source);
3732
4095
  if (afterTables === void 0) return unparsable("each table rows");
3733
4096
  const spyTypes = rewriteSpyInstanceType(tables.source, afterTables);
3734
- const afterSpyTypes = parse2(fileName, spyTypes.source);
4097
+ const afterSpyTypes = parse3(fileName, spyTypes.source);
3735
4098
  if (afterSpyTypes === void 0) return unparsable("SpyInstance types");
3736
4099
  const factoryGenerics = rewriteMockFactoryGenerics(spyTypes.source, afterSpyTypes);
3737
- const afterFactoryGenerics = parse2(fileName, factoryGenerics.source);
4100
+ const afterFactoryGenerics = parse3(fileName, factoryGenerics.source);
3738
4101
  if (afterFactoryGenerics === void 0) return unparsable("mock factory generics");
3739
4102
  const artefacts = rewriteRunnerArtefactNames(factoryGenerics.source, afterFactoryGenerics);
3740
- const afterArtefacts = parse2(fileName, artefacts);
4103
+ const afterArtefacts = parse3(fileName, artefacts);
3741
4104
  if (afterArtefacts === void 0) return unparsable("runner artefact names");
3742
4105
  const matchers = rewriteTypedMatchers(artefacts, afterArtefacts);
3743
- const afterMatchers = parse2(fileName, matchers.source);
4106
+ const afterMatchers = parse3(fileName, matchers.source);
3744
4107
  if (afterMatchers === void 0) return unparsable("matcher type arguments");
3745
4108
  const ruled = applyNamedRules(matchers.source, afterMatchers, fileName);
3746
4109
  const renamed = renameJestToVitest(fileName, ruled.source);
3747
- const afterRename = parse2(fileName, renamed.source);
4110
+ const afterRename = parse3(fileName, renamed.source);
3748
4111
  if (afterRename === void 0) return unparsable("jest to vi rename");
3749
4112
  const fakeTimers = rewriteFakeTimerOptions(renamed.source, afterRename);
3750
- const afterFakeTimers = parse2(fileName, fakeTimers.source);
4113
+ const afterFakeTimers = parse3(fileName, fakeTimers.source);
3751
4114
  if (afterFakeTimers === void 0) return unparsable("fake timer options");
3752
4115
  const mocks = rewriteMockAsValue(fakeTimers.source, afterFakeTimers);
3753
- const afterMocks = parse2(fileName, mocks.source);
4116
+ const afterMocks = parse3(fileName, mocks.source);
3754
4117
  if (afterMocks === void 0) return unparsable("mock as a value");
3755
4118
  const constructible = rewriteConstructibleMocks(
3756
4119
  mocks.source,
3757
4120
  afterMocks,
3758
4121
  mockFactories(afterMocks)
3759
4122
  );
3760
- const afterConstructible = parse2(fileName, constructible.source);
4123
+ const afterConstructible = parse3(fileName, constructible.source);
3761
4124
  if (afterConstructible === void 0) return unparsable("constructible mocks");
3762
4125
  const factoryDefault = rewriteMockFactoryDefault(
3763
4126
  constructible.source,
3764
4127
  afterConstructible,
3765
4128
  defaultImported
3766
4129
  );
3767
- const beforeReturnValue = parse2(fileName, factoryDefault.source);
4130
+ const beforeReturnValue = parse3(fileName, factoryDefault.source);
3768
4131
  if (beforeReturnValue === void 0) return unparsable("mock factory default");
3769
4132
  const returnValue = rewriteAssertionReturnValue(factoryDefault.source, beforeReturnValue);
3770
- const afterReturnValue = parse2(fileName, returnValue.source);
4133
+ const afterReturnValue = parse3(fileName, returnValue.source);
3771
4134
  if (afterReturnValue === void 0) return unparsable("assertion return value");
3772
4135
  const isolated = rewriteIsolateModules(returnValue.source, afterReturnValue);
3773
- const afterIsolate = parse2(fileName, isolated.source);
4136
+ const afterIsolate = parse3(fileName, isolated.source);
3774
4137
  if (afterIsolate === void 0) return unparsable("isolate modules");
3775
4138
  const emptyImplementations = rewriteEmptyMockImplementation(isolated.source, afterIsolate);
3776
- const afterEmpty = parse2(fileName, emptyImplementations.source);
4139
+ const afterEmpty = parse3(fileName, emptyImplementations.source);
3777
4140
  if (afterEmpty === void 0) return unparsable("empty mock implementation");
3778
4141
  const hooks = rewriteHookReturnValue(emptyImplementations.source, afterEmpty);
3779
- const afterHooks = parse2(fileName, hooks.source);
4142
+ const afterHooks = parse3(fileName, hooks.source);
3780
4143
  if (afterHooks === void 0) return unparsable("hook return value");
3781
4144
  const promises = rewriteMockPromiseResolve(hooks.source, afterHooks);
3782
4145
  const awaited = propagateAwait(fileName, promises.source, original, asyncHelpers);
3783
- const program = parse2(fileName, awaited.source);
4146
+ const program = parse3(fileName, awaited.source);
3784
4147
  if (program === void 0) return unparsable("hook body");
3785
4148
  const hoist = hoistCapturedBindings(
3786
4149
  awaited.source,
@@ -3789,10 +4152,10 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
3789
4152
  moduleBindings(program),
3790
4153
  importedNames(program)
3791
4154
  );
3792
- const afterHoist = parse2(fileName, hoist.source);
4155
+ const afterHoist = parse3(fileName, hoist.source);
3793
4156
  if (afterHoist === void 0) return unparsable("hoisting captured bindings");
3794
4157
  const mockedBindings = annotateMockedBindings(hoist.source, afterHoist);
3795
- const afterBindings = parse2(fileName, mockedBindings.source);
4158
+ const afterBindings = parse3(fileName, mockedBindings.source);
3796
4159
  if (afterBindings === void 0) return unparsable("mocked module bindings");
3797
4160
  const bareFactories = invokeBareMockFactories(mockedBindings.source, afterBindings);
3798
4161
  const withImport = injectTypeImport(fileName, bareFactories.source, [
@@ -3802,12 +4165,15 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
3802
4165
  ]);
3803
4166
  const deep = normaliseDeepImports(withImport);
3804
4167
  const repointed = modulesOwnMsw ? { source: deep.source, repointed: false } : repointMswServer(fileName, deep.source);
3805
- const finalProgram = parse2(fileName, repointed.source);
4168
+ const finalProgram = parse3(fileName, repointed.source);
3806
4169
  if (finalProgram === void 0) return unparsable("msw server import");
3807
4170
  const barrels = rewriteReexportBarrelMocks(fileName, repointed.source, finalProgram);
4171
+ const afterBarrels = parse3(fileName, barrels.source);
4172
+ if (afterBarrels === void 0) return unparsable("re-export barrel mocks");
4173
+ const internalSpies = rewriteInternalCallSpies(fileName, barrels.source, afterBarrels);
3808
4174
  return {
3809
- source: barrels.source,
3810
- changed: barrels.source !== source,
4175
+ source: internalSpies.source,
4176
+ changed: internalSpies.source !== source,
3811
4177
  renamed: renamed.rewritten,
3812
4178
  hoisted: hoist.hoisted,
3813
4179
  unhandled: renamed.unhandled,
@@ -3825,6 +4191,9 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
3825
4191
  ...bareFactories.rewritten > 0 ? ["vi.fn passed as a value, invoked"] : [],
3826
4192
  ...awaited.rewritten > 0 ? ["await propagated to a helper that became async"] : [],
3827
4193
  ...barrels.added.map((alias) => `mock carried to ${alias}, which re-exports the mocked file`),
4194
+ ...internalSpies.rewritten.map(
4195
+ (name) => `spy on ${name}, replaced by the value its own module reads`
4196
+ ),
3828
4197
  ...fakeTimers.translated
3829
4198
  ].sort(),
3830
4199
  refusals: [
@@ -3844,7 +4213,8 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
3844
4213
  ...constructible.refusals,
3845
4214
  ...isolated.refusals,
3846
4215
  ...matchers.refusals,
3847
- ...ruled.refusals
4216
+ ...ruled.refusals,
4217
+ ...internalSpies.refusals
3848
4218
  ],
3849
4219
  warnings: fakeTimers.warnings,
3850
4220
  mswRepointed: repointed.repointed
@@ -3852,13 +4222,13 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
3852
4222
  }
3853
4223
 
3854
4224
  // src/roles/test/codemod/test-support-files.ts
3855
- import { existsSync as existsSync14, readFileSync as readFileSync14, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
3856
- import { dirname as dirname8, join as join16, relative as relative3, resolve as resolve8 } from "node:path";
4225
+ import { existsSync as existsSync14, readFileSync as readFileSync15, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
4226
+ import { dirname as dirname9, join as join17, relative as relative4, resolve as resolve9 } from "node:path";
3857
4227
  var THE_SHIM_CANNOT_ANSWER = [/\brequire\s*\(/, /\bjest\.(?:fn|Mock|SpyInstance)\s*</];
3858
4228
  var SKIPPED = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage", ".git", ".vite"]);
3859
4229
  var TEST_FILE2 = /[.-](spec|test)\.[cm]?[jt]sx?$/;
3860
4230
  var SOURCE_FILE = /\.[cm]?[jt]sx?$/;
3861
- var EXTENSIONS3 = [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"];
4231
+ var EXTENSIONS4 = [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"];
3862
4232
  function relativeSpecifiers2(source) {
3863
4233
  const found = [];
3864
4234
  const patterns = [
@@ -3873,13 +4243,13 @@ function relativeSpecifiers2(source) {
3873
4243
  return found;
3874
4244
  }
3875
4245
  function resolveSpecifier(fromFile, specifier) {
3876
- const base = resolve8(dirname8(fromFile), specifier);
3877
- if (existsSync14(base) && statSync3(base).isFile()) return base;
3878
- for (const extension of EXTENSIONS3) {
4246
+ const base = resolve9(dirname9(fromFile), specifier);
4247
+ if (existsSync14(base) && statSync4(base).isFile()) return base;
4248
+ for (const extension of EXTENSIONS4) {
3879
4249
  if (existsSync14(`${base}${extension}`)) return `${base}${extension}`;
3880
4250
  }
3881
- for (const extension of EXTENSIONS3) {
3882
- const index = join16(base, `index${extension}`);
4251
+ for (const extension of EXTENSIONS4) {
4252
+ const index = join17(base, `index${extension}`);
3883
4253
  if (existsSync14(index)) return index;
3884
4254
  }
3885
4255
  return void 0;
@@ -3897,17 +4267,17 @@ function moduleSources(cwd) {
3897
4267
  }
3898
4268
  for (const entry of entries) {
3899
4269
  if (entry.isDirectory()) {
3900
- if (!SKIPPED.has(entry.name)) stack.push(join16(dir, entry.name));
4270
+ if (!SKIPPED.has(entry.name)) stack.push(join17(dir, entry.name));
3901
4271
  } else if (SOURCE_FILE.test(entry.name)) {
3902
- found.push(join16(dir, entry.name));
4272
+ found.push(join17(dir, entry.name));
3903
4273
  }
3904
4274
  }
3905
4275
  }
3906
4276
  return found;
3907
4277
  }
3908
- function read(path) {
4278
+ function read2(path) {
3909
4279
  try {
3910
- return readFileSync14(path, "utf8");
4280
+ return readFileSync15(path, "utf8");
3911
4281
  } catch {
3912
4282
  return void 0;
3913
4283
  }
@@ -3916,8 +4286,8 @@ function setupFilesInside(cwd, named) {
3916
4286
  const kept = [];
3917
4287
  for (const raw of named) {
3918
4288
  const withoutToken = raw.replaceAll("<rootDir>/", "").replaceAll("<rootDir>", ".");
3919
- const full = resolve8(cwd, withoutToken);
3920
- const inside = relative3(cwd, full);
4289
+ const full = resolve9(cwd, withoutToken);
4290
+ const inside = relative4(cwd, full);
3921
4291
  if (inside.startsWith("..") || inside === "") continue;
3922
4292
  if (!existsSync14(full)) continue;
3923
4293
  kept.push(inside.replaceAll("\\", "/"));
@@ -3928,7 +4298,7 @@ function listTestSupportFiles(cwd, setupFiles = []) {
3928
4298
  const sources = moduleSources(cwd);
3929
4299
  const contents = /* @__PURE__ */ new Map();
3930
4300
  for (const file of sources) {
3931
- const source = read(file);
4301
+ const source = read2(file);
3932
4302
  if (source !== void 0) contents.set(file, source);
3933
4303
  }
3934
4304
  const importedBy = /* @__PURE__ */ new Map();
@@ -3954,18 +4324,18 @@ function listTestSupportFiles(cwd, setupFiles = []) {
3954
4324
  queue.push(target);
3955
4325
  }
3956
4326
  }
3957
- const reachable = [...testOnly].filter((file) => THE_SHIM_CANNOT_ANSWER.some((it) => it.test(contents.get(file) ?? ""))).map((file) => relative3(cwd, file).replaceAll("\\", "/"));
4327
+ const reachable = [...testOnly].filter((file) => THE_SHIM_CANNOT_ANSWER.some((it) => it.test(contents.get(file) ?? ""))).map((file) => relative4(cwd, file).replaceAll("\\", "/"));
3958
4328
  return [.../* @__PURE__ */ new Set([...reachable, ...setupFilesInside(cwd, setupFiles)])].sort();
3959
4329
  }
3960
4330
 
3961
4331
  // src/roles/test/codemod/migrate-module.ts
3962
- function parse3(fileName, source) {
3963
- const { program, errors } = parseSync8(fileName, source, { sourceType: "module" });
4332
+ function parse4(fileName, source) {
4333
+ const { program, errors } = parseSync9(fileName, source, { sourceType: "module" });
3964
4334
  return errors.length > 0 ? void 0 : program;
3965
4335
  }
3966
- function read2(path) {
4336
+ function read3(path) {
3967
4337
  try {
3968
- return readFileSync15(path, "utf8");
4338
+ return readFileSync16(path, "utf8");
3969
4339
  } catch {
3970
4340
  return void 0;
3971
4341
  }
@@ -3983,10 +4353,10 @@ function defaultImportedSpecifiers(cwd) {
3983
4353
  }
3984
4354
  for (const entry of entries) {
3985
4355
  if (SKIPPED2.has(entry)) continue;
3986
- const path = join17(dir, entry);
4356
+ const path = join18(dir, entry);
3987
4357
  let directory;
3988
4358
  try {
3989
- directory = statSync4(path).isDirectory();
4359
+ directory = statSync5(path).isDirectory();
3990
4360
  } catch {
3991
4361
  continue;
3992
4362
  }
@@ -3995,7 +4365,7 @@ function defaultImportedSpecifiers(cwd) {
3995
4365
  continue;
3996
4366
  }
3997
4367
  if (!/\.tsx?$/.test(entry)) continue;
3998
- const source = read2(path);
4368
+ const source = read3(path);
3999
4369
  if (source === void 0) continue;
4000
4370
  for (const match of source.matchAll(pattern)) found.add(match[1]);
4001
4371
  }
@@ -4005,12 +4375,12 @@ function defaultImportedSpecifiers(cwd) {
4005
4375
  }
4006
4376
  function declaredInheritedMocks(fileName, source, inherited) {
4007
4377
  if (inherited.length === 0) return source;
4008
- const program = parse3(fileName, source);
4378
+ const program = parse4(fileName, source);
4009
4379
  return program === void 0 ? source : declareInheritedAutomocks(source, program, inherited);
4010
4380
  }
4011
4381
  function migrateModuleTests(cwd, setupFiles = []) {
4012
4382
  const modulesOwnMsw = ownsMswLifecycle(cwd, setupFiles);
4013
- const inherited = inheritedAutomocks(cwd, setupFiles, parse3);
4383
+ const inherited = inheritedAutomocks(cwd, setupFiles, parse4);
4014
4384
  const operations = [];
4015
4385
  const blockers = [];
4016
4386
  const collected = [];
@@ -4019,14 +4389,14 @@ function migrateModuleTests(cwd, setupFiles = []) {
4019
4389
  let hoisted = 0;
4020
4390
  const defaultImported = defaultImportedSpecifiers(cwd);
4021
4391
  const asyncHelpers = /* @__PURE__ */ new Set();
4022
- for (const relative7 of listTestSupportFiles(cwd, setupFiles)) {
4023
- const source = read2(join17(cwd, relative7));
4392
+ for (const relative8 of listTestSupportFiles(cwd, setupFiles)) {
4393
+ const source = read3(join18(cwd, relative8));
4024
4394
  if (source === void 0) {
4025
- blockers.push({ file: relative7, reason: "this file could not be read" });
4395
+ blockers.push({ file: relative8, reason: "this file could not be read" });
4026
4396
  continue;
4027
4397
  }
4028
4398
  const migrated = migrateTestFile(
4029
- join17(cwd, relative7),
4399
+ join18(cwd, relative8),
4030
4400
  source,
4031
4401
  defaultImported,
4032
4402
  /* @__PURE__ */ new Set(),
@@ -4034,19 +4404,19 @@ function migrateModuleTests(cwd, setupFiles = []) {
4034
4404
  );
4035
4405
  for (const member of migrated.unhandled) {
4036
4406
  blockers.push({
4037
- file: `${relative7}:${member.line}`,
4407
+ file: `${relative8}:${member.line}`,
4038
4408
  reason: member.rule === void 0 ? `${member.member} has no rule, and guessing one is how a suite goes green for the wrong reason` : `${member.member} needs ${member.rule}`
4039
4409
  });
4040
4410
  }
4041
4411
  for (const refusal of migrated.refusals) {
4042
- blockers.push({ file: relative7, reason: refusal.reason });
4412
+ blockers.push({ file: relative8, reason: refusal.reason });
4043
4413
  }
4044
4414
  collected.push(...migrated.warnings);
4045
- const renamedTo = vitestSupportName(relative7);
4046
- if (!migrated.changed && renamedTo === relative7) continue;
4047
- if (renamedTo !== relative7) operations.push({ kind: "delete", path: relative7 });
4048
- const before = parse3(join17(cwd, relative7), source);
4049
- const after = parse3(join17(cwd, relative7), migrated.source);
4415
+ const renamedTo = vitestSupportName(relative8);
4416
+ if (!migrated.changed && renamedTo === relative8) continue;
4417
+ if (renamedTo !== relative8) operations.push({ kind: "delete", path: relative8 });
4418
+ const before = parse4(join18(cwd, relative8), source);
4419
+ const after = parse4(join18(cwd, relative8), migrated.source);
4050
4420
  if (before !== void 0 && after !== void 0) {
4051
4421
  const wasAsync = asyncFunctionNames(before);
4052
4422
  for (const name of asyncFunctionNames(after)) {
@@ -4055,17 +4425,17 @@ function migrateModuleTests(cwd, setupFiles = []) {
4055
4425
  }
4056
4426
  files++;
4057
4427
  renamed += migrated.renamed;
4058
- const contents = /\bvi\./.test(migrated.source) ? injectViImport(join17(cwd, renamedTo), migrated.source) : migrated.source;
4428
+ const contents = /\bvi\./.test(migrated.source) ? injectViImport(join18(cwd, renamedTo), migrated.source) : migrated.source;
4059
4429
  operations.push({ kind: "write", path: renamedTo, contents });
4060
4430
  }
4061
- for (const relative7 of listTestFiles(cwd)) {
4062
- const source = read2(join17(cwd, relative7));
4431
+ for (const relative8 of listTestFiles(cwd)) {
4432
+ const source = read3(join18(cwd, relative8));
4063
4433
  if (source === void 0) {
4064
- blockers.push({ file: relative7, reason: "this file could not be read" });
4434
+ blockers.push({ file: relative8, reason: "this file could not be read" });
4065
4435
  continue;
4066
4436
  }
4067
4437
  const migrated = migrateTestFile(
4068
- join17(cwd, relative7),
4438
+ join18(cwd, relative8),
4069
4439
  source,
4070
4440
  defaultImported,
4071
4441
  asyncHelpers,
@@ -4073,32 +4443,32 @@ function migrateModuleTests(cwd, setupFiles = []) {
4073
4443
  );
4074
4444
  for (const member of migrated.unhandled) {
4075
4445
  blockers.push({
4076
- file: `${relative7}:${member.line}`,
4446
+ file: `${relative8}:${member.line}`,
4077
4447
  reason: member.rule === void 0 ? `${member.member} has no rule, and guessing one is how a suite goes green for the wrong reason` : `${member.member} needs ${member.rule}`
4078
4448
  });
4079
4449
  }
4080
4450
  collected.push(...migrated.warnings);
4081
4451
  for (const refusal of migrated.refusals) {
4082
- blockers.push({ file: relative7, reason: refusal.reason });
4452
+ blockers.push({ file: relative8, reason: refusal.reason });
4083
4453
  }
4084
4454
  if (!migrated.changed) continue;
4085
4455
  files++;
4086
4456
  renamed += migrated.renamed;
4087
4457
  if (migrated.hoisted.length > 0) hoisted++;
4088
4458
  const withInheritedMocks = declaredInheritedMocks(
4089
- join17(cwd, relative7),
4459
+ join18(cwd, relative8),
4090
4460
  migrated.source,
4091
4461
  inherited
4092
4462
  );
4093
- operations.push({ kind: "write", path: relative7, contents: withInheritedMocks });
4463
+ operations.push({ kind: "write", path: relative8, contents: withInheritedMocks });
4094
4464
  }
4095
4465
  const warnings = [...new Set(collected)];
4096
4466
  return blockers.length > 0 ? { operations: [], files, renamed, hoisted, blockers, warnings } : { operations, files, renamed, hoisted, blockers: [], warnings };
4097
4467
  }
4098
4468
 
4099
4469
  // src/roles/test/nest/generate-config.ts
4100
- import { readdirSync as readdirSync5, readFileSync as readFileSync16, statSync as statSync5 } from "node:fs";
4101
- import { join as join18, relative as relative4 } from "node:path";
4470
+ import { readdirSync as readdirSync5, readFileSync as readFileSync17, statSync as statSync6 } from "node:fs";
4471
+ import { join as join19, relative as relative5 } from "node:path";
4102
4472
  var NEST_TEST_PRESET_SPECIFIER = "@hublo/sentinel/test/nest";
4103
4473
  var REACT_TEST_PRESET_SPECIFIER = "@hublo/sentinel/test/react";
4104
4474
  function flavourOf(reading) {
@@ -4110,7 +4480,7 @@ var SENTINEL_WORKSPACE_SETUP = "@hublo/sentinel/test/setup/workspace";
4110
4480
  var SENTINEL_A11Y_SETUP = "@hublo/sentinel/test/setup/a11y";
4111
4481
  var SENTINEL_W3C_SETUP = "@hublo/sentinel/test/setup/w3c";
4112
4482
  function hopsToRoot(cwd, workspaceRoot) {
4113
- const up = relative4(cwd, workspaceRoot).replaceAll("\\", "/");
4483
+ const up = relative5(cwd, workspaceRoot).replaceAll("\\", "/");
4114
4484
  return up === "" ? "." : up;
4115
4485
  }
4116
4486
  function inlineBlock(packages) {
@@ -4144,10 +4514,10 @@ function readModuleTraits(cwd) {
4144
4514
  }
4145
4515
  for (const entry of entries) {
4146
4516
  if (SKIPPED3.has(entry)) continue;
4147
- const path = join18(dir, entry);
4517
+ const path = join19(dir, entry);
4148
4518
  let directory;
4149
4519
  try {
4150
- directory = statSync5(path).isDirectory();
4520
+ directory = statSync6(path).isDirectory();
4151
4521
  } catch {
4152
4522
  continue;
4153
4523
  }
@@ -4158,7 +4528,7 @@ function readModuleTraits(cwd) {
4158
4528
  if (!/\.tsx?$/.test(entry)) continue;
4159
4529
  let source;
4160
4530
  try {
4161
- source = readFileSync16(path, "utf8");
4531
+ source = readFileSync17(path, "utf8");
4162
4532
  } catch {
4163
4533
  continue;
4164
4534
  }
@@ -4293,8 +4663,8 @@ ${overrides.join("\n")}
4293
4663
  }
4294
4664
 
4295
4665
  // src/roles/test/nest/read-jest-config.ts
4296
- import { existsSync as existsSync15, readFileSync as readFileSync17 } from "node:fs";
4297
- import { join as join19 } from "node:path";
4666
+ import { existsSync as existsSync15, readFileSync as readFileSync18 } from "node:fs";
4667
+ import { join as join20 } from "node:path";
4298
4668
  var CARRIED = [
4299
4669
  "testEnvironment",
4300
4670
  "coverageDirectory",
@@ -4484,7 +4854,7 @@ function extendedConfig(source, cwd) {
4484
4854
  (name) => name.replace(/^\.\//, "")
4485
4855
  );
4486
4856
  const file = candidates.find(
4487
- (name) => /^jest[\w.-]*\.(ts|js)$/.test(name) && existsSync15(join19(cwd, name))
4857
+ (name) => /^jest[\w.-]*\.(ts|js)$/.test(name) && existsSync15(join20(cwd, name))
4488
4858
  );
4489
4859
  if (file === void 0) continue;
4490
4860
  if (!new RegExp(String.raw`\{\s*\.\.\.${binding}\b`).test(source)) continue;
@@ -4511,11 +4881,11 @@ function sideEffects(source, extended) {
4511
4881
  return found;
4512
4882
  }
4513
4883
  function readJestConfig(cwd, named) {
4514
- const file = named ?? JEST_CONFIG_FILES.find((name) => existsSync15(join19(cwd, name)));
4515
- if (file === void 0 || !existsSync15(join19(cwd, file))) return void 0;
4884
+ const file = named ?? JEST_CONFIG_FILES.find((name) => existsSync15(join20(cwd, name)));
4885
+ if (file === void 0 || !existsSync15(join20(cwd, file))) return void 0;
4516
4886
  let source;
4517
4887
  try {
4518
- source = readFileSync17(join19(cwd, file), "utf8");
4888
+ source = readFileSync18(join20(cwd, file), "utf8");
4519
4889
  } catch {
4520
4890
  return { file, refusals: [`${file} could not be read`], notes: [] };
4521
4891
  }
@@ -4597,7 +4967,7 @@ function readJestConfig(cwd, named) {
4597
4967
 
4598
4968
  // src/roles/test/nest/run-modes.ts
4599
4969
  import { existsSync as existsSync16 } from "node:fs";
4600
- import { basename as basename2, join as join20 } from "node:path";
4970
+ import { basename as basename2, join as join21 } from "node:path";
4601
4971
  var JEST_EXECUTOR = "@nx/jest:jest";
4602
4972
  var PRIMARY_CONFIG_FILE = "vitest.config.mts";
4603
4973
  var PRIMARY_TARGET = "test";
@@ -4618,7 +4988,7 @@ function runModes(cwd, fallback) {
4618
4988
  const jestConfig = entry.options?.jestConfig;
4619
4989
  if (jestConfig === void 0) continue;
4620
4990
  const name = configName(jestConfig);
4621
- if (!existsSync16(join20(cwd, name))) continue;
4991
+ if (!existsSync16(join21(cwd, name))) continue;
4622
4992
  declared.push({
4623
4993
  target,
4624
4994
  jestConfig: name,
@@ -4644,11 +5014,11 @@ function runModes(cwd, fallback) {
4644
5014
 
4645
5015
  // src/roles/test/written-file-formatter.ts
4646
5016
  import { existsSync as existsSync18 } from "node:fs";
4647
- import { dirname as dirname10, join as join23 } from "node:path";
5017
+ import { dirname as dirname11, join as join24 } from "node:path";
4648
5018
 
4649
5019
  // src/roles/format/read-adoption.ts
4650
- import { existsSync as existsSync17, readFileSync as readFileSync18 } from "node:fs";
4651
- import { join as join21 } from "node:path";
5020
+ import { existsSync as existsSync17, readFileSync as readFileSync19 } from "node:fs";
5021
+ import { join as join22 } from "node:path";
4652
5022
 
4653
5023
  // src/roles/format/config-policy.ts
4654
5024
  var FORMAT_CONFIG_FILE = ".oxfmtrc.json";
@@ -4754,11 +5124,11 @@ function sameValue(a, b) {
4754
5124
  return JSON.stringify(a) === JSON.stringify(b);
4755
5125
  }
4756
5126
  function readFormatAdoption(cwd) {
4757
- const path = join21(cwd, FORMAT_CONFIG_FILE);
5127
+ const path = join22(cwd, FORMAT_CONFIG_FILE);
4758
5128
  if (!existsSync17(path)) return NOT_ADOPTED(null);
4759
5129
  let parsed;
4760
5130
  try {
4761
- parsed = parseJsonc(readFileSync18(path, "utf8"), FORMAT_CONFIG_FILE);
5131
+ parsed = parseJsonc(readFileSync19(path, "utf8"), FORMAT_CONFIG_FILE);
4762
5132
  } catch (error) {
4763
5133
  const reason = error instanceof Error ? error.message : String(error);
4764
5134
  return NOT_ADOPTED(FORMAT_CONFIG_FILE, `${FORMAT_CONFIG_FILE} could not be parsed (${reason})`);
@@ -4802,9 +5172,9 @@ function readFormatAdoption(cwd) {
4802
5172
  }
4803
5173
 
4804
5174
  // src/roles/format/resolve-oxfmt.ts
4805
- import { readFileSync as readFileSync19 } from "node:fs";
5175
+ import { readFileSync as readFileSync20 } from "node:fs";
4806
5176
  import { createRequire as createRequire4 } from "node:module";
4807
- import { dirname as dirname9, join as join22 } from "node:path";
5177
+ import { dirname as dirname10, join as join23 } from "node:path";
4808
5178
  function resolveOxfmt(cwd) {
4809
5179
  return resolveBin(cwd, "oxfmt") ?? binFromOwnInstall("oxfmt", "oxfmt");
4810
5180
  }
@@ -4833,8 +5203,8 @@ function configSchema() {
4833
5203
  function readConfigSchema() {
4834
5204
  try {
4835
5205
  const manifest = createRequire4(import.meta.url).resolve("oxfmt/package.json");
4836
- const schemaPath = join22(dirname9(manifest), "configuration_schema.json");
4837
- return JSON.parse(readFileSync19(schemaPath, "utf8"));
5206
+ const schemaPath = join23(dirname10(manifest), "configuration_schema.json");
5207
+ return JSON.parse(readFileSync20(schemaPath, "utf8"));
4838
5208
  } catch {
4839
5209
  return void 0;
4840
5210
  }
@@ -4859,9 +5229,9 @@ function prettierConfigFrom(cwd) {
4859
5229
  let dir = cwd;
4860
5230
  for (; ; ) {
4861
5231
  for (const name of PRETTIER_CONFIGS) {
4862
- if (existsSync18(join23(dir, name))) return join23(dir, name);
5232
+ if (existsSync18(join24(dir, name))) return join24(dir, name);
4863
5233
  }
4864
- const parent = dirname10(dir);
5234
+ const parent = dirname11(dir);
4865
5235
  if (parent === dir) return void 0;
4866
5236
  dir = parent;
4867
5237
  }
@@ -4889,12 +5259,12 @@ function writtenFileFormatter(cwd) {
4889
5259
  var SPEC_TSCONFIG = "tsconfig.spec.json";
4890
5260
  var VITEST_GLOBAL_TYPES = "vitest/globals";
4891
5261
  function specTypesOperation(cwd) {
4892
- const path = join24(cwd, SPEC_TSCONFIG);
5262
+ const path = join25(cwd, SPEC_TSCONFIG);
4893
5263
  if (!existsSync19(path)) return void 0;
4894
5264
  let config;
4895
5265
  try {
4896
5266
  config = parseJsonc(
4897
- readFileSync20(path, "utf8"),
5267
+ readFileSync21(path, "utf8"),
4898
5268
  SPEC_TSCONFIG
4899
5269
  );
4900
5270
  } catch {
@@ -4914,10 +5284,10 @@ function specTypesOperation(cwd) {
4914
5284
  }
4915
5285
  function shippedTypesPath(cwd, declaration) {
4916
5286
  try {
4917
- const manifest = createRequire5(join24(cwd, "noop.cjs")).resolve("@hublo/sentinel/package.json");
4918
- const file = join24(dirname11(manifest), declaration);
5287
+ const manifest = createRequire5(join25(cwd, "noop.cjs")).resolve("@hublo/sentinel/package.json");
5288
+ const file = join25(dirname12(manifest), declaration);
4919
5289
  if (existsSync19(file)) {
4920
- const path = relative5(cwd, file);
5290
+ const path = relative6(cwd, file);
4921
5291
  return path.startsWith(".") ? path : `./${path}`;
4922
5292
  }
4923
5293
  } catch {
@@ -4935,7 +5305,7 @@ function helpersStillCallingJest(cwd) {
4935
5305
  return;
4936
5306
  }
4937
5307
  for (const entry of entries) {
4938
- const path = join24(dir, entry.name);
5308
+ const path = join25(dir, entry.name);
4939
5309
  if (entry.isDirectory()) {
4940
5310
  if (!skip.has(entry.name)) walk2(path);
4941
5311
  continue;
@@ -4945,12 +5315,12 @@ function helpersStillCallingJest(cwd) {
4945
5315
  if (/^(jest|vitest)\./.test(entry.name)) continue;
4946
5316
  let source;
4947
5317
  try {
4948
- source = readFileSync20(path, "utf8");
5318
+ source = readFileSync21(path, "utf8");
4949
5319
  } catch {
4950
5320
  continue;
4951
5321
  }
4952
5322
  if (/\bjest\./.test(source)) {
4953
- found.push(relative5(cwd, path).replaceAll("\\", "/"));
5323
+ found.push(relative6(cwd, path).replaceAll("\\", "/"));
4954
5324
  }
4955
5325
  }
4956
5326
  };
@@ -4982,7 +5352,7 @@ function tsconfigFileListOperations(cwd) {
4982
5352
  let config;
4983
5353
  try {
4984
5354
  config = parseJsonc(
4985
- readFileSync20(join24(cwd, name), "utf8"),
5355
+ readFileSync21(join25(cwd, name), "utf8"),
4986
5356
  name
4987
5357
  );
4988
5358
  } catch {
@@ -5285,11 +5655,11 @@ function plan(context) {
5285
5655
 
5286
5656
  // src/roles/test/written-file-linter.ts
5287
5657
  import { existsSync as existsSync24 } from "node:fs";
5288
- import { join as join29 } from "node:path";
5658
+ import { join as join30 } from "node:path";
5289
5659
 
5290
5660
  // src/roles/lint/module-baseline.ts
5291
- import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
5292
- import { join as join25 } from "node:path";
5661
+ import { existsSync as existsSync20, readFileSync as readFileSync22 } from "node:fs";
5662
+ import { join as join26 } from "node:path";
5293
5663
  var LINT_BASELINE_FILE = ".oxlintrc.baseline.json";
5294
5664
  var LINT_MEASURE_FILE = ".oxlintrc.measure.json";
5295
5665
  var LINT_BASELINE_SPECIFIER = `./${LINT_BASELINE_FILE}`;
@@ -5333,11 +5703,11 @@ function describeHolds(holds) {
5333
5703
  return `held ${holds.length} rule(s) at \`warn\` for this module, covering ${total} violation(s) that were already there: ${listed}${rest}. They are written to ${LINT_BASELINE_FILE}, they still report, and they return to \`error\` once fixed and re-initialized`;
5334
5704
  }
5335
5705
  function heldRules(cwd) {
5336
- const path = join25(cwd, LINT_BASELINE_FILE);
5706
+ const path = join26(cwd, LINT_BASELINE_FILE);
5337
5707
  if (!existsSync20(path)) return [];
5338
5708
  try {
5339
5709
  const parsed = parseJsonc(
5340
- readFileSync21(path, "utf8"),
5710
+ readFileSync22(path, "utf8"),
5341
5711
  LINT_BASELINE_FILE
5342
5712
  );
5343
5713
  return Object.keys(parsed.rules ?? {});
@@ -5386,8 +5756,8 @@ function lintTarget() {
5386
5756
  }
5387
5757
 
5388
5758
  // src/core/config/read-adoption.ts
5389
- import { existsSync as existsSync22, readFileSync as readFileSync23 } from "node:fs";
5390
- import { join as join27 } from "node:path";
5759
+ import { existsSync as existsSync22, readFileSync as readFileSync24 } from "node:fs";
5760
+ import { join as join28 } from "node:path";
5391
5761
 
5392
5762
  // src/core/config/owned-keys.ts
5393
5763
  function presetOwnedKeys(config, permitted, presetSets) {
@@ -5402,12 +5772,12 @@ function localOnlyKeys(config, permitted, presetSets) {
5402
5772
  }
5403
5773
 
5404
5774
  // src/core/config/resolve-config-target.ts
5405
- import { existsSync as existsSync21, readFileSync as readFileSync22 } from "node:fs";
5406
- import { join as join26 } from "node:path";
5775
+ import { existsSync as existsSync21, readFileSync as readFileSync23 } from "node:fs";
5776
+ import { join as join27 } from "node:path";
5407
5777
  function readExtends(absolutePath) {
5408
5778
  let parsed;
5409
5779
  try {
5410
- parsed = parseJsonc(readFileSync22(absolutePath, "utf8"), absolutePath);
5780
+ parsed = parseJsonc(readFileSync23(absolutePath, "utf8"), absolutePath);
5411
5781
  } catch {
5412
5782
  return [];
5413
5783
  }
@@ -5421,7 +5791,7 @@ function resolveConfigTarget(moduleDir, { candidates, markers, fallback }) {
5421
5791
  let existing;
5422
5792
  let existingExtendsSomething = false;
5423
5793
  for (const candidate of candidates) {
5424
- const absolutePath = join26(moduleDir, candidate);
5794
+ const absolutePath = join27(moduleDir, candidate);
5425
5795
  if (!existsSync21(absolutePath)) continue;
5426
5796
  const chain = readExtends(absolutePath);
5427
5797
  if (existing === void 0) {
@@ -5455,12 +5825,12 @@ var NOT_ADOPTED2 = (configFile, unreadable = null) => ({
5455
5825
  });
5456
5826
  function readAdoption(cwd, options) {
5457
5827
  const target = resolveConfigTarget(cwd, options);
5458
- if (target.reason === "none" || !existsSync22(join27(cwd, target.path))) {
5828
+ if (target.reason === "none" || !existsSync22(join28(cwd, target.path))) {
5459
5829
  return NOT_ADOPTED2(target.reason === "none" ? null : target.path);
5460
5830
  }
5461
5831
  let parsed;
5462
5832
  try {
5463
- parsed = parseJsonc(readFileSync23(join27(cwd, target.path), "utf8"), target.path);
5833
+ parsed = parseJsonc(readFileSync24(join28(cwd, target.path), "utf8"), target.path);
5464
5834
  } catch (error) {
5465
5835
  const reason = error instanceof Error ? error.message : String(error);
5466
5836
  return NOT_ADOPTED2(target.path, `${target.path} could not be parsed (${reason})`);
@@ -5496,7 +5866,7 @@ function readLintAdoption(cwd) {
5496
5866
  // src/roles/lint/resolve-oxlint.ts
5497
5867
  import { existsSync as existsSync23 } from "node:fs";
5498
5868
  import { createRequire as createRequire6 } from "node:module";
5499
- import { delimiter as delimiter2, dirname as dirname12, join as join28 } from "node:path";
5869
+ import { delimiter as delimiter2, dirname as dirname13, join as join29 } from "node:path";
5500
5870
  import { fileURLToPath } from "node:url";
5501
5871
  var PACKAGE_OF = {
5502
5872
  oxlint: "oxlint",
@@ -5512,7 +5882,7 @@ function canRunTypeAware(cwd) {
5512
5882
  function oxlintPath(cwd, env) {
5513
5883
  const shim = tsgolintShim(cwd);
5514
5884
  if (!shim) return env.PATH;
5515
- return [dirname12(shim), env.PATH].filter(Boolean).join(delimiter2);
5885
+ return [dirname13(shim), env.PATH].filter(Boolean).join(delimiter2);
5516
5886
  }
5517
5887
  function tsgolintShim(cwd) {
5518
5888
  const fromModule = resolveBin(cwd, "tsgolint");
@@ -5520,17 +5890,17 @@ function tsgolintShim(cwd) {
5520
5890
  const candidates = [];
5521
5891
  for (const owner of ["oxlint-tsgolint", "oxlint"]) {
5522
5892
  try {
5523
- const packageDir = dirname12(require3.resolve(`${owner}/package.json`));
5524
- candidates.push(join28(packageDir, "node_modules", ".bin", "tsgolint"));
5525
- candidates.push(join28(packageDir, "..", ".bin", "tsgolint"));
5893
+ const packageDir = dirname13(require3.resolve(`${owner}/package.json`));
5894
+ candidates.push(join29(packageDir, "node_modules", ".bin", "tsgolint"));
5895
+ candidates.push(join29(packageDir, "..", ".bin", "tsgolint"));
5526
5896
  } catch {
5527
5897
  }
5528
5898
  }
5529
5899
  try {
5530
- const ownRoot = dirname12(require3.resolve("@hublo/sentinel/package.json"));
5531
- candidates.push(join28(ownRoot, "node_modules", ".bin", "tsgolint"));
5900
+ const ownRoot = dirname13(require3.resolve("@hublo/sentinel/package.json"));
5901
+ candidates.push(join29(ownRoot, "node_modules", ".bin", "tsgolint"));
5532
5902
  } catch {
5533
- candidates.push(resolveBin(dirname12(fileURLToPath(import.meta.url)), "tsgolint") ?? "");
5903
+ candidates.push(resolveBin(dirname13(fileURLToPath(import.meta.url)), "tsgolint") ?? "");
5534
5904
  }
5535
5905
  return candidates.find((candidate) => candidate !== "" && existsSync23(candidate));
5536
5906
  }
@@ -5546,7 +5916,7 @@ function writtenFileLinter(cwd) {
5546
5916
  return { bin: oxlint, args: ["--fix"], origin: "this module's own oxlint config" };
5547
5917
  }
5548
5918
  }
5549
- const config = ESLINT_CONFIG_FILES.map((name) => join29(cwd, name)).find((path) => existsSync24(path));
5919
+ const config = ESLINT_CONFIG_FILES.map((name) => join30(cwd, name)).find((path) => existsSync24(path));
5550
5920
  const eslint = resolveBin(cwd, "eslint");
5551
5921
  if (config !== void 0 && eslint !== void 0) {
5552
5922
  return {
@@ -5563,16 +5933,17 @@ function writtenFileLinter(cwd) {
5563
5933
  }
5564
5934
 
5565
5935
  // src/roles/test/adapters/vitest/vitest.adapter.ts
5936
+ var REPORT_MAX_BUFFER = 1 << 28;
5566
5937
  var FORMATTABLE = /\.(m|c)?(j|t)sx?$|\.json$/;
5567
5938
  var FORMAT_BATCH = 25;
5568
5939
  function readIfPossible(path) {
5569
5940
  try {
5570
- return readFileSync24(path, "utf8");
5941
+ return readFileSync25(path, "utf8");
5571
5942
  } catch {
5572
5943
  return void 0;
5573
5944
  }
5574
5945
  }
5575
- var VitestAdapter = class extends BaseAdapter {
5946
+ var VitestAdapter = class _VitestAdapter extends BaseAdapter {
5576
5947
  target = "test";
5577
5948
  runner = "vitest";
5578
5949
  appliesTo(_preset) {
@@ -5693,12 +6064,12 @@ var VitestAdapter = class extends BaseAdapter {
5693
6064
  const linter = writtenFileLinter(ctx.cwd);
5694
6065
  if (linter === void 0) return;
5695
6066
  const workspaceRoot = findWorkspaceRoot(ctx.cwd) ?? ctx.cwd;
5696
- const fromRoot = (file) => relative6(workspaceRoot, join30(ctx.cwd, file));
6067
+ const fromRoot = (file) => relative7(workspaceRoot, join31(ctx.cwd, file));
5697
6068
  let unsettled = 0;
5698
6069
  const reverted = [];
5699
6070
  for (let at = 0; at < written.length; at += FORMAT_BATCH) {
5700
6071
  const batch = written.slice(at, at + FORMAT_BATCH);
5701
- const before = new Map(batch.map((file) => [file, readIfPossible(join30(ctx.cwd, file))]));
6072
+ const before = new Map(batch.map((file) => [file, readIfPossible(join31(ctx.cwd, file))]));
5702
6073
  const pass = spawnSync(linter.bin, [...linter.args, ...batch.map(fromRoot)], {
5703
6074
  cwd: workspaceRoot,
5704
6075
  encoding: "utf8"
@@ -5707,11 +6078,11 @@ var VitestAdapter = class extends BaseAdapter {
5707
6078
  for (const file of batch) {
5708
6079
  const was = before.get(file);
5709
6080
  if (was === void 0) continue;
5710
- const now = readIfPossible(join30(ctx.cwd, file));
6081
+ const now = readIfPossible(join31(ctx.cwd, file));
5711
6082
  if (now === void 0 || now === was) continue;
5712
6083
  const lost = lineLostByTheFixer(was, now);
5713
6084
  if (lost === void 0) continue;
5714
- writeFileSync2(join30(ctx.cwd, file), was);
6085
+ writeFileSync2(join31(ctx.cwd, file), was);
5715
6086
  reverted.push(`${file}: \`${lost.slice(0, 80)}\``);
5716
6087
  }
5717
6088
  }
@@ -5743,6 +6114,105 @@ var VitestAdapter = class extends BaseAdapter {
5743
6114
  unreadable: adoption.unreadable
5744
6115
  });
5745
6116
  }
6117
+ /**
6118
+ * The metrics for a module this role has nothing to say about.
6119
+ *
6120
+ * Every KEY the full path emits, so a consumer never sees a row whose shape depends on whether
6121
+ * the module adopted. The same stance, and the same reason, as `tsc.adapter.ts`.
6122
+ */
6123
+ static NOTHING_TO_REPORT = {
6124
+ suite: null,
6125
+ files: 0,
6126
+ tests: 0,
6127
+ passed: 0,
6128
+ failed: 0,
6129
+ skipped: 0,
6130
+ unloadable: 0,
6131
+ failures: [],
6132
+ failuresTruncated: false
6133
+ };
6134
+ /**
6135
+ * The counts, for `--run --test --json`.
6136
+ *
6137
+ * ⚠️ Until this existed, `--run --test --json` returned a WELL FORMED envelope saying the run
6138
+ * had failed, because `run-verb.ts` shapes `run + json` into a `report` and this adapter
6139
+ * implemented none while the other four roles do. A consumer reading `results[].ok` saw a
6140
+ * failed suite that had never run, which is the worst shape a gap can take.
6141
+ *
6142
+ * It reports the suite `run()` runs, and names it. A module can carry two (an integration or
6143
+ * prisma run mode beside the main one), each with its own nx target, and summing them would
6144
+ * invent a number no target produces. `suite` says which one this row is about.
6145
+ *
6146
+ * `readSnapshot` is reused rather than re-parsed: it is the same reader the migration proof
6147
+ * uses on both runners, so a count here and a count there cannot disagree about what a test is.
6148
+ * `fromWorkspaceRoot` likewise, cwd and `INIT_CWD` together, because a report that launches the
6149
+ * suite differently is reporting on something else.
6150
+ */
6151
+ async report(ctx) {
6152
+ const adoption = readTestAdoption(ctx.cwd);
6153
+ if (!adoption.adopted || adoption.configFile === null) {
6154
+ return { ok: true, code: 0, metrics: _VitestAdapter.NOTHING_TO_REPORT };
6155
+ }
6156
+ const { bin } = resolveVitest(ctx.cwd);
6157
+ if (bin === void 0) {
6158
+ return {
6159
+ ok: false,
6160
+ code: 1,
6161
+ metrics: { ..._VitestAdapter.NOTHING_TO_REPORT, error: "vitest not available" }
6162
+ };
6163
+ }
6164
+ const out = join31(mkdtempSync3(join31(tmpdir3(), "sentinel-test-report-")), "vitest.json");
6165
+ const { cwd: runFrom, options, env } = fromWorkspaceRoot(ctx.cwd, [], []);
6166
+ const result = spawnSync(bin, ["run", ...options, "--reporter=json", `--outputFile=${out}`], {
6167
+ cwd: runFrom,
6168
+ env: { ...process.env, ...env },
6169
+ encoding: "utf8",
6170
+ maxBuffer: REPORT_MAX_BUFFER
6171
+ });
6172
+ if (result.error) {
6173
+ return {
6174
+ ok: false,
6175
+ code: 1,
6176
+ metrics: { ..._VitestAdapter.NOTHING_TO_REPORT, error: result.error.message }
6177
+ };
6178
+ }
6179
+ if (!existsSync25(out)) {
6180
+ return {
6181
+ ok: false,
6182
+ code: 1,
6183
+ metrics: { ..._VitestAdapter.NOTHING_TO_REPORT, error: "vitest produced no report" }
6184
+ };
6185
+ }
6186
+ let parsed;
6187
+ try {
6188
+ parsed = JSON.parse(readFileSync25(out, "utf8"));
6189
+ } catch {
6190
+ return {
6191
+ ok: false,
6192
+ code: 1,
6193
+ metrics: { ..._VitestAdapter.NOTHING_TO_REPORT, error: "vitest report is not readable" }
6194
+ };
6195
+ }
6196
+ const snapshot = readSnapshot(parsed);
6197
+ const skipped = snapshot.skipped ?? [];
6198
+ const cap = ctx.maxDiagnostics === 0 ? Infinity : ctx.maxDiagnostics ?? DEFAULT_MAX_DIAGNOSTICS;
6199
+ const failures = Number.isFinite(cap) ? snapshot.failing.slice(0, cap) : snapshot.failing;
6200
+ return {
6201
+ ok: snapshot.failing.length === 0 && snapshot.unloadable.length === 0,
6202
+ code: result.status ?? 0,
6203
+ metrics: {
6204
+ suite: suiteOf(adoption.configFile),
6205
+ files: snapshot.files,
6206
+ tests: snapshot.passing.length + snapshot.failing.length + skipped.length,
6207
+ passed: snapshot.passing.length,
6208
+ failed: snapshot.failing.length,
6209
+ skipped: skipped.length,
6210
+ unloadable: snapshot.unloadable.length,
6211
+ failures,
6212
+ failuresTruncated: failures.length < snapshot.failing.length
6213
+ }
6214
+ };
6215
+ }
5746
6216
  /**
5747
6217
  * Run the module's tests.
5748
6218
  *
@@ -5801,7 +6271,7 @@ function fromWorkspaceRoot(moduleDir, options, paths) {
5801
6271
  if (root === void 0 || root === moduleDir) {
5802
6272
  return { cwd: moduleDir, options: [...options], paths: [...paths], env: {} };
5803
6273
  }
5804
- const absolute = (value) => isAbsolute2(value) ? value : resolve9(moduleDir, value);
6274
+ const absolute = (value) => isAbsolute2(value) ? value : resolve10(moduleDir, value);
5805
6275
  const moved = [];
5806
6276
  let previousWasConfig = false;
5807
6277
  for (const option of options) {