@hublo/sentinel 1.4.0-alpha.47 → 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.
- package/dist/bin/sentinel.js +4 -4
- package/dist/{chunk-CTAQLATY.js → chunk-5VNYQIFD.js} +1 -1
- package/dist/{chunk-CTAQLATY.js.map → chunk-5VNYQIFD.js.map} +1 -1
- package/dist/{chunk-VR67JXVN.js → chunk-GJ6MNAWG.js} +636 -166
- package/dist/{chunk-NW6UHNYX.js → chunk-KMKQDGI6.js} +3 -5
- package/dist/chunk-KMKQDGI6.js.map +1 -0
- package/dist/{chunk-QRQTA776.js → chunk-TBOMUODC.js} +2 -2
- package/dist/{chunk-DWVRVPMN.js → chunk-Z7L4FGKP.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/roles/test/nest/toolchain.js +1 -1
- package/dist/roles/test/react/toolchain.js +1 -1
- package/dist/roles/test/setup/jest-parity.js +2 -2
- package/dist/roles/test/setup/workspace-entry.js +2 -2
- package/dist/roles/test/shared-test-config.js +1 -1
- package/dist/{validate-4HSEVZUA.js → validate-5SSSUV7D.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-NW6UHNYX.js.map +0 -1
- /package/dist/{chunk-DWVRVPMN.js.map → chunk-Z7L4FGKP.js.map} +0 -0
|
@@ -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-
|
|
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
|
|
371
|
-
if (!
|
|
372
|
-
const executable = join5(dirname(manifest),
|
|
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
|
|
751
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
929
|
-
import { join as
|
|
930
|
-
import { parseSync as
|
|
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(
|
|
954
|
-
const cut =
|
|
955
|
-
const directory = cut === -1 ? "" :
|
|
956
|
-
const base =
|
|
957
|
-
if (!base.startsWith(JEST_PREFIX)) return
|
|
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,
|
|
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 =
|
|
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
|
|
1247
|
-
import { parseSync as
|
|
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
|
|
1860
|
-
if (imported.has(
|
|
1861
|
-
if (bindings.has(
|
|
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
|
|
1889
|
-
if (
|
|
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
|
|
2144
|
-
const next = resolveRelative(file,
|
|
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
|
|
2155
|
-
if (
|
|
2156
|
-
const declarator =
|
|
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,
|
|
2451
|
+
const castIfTestOnly = (specifier, read4) => {
|
|
2450
2452
|
const keys = testOnly.get(specifier);
|
|
2451
|
-
return keys === void 0 ?
|
|
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
|
|
3694
|
-
const { program, errors } =
|
|
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 =
|
|
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(
|
|
4087
|
+
findWorkspaceRoot(dirname8(fileName))
|
|
3725
4088
|
);
|
|
3726
4089
|
source = statics.source;
|
|
3727
|
-
const original = statics.rewritten === 0 ? beforeStatic :
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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:
|
|
3810
|
-
changed:
|
|
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
|
|
3856
|
-
import { dirname as
|
|
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
|
|
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 =
|
|
3877
|
-
if (existsSync14(base) &&
|
|
3878
|
-
for (const extension of
|
|
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
|
|
3882
|
-
const index =
|
|
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(
|
|
4270
|
+
if (!SKIPPED.has(entry.name)) stack.push(join17(dir, entry.name));
|
|
3901
4271
|
} else if (SOURCE_FILE.test(entry.name)) {
|
|
3902
|
-
found.push(
|
|
4272
|
+
found.push(join17(dir, entry.name));
|
|
3903
4273
|
}
|
|
3904
4274
|
}
|
|
3905
4275
|
}
|
|
3906
4276
|
return found;
|
|
3907
4277
|
}
|
|
3908
|
-
function
|
|
4278
|
+
function read2(path) {
|
|
3909
4279
|
try {
|
|
3910
|
-
return
|
|
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 =
|
|
3920
|
-
const inside =
|
|
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 =
|
|
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) =>
|
|
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
|
|
3963
|
-
const { program, errors } =
|
|
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
|
|
4336
|
+
function read3(path) {
|
|
3967
4337
|
try {
|
|
3968
|
-
return
|
|
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 =
|
|
4356
|
+
const path = join18(dir, entry);
|
|
3987
4357
|
let directory;
|
|
3988
4358
|
try {
|
|
3989
|
-
directory =
|
|
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 =
|
|
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 =
|
|
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,
|
|
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
|
|
4023
|
-
const source =
|
|
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:
|
|
4395
|
+
blockers.push({ file: relative8, reason: "this file could not be read" });
|
|
4026
4396
|
continue;
|
|
4027
4397
|
}
|
|
4028
4398
|
const migrated = migrateTestFile(
|
|
4029
|
-
|
|
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: `${
|
|
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:
|
|
4412
|
+
blockers.push({ file: relative8, reason: refusal.reason });
|
|
4043
4413
|
}
|
|
4044
4414
|
collected.push(...migrated.warnings);
|
|
4045
|
-
const renamedTo = vitestSupportName(
|
|
4046
|
-
if (!migrated.changed && renamedTo ===
|
|
4047
|
-
if (renamedTo !==
|
|
4048
|
-
const before =
|
|
4049
|
-
const after =
|
|
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(
|
|
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
|
|
4062
|
-
const source =
|
|
4431
|
+
for (const relative8 of listTestFiles(cwd)) {
|
|
4432
|
+
const source = read3(join18(cwd, relative8));
|
|
4063
4433
|
if (source === void 0) {
|
|
4064
|
-
blockers.push({ file:
|
|
4434
|
+
blockers.push({ file: relative8, reason: "this file could not be read" });
|
|
4065
4435
|
continue;
|
|
4066
4436
|
}
|
|
4067
4437
|
const migrated = migrateTestFile(
|
|
4068
|
-
|
|
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: `${
|
|
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:
|
|
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
|
-
|
|
4459
|
+
join18(cwd, relative8),
|
|
4090
4460
|
migrated.source,
|
|
4091
4461
|
inherited
|
|
4092
4462
|
);
|
|
4093
|
-
operations.push({ kind: "write", path:
|
|
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
|
|
4101
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
4517
|
+
const path = join19(dir, entry);
|
|
4148
4518
|
let directory;
|
|
4149
4519
|
try {
|
|
4150
|
-
directory =
|
|
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 =
|
|
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
|
|
4297
|
-
import { join as
|
|
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(
|
|
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(
|
|
4515
|
-
if (file === void 0 || !existsSync15(
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
4651
|
-
import { join as
|
|
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 =
|
|
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(
|
|
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
|
|
5175
|
+
import { readFileSync as readFileSync20 } from "node:fs";
|
|
4806
5176
|
import { createRequire as createRequire4 } from "node:module";
|
|
4807
|
-
import { dirname as
|
|
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 =
|
|
4837
|
-
return JSON.parse(
|
|
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(
|
|
5232
|
+
if (existsSync18(join24(dir, name))) return join24(dir, name);
|
|
4863
5233
|
}
|
|
4864
|
-
const parent =
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
4918
|
-
const file =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
5318
|
+
source = readFileSync21(path, "utf8");
|
|
4949
5319
|
} catch {
|
|
4950
5320
|
continue;
|
|
4951
5321
|
}
|
|
4952
5322
|
if (/\bjest\./.test(source)) {
|
|
4953
|
-
found.push(
|
|
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
|
-
|
|
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
|
|
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
|
|
5292
|
-
import { join as
|
|
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 =
|
|
5706
|
+
const path = join26(cwd, LINT_BASELINE_FILE);
|
|
5337
5707
|
if (!existsSync20(path)) return [];
|
|
5338
5708
|
try {
|
|
5339
5709
|
const parsed = parseJsonc(
|
|
5340
|
-
|
|
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
|
|
5390
|
-
import { join as
|
|
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
|
|
5406
|
-
import { join as
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
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 [
|
|
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 =
|
|
5524
|
-
candidates.push(
|
|
5525
|
-
candidates.push(
|
|
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 =
|
|
5531
|
-
candidates.push(
|
|
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(
|
|
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) =>
|
|
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
|
|
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) =>
|
|
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(
|
|
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(
|
|
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(
|
|
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 :
|
|
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) {
|