@hublo/sentinel 1.4.0-alpha.2 → 1.4.0-alpha.20
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.d.ts +1 -0
- package/dist/bin/sentinel.js +10 -11
- package/dist/chunk-2XLX6PFR.js.map +1 -0
- package/dist/{chunk-3TDUIKVQ.js → chunk-3ZKTXODW.js} +6 -6
- package/dist/chunk-3ZKTXODW.js.map +1 -0
- package/dist/{chunk-4UIZJ3TR.js → chunk-BPLSUYU7.js} +936 -179
- package/dist/{chunk-WLFE5RUU.js → chunk-NW6UHNYX.js} +6 -6
- package/dist/chunk-NW6UHNYX.js.map +1 -0
- package/dist/chunk-PWV3BMDA.js.map +1 -0
- package/dist/{chunk-CPCUPK4J.js → chunk-TKDVAYYV.js} +4 -4
- package/dist/chunk-TKDVAYYV.js.map +1 -0
- package/dist/chunk-ZV5KMOS7.js +427 -0
- package/dist/chunk-ZV5KMOS7.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +2 -3
- package/dist/roles/build/nest/toolchain.js +7 -7
- package/dist/roles/test/nest/toolchain.d.ts +3 -23
- package/dist/roles/test/nest/toolchain.js +5 -269
- package/dist/roles/test/nest/toolchain.js.map +1 -1
- package/dist/roles/test/react/toolchain.d.ts +5 -1
- package/dist/roles/test/react/toolchain.js +11 -3
- package/dist/roles/test/react/toolchain.js.map +1 -1
- package/dist/roles/test/setup/{nest.js → jest-parity.js} +5 -5
- package/dist/roles/test/setup/jest-parity.js.map +1 -0
- package/dist/roles/test/setup/workspace-entry.js +2 -2
- package/dist/roles/test/setup/workspace-entry.js.map +1 -1
- package/dist/roles/test/shared-test-config.d.ts +32 -0
- package/dist/roles/test/shared-test-config.js +8 -0
- package/dist/roles/test/shared-test-config.js.map +1 -0
- package/dist/roles/test/tools/msw.d.ts +1 -0
- package/dist/roles/test/tools/msw.js +3 -0
- package/dist/roles/test/tools/msw.js.map +1 -0
- package/docs/test-adoption.md +54 -10
- package/docs/using-sentinel.md +25 -10
- package/package.json +14 -7
- package/types/jest-global.d.ts +41 -0
- package/types/mock-extended.d.ts +52 -0
- package/dist/chunk-NX4GHIHF.js +0 -25
- package/dist/roles/test/setup/nest.js.map +0 -1
- /package/dist/roles/test/setup/{nest.d.ts → jest-parity.d.ts} +0 -0
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
readNxProjectName,
|
|
6
6
|
readOwnPackage,
|
|
7
7
|
readProjectPackageJson
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NW6UHNYX.js";
|
|
9
9
|
|
|
10
10
|
// src/core/registry.ts
|
|
11
11
|
var adapters = [];
|
|
@@ -109,7 +109,7 @@ var SWEEPABLE_TARGETS = TARGETS.filter(
|
|
|
109
109
|
var PRESET_NAMES = ["react", "nest", "svelte", "node", "tools"];
|
|
110
110
|
|
|
111
111
|
// src/shared/color.ts
|
|
112
|
-
import { styleText } from "util";
|
|
112
|
+
import { styleText } from "node:util";
|
|
113
113
|
function palette(stream) {
|
|
114
114
|
const paint = (format, text) => styleText(format, text, { stream });
|
|
115
115
|
return {
|
|
@@ -123,8 +123,8 @@ function palette(stream) {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
// src/roles/build/nest/nx-target.ts
|
|
126
|
-
import { existsSync, readFileSync } from "fs";
|
|
127
|
-
import { join, relative } from "path";
|
|
126
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
127
|
+
import { join, relative } from "node:path";
|
|
128
128
|
var DEPRECATED_NX_BUILDER = "@nx/webpack:webpack";
|
|
129
129
|
var TRANSLATABLE_WEBPACK_CONFIG = "tools/webpack-configs/update.webpack.config.js";
|
|
130
130
|
var readProjectJson = (cwd) => {
|
|
@@ -195,8 +195,8 @@ function workspaceRootHops(cwd, target) {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
// src/roles/build/read-adoption.ts
|
|
198
|
-
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
199
|
-
import { join as join4 } from "path";
|
|
198
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
199
|
+
import { join as join4 } from "node:path";
|
|
200
200
|
|
|
201
201
|
// src/core/config/module-type.ts
|
|
202
202
|
function esmReadiness(fileName, packageType) {
|
|
@@ -210,8 +210,8 @@ function esmReadiness(fileName, packageType) {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
// src/core/config/manifest.ts
|
|
213
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
214
|
-
import { basename, join as join2 } from "path";
|
|
213
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
214
|
+
import { basename, join as join2 } from "node:path";
|
|
215
215
|
function moduleScripts(cwd) {
|
|
216
216
|
try {
|
|
217
217
|
const pkg = JSON.parse(readFileSync2(join2(cwd, "package.json"), "utf8"));
|
|
@@ -220,6 +220,14 @@ function moduleScripts(cwd) {
|
|
|
220
220
|
return {};
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
|
+
function manifestTargets(cwd) {
|
|
224
|
+
try {
|
|
225
|
+
const pkg = JSON.parse(readFileSync2(join2(cwd, "package.json"), "utf8"));
|
|
226
|
+
return pkg.nx?.targets ?? {};
|
|
227
|
+
} catch {
|
|
228
|
+
return {};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
223
231
|
function moduleName2(cwd) {
|
|
224
232
|
try {
|
|
225
233
|
const pkg = JSON.parse(readFileSync2(join2(cwd, "package.json"), "utf8"));
|
|
@@ -256,8 +264,8 @@ function manifestOperation(cwd, scripts) {
|
|
|
256
264
|
}
|
|
257
265
|
|
|
258
266
|
// src/core/config/project-json.ts
|
|
259
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
260
|
-
import { join as join3 } from "path";
|
|
267
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
268
|
+
import { join as join3 } from "node:path";
|
|
261
269
|
function projectTargets(cwd) {
|
|
262
270
|
const path = join3(cwd, "project.json");
|
|
263
271
|
if (!existsSync3(path)) return void 0;
|
|
@@ -514,11 +522,11 @@ function readBuildAdoption(cwd) {
|
|
|
514
522
|
}
|
|
515
523
|
|
|
516
524
|
// src/roles/build/adapters/vite/vite-role.adapter.ts
|
|
517
|
-
import { spawnSync } from "child_process";
|
|
525
|
+
import { spawnSync } from "node:child_process";
|
|
518
526
|
|
|
519
527
|
// src/core/config/tool-args.ts
|
|
520
|
-
import { existsSync as existsSync5 } from "fs";
|
|
521
|
-
import { isAbsolute, resolve as resolve2 } from "path";
|
|
528
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
529
|
+
import { isAbsolute, resolve as resolve2 } from "node:path";
|
|
522
530
|
function splitToolArgs(cwd, toolArgs = [], { valueFlags = [] } = {}) {
|
|
523
531
|
const takesValue = new Set(valueFlags);
|
|
524
532
|
const options = [];
|
|
@@ -535,9 +543,9 @@ function splitToolArgs(cwd, toolArgs = [], { valueFlags = [] } = {}) {
|
|
|
535
543
|
}
|
|
536
544
|
|
|
537
545
|
// src/shared/resolve-bin.ts
|
|
538
|
-
import { existsSync as existsSync6 } from "fs";
|
|
539
|
-
import { createRequire } from "module";
|
|
540
|
-
import { delimiter, dirname, join as join5 } from "path";
|
|
546
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
547
|
+
import { createRequire } from "node:module";
|
|
548
|
+
import { delimiter, dirname, join as join5 } from "node:path";
|
|
541
549
|
var require2 = createRequire(import.meta.url);
|
|
542
550
|
function resolveBin(fromDir, name) {
|
|
543
551
|
let dir = fromDir;
|
|
@@ -553,9 +561,9 @@ function binFromOwnInstall(packageName, binName) {
|
|
|
553
561
|
try {
|
|
554
562
|
const manifest = require2.resolve(`${packageName}/package.json`);
|
|
555
563
|
const bin = require2(manifest).bin;
|
|
556
|
-
const
|
|
557
|
-
if (!
|
|
558
|
-
const executable = join5(dirname(manifest),
|
|
564
|
+
const relative8 = typeof bin === "string" ? bin : bin?.[binName];
|
|
565
|
+
if (!relative8) return void 0;
|
|
566
|
+
const executable = join5(dirname(manifest), relative8);
|
|
559
567
|
return existsSync6(executable) ? executable : void 0;
|
|
560
568
|
} catch {
|
|
561
569
|
return void 0;
|
|
@@ -568,12 +576,12 @@ function binSearchPath(cwd) {
|
|
|
568
576
|
}
|
|
569
577
|
|
|
570
578
|
// src/roles/build/plan.ts
|
|
571
|
-
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
572
|
-
import { join as join10 } from "path";
|
|
579
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
|
|
580
|
+
import { join as join10 } from "node:path";
|
|
573
581
|
|
|
574
582
|
// src/core/config/existing-command.ts
|
|
575
|
-
import { readFileSync as readFileSync5 } from "fs";
|
|
576
|
-
import { join as join6 } from "path";
|
|
583
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
584
|
+
import { join as join6 } from "node:path";
|
|
577
585
|
|
|
578
586
|
// src/shared/jsonc.ts
|
|
579
587
|
import { parse, printParseErrorCode } from "jsonc-parser";
|
|
@@ -623,8 +631,8 @@ function existingCommand(cwd, target) {
|
|
|
623
631
|
}
|
|
624
632
|
|
|
625
633
|
// src/core/config/nx-target.ts
|
|
626
|
-
import { existsSync as existsSync7 } from "fs";
|
|
627
|
-
import { join as join7 } from "path";
|
|
634
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
635
|
+
import { join as join7 } from "node:path";
|
|
628
636
|
function nxTargetMetadataOperations(options) {
|
|
629
637
|
const { targets } = options;
|
|
630
638
|
if (Object.keys(targets).length === 0) return [];
|
|
@@ -689,8 +697,8 @@ function keepsOtherCommands(script, sentinelCommand) {
|
|
|
689
697
|
}
|
|
690
698
|
|
|
691
699
|
// src/roles/format/read-adoption.ts
|
|
692
|
-
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
693
|
-
import { join as join8 } from "path";
|
|
700
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
|
|
701
|
+
import { join as join8 } from "node:path";
|
|
694
702
|
|
|
695
703
|
// src/roles/format/config-policy.ts
|
|
696
704
|
var FORMAT_CONFIG_FILE = ".oxfmtrc.json";
|
|
@@ -901,8 +909,8 @@ function devTargetOperations(cwd, devScript) {
|
|
|
901
909
|
}
|
|
902
910
|
|
|
903
911
|
// src/roles/build/nest/adopt.ts
|
|
904
|
-
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
905
|
-
import { join as join9 } from "path";
|
|
912
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
|
|
913
|
+
import { join as join9 } from "node:path";
|
|
906
914
|
var NEST_CONFIG_FILE = "vite.config.mts";
|
|
907
915
|
function asSource(value, depth) {
|
|
908
916
|
const pad = " ".repeat(depth);
|
|
@@ -1445,8 +1453,8 @@ var ViteDevAdapter = class extends ViteRoleAdapter {
|
|
|
1445
1453
|
};
|
|
1446
1454
|
|
|
1447
1455
|
// src/roles/build/declared-outputs.ts
|
|
1448
|
-
import { existsSync as existsSync11, rmSync, statSync, writeFileSync } from "fs";
|
|
1449
|
-
import { join as join11 } from "path";
|
|
1456
|
+
import { existsSync as existsSync11, rmSync, statSync, writeFileSync } from "node:fs";
|
|
1457
|
+
import { join as join11 } from "node:path";
|
|
1450
1458
|
function declaredBuildTarget(cwd) {
|
|
1451
1459
|
return readProjectPackageJson(cwd).nx?.targets?.[BUILD_SCRIPT_NAME];
|
|
1452
1460
|
}
|
|
@@ -1492,7 +1500,7 @@ function outputsProblem(cwd, startedAt) {
|
|
|
1492
1500
|
}
|
|
1493
1501
|
|
|
1494
1502
|
// src/roles/build/prerequisite.ts
|
|
1495
|
-
import { spawnSync as spawnSync2 } from "child_process";
|
|
1503
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
1496
1504
|
var PREBUILD_SCRIPT_NAME = `pre${BUILD_SCRIPT_NAME}`;
|
|
1497
1505
|
function insideBuildScript(env = process.env) {
|
|
1498
1506
|
return env.npm_lifecycle_event === BUILD_SCRIPT_NAME;
|
|
@@ -1618,13 +1626,13 @@ function registerBuild() {
|
|
|
1618
1626
|
}
|
|
1619
1627
|
|
|
1620
1628
|
// src/roles/format/adapters/oxfmt/oxfmt.adapter.ts
|
|
1621
|
-
import { spawnSync as spawnSync3 } from "child_process";
|
|
1622
|
-
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
1623
|
-
import { join as join16 } from "path";
|
|
1629
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
1630
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "node:fs";
|
|
1631
|
+
import { join as join16 } from "node:path";
|
|
1624
1632
|
|
|
1625
1633
|
// src/core/config/has-source.ts
|
|
1626
|
-
import { readdirSync } from "fs";
|
|
1627
|
-
import { extname, join as join12, relative as relative2 } from "path";
|
|
1634
|
+
import { readdirSync } from "node:fs";
|
|
1635
|
+
import { extname, join as join12, relative as relative2 } from "node:path";
|
|
1628
1636
|
var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
1629
1637
|
"node_modules",
|
|
1630
1638
|
"dist",
|
|
@@ -1742,8 +1750,8 @@ function writesWhenRewritten(name, command) {
|
|
|
1742
1750
|
var CHECK_SCRIPT_NAMES = /* @__PURE__ */ new Set(["lint", "format", "check", "test", "typecheck", "verify"]);
|
|
1743
1751
|
|
|
1744
1752
|
// src/roles/format/inherited-ignores.ts
|
|
1745
|
-
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
1746
|
-
import { join as join13 } from "path";
|
|
1753
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
|
|
1754
|
+
import { join as join13 } from "node:path";
|
|
1747
1755
|
var ROOT_IGNORE_FILE = ".prettierignore";
|
|
1748
1756
|
function isPattern(line) {
|
|
1749
1757
|
const trimmed = line.trim();
|
|
@@ -1808,13 +1816,13 @@ function needsSvelteCompiler(output) {
|
|
|
1808
1816
|
}
|
|
1809
1817
|
|
|
1810
1818
|
// src/roles/format/prettier-config.ts
|
|
1811
|
-
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
|
|
1812
|
-
import { join as join15 } from "path";
|
|
1819
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
|
|
1820
|
+
import { join as join15 } from "node:path";
|
|
1813
1821
|
|
|
1814
1822
|
// src/roles/format/resolve-oxfmt.ts
|
|
1815
|
-
import { readFileSync as readFileSync10 } from "fs";
|
|
1816
|
-
import { createRequire as createRequire2 } from "module";
|
|
1817
|
-
import { dirname as dirname2, join as join14 } from "path";
|
|
1823
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
1824
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
1825
|
+
import { dirname as dirname2, join as join14 } from "node:path";
|
|
1818
1826
|
function resolveOxfmt(cwd) {
|
|
1819
1827
|
return resolveBin(cwd, "oxfmt") ?? binFromOwnInstall("oxfmt", "oxfmt");
|
|
1820
1828
|
}
|
|
@@ -2314,9 +2322,9 @@ function registerFormat() {
|
|
|
2314
2322
|
}
|
|
2315
2323
|
|
|
2316
2324
|
// src/roles/lint/adapters/oxlint/oxlint.adapter.ts
|
|
2317
|
-
import { spawnSync as spawnSync4 } from "child_process";
|
|
2318
|
-
import { existsSync as existsSync22, readFileSync as readFileSync20, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2319
|
-
import { join as join26 } from "path";
|
|
2325
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
2326
|
+
import { existsSync as existsSync22, readFileSync as readFileSync20, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2327
|
+
import { join as join26 } from "node:path";
|
|
2320
2328
|
|
|
2321
2329
|
// src/core/config/deferred-rules.ts
|
|
2322
2330
|
function deferredRuleNames(rules) {
|
|
@@ -2324,8 +2332,8 @@ function deferredRuleNames(rules) {
|
|
|
2324
2332
|
}
|
|
2325
2333
|
|
|
2326
2334
|
// src/core/fix-report.ts
|
|
2327
|
-
import { statSync as statSync2 } from "fs";
|
|
2328
|
-
import { join as join17 } from "path";
|
|
2335
|
+
import { statSync as statSync2 } from "node:fs";
|
|
2336
|
+
import { join as join17 } from "node:path";
|
|
2329
2337
|
var CONFIG_REFUSED = /failed to parse .*config|invalid config file/i;
|
|
2330
2338
|
function readFailure(output) {
|
|
2331
2339
|
const lines = output.split("\n").map((line2) => line2.trim());
|
|
@@ -2360,8 +2368,12 @@ function changedSince(cwd, before, extensions) {
|
|
|
2360
2368
|
return changed.sort();
|
|
2361
2369
|
}
|
|
2362
2370
|
var NAMED_IN_REPORT = 5;
|
|
2371
|
+
var NOT_INSTALLED_YET = /invalid config file[\s\S]*node_modules/i;
|
|
2363
2372
|
function describeFixOutcome(outcome, toolLabel) {
|
|
2364
2373
|
if (outcome.failure !== void 0) {
|
|
2374
|
+
if (NOT_INSTALLED_YET.test(outcome.failure)) {
|
|
2375
|
+
return `the ${toolLabel} preset is not installed in this module yet, which is expected: this command just added it and the install comes next. Nothing was fixed. Run \`pnpm install\`, then \`sentinel --run --${toolLabel} --fix\` to clear what a machine can clear.`;
|
|
2376
|
+
}
|
|
2365
2377
|
return `the ${toolLabel} autofix could not run, so nothing was fixed and this module has not been checked: ${outcome.failure}`;
|
|
2366
2378
|
}
|
|
2367
2379
|
if (outcome.changed.length === 0) return `the ${toolLabel} autofix changed nothing`;
|
|
@@ -2372,8 +2384,8 @@ function describeFixOutcome(outcome, toolLabel) {
|
|
|
2372
2384
|
}
|
|
2373
2385
|
|
|
2374
2386
|
// src/roles/lint/module-baseline.ts
|
|
2375
|
-
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
2376
|
-
import { join as join18 } from "path";
|
|
2387
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
|
|
2388
|
+
import { join as join18 } from "node:path";
|
|
2377
2389
|
var LINT_BASELINE_FILE = ".oxlintrc.baseline.json";
|
|
2378
2390
|
var LINT_MEASURE_FILE = ".oxlintrc.measure.json";
|
|
2379
2391
|
var LINT_BASELINE_SPECIFIER = `./${LINT_BASELINE_FILE}`;
|
|
@@ -4247,8 +4259,8 @@ function downgradedRulesFor(preset) {
|
|
|
4247
4259
|
}
|
|
4248
4260
|
|
|
4249
4261
|
// src/roles/lint/extra-layers.ts
|
|
4250
|
-
import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
|
|
4251
|
-
import { isAbsolute as isAbsolute2, join as join19, resolve as resolve3 } from "path";
|
|
4262
|
+
import { existsSync as existsSync16, readFileSync as readFileSync14 } from "node:fs";
|
|
4263
|
+
import { isAbsolute as isAbsolute2, join as join19, resolve as resolve3 } from "node:path";
|
|
4252
4264
|
function ruleCount(cwd, specifier) {
|
|
4253
4265
|
const path = isAbsolute2(specifier) ? specifier : resolve3(cwd, specifier);
|
|
4254
4266
|
if (!existsSync16(path)) return void 0;
|
|
@@ -4333,8 +4345,8 @@ function errorCountsByConfigRule(stdout) {
|
|
|
4333
4345
|
}
|
|
4334
4346
|
|
|
4335
4347
|
// src/core/config/read-adoption.ts
|
|
4336
|
-
import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
|
|
4337
|
-
import { join as join21 } from "path";
|
|
4348
|
+
import { existsSync as existsSync18, readFileSync as readFileSync16 } from "node:fs";
|
|
4349
|
+
import { join as join21 } from "node:path";
|
|
4338
4350
|
|
|
4339
4351
|
// src/core/config/owned-keys.ts
|
|
4340
4352
|
function presetOwnedKeys(config, permitted, presetSets) {
|
|
@@ -4349,8 +4361,8 @@ function localOnlyKeys(config, permitted, presetSets) {
|
|
|
4349
4361
|
}
|
|
4350
4362
|
|
|
4351
4363
|
// src/core/config/resolve-config-target.ts
|
|
4352
|
-
import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
|
|
4353
|
-
import { join as join20 } from "path";
|
|
4364
|
+
import { existsSync as existsSync17, readFileSync as readFileSync15 } from "node:fs";
|
|
4365
|
+
import { join as join20 } from "node:path";
|
|
4354
4366
|
function readExtends(absolutePath) {
|
|
4355
4367
|
let parsed;
|
|
4356
4368
|
try {
|
|
@@ -4441,10 +4453,10 @@ function readLintAdoption(cwd) {
|
|
|
4441
4453
|
}
|
|
4442
4454
|
|
|
4443
4455
|
// src/roles/lint/resolve-oxlint.ts
|
|
4444
|
-
import { existsSync as existsSync19 } from "fs";
|
|
4445
|
-
import { createRequire as createRequire3 } from "module";
|
|
4446
|
-
import { delimiter as delimiter2, dirname as dirname3, join as join22 } from "path";
|
|
4447
|
-
import { fileURLToPath } from "url";
|
|
4456
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
4457
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
4458
|
+
import { delimiter as delimiter2, dirname as dirname3, join as join22 } from "node:path";
|
|
4459
|
+
import { fileURLToPath } from "node:url";
|
|
4448
4460
|
var PACKAGE_OF = {
|
|
4449
4461
|
oxlint: "oxlint",
|
|
4450
4462
|
tsgolint: "oxlint-tsgolint"
|
|
@@ -4486,12 +4498,12 @@ function oxlintSearchPath(cwd) {
|
|
|
4486
4498
|
}
|
|
4487
4499
|
|
|
4488
4500
|
// src/roles/lint/adapters/oxlint/plan.ts
|
|
4489
|
-
import { existsSync as existsSync21, readFileSync as readFileSync19 } from "fs";
|
|
4490
|
-
import { join as join25 } from "path";
|
|
4501
|
+
import { existsSync as existsSync21, readFileSync as readFileSync19 } from "node:fs";
|
|
4502
|
+
import { join as join25 } from "node:path";
|
|
4491
4503
|
|
|
4492
4504
|
// src/roles/lint/eslint-ignores.ts
|
|
4493
|
-
import { existsSync as existsSync20, readFileSync as readFileSync17 } from "fs";
|
|
4494
|
-
import { join as join23 } from "path";
|
|
4505
|
+
import { existsSync as existsSync20, readFileSync as readFileSync17 } from "node:fs";
|
|
4506
|
+
import { join as join23 } from "node:path";
|
|
4495
4507
|
import { parseSync as parseSync2 } from "oxc-parser";
|
|
4496
4508
|
|
|
4497
4509
|
// src/shared/ast.ts
|
|
@@ -4615,8 +4627,8 @@ function lintPresetFor(preset) {
|
|
|
4615
4627
|
}
|
|
4616
4628
|
|
|
4617
4629
|
// src/roles/lint/rename-suppressions.ts
|
|
4618
|
-
import { readdirSync as readdirSync2, readFileSync as readFileSync18, statSync as statSync3 } from "fs";
|
|
4619
|
-
import { join as join24, relative as relative3 } from "path";
|
|
4630
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync18, statSync as statSync3 } from "node:fs";
|
|
4631
|
+
import { join as join24, relative as relative3 } from "node:path";
|
|
4620
4632
|
var SOURCE_EXTENSIONS = [
|
|
4621
4633
|
".ts",
|
|
4622
4634
|
".tsx",
|
|
@@ -5393,7 +5405,10 @@ function registerLint() {
|
|
|
5393
5405
|
}
|
|
5394
5406
|
|
|
5395
5407
|
// src/roles/test/adapters/vitest/vitest.adapter.ts
|
|
5396
|
-
import { spawnSync as spawnSync5 } from "child_process";
|
|
5408
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
5409
|
+
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync30 } from "node:fs";
|
|
5410
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
5411
|
+
import { isAbsolute as isAbsolute3, join as join38, resolve as resolve6 } from "node:path";
|
|
5397
5412
|
|
|
5398
5413
|
// src/roles/test/presets/toolchain.json
|
|
5399
5414
|
var toolchain_default2 = {
|
|
@@ -5470,17 +5485,18 @@ function testTarget(options) {
|
|
|
5470
5485
|
}
|
|
5471
5486
|
|
|
5472
5487
|
// src/roles/test/jest-migration.ts
|
|
5473
|
-
import { existsSync as
|
|
5474
|
-
import {
|
|
5488
|
+
import { existsSync as existsSync30, readFileSync as readFileSync28, readdirSync as readdirSync8 } from "node:fs";
|
|
5489
|
+
import { createRequire as createRequire6 } from "node:module";
|
|
5490
|
+
import { dirname as dirname8, join as join36, relative as relative7 } from "node:path";
|
|
5475
5491
|
|
|
5476
5492
|
// src/roles/test/codemod/migrate-module.ts
|
|
5477
|
-
import { readdirSync as readdirSync6, readFileSync as readFileSync25, statSync as statSync6 } from "fs";
|
|
5478
|
-
import { join as join31 } from "path";
|
|
5493
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync25, statSync as statSync6 } from "node:fs";
|
|
5494
|
+
import { join as join31 } from "node:path";
|
|
5479
5495
|
import { parseSync as parseSync8 } from "oxc-parser";
|
|
5480
5496
|
|
|
5481
5497
|
// src/roles/test/read-adoption.ts
|
|
5482
|
-
import { existsSync as existsSync23, readFileSync as readFileSync21, readdirSync as readdirSync3 } from "fs";
|
|
5483
|
-
import { join as join27, relative as relative4 } from "path";
|
|
5498
|
+
import { existsSync as existsSync23, readFileSync as readFileSync21, readdirSync as readdirSync3 } from "node:fs";
|
|
5499
|
+
import { join as join27, relative as relative4 } from "node:path";
|
|
5484
5500
|
var JEST_CONFIG_FILES = [
|
|
5485
5501
|
"jest.config.ts",
|
|
5486
5502
|
"jest.config.js",
|
|
@@ -5600,6 +5616,17 @@ function readTestAdoption(cwd) {
|
|
|
5600
5616
|
};
|
|
5601
5617
|
}
|
|
5602
5618
|
|
|
5619
|
+
// src/roles/test/support-file-name.ts
|
|
5620
|
+
var JEST_PREFIX = "jest.";
|
|
5621
|
+
var VITEST_PREFIX = "vitest.";
|
|
5622
|
+
function vitestSupportName(relative8) {
|
|
5623
|
+
const cut = relative8.lastIndexOf("/");
|
|
5624
|
+
const directory = cut === -1 ? "" : relative8.slice(0, cut + 1);
|
|
5625
|
+
const base = relative8.slice(cut + 1);
|
|
5626
|
+
if (!base.startsWith(JEST_PREFIX)) return relative8;
|
|
5627
|
+
return `${directory}${VITEST_PREFIX}${base.slice(JEST_PREFIX.length)}`;
|
|
5628
|
+
}
|
|
5629
|
+
|
|
5603
5630
|
// src/roles/test/codemod/migrate-file.ts
|
|
5604
5631
|
import { parseSync as parseSync7 } from "oxc-parser";
|
|
5605
5632
|
|
|
@@ -5715,7 +5742,7 @@ function rewriteAssertionReturnValue(source, program) {
|
|
|
5715
5742
|
for (const reading of readings) {
|
|
5716
5743
|
const from = reading.start;
|
|
5717
5744
|
let to = reading.end;
|
|
5718
|
-
while (to < source.length && /[\
|
|
5745
|
+
while (to < source.length && /[ \t;]/.test(source[to])) to += 1;
|
|
5719
5746
|
let head = from;
|
|
5720
5747
|
while (head > 0 && /[ \t]/.test(source[head - 1])) head -= 1;
|
|
5721
5748
|
if (head > 0 && source[head - 1] === "\n") head -= 1;
|
|
@@ -6450,9 +6477,9 @@ ${source}`;
|
|
|
6450
6477
|
}
|
|
6451
6478
|
|
|
6452
6479
|
// src/roles/test/codemod/named-rules.ts
|
|
6453
|
-
import { existsSync as existsSync24, readFileSync as readFileSync22 } from "fs";
|
|
6454
|
-
import { createRequire as createRequire4 } from "module";
|
|
6455
|
-
import { dirname as dirname4, join as join28 } from "path";
|
|
6480
|
+
import { existsSync as existsSync24, readFileSync as readFileSync22 } from "node:fs";
|
|
6481
|
+
import { createRequire as createRequire4 } from "node:module";
|
|
6482
|
+
import { dirname as dirname4, join as join28 } from "node:path";
|
|
6456
6483
|
function isCommonJsPackage(fromFile, specifier) {
|
|
6457
6484
|
if (specifier.startsWith(".") || specifier.startsWith("/")) return false;
|
|
6458
6485
|
let resolved;
|
|
@@ -6581,12 +6608,47 @@ function applyNamedRules(source, program, fileName = "") {
|
|
|
6581
6608
|
const applied = /* @__PURE__ */ new Set();
|
|
6582
6609
|
const asyncify = /* @__PURE__ */ new Set();
|
|
6583
6610
|
const mocked = mockedSpecifiers(program);
|
|
6584
|
-
const wholeStatement = /* @__PURE__ */ new
|
|
6611
|
+
const wholeStatement = /* @__PURE__ */ new Map();
|
|
6585
6612
|
walk(program, (node) => {
|
|
6586
6613
|
if (node.type !== "ExpressionStatement") return;
|
|
6587
6614
|
const expression = node.expression;
|
|
6588
|
-
if (expression !== void 0) wholeStatement.
|
|
6615
|
+
if (expression !== void 0) wholeStatement.set(expression, node);
|
|
6616
|
+
});
|
|
6617
|
+
const standsAlone = /* @__PURE__ */ new Set();
|
|
6618
|
+
const alone = (node) => {
|
|
6619
|
+
if (node !== void 0) standsAlone.add(node);
|
|
6620
|
+
};
|
|
6621
|
+
walk(program, (node) => {
|
|
6622
|
+
switch (node.type) {
|
|
6623
|
+
case "VariableDeclarator":
|
|
6624
|
+
return alone(node.init);
|
|
6625
|
+
case "AssignmentExpression":
|
|
6626
|
+
return alone(node.right);
|
|
6627
|
+
case "ReturnStatement":
|
|
6628
|
+
case "ExportDefaultDeclaration":
|
|
6629
|
+
return alone(node.argument ?? node.declaration);
|
|
6630
|
+
case "ArrowFunctionExpression":
|
|
6631
|
+
return node.expression === true ? alone(node.body) : void 0;
|
|
6632
|
+
case "ConditionalExpression":
|
|
6633
|
+
alone(node.consequent);
|
|
6634
|
+
return alone(node.alternate);
|
|
6635
|
+
case "ArrayExpression":
|
|
6636
|
+
return (node.elements ?? []).forEach((e) => alone(e ?? void 0));
|
|
6637
|
+
case "Property":
|
|
6638
|
+
case "ObjectProperty":
|
|
6639
|
+
return node.shorthand === true ? void 0 : alone(node.value);
|
|
6640
|
+
case "CallExpression":
|
|
6641
|
+
case "NewExpression":
|
|
6642
|
+
return (node.arguments ?? []).forEach((a) => alone(a));
|
|
6643
|
+
default:
|
|
6644
|
+
return;
|
|
6645
|
+
}
|
|
6589
6646
|
});
|
|
6647
|
+
const placed = (node, expression, trailing = "") => {
|
|
6648
|
+
if (trailing !== "") return `(${expression})${trailing}`;
|
|
6649
|
+
if (wholeStatement.has(node)) return expression;
|
|
6650
|
+
return standsAlone.has(node) ? expression : `(${expression})`;
|
|
6651
|
+
};
|
|
6590
6652
|
const handedToTheRunnerAsAModule = /* @__PURE__ */ new Set();
|
|
6591
6653
|
walk(program, (node) => {
|
|
6592
6654
|
const returned = mockFactoryReturn(node);
|
|
@@ -6616,7 +6678,7 @@ function applyNamedRules(source, program, fileName = "") {
|
|
|
6616
6678
|
edits.push({
|
|
6617
6679
|
start: node.start,
|
|
6618
6680
|
end: node.end,
|
|
6619
|
-
text: `
|
|
6681
|
+
text: placed(node, `await import(${argumentText(source, node)})`)
|
|
6620
6682
|
});
|
|
6621
6683
|
applied.add("jest.requireMock");
|
|
6622
6684
|
if (enclosing !== void 0 && enclosing.async !== true) asyncify.add(enclosing);
|
|
@@ -6628,7 +6690,7 @@ function applyNamedRules(source, program, fileName = "") {
|
|
|
6628
6690
|
edits.push({
|
|
6629
6691
|
start: node.start,
|
|
6630
6692
|
end: node.end,
|
|
6631
|
-
text: `
|
|
6693
|
+
text: placed(node, `await vi.${asyncReplacement}(${argumentText(source, node)})`)
|
|
6632
6694
|
});
|
|
6633
6695
|
applied.add(`jest.${member}`);
|
|
6634
6696
|
if (enclosing !== void 0 && enclosing.async !== true) asyncify.add(enclosing);
|
|
@@ -6641,7 +6703,7 @@ function applyNamedRules(source, program, fileName = "") {
|
|
|
6641
6703
|
edits.push({
|
|
6642
6704
|
start: node.start,
|
|
6643
6705
|
end: node.end,
|
|
6644
|
-
text:
|
|
6706
|
+
text: placed(node, `await import(${argumentText(source, node)})`, interop)
|
|
6645
6707
|
});
|
|
6646
6708
|
applied.add("require");
|
|
6647
6709
|
if (enclosing !== void 0 && enclosing.async !== true) asyncify.add(enclosing);
|
|
@@ -6932,6 +6994,78 @@ function rewriteMockFactoryDefault(source, program, defaultImported) {
|
|
|
6932
6994
|
};
|
|
6933
6995
|
}
|
|
6934
6996
|
|
|
6997
|
+
// src/roles/test/codemod/mock-promise-resolve.ts
|
|
6998
|
+
var MOCK_FACTORY = /* @__PURE__ */ new Set(["fn", "spyOn"]);
|
|
6999
|
+
function isMockFactory(source, node) {
|
|
7000
|
+
if (node.type !== "CallExpression") return false;
|
|
7001
|
+
const callee = node.callee;
|
|
7002
|
+
if (callee?.type !== "MemberExpression") return false;
|
|
7003
|
+
const object = callee.object;
|
|
7004
|
+
const property = callee.property;
|
|
7005
|
+
if (object?.type !== "Identifier" || property?.type !== "Identifier") return false;
|
|
7006
|
+
const runner = source.slice(object.start, object.end);
|
|
7007
|
+
const method = source.slice(property.start, property.end);
|
|
7008
|
+
return (runner === "jest" || runner === "vi") && MOCK_FACTORY.has(method);
|
|
7009
|
+
}
|
|
7010
|
+
function resolvedValue(source, executor) {
|
|
7011
|
+
if (executor.type !== "ArrowFunctionExpression" && executor.type !== "FunctionExpression") {
|
|
7012
|
+
return void 0;
|
|
7013
|
+
}
|
|
7014
|
+
const params = executor.params;
|
|
7015
|
+
if (params?.length !== 1) return void 0;
|
|
7016
|
+
const parameter = params[0];
|
|
7017
|
+
if (parameter.type !== "Identifier") return void 0;
|
|
7018
|
+
const resolveName = source.slice(parameter.start, parameter.end);
|
|
7019
|
+
const body = executor.body;
|
|
7020
|
+
if (body === void 0) return void 0;
|
|
7021
|
+
let call;
|
|
7022
|
+
if (body.type === "BlockStatement") {
|
|
7023
|
+
const statements = body.body;
|
|
7024
|
+
if (statements?.length !== 1) return void 0;
|
|
7025
|
+
const only = statements[0];
|
|
7026
|
+
if (only.type !== "ExpressionStatement") return void 0;
|
|
7027
|
+
call = only.expression;
|
|
7028
|
+
} else {
|
|
7029
|
+
call = body;
|
|
7030
|
+
}
|
|
7031
|
+
if (call?.type !== "CallExpression") return void 0;
|
|
7032
|
+
const callee = call.callee;
|
|
7033
|
+
if (callee?.type !== "Identifier") return void 0;
|
|
7034
|
+
if (source.slice(callee.start, callee.end) !== resolveName) return void 0;
|
|
7035
|
+
const args = call.arguments;
|
|
7036
|
+
if (args?.length !== 1) return void 0;
|
|
7037
|
+
return args[0];
|
|
7038
|
+
}
|
|
7039
|
+
function rewriteMockPromiseResolve(source, program) {
|
|
7040
|
+
const edits = [];
|
|
7041
|
+
walk(program, (node) => {
|
|
7042
|
+
if (!isMockFactory(source, node)) return;
|
|
7043
|
+
for (const argument of node.arguments ?? []) {
|
|
7044
|
+
walk(argument, (inner) => {
|
|
7045
|
+
if (inner.type !== "NewExpression") return;
|
|
7046
|
+
const callee = inner.callee;
|
|
7047
|
+
if (callee?.type !== "Identifier") return;
|
|
7048
|
+
if (source.slice(callee.start, callee.end) !== "Promise") return;
|
|
7049
|
+
if ((inner.typeArguments ?? inner.typeParameters) !== void 0) return;
|
|
7050
|
+
const executors = inner.arguments ?? [];
|
|
7051
|
+
if (executors.length !== 1) return;
|
|
7052
|
+
const value = resolvedValue(source, executors[0]);
|
|
7053
|
+
if (value === void 0) return;
|
|
7054
|
+
edits.push({
|
|
7055
|
+
start: inner.start,
|
|
7056
|
+
end: inner.end,
|
|
7057
|
+
text: `Promise.resolve(${source.slice(value.start, value.end)})`
|
|
7058
|
+
});
|
|
7059
|
+
});
|
|
7060
|
+
}
|
|
7061
|
+
});
|
|
7062
|
+
let result = source;
|
|
7063
|
+
for (const edit of [...edits].sort((left, right) => right.start - left.start)) {
|
|
7064
|
+
result = `${result.slice(0, edit.start)}${edit.text}${result.slice(edit.end)}`;
|
|
7065
|
+
}
|
|
7066
|
+
return { source: result, rewritten: edits.length };
|
|
7067
|
+
}
|
|
7068
|
+
|
|
6935
7069
|
// src/roles/test/codemod/propagate-await.ts
|
|
6936
7070
|
import { parseSync as parseSync5 } from "oxc-parser";
|
|
6937
7071
|
var FUNCTIONS3 = /* @__PURE__ */ new Set(["ArrowFunctionExpression", "FunctionExpression", "FunctionDeclaration"]);
|
|
@@ -7059,9 +7193,9 @@ function propagateAwait(fileName, source, original, elsewhere = /* @__PURE__ */
|
|
|
7059
7193
|
}
|
|
7060
7194
|
|
|
7061
7195
|
// src/roles/test/codemod/reexport-barrel-mock.ts
|
|
7062
|
-
import { readFileSync as readFileSync23, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
7063
|
-
import { createRequire as createRequire5 } from "module";
|
|
7064
|
-
import { dirname as dirname5, join as join29 } from "path";
|
|
7196
|
+
import { readFileSync as readFileSync23, readdirSync as readdirSync4, statSync as statSync4 } from "node:fs";
|
|
7197
|
+
import { createRequire as createRequire5 } from "node:module";
|
|
7198
|
+
import { dirname as dirname5, join as join29 } from "node:path";
|
|
7065
7199
|
var MEMBER_TYPES13 = /* @__PURE__ */ new Set(["MemberExpression", "StaticMemberExpression"]);
|
|
7066
7200
|
var PACKAGE_SUBPATH = /^(@[^/]+\/[^/]+|[^@.][^/]*)\/(.+)$/;
|
|
7067
7201
|
var PURE_REEXPORT = /^module\.exports\s*=\s*require\(\s*(['"])([^'"]+)\1\s*\)\s*;?$/;
|
|
@@ -7160,12 +7294,9 @@ ${text}` });
|
|
|
7160
7294
|
// src/roles/test/codemod/repoint-msw.ts
|
|
7161
7295
|
import { parseSync as parseSync6 } from "oxc-parser";
|
|
7162
7296
|
var SENTINEL_MSW_SERVER = "@hublo/sentinel/test/msw";
|
|
7163
|
-
var
|
|
7297
|
+
var SENTINEL_MSW_API = "@hublo/sentinel/test/tools/msw";
|
|
7164
7298
|
var MSW_PACKAGE = /^msw$/;
|
|
7165
|
-
|
|
7166
|
-
const source = declaration.source?.value;
|
|
7167
|
-
return source !== void 0 && MSW_PACKAGE.test(source);
|
|
7168
|
-
}
|
|
7299
|
+
var MSW_SERVER_MODULE = /(^|\/)msw\/server$/;
|
|
7169
7300
|
function importsServer(declaration) {
|
|
7170
7301
|
return (declaration.specifiers ?? []).some((specifier) => {
|
|
7171
7302
|
const imported = specifier.imported;
|
|
@@ -7181,21 +7312,28 @@ function repointMswServer(fileName, source) {
|
|
|
7181
7312
|
if (node.type !== "ImportDeclaration") return;
|
|
7182
7313
|
const specifier = node.source;
|
|
7183
7314
|
if (specifier?.value === void 0) return;
|
|
7184
|
-
const
|
|
7185
|
-
|
|
7186
|
-
|
|
7315
|
+
const isServer = MSW_SERVER_MODULE.test(specifier.value) && importsServer(node);
|
|
7316
|
+
const isPackage2 = MSW_PACKAGE.test(specifier.value);
|
|
7317
|
+
if (!isServer && !isPackage2) return;
|
|
7318
|
+
edits.push({
|
|
7319
|
+
start: specifier.start,
|
|
7320
|
+
end: specifier.end,
|
|
7321
|
+
to: isServer ? SENTINEL_MSW_SERVER : SENTINEL_MSW_API
|
|
7322
|
+
});
|
|
7187
7323
|
});
|
|
7188
7324
|
if (edits.length === 0) return { source, repointed: false };
|
|
7189
7325
|
let rewritten = source;
|
|
7190
7326
|
for (const edit of edits.sort((left, right) => right.start - left.start)) {
|
|
7191
7327
|
const quote = source[edit.start] === '"' ? '"' : "'";
|
|
7192
|
-
rewritten = rewritten.slice(0, edit.start) + `${quote}${
|
|
7328
|
+
rewritten = rewritten.slice(0, edit.start) + `${quote}${edit.to}${quote}` + rewritten.slice(edit.end);
|
|
7193
7329
|
}
|
|
7194
7330
|
return { source: rewritten, repointed: true };
|
|
7195
7331
|
}
|
|
7196
7332
|
|
|
7197
7333
|
// src/roles/test/codemod/spy-instance-type.ts
|
|
7198
7334
|
var SPY_TYPES = /* @__PURE__ */ new Set(["SpyInstance"]);
|
|
7335
|
+
var QUALIFIED_ONLY = /* @__PURE__ */ new Set(["Mock"]);
|
|
7336
|
+
var VITEST_NAME = { SpyInstance: "MockInstance", Mock: "Mock" };
|
|
7199
7337
|
function typeNameOf(source, node) {
|
|
7200
7338
|
const name = node.typeName;
|
|
7201
7339
|
if (name === void 0) return void 0;
|
|
@@ -7207,17 +7345,20 @@ function rewriteSpyInstanceType(source, program) {
|
|
|
7207
7345
|
if (node.type !== "TSTypeReference") return;
|
|
7208
7346
|
const written = typeNameOf(source, node);
|
|
7209
7347
|
if (written === void 0) return;
|
|
7210
|
-
const
|
|
7211
|
-
|
|
7348
|
+
const qualified = written.startsWith("jest.");
|
|
7349
|
+
const bare = qualified ? written.slice("jest.".length) : written;
|
|
7350
|
+
if (!SPY_TYPES.has(bare) && !(qualified && QUALIFIED_ONLY.has(bare))) return;
|
|
7212
7351
|
const args = (node.typeArguments ?? node.typeParameters)?.params;
|
|
7213
7352
|
if (args === void 0 || args.length === 0) return;
|
|
7214
7353
|
const text = (argument) => source.slice(argument.start, argument.end);
|
|
7215
7354
|
const returns = text(args[0]);
|
|
7216
|
-
const parameters = args.length > 1 ? `...args: ${text(args[1])}` : "...args:
|
|
7355
|
+
const parameters = args.length > 1 ? `...args: ${text(args[1])}` : "...args: never[]";
|
|
7356
|
+
const target = VITEST_NAME[bare];
|
|
7217
7357
|
edits.push({
|
|
7218
7358
|
start: node.start,
|
|
7219
7359
|
end: node.end,
|
|
7220
|
-
text:
|
|
7360
|
+
text: `${target}<(${parameters}) => ${returns}>`,
|
|
7361
|
+
imports: target
|
|
7221
7362
|
});
|
|
7222
7363
|
});
|
|
7223
7364
|
let result = source;
|
|
@@ -7227,7 +7368,8 @@ function rewriteSpyInstanceType(source, program) {
|
|
|
7227
7368
|
return {
|
|
7228
7369
|
source: result,
|
|
7229
7370
|
rewritten: edits.length,
|
|
7230
|
-
|
|
7371
|
+
// Only the names actually written, so a file does not import a type it never uses.
|
|
7372
|
+
typeImports: [...new Set(edits.map((edit) => edit.imports))]
|
|
7231
7373
|
};
|
|
7232
7374
|
}
|
|
7233
7375
|
|
|
@@ -7315,7 +7457,10 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
|
|
|
7315
7457
|
const afterEmpty = parse2(fileName, emptyImplementations.source);
|
|
7316
7458
|
if (afterEmpty === void 0) return unparsable("empty mock implementation");
|
|
7317
7459
|
const hooks = rewriteHookReturnValue(emptyImplementations.source, afterEmpty);
|
|
7318
|
-
const
|
|
7460
|
+
const afterHooks = parse2(fileName, hooks.source);
|
|
7461
|
+
if (afterHooks === void 0) return unparsable("hook return value");
|
|
7462
|
+
const promises = rewriteMockPromiseResolve(hooks.source, afterHooks);
|
|
7463
|
+
const awaited = propagateAwait(fileName, promises.source, original, asyncHelpers);
|
|
7319
7464
|
const program = parse2(fileName, awaited.source);
|
|
7320
7465
|
if (program === void 0) return unparsable("hook body");
|
|
7321
7466
|
const hoist = hoistCapturedBindings(
|
|
@@ -7366,8 +7511,8 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
|
|
|
7366
7511
|
}
|
|
7367
7512
|
|
|
7368
7513
|
// src/roles/test/codemod/test-support-files.ts
|
|
7369
|
-
import { existsSync as existsSync25, readFileSync as readFileSync24, readdirSync as readdirSync5, statSync as statSync5 } from "fs";
|
|
7370
|
-
import { dirname as dirname6, join as join30, relative as relative5, resolve as resolve4 } from "path";
|
|
7514
|
+
import { existsSync as existsSync25, readFileSync as readFileSync24, readdirSync as readdirSync5, statSync as statSync5 } from "node:fs";
|
|
7515
|
+
import { dirname as dirname6, join as join30, relative as relative5, resolve as resolve4 } from "node:path";
|
|
7371
7516
|
var SKIPPED = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage", ".git", ".vite"]);
|
|
7372
7517
|
var TEST_FILE2 = /\.(spec|test)\.[cm]?[jt]sx?$/;
|
|
7373
7518
|
var SOURCE_FILE = /\.[cm]?[jt]sx?$/;
|
|
@@ -7524,14 +7669,16 @@ function migrateModuleTests(cwd, setupFiles = []) {
|
|
|
7524
7669
|
let hoisted = 0;
|
|
7525
7670
|
const defaultImported = defaultImportedSpecifiers(cwd);
|
|
7526
7671
|
const asyncHelpers = /* @__PURE__ */ new Set();
|
|
7527
|
-
for (const
|
|
7528
|
-
const source = read2(join31(cwd,
|
|
7672
|
+
for (const relative8 of listTestSupportFiles(cwd, setupFiles)) {
|
|
7673
|
+
const source = read2(join31(cwd, relative8));
|
|
7529
7674
|
if (source === void 0) {
|
|
7530
|
-
blockers.push({ file:
|
|
7675
|
+
blockers.push({ file: relative8, reason: "this file could not be read" });
|
|
7531
7676
|
continue;
|
|
7532
7677
|
}
|
|
7533
|
-
const migrated = migrateTestFile(join31(cwd,
|
|
7534
|
-
|
|
7678
|
+
const migrated = migrateTestFile(join31(cwd, relative8), source, defaultImported);
|
|
7679
|
+
const renamedTo = vitestSupportName(relative8);
|
|
7680
|
+
if (!migrated.changed && renamedTo === relative8) continue;
|
|
7681
|
+
if (renamedTo !== relative8) operations.push({ kind: "delete", path: relative8 });
|
|
7535
7682
|
const before = parse3(source);
|
|
7536
7683
|
const after = parse3(migrated.source);
|
|
7537
7684
|
if (before !== void 0 && after !== void 0) {
|
|
@@ -7542,41 +7689,44 @@ function migrateModuleTests(cwd, setupFiles = []) {
|
|
|
7542
7689
|
}
|
|
7543
7690
|
files++;
|
|
7544
7691
|
renamed += migrated.renamed;
|
|
7545
|
-
operations.push({ kind: "write", path:
|
|
7692
|
+
operations.push({ kind: "write", path: renamedTo, contents: migrated.source });
|
|
7546
7693
|
}
|
|
7547
|
-
for (const
|
|
7548
|
-
const source = read2(join31(cwd,
|
|
7694
|
+
for (const relative8 of listTestFiles(cwd)) {
|
|
7695
|
+
const source = read2(join31(cwd, relative8));
|
|
7549
7696
|
if (source === void 0) {
|
|
7550
|
-
blockers.push({ file:
|
|
7697
|
+
blockers.push({ file: relative8, reason: "this file could not be read" });
|
|
7551
7698
|
continue;
|
|
7552
7699
|
}
|
|
7553
|
-
const migrated = migrateTestFile(join31(cwd,
|
|
7700
|
+
const migrated = migrateTestFile(join31(cwd, relative8), source, defaultImported, asyncHelpers);
|
|
7554
7701
|
for (const member of migrated.unhandled) {
|
|
7555
7702
|
blockers.push({
|
|
7556
|
-
file: `${
|
|
7703
|
+
file: `${relative8}:${member.line}`,
|
|
7557
7704
|
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}`
|
|
7558
7705
|
});
|
|
7559
7706
|
}
|
|
7560
7707
|
for (const refusal of migrated.refusals) {
|
|
7561
|
-
blockers.push({ file:
|
|
7708
|
+
blockers.push({ file: relative8, reason: refusal.reason });
|
|
7562
7709
|
}
|
|
7563
7710
|
if (!migrated.changed) continue;
|
|
7564
7711
|
files++;
|
|
7565
7712
|
renamed += migrated.renamed;
|
|
7566
7713
|
if (migrated.hoisted.length > 0) hoisted++;
|
|
7567
|
-
operations.push({ kind: "write", path:
|
|
7714
|
+
operations.push({ kind: "write", path: relative8, contents: migrated.source });
|
|
7568
7715
|
}
|
|
7569
7716
|
return blockers.length > 0 ? { operations: [], files, renamed, hoisted, blockers } : { operations, files, renamed, hoisted, blockers: [] };
|
|
7570
7717
|
}
|
|
7571
7718
|
|
|
7572
7719
|
// src/roles/test/nest/generate-config.ts
|
|
7573
|
-
import { readdirSync as readdirSync7, readFileSync as readFileSync26, statSync as statSync7 } from "fs";
|
|
7574
|
-
import { join as join32, relative as relative6 } from "path";
|
|
7720
|
+
import { existsSync as existsSync26, readdirSync as readdirSync7, readFileSync as readFileSync26, statSync as statSync7 } from "node:fs";
|
|
7721
|
+
import { join as join32, relative as relative6 } from "node:path";
|
|
7575
7722
|
var NEST_TEST_PRESET_SPECIFIER = "@hublo/sentinel/test/nest";
|
|
7576
|
-
var
|
|
7723
|
+
var REACT_TEST_PRESET_SPECIFIER = "@hublo/sentinel/test/react";
|
|
7724
|
+
function flavourOf(reading) {
|
|
7725
|
+
return reading.testEnvironment === "jsdom" ? "react" : "nest";
|
|
7726
|
+
}
|
|
7727
|
+
var SENTINEL_TEST_SETUP = "@hublo/sentinel/test/setup/jest-parity";
|
|
7577
7728
|
var SENTINEL_MSW_SETUP = "@hublo/sentinel/test/setup/msw-lifecycle";
|
|
7578
7729
|
var SENTINEL_WORKSPACE_SETUP = "@hublo/sentinel/test/setup/workspace";
|
|
7579
|
-
var NEST_TEST_CONFIG_FILE = "vitest.config.mts";
|
|
7580
7730
|
function hopsToRoot(cwd, workspaceRoot) {
|
|
7581
7731
|
const up = relative6(cwd, workspaceRoot).replaceAll("\\", "/");
|
|
7582
7732
|
return up === "" ? "." : up;
|
|
@@ -7643,14 +7793,29 @@ function readModuleTraits(cwd) {
|
|
|
7643
7793
|
return traits;
|
|
7644
7794
|
}
|
|
7645
7795
|
var withoutRootDir = (value) => value.replaceAll("<rootDir>/", "./").replaceAll("<rootDir>", ".");
|
|
7646
|
-
function
|
|
7796
|
+
function ownsMswLifecycle(cwd, reading) {
|
|
7797
|
+
const declared = [...reading.setupFiles ?? [], ...reading.setupFilesAfterEnv ?? []].filter(
|
|
7798
|
+
(file) => !file.endsWith("jest.setup.after.env.js")
|
|
7799
|
+
);
|
|
7800
|
+
for (const raw of declared) {
|
|
7801
|
+
const file = join32(cwd, raw.replaceAll("<rootDir>/", "").replaceAll("<rootDir>", "."));
|
|
7802
|
+
if (!existsSync26(file)) continue;
|
|
7803
|
+
try {
|
|
7804
|
+
if (/\.listen\s*\(/.test(readFileSync26(file, "utf8"))) return true;
|
|
7805
|
+
} catch {
|
|
7806
|
+
continue;
|
|
7807
|
+
}
|
|
7808
|
+
}
|
|
7809
|
+
return false;
|
|
7810
|
+
}
|
|
7811
|
+
function setupBlock(cwd, reading, msw) {
|
|
7647
7812
|
const declared = [...reading.setupFiles ?? [], ...reading.setupFilesAfterEnv ?? []];
|
|
7648
7813
|
const namedTheRootSetup = declared.some((file) => file.endsWith("jest.setup.after.env.js"));
|
|
7649
|
-
const own = declared.filter((file) => !file.endsWith("jest.setup.after.env.js")).map((file) => `'${withoutRootDir(file)}'`);
|
|
7814
|
+
const own = declared.filter((file) => !file.endsWith("jest.setup.after.env.js")).map((file) => `'${vitestSupportName(withoutRootDir(file))}'`);
|
|
7650
7815
|
const sentinel = [
|
|
7651
7816
|
`'${SENTINEL_TEST_SETUP}'`,
|
|
7652
7817
|
...namedTheRootSetup ? [`'${SENTINEL_WORKSPACE_SETUP}'`] : [],
|
|
7653
|
-
...msw ? [`'${SENTINEL_MSW_SETUP}'`] : []
|
|
7818
|
+
...msw && namedTheRootSetup && !ownsMswLifecycle(cwd, reading) ? [`'${SENTINEL_MSW_SETUP}'`] : []
|
|
7654
7819
|
];
|
|
7655
7820
|
return ` setupFiles: [${[...sentinel, ...own].join(", ")}],`;
|
|
7656
7821
|
}
|
|
@@ -7677,7 +7842,7 @@ function timeoutBlock(reading) {
|
|
|
7677
7842
|
}
|
|
7678
7843
|
function globalSetupBlock(reading) {
|
|
7679
7844
|
if (reading.globalSetup === void 0) return "";
|
|
7680
|
-
const files = reading.globalSetup.map((file) => `'${withoutRootDir(file)}'`).join(", ");
|
|
7845
|
+
const files = reading.globalSetup.map((file) => `'${vitestSupportName(withoutRootDir(file))}'`).join(", ");
|
|
7681
7846
|
return ` globalSetup: [${files}],`;
|
|
7682
7847
|
}
|
|
7683
7848
|
function includeBlock(reading) {
|
|
@@ -7697,7 +7862,8 @@ function aliasBlock(reading) {
|
|
|
7697
7862
|
if (reading.aliases === void 0 || reading.aliases.length === 0) return "";
|
|
7698
7863
|
const entries = reading.aliases.map((alias) => {
|
|
7699
7864
|
const pattern = alias.find.replaceAll(String.raw`\\`, "\\").replaceAll("/", String.raw`\/`);
|
|
7700
|
-
const
|
|
7865
|
+
const deadToken = /<rootdir>/i.test(alias.replacement) && !alias.replacement.includes("<rootDir>");
|
|
7866
|
+
const target = isPackage(alias.replacement) ? `'${alias.replacement}'` : deadToken ? `'${alias.replacement}'` : `path.resolve(import.meta.dirname, '${withoutRootDir(alias.replacement)}')`;
|
|
7701
7867
|
return ` { find: /${pattern}/, replacement: ${target} },`;
|
|
7702
7868
|
});
|
|
7703
7869
|
return [" resolve: {", " alias: [", ...entries, " ],", " },"].join("\n");
|
|
@@ -7712,7 +7878,7 @@ function generateNestTestConfig(cwd, reading, target = "test") {
|
|
|
7712
7878
|
const up = hopsToRoot(cwd, workspaceRoot);
|
|
7713
7879
|
const test = [
|
|
7714
7880
|
environmentBlock(reading),
|
|
7715
|
-
setupBlock(reading, traits.msw),
|
|
7881
|
+
setupBlock(cwd, reading, traits.msw),
|
|
7716
7882
|
envBlock(reading),
|
|
7717
7883
|
timeoutBlock(reading),
|
|
7718
7884
|
concurrencyBlock(cwd, reading, target),
|
|
@@ -7727,7 +7893,7 @@ function generateNestTestConfig(cwd, reading, target = "test") {
|
|
|
7727
7893
|
`import path from 'node:path'`,
|
|
7728
7894
|
"",
|
|
7729
7895
|
...needsDefaults ? [`import { configDefaults } from 'vitest/config'`, ""] : [],
|
|
7730
|
-
`import { defineConfig, nestTestConfig } from '${NEST_TEST_PRESET_SPECIFIER}'`
|
|
7896
|
+
flavourOf(reading) === "react" ? `import { defineConfig, reactTestConfig } from '${REACT_TEST_PRESET_SPECIFIER}'` : `import { defineConfig, nestTestConfig } from '${NEST_TEST_PRESET_SPECIFIER}'`
|
|
7731
7897
|
].join("\n");
|
|
7732
7898
|
const overrides = [aliasBlock(reading), " test: {", ...test, " },"].filter(
|
|
7733
7899
|
(line) => line !== ""
|
|
@@ -7738,7 +7904,7 @@ function generateNestTestConfig(cwd, reading, target = "test") {
|
|
|
7738
7904
|
source: `${imports}
|
|
7739
7905
|
|
|
7740
7906
|
export default defineConfig(
|
|
7741
|
-
nestTestConfig({
|
|
7907
|
+
${flavourOf(reading) === "react" ? "reactTestConfig" : "nestTestConfig"}({
|
|
7742
7908
|
root: import.meta.dirname,
|
|
7743
7909
|
workspaceRoot: path.resolve(import.meta.dirname, '${up}'),${decoratorLine}
|
|
7744
7910
|
overrides: {
|
|
@@ -7751,8 +7917,8 @@ ${overrides.join("\n")}
|
|
|
7751
7917
|
}
|
|
7752
7918
|
|
|
7753
7919
|
// src/roles/test/nest/read-jest-config.ts
|
|
7754
|
-
import { existsSync as
|
|
7755
|
-
import { join as join33 } from "path";
|
|
7920
|
+
import { existsSync as existsSync27, readFileSync as readFileSync27 } from "node:fs";
|
|
7921
|
+
import { join as join33 } from "node:path";
|
|
7756
7922
|
var CARRIED = [
|
|
7757
7923
|
"testEnvironment",
|
|
7758
7924
|
"coverageDirectory",
|
|
@@ -7942,7 +8108,7 @@ function extendedConfig(source, cwd) {
|
|
|
7942
8108
|
(name) => name.replace(/^\.\//, "")
|
|
7943
8109
|
);
|
|
7944
8110
|
const file = candidates.find(
|
|
7945
|
-
(name) => /^jest[\w.-]*\.(ts|js)$/.test(name) &&
|
|
8111
|
+
(name) => /^jest[\w.-]*\.(ts|js)$/.test(name) && existsSync27(join33(cwd, name))
|
|
7946
8112
|
);
|
|
7947
8113
|
if (file === void 0) continue;
|
|
7948
8114
|
if (!new RegExp(String.raw`\{\s*\.\.\.${binding}\b`).test(source)) continue;
|
|
@@ -7969,8 +8135,8 @@ function sideEffects(source, extended) {
|
|
|
7969
8135
|
return found;
|
|
7970
8136
|
}
|
|
7971
8137
|
function readJestConfig(cwd, named) {
|
|
7972
|
-
const file = named ?? JEST_CONFIG_FILES.find((name) =>
|
|
7973
|
-
if (file === void 0 || !
|
|
8138
|
+
const file = named ?? JEST_CONFIG_FILES.find((name) => existsSync27(join33(cwd, name)));
|
|
8139
|
+
if (file === void 0 || !existsSync27(join33(cwd, file))) return void 0;
|
|
7974
8140
|
let source;
|
|
7975
8141
|
try {
|
|
7976
8142
|
source = readFileSync27(join33(cwd, file), "utf8");
|
|
@@ -8054,8 +8220,8 @@ function readJestConfig(cwd, named) {
|
|
|
8054
8220
|
}
|
|
8055
8221
|
|
|
8056
8222
|
// src/roles/test/nest/run-modes.ts
|
|
8057
|
-
import { existsSync as
|
|
8058
|
-
import { basename as basename2, join as join34 } from "path";
|
|
8223
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
8224
|
+
import { basename as basename2, join as join34 } from "node:path";
|
|
8059
8225
|
var JEST_EXECUTOR = "@nx/jest:jest";
|
|
8060
8226
|
var PRIMARY_CONFIG_FILE = "vitest.config.mts";
|
|
8061
8227
|
var PRIMARY_TARGET = "test";
|
|
@@ -8076,7 +8242,7 @@ function runModes(cwd, fallback) {
|
|
|
8076
8242
|
const jestConfig = entry.options?.jestConfig;
|
|
8077
8243
|
if (jestConfig === void 0) continue;
|
|
8078
8244
|
const name = configName(jestConfig);
|
|
8079
|
-
if (!
|
|
8245
|
+
if (!existsSync28(join34(cwd, name))) continue;
|
|
8080
8246
|
declared.push({
|
|
8081
8247
|
target,
|
|
8082
8248
|
jestConfig: name,
|
|
@@ -8100,12 +8266,59 @@ function runModes(cwd, fallback) {
|
|
|
8100
8266
|
);
|
|
8101
8267
|
}
|
|
8102
8268
|
|
|
8269
|
+
// src/roles/test/written-file-formatter.ts
|
|
8270
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
8271
|
+
import { dirname as dirname7, join as join35 } from "node:path";
|
|
8272
|
+
var PRETTIER_CONFIGS = [
|
|
8273
|
+
".prettierrc",
|
|
8274
|
+
".prettierrc.json",
|
|
8275
|
+
".prettierrc.json5",
|
|
8276
|
+
".prettierrc.yaml",
|
|
8277
|
+
".prettierrc.yml",
|
|
8278
|
+
".prettierrc.js",
|
|
8279
|
+
".prettierrc.cjs",
|
|
8280
|
+
".prettierrc.mjs",
|
|
8281
|
+
".prettierrc.toml",
|
|
8282
|
+
"prettier.config.js",
|
|
8283
|
+
"prettier.config.cjs",
|
|
8284
|
+
"prettier.config.mjs"
|
|
8285
|
+
];
|
|
8286
|
+
function prettierConfigFrom(cwd) {
|
|
8287
|
+
let dir = cwd;
|
|
8288
|
+
for (; ; ) {
|
|
8289
|
+
for (const name of PRETTIER_CONFIGS) {
|
|
8290
|
+
if (existsSync29(join35(dir, name))) return join35(dir, name);
|
|
8291
|
+
}
|
|
8292
|
+
const parent = dirname7(dir);
|
|
8293
|
+
if (parent === dir) return void 0;
|
|
8294
|
+
dir = parent;
|
|
8295
|
+
}
|
|
8296
|
+
}
|
|
8297
|
+
function writtenFileFormatter(cwd) {
|
|
8298
|
+
if (readFormatAdoption(cwd).configFile !== null) {
|
|
8299
|
+
const oxfmt = resolveOxfmt(cwd);
|
|
8300
|
+
if (oxfmt !== void 0) {
|
|
8301
|
+
return { bin: oxfmt, args: ["--write"], origin: "this module's own oxfmt config" };
|
|
8302
|
+
}
|
|
8303
|
+
}
|
|
8304
|
+
const config = prettierConfigFrom(cwd);
|
|
8305
|
+
const prettier = resolveBin(cwd, "prettier");
|
|
8306
|
+
if (config !== void 0 && prettier !== void 0) {
|
|
8307
|
+
return {
|
|
8308
|
+
bin: prettier,
|
|
8309
|
+
args: ["--write", "--log-level", "warn"],
|
|
8310
|
+
origin: `the workspace's Prettier`
|
|
8311
|
+
};
|
|
8312
|
+
}
|
|
8313
|
+
return void 0;
|
|
8314
|
+
}
|
|
8315
|
+
|
|
8103
8316
|
// src/roles/test/jest-migration.ts
|
|
8104
8317
|
var SPEC_TSCONFIG = "tsconfig.spec.json";
|
|
8105
8318
|
var VITEST_GLOBAL_TYPES = "vitest/globals";
|
|
8106
8319
|
function specTypesOperation(cwd) {
|
|
8107
|
-
const path =
|
|
8108
|
-
if (!
|
|
8320
|
+
const path = join36(cwd, SPEC_TSCONFIG);
|
|
8321
|
+
if (!existsSync30(path)) return void 0;
|
|
8109
8322
|
let config;
|
|
8110
8323
|
try {
|
|
8111
8324
|
config = parseJsonc(
|
|
@@ -8127,6 +8340,70 @@ function specTypesOperation(cwd) {
|
|
|
8127
8340
|
}
|
|
8128
8341
|
};
|
|
8129
8342
|
}
|
|
8343
|
+
function shippedTypesPath(cwd, declaration) {
|
|
8344
|
+
try {
|
|
8345
|
+
const manifest = createRequire6(join36(cwd, "noop.cjs")).resolve("@hublo/sentinel/package.json");
|
|
8346
|
+
const file = join36(dirname8(manifest), declaration);
|
|
8347
|
+
if (existsSync30(file)) {
|
|
8348
|
+
const path = relative7(cwd, file);
|
|
8349
|
+
return path.startsWith(".") ? path : `./${path}`;
|
|
8350
|
+
}
|
|
8351
|
+
} catch {
|
|
8352
|
+
}
|
|
8353
|
+
return `./node_modules/@hublo/sentinel/${declaration}`;
|
|
8354
|
+
}
|
|
8355
|
+
function tsconfigFileListOperations(cwd) {
|
|
8356
|
+
const operations = [];
|
|
8357
|
+
let names;
|
|
8358
|
+
try {
|
|
8359
|
+
names = readdirSync8(cwd).filter((name) => TSCONFIG_FILE.test(name));
|
|
8360
|
+
} catch {
|
|
8361
|
+
return operations;
|
|
8362
|
+
}
|
|
8363
|
+
for (const name of names) {
|
|
8364
|
+
let config;
|
|
8365
|
+
try {
|
|
8366
|
+
config = parseJsonc(
|
|
8367
|
+
readFileSync28(join36(cwd, name), "utf8"),
|
|
8368
|
+
name
|
|
8369
|
+
);
|
|
8370
|
+
} catch {
|
|
8371
|
+
continue;
|
|
8372
|
+
}
|
|
8373
|
+
const include = Array.isArray(config?.include) ? rewriteFileList(config.include) : void 0;
|
|
8374
|
+
const exclude = Array.isArray(config?.exclude) ? rewriteFileList(config.exclude) : void 0;
|
|
8375
|
+
const files = Array.isArray(config?.files) ? rewriteFileList(config.files) : void 0;
|
|
8376
|
+
const withTypes = name === SPEC_TSCONFIG ? withShippedTypes(cwd, files ?? config?.files) : void 0;
|
|
8377
|
+
if (include === void 0 && exclude === void 0 && files === void 0 && withTypes === void 0)
|
|
8378
|
+
continue;
|
|
8379
|
+
operations.push({
|
|
8380
|
+
kind: "merge-json",
|
|
8381
|
+
path: name,
|
|
8382
|
+
value: {
|
|
8383
|
+
...include === void 0 ? {} : { include },
|
|
8384
|
+
...exclude === void 0 ? {} : { exclude },
|
|
8385
|
+
...withTypes === void 0 ? files === void 0 ? {} : { files } : { files: withTypes }
|
|
8386
|
+
}
|
|
8387
|
+
});
|
|
8388
|
+
}
|
|
8389
|
+
return operations;
|
|
8390
|
+
}
|
|
8391
|
+
function rewriteFileList(entries) {
|
|
8392
|
+
const rewritten = entries.filter((entry) => typeof entry !== "string" || !JEST_CONFIG_ENTRY.test(entry)).map((entry) => typeof entry === "string" ? vitestSupportName(entry) : entry);
|
|
8393
|
+
const unchanged = rewritten.length === entries.length && rewritten.every((entry, index) => entry === entries[index]);
|
|
8394
|
+
return unchanged ? void 0 : rewritten;
|
|
8395
|
+
}
|
|
8396
|
+
function withShippedTypes(cwd, files) {
|
|
8397
|
+
const entries = Array.isArray(files) ? files : [];
|
|
8398
|
+
const missing = SHIPPED_TYPES.filter(
|
|
8399
|
+
(declaration) => !entries.some((entry) => entry.endsWith(declaration))
|
|
8400
|
+
);
|
|
8401
|
+
if (missing.length === 0) return void 0;
|
|
8402
|
+
return [...entries, ...missing.map((declaration) => shippedTypesPath(cwd, declaration))];
|
|
8403
|
+
}
|
|
8404
|
+
var SHIPPED_TYPES = ["types/mock-extended.d.ts", "types/jest-global.d.ts"];
|
|
8405
|
+
var JEST_CONFIG_ENTRY = /(^|\/)jest\.[\w.-]*config\.[cm]?[jt]s$/;
|
|
8406
|
+
var TSCONFIG_FILE = /^tsconfig[\w.-]*\.json$/;
|
|
8130
8407
|
function siblingScriptRewrites(cwd) {
|
|
8131
8408
|
const rewrites = {};
|
|
8132
8409
|
for (const [name, command] of Object.entries(moduleScripts(cwd))) {
|
|
@@ -8173,8 +8450,9 @@ function planJestMigration(context, adoption) {
|
|
|
8173
8450
|
`${migration.blockers.length} thing(s) in this module's test files need a human before it can move. Nothing was written, because a config without its files is a suite calling jest.* under Vitest.${listed}`
|
|
8174
8451
|
);
|
|
8175
8452
|
}
|
|
8176
|
-
const
|
|
8177
|
-
const
|
|
8453
|
+
const fromProject = projectTargets(context.cwd) ?? {};
|
|
8454
|
+
const fromManifest = manifestTargets(context.cwd);
|
|
8455
|
+
const existingFor = (name) => fromProject[name] ?? fromManifest[name];
|
|
8178
8456
|
const siblings = siblingScriptRewrites(context.cwd);
|
|
8179
8457
|
const specTypes = specTypesOperation(context.cwd);
|
|
8180
8458
|
const scripts = Object.fromEntries(
|
|
@@ -8191,6 +8469,7 @@ function planJestMigration(context, adoption) {
|
|
|
8191
8469
|
})),
|
|
8192
8470
|
...migration.operations,
|
|
8193
8471
|
...specTypes === void 0 ? [] : [specTypes],
|
|
8472
|
+
...tsconfigFileListOperations(context.cwd),
|
|
8194
8473
|
manifestOperation(context.cwd, { ...scripts, ...siblings }),
|
|
8195
8474
|
...prepared.flatMap(
|
|
8196
8475
|
({ mode }) => nxTargetOperations({
|
|
@@ -8208,18 +8487,26 @@ function planJestMigration(context, adoption) {
|
|
|
8208
8487
|
(name) => ({ kind: "delete", path: name })
|
|
8209
8488
|
)
|
|
8210
8489
|
];
|
|
8490
|
+
const escaping = (prepared[0]?.reading.aliases ?? []).map((alias) => alias.replacement).filter((replacement) => replacement.includes(".."));
|
|
8211
8491
|
const notes = [
|
|
8212
8492
|
`${migration.files} test file(s) migrated from jest to Vitest, ${migration.renamed} site(s) rewritten${migration.hoisted > 0 ? `, ${migration.hoisted} of them moving a mock factory's captures into vi.hoisted` : ""}.`,
|
|
8213
|
-
`${
|
|
8493
|
+
`${prepared.map(({ mode }) => mode.vitestConfig).join(", ")} written from what ${prepared.map(({ mode }) => mode.jestConfig).join(", ")} declared, and that config deleted.` + (escaping.length === 0 ? ` Every specifier in the new one is a package name: nothing in it reaches outside this module.` : ` \u26A0\uFE0F ${escaping.length} alias(es) carried from that config reach OUTSIDE this module: ${escaping.slice(0, 3).join(", ")}. They came from its \`moduleNameMapper\` and are kept as they were, but a module that reaches out cannot be moved on its own.`)
|
|
8214
8494
|
];
|
|
8215
8495
|
if (specTypes !== void 0) {
|
|
8216
8496
|
notes.push(
|
|
8217
8497
|
`${SPEC_TSCONFIG} now declares \`${VITEST_GLOBAL_TYPES}\` instead of \`jest\`. Without it the module keeps depending on @types/jest and \`jest.Mock\` still resolves, so the migration would look finished while the dependency it cuts is still declared.`
|
|
8218
8498
|
);
|
|
8219
8499
|
}
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8500
|
+
const formatter = writtenFileFormatter(context.cwd);
|
|
8501
|
+
if (formatter !== void 0) {
|
|
8502
|
+
notes.push(
|
|
8503
|
+
`the files this rewrote were run through ${formatter.origin} afterwards. Two things needed it: the Vitest type imports land beside the existing ones, and a rewritten call is often SHORTER than the one it replaced, so the line breaks around it are no longer the ones your config would pick. Neither is a defect, and both would have failed your format check. What no formatter does is ORDER imports, so if your lint enforces \`import/order\`, run its fixer too.`
|
|
8504
|
+
);
|
|
8505
|
+
} else {
|
|
8506
|
+
notes.push(
|
|
8507
|
+
`no style is declared here, by this module or its workspace, so what was written keeps the shape it was written in, and the Vitest type imports sit beside the existing ones rather than where your \`import/order\` wants them. That rule is classified by a prefix list this package cannot read, so it is left to the tool that owns it: run your module's own lint fixer (\`nx run <module>:lint --fix\` here) and its formatter before committing.`
|
|
8508
|
+
);
|
|
8509
|
+
}
|
|
8223
8510
|
notes.push(
|
|
8224
8511
|
`then run this module's TYPECHECK, not only its tests. The migration rewrites types as well as calls, and a wrong type is invisible to a green suite: measured on a real module, \`--run --test\` passed while \`--run --typescript\` reported ten errors it had not had before.`
|
|
8225
8512
|
);
|
|
@@ -8308,7 +8595,7 @@ function plan3(context) {
|
|
|
8308
8595
|
`${configFile} now imports ${rewrite.moved.join(", ")} from sentinel. Only the import lines changed: every alias, setup file, timeout and coverage path is untouched, so this suite runs what it ran before.`
|
|
8309
8596
|
);
|
|
8310
8597
|
}
|
|
8311
|
-
const existing = projectTargets(context.cwd)?.[TEST_SCRIPT_NAME];
|
|
8598
|
+
const existing = projectTargets(context.cwd)?.[TEST_SCRIPT_NAME] ?? manifestTargets(context.cwd)[TEST_SCRIPT_NAME];
|
|
8312
8599
|
const outputs = existing?.outputs ?? [];
|
|
8313
8600
|
const dependsOn = existing?.dependsOn ?? [];
|
|
8314
8601
|
const configurations = existing?.configurations ?? {};
|
|
@@ -8363,6 +8650,329 @@ function plan3(context) {
|
|
|
8363
8650
|
return { operations, notes };
|
|
8364
8651
|
}
|
|
8365
8652
|
|
|
8653
|
+
// src/roles/test/reference.ts
|
|
8654
|
+
import { execFileSync } from "node:child_process";
|
|
8655
|
+
import { createHash } from "node:crypto";
|
|
8656
|
+
import { existsSync as existsSync31, mkdirSync, mkdtempSync, readFileSync as readFileSync29, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
8657
|
+
import { tmpdir } from "node:os";
|
|
8658
|
+
import { join as join37, resolve as resolve5 } from "node:path";
|
|
8659
|
+
|
|
8660
|
+
// src/roles/test/baseline.ts
|
|
8661
|
+
function readSnapshot(report) {
|
|
8662
|
+
const files = report?.testResults ?? [];
|
|
8663
|
+
const passing = [];
|
|
8664
|
+
const failing = [];
|
|
8665
|
+
const unloadable = [];
|
|
8666
|
+
for (const file of files) {
|
|
8667
|
+
const assertions = file?.assertionResults ?? [];
|
|
8668
|
+
if (file?.status === "failed" && assertions.length === 0) {
|
|
8669
|
+
unloadable.push(typeof file.name === "string" ? file.name : "an unnamed file");
|
|
8670
|
+
}
|
|
8671
|
+
for (const assertion of assertions) {
|
|
8672
|
+
const name = assertion?.fullName;
|
|
8673
|
+
if (typeof name !== "string" || name === "") continue;
|
|
8674
|
+
if (assertion.status === "passed") passing.push(name);
|
|
8675
|
+
else if (assertion.status === "failed") failing.push(name);
|
|
8676
|
+
}
|
|
8677
|
+
}
|
|
8678
|
+
const claimed = report?.numFailedTests;
|
|
8679
|
+
return {
|
|
8680
|
+
files: files.length,
|
|
8681
|
+
passing: passing.sort(),
|
|
8682
|
+
failing: failing.sort(),
|
|
8683
|
+
unloadable: unloadable.sort(),
|
|
8684
|
+
claimedFailing: typeof claimed === "number" ? claimed : failing.length
|
|
8685
|
+
};
|
|
8686
|
+
}
|
|
8687
|
+
function difference(left, right) {
|
|
8688
|
+
const remaining = /* @__PURE__ */ new Map();
|
|
8689
|
+
for (const name of right) remaining.set(name, (remaining.get(name) ?? 0) + 1);
|
|
8690
|
+
const missing = [];
|
|
8691
|
+
for (const name of left) {
|
|
8692
|
+
const left_ = remaining.get(name) ?? 0;
|
|
8693
|
+
if (left_ === 0) {
|
|
8694
|
+
missing.push(name);
|
|
8695
|
+
continue;
|
|
8696
|
+
}
|
|
8697
|
+
remaining.set(name, left_ - 1);
|
|
8698
|
+
}
|
|
8699
|
+
return missing.sort();
|
|
8700
|
+
}
|
|
8701
|
+
var withoutRenderingQuotes = (name) => name.replaceAll(String.raw`\'`, "'").replaceAll(String.raw`\"`, '"').replaceAll(/['"`]\s*\+\s*['"`]/g, "").replaceAll(/(^|[\s:,[{(])(''|"")($|[\s,)\]}])/g, "$1<empty-string>$3").replaceAll(/['"`]/g, "");
|
|
8702
|
+
function withoutRenderingNoise(name) {
|
|
8703
|
+
const spelled = withSortedKeys(
|
|
8704
|
+
withoutUndefinedEntries(
|
|
8705
|
+
withoutErrorRendering(
|
|
8706
|
+
withoutNaN(
|
|
8707
|
+
withoutTimestamps(withoutRenderingQuotes(withoutClassBodies(withoutStackFrames(name))))
|
|
8708
|
+
)
|
|
8709
|
+
)
|
|
8710
|
+
)
|
|
8711
|
+
);
|
|
8712
|
+
return withoutArrayBrackets(
|
|
8713
|
+
spelled.replaceAll(/\[\s+/g, "[").replaceAll(/\s+\]/g, "]").replaceAll(/\{\s+/g, "{").replaceAll(/\s+\}/g, "}").replaceAll(/(?<=[[{,\s])\+0\b/g, "0").replaceAll(/:\s+/g, ":").replaceAll(/,\s+/g, ",")
|
|
8714
|
+
);
|
|
8715
|
+
}
|
|
8716
|
+
function withoutArrayBrackets(name) {
|
|
8717
|
+
return name.replaceAll(
|
|
8718
|
+
/\[([^[\]]*)\]/g,
|
|
8719
|
+
(match, inner) => (
|
|
8720
|
+
// `[Object]` and `[Array]` are the formatter saying it STOPPED, not an array literal.
|
|
8721
|
+
// Stripping their brackets would erase the marker `collapsedOf` pairs on.
|
|
8722
|
+
/^(?:Object|Array)$/.test(inner) ? match : inner.replaceAll(/,\s+/g, ",")
|
|
8723
|
+
)
|
|
8724
|
+
);
|
|
8725
|
+
}
|
|
8726
|
+
var withoutClassBodies = (name) => name.replaceAll(
|
|
8727
|
+
/class\s+([A-Za-z_$][\w$]*)\s*\{\s*\}/g,
|
|
8728
|
+
(_match, className) => `class ${className}`
|
|
8729
|
+
).replaceAll(
|
|
8730
|
+
/class\s+([A-Za-z_$][\w$]*)[\s\S]*?\n\}/g,
|
|
8731
|
+
(_match, className) => `class ${className}`
|
|
8732
|
+
);
|
|
8733
|
+
var withoutTimestamps = (name) => name.replaceAll(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z/g, "<instant>");
|
|
8734
|
+
var withoutNaN = (name) => name.replaceAll(/\bNaN\b/g, "null");
|
|
8735
|
+
function entriesOf(inner) {
|
|
8736
|
+
const parts = [];
|
|
8737
|
+
let depth = 0;
|
|
8738
|
+
let start = 0;
|
|
8739
|
+
for (let index = 0; index < inner.length; index += 1) {
|
|
8740
|
+
const character = inner[index];
|
|
8741
|
+
if (character === "[" || character === "(") depth += 1;
|
|
8742
|
+
else if (character === "]" || character === ")") depth -= 1;
|
|
8743
|
+
else if (character === "," && depth === 0) {
|
|
8744
|
+
parts.push(inner.slice(start, index));
|
|
8745
|
+
start = index + 1;
|
|
8746
|
+
}
|
|
8747
|
+
if (depth < 0) return void 0;
|
|
8748
|
+
}
|
|
8749
|
+
if (depth !== 0) return void 0;
|
|
8750
|
+
parts.push(inner.slice(start));
|
|
8751
|
+
return parts.map((part) => part.trim()).filter(Boolean);
|
|
8752
|
+
}
|
|
8753
|
+
var sortedGroup = (match, inner) => {
|
|
8754
|
+
if (!inner.includes(":")) return match;
|
|
8755
|
+
const parts = entriesOf(inner);
|
|
8756
|
+
if (parts === void 0) return match;
|
|
8757
|
+
if (parts.length < 2 || !parts.every((part) => part.includes(":"))) return match;
|
|
8758
|
+
return `{${[...parts].sort().join(",")}}`;
|
|
8759
|
+
};
|
|
8760
|
+
var HELD = "\u241F";
|
|
8761
|
+
var withSortedKeys = (name) => {
|
|
8762
|
+
const held = [];
|
|
8763
|
+
let current = name;
|
|
8764
|
+
for (let depth = 0; depth < 24; depth += 1) {
|
|
8765
|
+
const next = current.replaceAll(/\{([^{}]*)\}/g, (match, inner) => {
|
|
8766
|
+
held.push(sortedGroup(match, inner));
|
|
8767
|
+
return `${HELD}${held.length - 1}${HELD}`;
|
|
8768
|
+
});
|
|
8769
|
+
if (next === current) break;
|
|
8770
|
+
current = next;
|
|
8771
|
+
}
|
|
8772
|
+
for (let depth = 0; depth < 24 && current.includes(HELD); depth += 1) {
|
|
8773
|
+
current = current.replaceAll(
|
|
8774
|
+
new RegExp(`${HELD}(\\d+)${HELD}`, "g"),
|
|
8775
|
+
(_match, index) => held[Number(index)]
|
|
8776
|
+
);
|
|
8777
|
+
}
|
|
8778
|
+
return current;
|
|
8779
|
+
};
|
|
8780
|
+
var withoutStackFrames = (name) => {
|
|
8781
|
+
const frame = name.search(/\n\s+at\s/);
|
|
8782
|
+
return frame === -1 ? name : name.slice(0, frame);
|
|
8783
|
+
};
|
|
8784
|
+
var withoutErrorRendering = (name) => name.replaceAll(/\[[A-Za-z]*(?:Error|Exception):[^\][]*\]/g, "{}");
|
|
8785
|
+
var withoutUndefinedEntries = (name) => name.replaceAll(/([{,])\s*[A-Za-z_$][\w$]*\s*:\s*undefined\s*(?=[,}])/g, "$1").replaceAll(/\{\s*,/g, "{").replaceAll(/,\s*\}/g, "}").replaceAll(/\{\s*\}/g, "{}");
|
|
8786
|
+
function collapsedOf(candidate, name) {
|
|
8787
|
+
if (!/\[(?:Object|Array)\]/.test(candidate)) return false;
|
|
8788
|
+
const pattern = candidate.split(/\[(?:Object|Array)\]/).map((part) => part.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*");
|
|
8789
|
+
return new RegExp(`^${pattern}$`, "s").test(name);
|
|
8790
|
+
}
|
|
8791
|
+
function truncationOf(candidate, name) {
|
|
8792
|
+
const cut = candidate.indexOf("\u2026");
|
|
8793
|
+
if (cut === -1) return false;
|
|
8794
|
+
const head = candidate.slice(0, cut);
|
|
8795
|
+
const tail = candidate.slice(cut + 1);
|
|
8796
|
+
return name.startsWith(head) && name.endsWith(tail);
|
|
8797
|
+
}
|
|
8798
|
+
function pairRenamed(missing, added) {
|
|
8799
|
+
const remaining = [...added];
|
|
8800
|
+
const renamed = [];
|
|
8801
|
+
const unmatched = [];
|
|
8802
|
+
for (const name of missing) {
|
|
8803
|
+
const normalised = withoutRenderingNoise(name);
|
|
8804
|
+
const at = remaining.findIndex((candidate) => withoutRenderingNoise(candidate) === normalised);
|
|
8805
|
+
if (at === -1) {
|
|
8806
|
+
unmatched.push(name);
|
|
8807
|
+
continue;
|
|
8808
|
+
}
|
|
8809
|
+
renamed.push({ before: name, after: remaining[at] });
|
|
8810
|
+
remaining.splice(at, 1);
|
|
8811
|
+
}
|
|
8812
|
+
const lost = [];
|
|
8813
|
+
for (const name of unmatched) {
|
|
8814
|
+
const normalised = withoutRenderingNoise(name);
|
|
8815
|
+
const at = remaining.findIndex(
|
|
8816
|
+
(candidate) => truncationOf(withoutRenderingNoise(candidate), normalised) || collapsedOf(normalised, withoutRenderingNoise(candidate))
|
|
8817
|
+
);
|
|
8818
|
+
if (at === -1) {
|
|
8819
|
+
lost.push(name);
|
|
8820
|
+
continue;
|
|
8821
|
+
}
|
|
8822
|
+
renamed.push({ before: name, after: remaining[at] });
|
|
8823
|
+
remaining.splice(at, 1);
|
|
8824
|
+
}
|
|
8825
|
+
return { renamed, missing: lost, added: remaining };
|
|
8826
|
+
}
|
|
8827
|
+
function compareSnapshots(before, after) {
|
|
8828
|
+
const beforeAll = [...before.passing, ...before.failing];
|
|
8829
|
+
const afterAll = [...after.passing, ...after.failing];
|
|
8830
|
+
const paired = pairRenamed(difference(beforeAll, afterAll), difference(afterAll, beforeAll));
|
|
8831
|
+
const renamedBefore = new Set(paired.renamed.map((pair) => pair.before));
|
|
8832
|
+
const regressed = difference(before.passing, after.passing).filter(
|
|
8833
|
+
(name) => !paired.missing.includes(name) && !renamedBefore.has(name)
|
|
8834
|
+
);
|
|
8835
|
+
const failedBefore = new Set(before.failing);
|
|
8836
|
+
const recovered = after.passing.filter((name) => failedBefore.has(name));
|
|
8837
|
+
return {
|
|
8838
|
+
identical: paired.missing.length === 0 && paired.added.length === 0 && regressed.length === 0,
|
|
8839
|
+
missing: paired.missing,
|
|
8840
|
+
added: paired.added,
|
|
8841
|
+
regressed,
|
|
8842
|
+
renamed: paired.renamed,
|
|
8843
|
+
recovered,
|
|
8844
|
+
alreadyFailing: before.failing.length
|
|
8845
|
+
};
|
|
8846
|
+
}
|
|
8847
|
+
function unusableAsBaseline(snapshot) {
|
|
8848
|
+
if (snapshot.unloadable.length > 0) {
|
|
8849
|
+
return `${snapshot.unloadable.length} file(s) in the reference run did not load, so they reported no test at all and the run is short by however many they hold: ${snapshot.unloadable.slice(0, 3).join(", ")}. A reference that quietly lost tests cannot prove anything about the run after it`;
|
|
8850
|
+
}
|
|
8851
|
+
if (snapshot.claimedFailing > snapshot.failing.length) {
|
|
8852
|
+
return `the reference run says ${snapshot.claimedFailing} test(s) failed and names only ${snapshot.failing.length} of them, so the comparison has nothing to pair the rest against and would report them as invented by the migration`;
|
|
8853
|
+
}
|
|
8854
|
+
if (snapshot.passing.length === 0) {
|
|
8855
|
+
return `the reference run reports no passing test at all, so a green run after would prove nothing: there is no claim to preserve`;
|
|
8856
|
+
}
|
|
8857
|
+
return void 0;
|
|
8858
|
+
}
|
|
8859
|
+
function renamedExample(before, after) {
|
|
8860
|
+
let shared = 0;
|
|
8861
|
+
while (shared < before.length && shared < after.length && before[shared] === after[shared]) {
|
|
8862
|
+
shared += 1;
|
|
8863
|
+
}
|
|
8864
|
+
const boundary = before.lastIndexOf(" ", shared);
|
|
8865
|
+
const from = boundary > 0 ? boundary + 1 : 0;
|
|
8866
|
+
const ellipsis = from > 0 ? "\u2026" : "";
|
|
8867
|
+
return `"${ellipsis}${before.slice(from)}" -> "${ellipsis}${after.slice(from)}"`;
|
|
8868
|
+
}
|
|
8869
|
+
function describeComparison(comparison) {
|
|
8870
|
+
const firstRename = comparison.renamed[0];
|
|
8871
|
+
const renamedNote = firstRename === void 0 ? "" : ` ${comparison.renamed.length} test(s) kept their identity and changed name, because the runners render an each title's interpolated value differently (e.g. ${renamedExample(firstRename.before, firstRename.after)}).`;
|
|
8872
|
+
const alreadyRedNote = comparison.alreadyFailing > 0 ? ` ${comparison.alreadyFailing} test(s) were already failing before the migration and are outside this comparison, which covers only what passed.` : "";
|
|
8873
|
+
const recoveredNote = comparison.recovered.length > 0 ? ` ${comparison.recovered.length} test(s) were failing before and pass now.` : "";
|
|
8874
|
+
if (comparison.identical) {
|
|
8875
|
+
return `every test that ran before runs and passes now.${recoveredNote}${renamedNote}${alreadyRedNote}`;
|
|
8876
|
+
}
|
|
8877
|
+
const parts = [];
|
|
8878
|
+
if (comparison.recovered.length > 0) {
|
|
8879
|
+
parts.push(`${comparison.recovered.length} test(s) were failing before and pass now`);
|
|
8880
|
+
}
|
|
8881
|
+
if (comparison.missing.length > 0) {
|
|
8882
|
+
parts.push(
|
|
8883
|
+
`${comparison.missing.length} test(s) no longer exist: ${comparison.missing.slice(0, 3).join(", ")}`
|
|
8884
|
+
);
|
|
8885
|
+
}
|
|
8886
|
+
if (comparison.added.length > 0) {
|
|
8887
|
+
parts.push(
|
|
8888
|
+
`${comparison.added.length} test(s) appeared, which a migration should not do: ${comparison.added.slice(0, 3).join(", ")}`
|
|
8889
|
+
);
|
|
8890
|
+
}
|
|
8891
|
+
if (comparison.regressed.length > 0) {
|
|
8892
|
+
parts.push(
|
|
8893
|
+
`${comparison.regressed.length} test(s) passed before and do not now: ${comparison.regressed.slice(0, 3).join(", ")}`
|
|
8894
|
+
);
|
|
8895
|
+
}
|
|
8896
|
+
return `${parts.join("; ")}.${renamedNote}${alreadyRedNote}`;
|
|
8897
|
+
}
|
|
8898
|
+
|
|
8899
|
+
// src/roles/test/reference.ts
|
|
8900
|
+
function referencePath(cwd) {
|
|
8901
|
+
const key = createHash("sha256").update(resolve5(cwd)).digest("hex").slice(0, 16);
|
|
8902
|
+
return join37(tmpdir(), "sentinel-test-reference", `${key}.json`);
|
|
8903
|
+
}
|
|
8904
|
+
function runJest(cwd, config) {
|
|
8905
|
+
const bin = resolveBin(cwd, "jest");
|
|
8906
|
+
if (bin === void 0) return void 0;
|
|
8907
|
+
const root = findWorkspaceRoot(cwd) ?? cwd;
|
|
8908
|
+
const out = join37(mkdtempSync(join37(tmpdir(), "sentinel-reference-")), "jest.json");
|
|
8909
|
+
try {
|
|
8910
|
+
execFileSync(bin, ["-c", join37(cwd, config), "--json", `--outputFile=${out}`, "--silent"], {
|
|
8911
|
+
cwd: root,
|
|
8912
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
8913
|
+
maxBuffer: 1 << 28
|
|
8914
|
+
});
|
|
8915
|
+
} catch {
|
|
8916
|
+
}
|
|
8917
|
+
if (!existsSync31(out)) return void 0;
|
|
8918
|
+
try {
|
|
8919
|
+
return JSON.parse(readFileSync29(out, "utf8"));
|
|
8920
|
+
} catch {
|
|
8921
|
+
return void 0;
|
|
8922
|
+
}
|
|
8923
|
+
}
|
|
8924
|
+
function captureReference(cwd, config) {
|
|
8925
|
+
const report = runJest(cwd, config);
|
|
8926
|
+
if (report === void 0) {
|
|
8927
|
+
return {
|
|
8928
|
+
captured: false,
|
|
8929
|
+
message: `could not run jest to record a reference, so this migration cannot be PROVED. It is still applied: a module whose jest run is broken can move, it just cannot be compared.`
|
|
8930
|
+
};
|
|
8931
|
+
}
|
|
8932
|
+
const snapshot = readSnapshot(report);
|
|
8933
|
+
const unusable = unusableAsBaseline(snapshot);
|
|
8934
|
+
if (unusable !== void 0) {
|
|
8935
|
+
return {
|
|
8936
|
+
captured: false,
|
|
8937
|
+
message: `no reference recorded: ${unusable} The migration is applied anyway, unproved.`
|
|
8938
|
+
};
|
|
8939
|
+
}
|
|
8940
|
+
const stored = {
|
|
8941
|
+
runner: "jest",
|
|
8942
|
+
config,
|
|
8943
|
+
takenAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8944
|
+
snapshot
|
|
8945
|
+
};
|
|
8946
|
+
const path = referencePath(cwd);
|
|
8947
|
+
mkdirSync(join37(path, ".."), { recursive: true });
|
|
8948
|
+
writeFileSync3(path, `${JSON.stringify(stored, void 0, 2)}
|
|
8949
|
+
`);
|
|
8950
|
+
return {
|
|
8951
|
+
captured: true,
|
|
8952
|
+
message: `recorded ${snapshot.passing.length} passing test name(s) from ${config} as the reference. The next \`sentinel --run --test\` compares against it.`
|
|
8953
|
+
};
|
|
8954
|
+
}
|
|
8955
|
+
function hasReference(cwd) {
|
|
8956
|
+
return existsSync31(referencePath(cwd));
|
|
8957
|
+
}
|
|
8958
|
+
function compareAgainstReference(cwd, report) {
|
|
8959
|
+
const path = referencePath(cwd);
|
|
8960
|
+
let stored;
|
|
8961
|
+
try {
|
|
8962
|
+
stored = JSON.parse(readFileSync29(path, "utf8"));
|
|
8963
|
+
} catch {
|
|
8964
|
+
rmSync3(path, { force: true });
|
|
8965
|
+
return { ok: true, message: `the recorded reference could not be read, so nothing was proved.` };
|
|
8966
|
+
}
|
|
8967
|
+
const comparison = compareSnapshots(stored.snapshot, readSnapshot(report));
|
|
8968
|
+
if (comparison.identical) rmSync3(path, { force: true });
|
|
8969
|
+
const kept = comparison.identical ? "" : ` The reference is KEPT until a run passes it, so this does not go green by running again. Fix the suite and re-run, or delete ${path} to abandon the proof on purpose.`;
|
|
8970
|
+
return {
|
|
8971
|
+
ok: comparison.identical,
|
|
8972
|
+
message: `${describeComparison(comparison)} (reference: ${stored.config}, ${stored.takenAt})${kept}`
|
|
8973
|
+
};
|
|
8974
|
+
}
|
|
8975
|
+
|
|
8366
8976
|
// src/roles/test/adapters/vitest/vitest.adapter.ts
|
|
8367
8977
|
function resolveVitest(cwd) {
|
|
8368
8978
|
const fromSentinel = binFromOwnInstall("vitest", "vitest");
|
|
@@ -8370,12 +8980,102 @@ function resolveVitest(cwd) {
|
|
|
8370
8980
|
const fromModule = resolveBin(cwd, "vitest");
|
|
8371
8981
|
return fromModule !== void 0 ? { bin: fromModule, origin: "module" } : { bin: void 0, origin: "module" };
|
|
8372
8982
|
}
|
|
8983
|
+
var FORMATTABLE = /\.(m|c)?(j|t)sx?$|\.json$/;
|
|
8984
|
+
var FORMAT_BATCH = 25;
|
|
8373
8985
|
var VitestAdapter = class extends BaseAdapter {
|
|
8374
8986
|
target = "test";
|
|
8375
8987
|
runner = "vitest";
|
|
8376
8988
|
appliesTo(_preset) {
|
|
8377
8989
|
return true;
|
|
8378
8990
|
}
|
|
8991
|
+
/**
|
|
8992
|
+
* Record what this module's tests do TODAY, at the last moment it is still true.
|
|
8993
|
+
*
|
|
8994
|
+
* The plan about to be applied deletes the jest config, and with it any way of asking again. A
|
|
8995
|
+
* migration that cannot be compared to what came before is a migration nobody can check, which
|
|
8996
|
+
* is the whole reason this role has a baseline gate at all.
|
|
8997
|
+
*/
|
|
8998
|
+
async beforeInit(ctx) {
|
|
8999
|
+
const adoption = readTestAdoption(ctx.cwd);
|
|
9000
|
+
if (adoption.state !== "jest") return Promise.resolve();
|
|
9001
|
+
const [config] = adoption.jestConfigs;
|
|
9002
|
+
if (config === void 0) return Promise.resolve();
|
|
9003
|
+
this.say(`recording the jest reference before migrating...`);
|
|
9004
|
+
this.say(captureReference(ctx.cwd, config).message);
|
|
9005
|
+
return Promise.resolve();
|
|
9006
|
+
}
|
|
9007
|
+
/**
|
|
9008
|
+
* Hand what was just written to the module's OWN formatter.
|
|
9009
|
+
*
|
|
9010
|
+
* ⚠️ Not a style sentinel imposes: the module adopted `.oxfmtrc.json`, and this runs that
|
|
9011
|
+
* config over the files this role created. Without it, `--init --test` leaves a module whose
|
|
9012
|
+
* own `--run --format` fails on a file the module never wrote.
|
|
9013
|
+
*
|
|
9014
|
+
* Measured on `apps/nest/microservices/agency`, in this repository's CI:
|
|
9015
|
+
*
|
|
9016
|
+
* agency:format 12 files, including vitest.config.mts and
|
|
9017
|
+
* vitest.integration.config.mts, both generated here
|
|
9018
|
+
*
|
|
9019
|
+
* The generator cannot avoid it by wrapping at a fixed width, because the width is the
|
|
9020
|
+
* MODULE's decision: `printWidth` lives in its own config, and 12 modules already set their
|
|
9021
|
+
* own. Guessing it is how a tool starts owning a setting it does not own.
|
|
9022
|
+
*
|
|
9023
|
+
* The same delegation the build role asks a human to do by hand, done.
|
|
9024
|
+
*
|
|
9025
|
+
* ## ⚠️ Two boundaries, both learned by crossing them
|
|
9026
|
+
*
|
|
9027
|
+
* ### Only a module that has a formatter CONFIG
|
|
9028
|
+
*
|
|
9029
|
+
* The question is not "can a formatter be found". It always can: sentinel depends on oxfmt and
|
|
9030
|
+
* ships the binary, so `resolveOxfmt` falls back to its own install and never returns undefined
|
|
9031
|
+
* once sentinel is installed. Asking it that way meant a module with no oxfmt config was
|
|
9032
|
+
* formatted with oxfmt's DEFAULTS, which is a style nobody here chose.
|
|
9033
|
+
*
|
|
9034
|
+
* So the CONFIG is read. A module that wrote its own `.oxfmtrc.json` without adopting the role
|
|
9035
|
+
* has still stated a style, and running it is delegation; running oxfmt with no config is us
|
|
9036
|
+
* choosing one.
|
|
9037
|
+
*
|
|
9038
|
+
* Measured on `apps/nest/microservices/institution`, which has not adopted format and whose lint
|
|
9039
|
+
* forbids semicolons: `institution:lint` went from green to failing, thousands of `semi`, on a
|
|
9040
|
+
* module whose 2958 tests pass. So the adoption is read, not the binary.
|
|
9041
|
+
*
|
|
9042
|
+
* ### Only the files this just WROTE
|
|
9043
|
+
*
|
|
9044
|
+
* `--write .` is a whole-directory pass, and that is not a tidy-up. Same measurement: 1813 files
|
|
9045
|
+
* changed, 47333 insertions, 55506 deletions, in a module whose migration touched 268. A
|
|
9046
|
+
* migration diff is read by a person.
|
|
9047
|
+
*
|
|
9048
|
+
* A module that has not adopted format keeps what was written exactly as written, and is told so.
|
|
9049
|
+
* The codemod rewrites tokens in place, so those files keep the shape they already had.
|
|
9050
|
+
*/
|
|
9051
|
+
async afterInit(ctx) {
|
|
9052
|
+
const written = (ctx.written ?? []).filter((file) => FORMATTABLE.test(file));
|
|
9053
|
+
if (written.length === 0) return Promise.resolve();
|
|
9054
|
+
const formatter = writtenFileFormatter(ctx.cwd);
|
|
9055
|
+
if (formatter === void 0) {
|
|
9056
|
+
this.say(
|
|
9057
|
+
`no style is declared here, by this module or its workspace, so the ${written.length} file(s) this wrote keep the shape they were written in. Run your own formatter over them before committing.`
|
|
9058
|
+
);
|
|
9059
|
+
return Promise.resolve();
|
|
9060
|
+
}
|
|
9061
|
+
let failed = 0;
|
|
9062
|
+
for (let at = 0; at < written.length; at += FORMAT_BATCH) {
|
|
9063
|
+
const batch = written.slice(at, at + FORMAT_BATCH);
|
|
9064
|
+
const pass = spawnSync5(formatter.bin, [...formatter.args, ...batch], {
|
|
9065
|
+
cwd: ctx.cwd,
|
|
9066
|
+
encoding: "utf8"
|
|
9067
|
+
});
|
|
9068
|
+
if (pass.status !== 0) failed += batch.length;
|
|
9069
|
+
}
|
|
9070
|
+
this.say(
|
|
9071
|
+
failed === 0 ? `formatted the ${written.length} file(s) this wrote with ${formatter.origin}, so a format check passes on files it did not write itself.` : (
|
|
9072
|
+
// Adoption SUCCEEDED; only the tidy-up did not. Failing here would revert a migration
|
|
9073
|
+
// over a formatting pass, which is the wrong trade.
|
|
9074
|
+
`${formatter.origin} could not run over ${failed} of the ${written.length} file(s) this wrote. Adoption itself is applied; run your formatter before committing.`
|
|
9075
|
+
)
|
|
9076
|
+
);
|
|
9077
|
+
return Promise.resolve();
|
|
9078
|
+
}
|
|
8379
9079
|
plan(context) {
|
|
8380
9080
|
return plan3(context);
|
|
8381
9081
|
}
|
|
@@ -8420,15 +9120,36 @@ var VitestAdapter = class extends BaseAdapter {
|
|
|
8420
9120
|
`using the MODULE's vitest, not sentinel's. An adopted config gets its plugins from sentinel, and two copies fail in ways that never mention a version. Remove vitest from this module's package.json.`
|
|
8421
9121
|
);
|
|
8422
9122
|
}
|
|
8423
|
-
const { options, paths } = splitToolArgs(ctx.cwd, ctx.toolArgs, {
|
|
9123
|
+
const { options: typedOptions, paths: typedPaths } = splitToolArgs(ctx.cwd, ctx.toolArgs, {
|
|
9124
|
+
valueFlags: ["--config", "-c"]
|
|
9125
|
+
});
|
|
9126
|
+
const { cwd: runFrom, options, paths } = fromWorkspaceRoot(ctx.cwd, typedOptions, typedPaths);
|
|
8424
9127
|
const watching = options.includes("--watch") || options.includes("-w");
|
|
8425
|
-
const
|
|
8426
|
-
const
|
|
9128
|
+
const proving = !watching && hasReference(ctx.cwd);
|
|
9129
|
+
const reportFile = proving ? join38(mkdtempSync2(join38(tmpdir2(), "sentinel-run-")), "vitest.json") : void 0;
|
|
9130
|
+
const reporters = reportFile === void 0 ? [] : ["--reporter=default", "--reporter=json", `--outputFile=${reportFile}`];
|
|
9131
|
+
const argv = watching ? [...options, ...paths] : ["run", ...reporters, ...options, ...paths];
|
|
9132
|
+
const result = spawnSync5(bin, argv, { cwd: runFrom, stdio: "inherit" });
|
|
8427
9133
|
if (result.error) {
|
|
8428
9134
|
this.say(`could not run vitest (${result.error.message})`);
|
|
8429
9135
|
return Promise.resolve({ ok: false, code: 1 });
|
|
8430
9136
|
}
|
|
8431
9137
|
const code = result.status ?? 1;
|
|
9138
|
+
if (reportFile !== void 0) {
|
|
9139
|
+
let report;
|
|
9140
|
+
try {
|
|
9141
|
+
report = JSON.parse(readFileSync30(reportFile, "utf8"));
|
|
9142
|
+
} catch {
|
|
9143
|
+
report = void 0;
|
|
9144
|
+
}
|
|
9145
|
+
if (report === void 0) {
|
|
9146
|
+
this.say(`vitest wrote no report, so this run proved nothing against the reference.`);
|
|
9147
|
+
} else {
|
|
9148
|
+
const outcome = compareAgainstReference(ctx.cwd, report);
|
|
9149
|
+
this.say(outcome.message);
|
|
9150
|
+
if (!outcome.ok) return Promise.resolve({ ok: false, code: code === 0 ? 1 : code });
|
|
9151
|
+
}
|
|
9152
|
+
}
|
|
8432
9153
|
return Promise.resolve({ ok: code === 0, code });
|
|
8433
9154
|
}
|
|
8434
9155
|
say(message) {
|
|
@@ -8436,6 +9157,31 @@ var VitestAdapter = class extends BaseAdapter {
|
|
|
8436
9157
|
`);
|
|
8437
9158
|
}
|
|
8438
9159
|
};
|
|
9160
|
+
function fromWorkspaceRoot(moduleDir, options, paths) {
|
|
9161
|
+
const root = findWorkspaceRoot(moduleDir);
|
|
9162
|
+
if (root === void 0 || root === moduleDir) {
|
|
9163
|
+
return { cwd: moduleDir, options: [...options], paths: [...paths] };
|
|
9164
|
+
}
|
|
9165
|
+
const absolute = (value) => isAbsolute3(value) ? value : resolve6(moduleDir, value);
|
|
9166
|
+
const moved = [];
|
|
9167
|
+
let previousWasConfig = false;
|
|
9168
|
+
for (const option of options) {
|
|
9169
|
+
if (previousWasConfig) {
|
|
9170
|
+
moved.push(absolute(option));
|
|
9171
|
+
previousWasConfig = false;
|
|
9172
|
+
continue;
|
|
9173
|
+
}
|
|
9174
|
+
if (option.startsWith("--config=")) moved.push(`--config=${absolute(option.slice(9))}`);
|
|
9175
|
+
else moved.push(option);
|
|
9176
|
+
previousWasConfig = option === "--config" || option === "-c";
|
|
9177
|
+
}
|
|
9178
|
+
return {
|
|
9179
|
+
cwd: root,
|
|
9180
|
+
// `--root` LAST, so a caller cannot be silently overridden by our own flag.
|
|
9181
|
+
options: [...moved, "--root", moduleDir],
|
|
9182
|
+
paths: paths.map((path) => absolute(path))
|
|
9183
|
+
};
|
|
9184
|
+
}
|
|
8439
9185
|
|
|
8440
9186
|
// src/roles/test/register.ts
|
|
8441
9187
|
function registerTest() {
|
|
@@ -8444,10 +9190,10 @@ function registerTest() {
|
|
|
8444
9190
|
}
|
|
8445
9191
|
|
|
8446
9192
|
// src/roles/typescript/adapters/tsc/tsc.adapter.ts
|
|
8447
|
-
import { spawnSync as spawnSync6 } from "child_process";
|
|
8448
|
-
import { existsSync as
|
|
8449
|
-
import { createRequire as
|
|
8450
|
-
import { join as
|
|
9193
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
9194
|
+
import { existsSync as existsSync32, readFileSync as readFileSync32 } from "node:fs";
|
|
9195
|
+
import { createRequire as createRequire7 } from "node:module";
|
|
9196
|
+
import { join as join40 } from "node:path";
|
|
8451
9197
|
|
|
8452
9198
|
// src/roles/typescript/presets/nest.json
|
|
8453
9199
|
var nest_default2 = {
|
|
@@ -8628,8 +9374,8 @@ function readTsconfigAdoption(cwd) {
|
|
|
8628
9374
|
}
|
|
8629
9375
|
|
|
8630
9376
|
// src/roles/typescript/adapters/tsc/plan.ts
|
|
8631
|
-
import { readFileSync as
|
|
8632
|
-
import { join as
|
|
9377
|
+
import { readFileSync as readFileSync31 } from "node:fs";
|
|
9378
|
+
import { join as join39 } from "node:path";
|
|
8633
9379
|
|
|
8634
9380
|
// src/roles/typescript/typecheck-script.ts
|
|
8635
9381
|
var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
|
|
@@ -8722,7 +9468,7 @@ function planAdoption(context) {
|
|
|
8722
9468
|
};
|
|
8723
9469
|
}
|
|
8724
9470
|
const existing = parseJsonc(
|
|
8725
|
-
|
|
9471
|
+
readFileSync31(join39(context.cwd, target.path), "utf8"),
|
|
8726
9472
|
target.path
|
|
8727
9473
|
);
|
|
8728
9474
|
const extendsChain = composeExtends(existing.extends, preset);
|
|
@@ -8860,7 +9606,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
8860
9606
|
let chain;
|
|
8861
9607
|
try {
|
|
8862
9608
|
const parsed = parseJsonc(
|
|
8863
|
-
|
|
9609
|
+
readFileSync32(join40(cwd, target.path), "utf8"),
|
|
8864
9610
|
target.path
|
|
8865
9611
|
);
|
|
8866
9612
|
chain = parsed.extends;
|
|
@@ -8873,7 +9619,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
8873
9619
|
);
|
|
8874
9620
|
if (preset === void 0) return void 0;
|
|
8875
9621
|
try {
|
|
8876
|
-
|
|
9622
|
+
createRequire7(join40(cwd, "noop.js")).resolve(preset);
|
|
8877
9623
|
return void 0;
|
|
8878
9624
|
} catch {
|
|
8879
9625
|
return preset;
|
|
@@ -8969,7 +9715,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
8969
9715
|
referencedProjects(cwd, config) {
|
|
8970
9716
|
try {
|
|
8971
9717
|
const parsed = parseJsonc(
|
|
8972
|
-
|
|
9718
|
+
readFileSync32(join40(cwd, config), "utf8"),
|
|
8973
9719
|
config
|
|
8974
9720
|
);
|
|
8975
9721
|
const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path) => typeof path === "string" && path.length > 0);
|
|
@@ -8985,7 +9731,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
8985
9731
|
* check.
|
|
8986
9732
|
*/
|
|
8987
9733
|
typecheckTarget(cwd) {
|
|
8988
|
-
if (
|
|
9734
|
+
if (existsSync32(join40(cwd, "tsconfig.json"))) return "tsconfig.json";
|
|
8989
9735
|
const target = resolveTsconfigTarget(cwd);
|
|
8990
9736
|
return target.reason === "none" ? null : target.path;
|
|
8991
9737
|
}
|
|
@@ -9134,7 +9880,7 @@ function detectFramework(packageJson) {
|
|
|
9134
9880
|
}
|
|
9135
9881
|
|
|
9136
9882
|
// src/core/dispatch.ts
|
|
9137
|
-
import { basename as basename3 } from "path";
|
|
9883
|
+
import { basename as basename3 } from "node:path";
|
|
9138
9884
|
|
|
9139
9885
|
// src/shared/text.ts
|
|
9140
9886
|
function ensureLines(current, lines) {
|
|
@@ -9190,8 +9936,8 @@ function replaceLines(current, replacements) {
|
|
|
9190
9936
|
}
|
|
9191
9937
|
|
|
9192
9938
|
// src/core/apply-plan.ts
|
|
9193
|
-
import { existsSync as
|
|
9194
|
-
import { resolve as
|
|
9939
|
+
import { existsSync as existsSync33, readFileSync as readFileSync33, renameSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
9940
|
+
import { resolve as resolve7, sep } from "node:path";
|
|
9195
9941
|
import { applyEdits as applyEdits3, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
|
|
9196
9942
|
|
|
9197
9943
|
// src/shared/deep-merge.ts
|
|
@@ -9201,15 +9947,15 @@ function isPlainObject(value) {
|
|
|
9201
9947
|
|
|
9202
9948
|
// src/core/apply-plan.ts
|
|
9203
9949
|
function resolveWithinRoot(cwd, relativePath) {
|
|
9204
|
-
const root =
|
|
9205
|
-
const absolutePath =
|
|
9950
|
+
const root = resolve7(cwd);
|
|
9951
|
+
const absolutePath = resolve7(root, relativePath);
|
|
9206
9952
|
if (absolutePath !== root && !absolutePath.startsWith(root + sep)) {
|
|
9207
9953
|
throw new Error(`Refusing to write outside the module root: "${relativePath}".`);
|
|
9208
9954
|
}
|
|
9209
9955
|
return absolutePath;
|
|
9210
9956
|
}
|
|
9211
9957
|
function readIfExists(absolutePath) {
|
|
9212
|
-
return
|
|
9958
|
+
return existsSync33(absolutePath) ? readFileSync33(absolutePath, "utf8") : void 0;
|
|
9213
9959
|
}
|
|
9214
9960
|
function* leaves(value, prefix = []) {
|
|
9215
9961
|
for (const [key, keyValue] of Object.entries(value)) {
|
|
@@ -9311,14 +10057,14 @@ function preparePlan(cwd, plan4) {
|
|
|
9311
10057
|
}
|
|
9312
10058
|
function writeFileAtomic(absolutePath, contents) {
|
|
9313
10059
|
const tempPath = `${absolutePath}.sentinel-${process.pid}.tmp`;
|
|
9314
|
-
|
|
10060
|
+
writeFileSync4(tempPath, contents);
|
|
9315
10061
|
renameSync(tempPath, absolutePath);
|
|
9316
10062
|
}
|
|
9317
10063
|
function applyPlan(cwd, plan4) {
|
|
9318
10064
|
const changed = preparePlan(cwd, plan4).filter((file) => file.before !== file.after);
|
|
9319
10065
|
for (const file of changed) {
|
|
9320
10066
|
if (file.deleted) {
|
|
9321
|
-
|
|
10067
|
+
rmSync4(file.absolutePath, { force: true });
|
|
9322
10068
|
continue;
|
|
9323
10069
|
}
|
|
9324
10070
|
writeFileAtomic(file.absolutePath, file.after);
|
|
@@ -9327,8 +10073,8 @@ function applyPlan(cwd, plan4) {
|
|
|
9327
10073
|
}
|
|
9328
10074
|
|
|
9329
10075
|
// src/core/config/preset-evidence.ts
|
|
9330
|
-
import { existsSync as
|
|
9331
|
-
import { join as
|
|
10076
|
+
import { existsSync as existsSync34, readdirSync as readdirSync9, readFileSync as readFileSync34 } from "node:fs";
|
|
10077
|
+
import { join as join41 } from "node:path";
|
|
9332
10078
|
var PATH_SIGNALS = [
|
|
9333
10079
|
{
|
|
9334
10080
|
preset: "nest",
|
|
@@ -9356,18 +10102,18 @@ function sourceImports(cwd, pattern) {
|
|
|
9356
10102
|
const dir = stack.pop();
|
|
9357
10103
|
let entries;
|
|
9358
10104
|
try {
|
|
9359
|
-
entries =
|
|
10105
|
+
entries = readdirSync9(dir, { withFileTypes: true });
|
|
9360
10106
|
} catch {
|
|
9361
10107
|
continue;
|
|
9362
10108
|
}
|
|
9363
10109
|
for (const entry of entries) {
|
|
9364
10110
|
if (entry.isDirectory()) {
|
|
9365
|
-
if (!NOT_SCANNED.has(entry.name)) stack.push(
|
|
10111
|
+
if (!NOT_SCANNED.has(entry.name)) stack.push(join41(dir, entry.name));
|
|
9366
10112
|
continue;
|
|
9367
10113
|
}
|
|
9368
10114
|
if (!SOURCE_FILE2.test(entry.name)) continue;
|
|
9369
10115
|
try {
|
|
9370
|
-
if (pattern.test(
|
|
10116
|
+
if (pattern.test(readFileSync34(join41(dir, entry.name), "utf8"))) return true;
|
|
9371
10117
|
} catch {
|
|
9372
10118
|
continue;
|
|
9373
10119
|
}
|
|
@@ -9376,11 +10122,11 @@ function sourceImports(cwd, pattern) {
|
|
|
9376
10122
|
return false;
|
|
9377
10123
|
}
|
|
9378
10124
|
function dependencyNames(cwd) {
|
|
9379
|
-
const path =
|
|
9380
|
-
if (!
|
|
10125
|
+
const path = join41(cwd, "package.json");
|
|
10126
|
+
if (!existsSync34(path)) return [];
|
|
9381
10127
|
try {
|
|
9382
10128
|
const manifest = parseJsonc(
|
|
9383
|
-
|
|
10129
|
+
readFileSync34(path, "utf8"),
|
|
9384
10130
|
path
|
|
9385
10131
|
);
|
|
9386
10132
|
return [
|
|
@@ -9394,7 +10140,7 @@ function dependencyNames(cwd) {
|
|
|
9394
10140
|
function declaresJsx(cwd) {
|
|
9395
10141
|
let entries;
|
|
9396
10142
|
try {
|
|
9397
|
-
entries =
|
|
10143
|
+
entries = readdirSync9(cwd).filter(
|
|
9398
10144
|
(name) => name.startsWith("tsconfig") && name.endsWith(".json")
|
|
9399
10145
|
);
|
|
9400
10146
|
} catch {
|
|
@@ -9403,7 +10149,7 @@ function declaresJsx(cwd) {
|
|
|
9403
10149
|
for (const name of entries) {
|
|
9404
10150
|
try {
|
|
9405
10151
|
const config = parseJsonc(
|
|
9406
|
-
|
|
10152
|
+
readFileSync34(join41(cwd, name), "utf8"),
|
|
9407
10153
|
name
|
|
9408
10154
|
);
|
|
9409
10155
|
if (config.compilerOptions?.jsx !== void 0) return true;
|
|
@@ -9548,9 +10294,20 @@ async function dispatch(opts) {
|
|
|
9548
10294
|
if (opts.dryRun) {
|
|
9549
10295
|
return previewPlan(opts, plan4);
|
|
9550
10296
|
}
|
|
10297
|
+
if (adapter.beforeInit) {
|
|
10298
|
+
await adapter.beforeInit({
|
|
10299
|
+
module: basename3(opts.cwd),
|
|
10300
|
+
cwd: opts.cwd,
|
|
10301
|
+
preset,
|
|
10302
|
+
ci: false,
|
|
10303
|
+
fix: false
|
|
10304
|
+
});
|
|
10305
|
+
}
|
|
10306
|
+
const written = [];
|
|
9551
10307
|
for (const change of applyPlan(opts.cwd, plan4)) {
|
|
9552
10308
|
process.stderr.write(` ${change.deleted ? "removed" : "wrote"} ${change.path}
|
|
9553
10309
|
`);
|
|
10310
|
+
if (!change.deleted) written.push(change.path);
|
|
9554
10311
|
}
|
|
9555
10312
|
for (const note of plan4.notes ?? []) process.stderr.write(` ${note}
|
|
9556
10313
|
`);
|
|
@@ -9562,7 +10319,8 @@ async function dispatch(opts) {
|
|
|
9562
10319
|
cwd: opts.cwd,
|
|
9563
10320
|
preset,
|
|
9564
10321
|
ci: false,
|
|
9565
|
-
fix: true
|
|
10322
|
+
fix: true,
|
|
10323
|
+
written
|
|
9566
10324
|
});
|
|
9567
10325
|
}
|
|
9568
10326
|
return 0;
|
|
@@ -9591,4 +10349,3 @@ export {
|
|
|
9591
10349
|
detectFramework,
|
|
9592
10350
|
dispatch
|
|
9593
10351
|
};
|
|
9594
|
-
//# sourceMappingURL=chunk-4UIZJ3TR.js.map
|