@hublo/sentinel 1.4.0-alpha.23 → 1.4.0-alpha.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/sentinel.js +7 -96
- package/dist/{chunk-7ADFV7P4.js → chunk-U744PK2U.js} +401 -128
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/types/jest-global.d.ts +36 -0
package/dist/bin/sentinel.js
CHANGED
|
@@ -15,14 +15,12 @@ import {
|
|
|
15
15
|
palette,
|
|
16
16
|
registerAdapters,
|
|
17
17
|
resolve,
|
|
18
|
-
|
|
19
|
-
} from "../chunk-
|
|
18
|
+
resolveContext
|
|
19
|
+
} from "../chunk-U744PK2U.js";
|
|
20
20
|
import {
|
|
21
|
-
WORKSPACE_ROOT_MARKER,
|
|
22
21
|
ensureWorkspacePrep,
|
|
23
22
|
findWorkspaceRoot,
|
|
24
23
|
inspectWorkspacePrep,
|
|
25
|
-
moduleName,
|
|
26
24
|
readOwnVersion,
|
|
27
25
|
readProjectPackageJson
|
|
28
26
|
} from "../chunk-NW6UHNYX.js";
|
|
@@ -30,102 +28,15 @@ import {
|
|
|
30
28
|
// bin/sentinel.ts
|
|
31
29
|
import { program } from "commander";
|
|
32
30
|
|
|
33
|
-
// src/core/context.ts
|
|
34
|
-
import { existsSync } from "node:fs";
|
|
35
|
-
import { join as join2 } from "node:path";
|
|
36
|
-
|
|
37
|
-
// src/core/discover-modules.ts
|
|
38
|
-
import { execFileSync } from "node:child_process";
|
|
39
|
-
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
40
|
-
import { tmpdir } from "node:os";
|
|
41
|
-
import { join } from "node:path";
|
|
42
|
-
function runNx(cwd2, args) {
|
|
43
|
-
const nx = resolveBin(cwd2, "nx") ?? "nx";
|
|
44
|
-
try {
|
|
45
|
-
return execFileSync(nx, args, {
|
|
46
|
-
cwd: cwd2,
|
|
47
|
-
encoding: "utf8",
|
|
48
|
-
env: { ...process.env, NX_DAEMON: "false" }
|
|
49
|
-
});
|
|
50
|
-
} catch (error) {
|
|
51
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
52
|
-
throw new Error(
|
|
53
|
-
`sentinel: could not run nx (${message}). Is nx installed in this workspace, and are you at its root?`,
|
|
54
|
-
{ cause: error }
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function readGraph(cwd2) {
|
|
59
|
-
const dir = mkdtempSync(join(tmpdir(), "sentinel-nx-"));
|
|
60
|
-
const file = join(dir, "graph.json");
|
|
61
|
-
try {
|
|
62
|
-
runNx(cwd2, ["graph", "--file", file]);
|
|
63
|
-
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
64
|
-
const nodes = parsed.graph?.nodes;
|
|
65
|
-
if (!nodes || typeof nodes !== "object") {
|
|
66
|
-
throw new Error(
|
|
67
|
-
"sentinel: unexpected nx graph output (no graph.nodes); the installed nx version may be incompatible."
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
return Object.entries(nodes).map(([name, node]) => ({
|
|
71
|
-
name,
|
|
72
|
-
root: join(cwd2, node.data.root)
|
|
73
|
-
}));
|
|
74
|
-
} finally {
|
|
75
|
-
rmSync(dir, { recursive: true, force: true });
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
function discoverModules(cwd2, options = {}) {
|
|
79
|
-
const modules = readGraph(cwd2);
|
|
80
|
-
if (!options.affected) return modules;
|
|
81
|
-
const affected = new Set(
|
|
82
|
-
JSON.parse(runNx(cwd2, ["show", "projects", "--affected", "--json"]))
|
|
83
|
-
);
|
|
84
|
-
return modules.filter((module) => affected.has(module.name));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// src/core/context.ts
|
|
88
|
-
var MODULE_MARKERS = ["package.json", "project.json"];
|
|
89
|
-
function isModuleDir(cwd2) {
|
|
90
|
-
return MODULE_MARKERS.some((marker) => existsSync(join2(cwd2, marker)));
|
|
91
|
-
}
|
|
92
|
-
function resolveContext(cwd2, opts2) {
|
|
93
|
-
const atRoot = existsSync(join2(cwd2, WORKSPACE_ROOT_MARKER));
|
|
94
|
-
if (!atRoot && isModuleDir(cwd2)) {
|
|
95
|
-
if (opts2.module) {
|
|
96
|
-
throw new Error(
|
|
97
|
-
"You are in a module directory: drop --module (the context is the current module)."
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
if (opts2.ci) {
|
|
101
|
-
throw new Error("--ci selects the affected set from the workspace root; run it there.");
|
|
102
|
-
}
|
|
103
|
-
const name = moduleName(cwd2);
|
|
104
|
-
return { modules: [{ name, root: cwd2 }], scope: "cwd-module" };
|
|
105
|
-
}
|
|
106
|
-
if (atRoot) {
|
|
107
|
-
if (opts2.module) {
|
|
108
|
-
const found = discoverModules(cwd2).find((module) => module.name === opts2.module);
|
|
109
|
-
if (!found) throw new Error(`module "${opts2.module}" not found in the workspace.`);
|
|
110
|
-
return { modules: [found], scope: "named-module" };
|
|
111
|
-
}
|
|
112
|
-
if (opts2.ci) return { modules: discoverModules(cwd2, { affected: true }), scope: "affected" };
|
|
113
|
-
return { modules: discoverModules(cwd2), scope: "all" };
|
|
114
|
-
}
|
|
115
|
-
throw new Error(
|
|
116
|
-
`Run sentinel from a module directory or the workspace root (found neither ${MODULE_MARKERS.join("/")} nor ${WORKSPACE_ROOT_MARKER} here).`
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
31
|
// src/roles/build/declined.ts
|
|
121
|
-
import { existsSync
|
|
122
|
-
import { join
|
|
32
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
33
|
+
import { join } from "node:path";
|
|
123
34
|
var DEPRECATED_NX_BUILDER = "@nx/webpack:webpack";
|
|
124
35
|
function buildsWithDeprecatedExecutor(cwd2) {
|
|
125
|
-
const projectJson =
|
|
126
|
-
if (!
|
|
36
|
+
const projectJson = join(cwd2, "project.json");
|
|
37
|
+
if (!existsSync(projectJson)) return false;
|
|
127
38
|
try {
|
|
128
|
-
const project = JSON.parse(
|
|
39
|
+
const project = JSON.parse(readFileSync(projectJson, "utf8"));
|
|
129
40
|
return project.targets?.build?.executor === DEPRECATED_NX_BUILDER;
|
|
130
41
|
} catch {
|
|
131
42
|
return false;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_MAX_DIAGNOSTICS,
|
|
3
|
+
WORKSPACE_ROOT_MARKER,
|
|
3
4
|
findWorkspaceRoot,
|
|
4
5
|
moduleName,
|
|
5
6
|
readNxProjectName,
|
|
@@ -561,9 +562,9 @@ function binFromOwnInstall(packageName, binName) {
|
|
|
561
562
|
try {
|
|
562
563
|
const manifest = require2.resolve(`${packageName}/package.json`);
|
|
563
564
|
const bin = require2(manifest).bin;
|
|
564
|
-
const
|
|
565
|
-
if (!
|
|
566
|
-
const executable = join5(dirname(manifest),
|
|
565
|
+
const relative9 = typeof bin === "string" ? bin : bin?.[binName];
|
|
566
|
+
if (!relative9) return void 0;
|
|
567
|
+
const executable = join5(dirname(manifest), relative9);
|
|
567
568
|
return existsSync6(executable) ? executable : void 0;
|
|
568
569
|
} catch {
|
|
569
570
|
return void 0;
|
|
@@ -5406,7 +5407,7 @@ function registerLint() {
|
|
|
5406
5407
|
|
|
5407
5408
|
// src/roles/test/adapters/vitest/vitest.adapter.ts
|
|
5408
5409
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
5409
|
-
import { isAbsolute as isAbsolute3, resolve as
|
|
5410
|
+
import { isAbsolute as isAbsolute3, resolve as resolve9 } from "node:path";
|
|
5410
5411
|
|
|
5411
5412
|
// src/roles/test/presets/toolchain.json
|
|
5412
5413
|
var toolchain_default2 = {
|
|
@@ -5483,13 +5484,13 @@ function testTarget(options) {
|
|
|
5483
5484
|
}
|
|
5484
5485
|
|
|
5485
5486
|
// src/roles/test/jest-migration.ts
|
|
5486
|
-
import { existsSync as
|
|
5487
|
+
import { existsSync as existsSync32, readFileSync as readFileSync31, readdirSync as readdirSync8 } from "node:fs";
|
|
5487
5488
|
import { createRequire as createRequire6 } from "node:module";
|
|
5488
|
-
import { dirname as
|
|
5489
|
+
import { dirname as dirname11, join as join40, relative as relative8 } from "node:path";
|
|
5489
5490
|
|
|
5490
5491
|
// src/roles/test/codemod/migrate-module.ts
|
|
5491
|
-
import { readdirSync as readdirSync6, readFileSync as
|
|
5492
|
-
import { join as
|
|
5492
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync28, statSync as statSync6 } from "node:fs";
|
|
5493
|
+
import { join as join35 } from "node:path";
|
|
5493
5494
|
import { parseSync as parseSync8 } from "oxc-parser";
|
|
5494
5495
|
|
|
5495
5496
|
// src/roles/test/msw-lifecycle.ts
|
|
@@ -5635,15 +5636,16 @@ function readTestAdoption(cwd) {
|
|
|
5635
5636
|
// src/roles/test/support-file-name.ts
|
|
5636
5637
|
var JEST_PREFIX = "jest.";
|
|
5637
5638
|
var VITEST_PREFIX = "vitest.";
|
|
5638
|
-
function vitestSupportName(
|
|
5639
|
-
const cut =
|
|
5640
|
-
const directory = cut === -1 ? "" :
|
|
5641
|
-
const base =
|
|
5642
|
-
if (!base.startsWith(JEST_PREFIX)) return
|
|
5639
|
+
function vitestSupportName(relative9) {
|
|
5640
|
+
const cut = relative9.lastIndexOf("/");
|
|
5641
|
+
const directory = cut === -1 ? "" : relative9.slice(0, cut + 1);
|
|
5642
|
+
const base = relative9.slice(cut + 1);
|
|
5643
|
+
if (!base.startsWith(JEST_PREFIX)) return relative9;
|
|
5643
5644
|
return `${directory}${VITEST_PREFIX}${base.slice(JEST_PREFIX.length)}`;
|
|
5644
5645
|
}
|
|
5645
5646
|
|
|
5646
5647
|
// src/roles/test/codemod/migrate-file.ts
|
|
5648
|
+
import { dirname as dirname8 } from "node:path";
|
|
5647
5649
|
import { parseSync as parseSync7 } from "oxc-parser";
|
|
5648
5650
|
|
|
5649
5651
|
// src/roles/test/codemod/assertion-return-value.ts
|
|
@@ -6006,6 +6008,24 @@ var FAKEABLE = [
|
|
|
6006
6008
|
"cancelIdleCallback",
|
|
6007
6009
|
"performance"
|
|
6008
6010
|
];
|
|
6011
|
+
function withSeparator(source, options, property) {
|
|
6012
|
+
const properties = options.properties ?? [];
|
|
6013
|
+
const index = properties.indexOf(property);
|
|
6014
|
+
const start = property.start;
|
|
6015
|
+
const end = property.end;
|
|
6016
|
+
if (index < properties.length - 1) {
|
|
6017
|
+
const next = properties[index + 1]?.start;
|
|
6018
|
+
const between2 = next === void 0 ? "" : source.slice(end, next);
|
|
6019
|
+
const comma2 = between2.indexOf(",");
|
|
6020
|
+
if (comma2 >= 0) return { start, end: end + comma2 + 1 };
|
|
6021
|
+
return { start, end };
|
|
6022
|
+
}
|
|
6023
|
+
const previous = properties[index - 1]?.end;
|
|
6024
|
+
if (previous === void 0) return { start, end };
|
|
6025
|
+
const between = source.slice(previous, start);
|
|
6026
|
+
const comma = between.lastIndexOf(",");
|
|
6027
|
+
return comma >= 0 ? { start: previous + comma, end } : { start, end };
|
|
6028
|
+
}
|
|
6009
6029
|
function stringLiterals2(node) {
|
|
6010
6030
|
if (node.type !== "ArrayExpression") return void 0;
|
|
6011
6031
|
const values = [];
|
|
@@ -6019,6 +6039,7 @@ function rewriteFakeTimerOptions(source, program) {
|
|
|
6019
6039
|
const edits = [];
|
|
6020
6040
|
const translated = [];
|
|
6021
6041
|
const refusals = [];
|
|
6042
|
+
const warnings = [];
|
|
6022
6043
|
walk(program, (node) => {
|
|
6023
6044
|
const options = timerOptions(node);
|
|
6024
6045
|
if (options === void 0) return;
|
|
@@ -6048,6 +6069,16 @@ function rewriteFakeTimerOptions(source, program) {
|
|
|
6048
6069
|
});
|
|
6049
6070
|
continue;
|
|
6050
6071
|
}
|
|
6072
|
+
if (key.name === "legacyFakeTimers") {
|
|
6073
|
+
const asked = value.type === "Literal" && value.value === true;
|
|
6074
|
+
edits.push({ ...withSeparator(source, options, property), text: "" });
|
|
6075
|
+
if (asked) {
|
|
6076
|
+
warnings.push(
|
|
6077
|
+
"`useFakeTimers({ legacyFakeTimers: true })` was REMOVED, not translated: it selects jest's pre-27 timer implementation, and Vitest installs sinon's clock with no equivalent. The call now uses the modern clock, which is what jest itself defaults to. Re-read any test that depended on the old one."
|
|
6078
|
+
);
|
|
6079
|
+
}
|
|
6080
|
+
continue;
|
|
6081
|
+
}
|
|
6051
6082
|
if (key.name === "doNotFake") {
|
|
6052
6083
|
const excluded = stringLiterals2(value);
|
|
6053
6084
|
if (excluded === void 0) {
|
|
@@ -6066,12 +6097,12 @@ function rewriteFakeTimerOptions(source, program) {
|
|
|
6066
6097
|
}
|
|
6067
6098
|
}
|
|
6068
6099
|
});
|
|
6069
|
-
if (edits.length === 0) return { source, translated, refusals };
|
|
6100
|
+
if (edits.length === 0) return { source, translated, refusals, warnings };
|
|
6070
6101
|
let result = source;
|
|
6071
6102
|
for (const edit of [...edits].sort((left, right) => right.start - left.start)) {
|
|
6072
6103
|
result = result.slice(0, edit.start) + edit.text + result.slice(edit.end);
|
|
6073
6104
|
}
|
|
6074
|
-
return { source: result, translated, refusals };
|
|
6105
|
+
return { source: result, translated, refusals, warnings };
|
|
6075
6106
|
}
|
|
6076
6107
|
|
|
6077
6108
|
// src/roles/test/codemod/scope.ts
|
|
@@ -6135,9 +6166,9 @@ function patternNames(pattern) {
|
|
|
6135
6166
|
}
|
|
6136
6167
|
function moduleBindings(program) {
|
|
6137
6168
|
const bindings = /* @__PURE__ */ new Map();
|
|
6138
|
-
for (const
|
|
6139
|
-
if (
|
|
6140
|
-
for (const declarator of
|
|
6169
|
+
for (const statement2 of program.body ?? []) {
|
|
6170
|
+
if (statement2.type !== "VariableDeclaration") continue;
|
|
6171
|
+
for (const declarator of statement2.declarations ?? []) {
|
|
6141
6172
|
const init = declarator.init ?? void 0;
|
|
6142
6173
|
for (const name of patternNames(declarator.id)) bindings.set(name, init);
|
|
6143
6174
|
}
|
|
@@ -6146,9 +6177,9 @@ function moduleBindings(program) {
|
|
|
6146
6177
|
}
|
|
6147
6178
|
function importedNames(program) {
|
|
6148
6179
|
const names = /* @__PURE__ */ new Set();
|
|
6149
|
-
for (const
|
|
6150
|
-
if (
|
|
6151
|
-
for (const specifier of
|
|
6180
|
+
for (const statement2 of program.body ?? []) {
|
|
6181
|
+
if (statement2.type !== "ImportDeclaration") continue;
|
|
6182
|
+
for (const specifier of statement2.specifiers ?? []) {
|
|
6152
6183
|
const local = specifier.local;
|
|
6153
6184
|
if (local?.type === "Identifier") names.add(local.name);
|
|
6154
6185
|
}
|
|
@@ -6205,15 +6236,15 @@ function hoistable(name, bindings, imported, seen = /* @__PURE__ */ new Set()) {
|
|
|
6205
6236
|
// src/roles/test/codemod/hoist.ts
|
|
6206
6237
|
function simpleDeclarations(program) {
|
|
6207
6238
|
const found = /* @__PURE__ */ new Map();
|
|
6208
|
-
for (const
|
|
6209
|
-
if (
|
|
6210
|
-
const declarations =
|
|
6239
|
+
for (const statement2 of program.body ?? []) {
|
|
6240
|
+
if (statement2.type !== "VariableDeclaration") continue;
|
|
6241
|
+
const declarations = statement2.declarations ?? [];
|
|
6211
6242
|
if (declarations.length !== 1) continue;
|
|
6212
6243
|
const declarator = declarations[0];
|
|
6213
6244
|
const id = declarator.id;
|
|
6214
6245
|
const init = declarator.init;
|
|
6215
6246
|
if (id?.type !== "Identifier" || init === void 0 || init === null) continue;
|
|
6216
|
-
found.set(id.name, { statement, init, kind:
|
|
6247
|
+
found.set(id.name, { statement: statement2, init, kind: statement2.kind ?? "const" });
|
|
6217
6248
|
}
|
|
6218
6249
|
return found;
|
|
6219
6250
|
}
|
|
@@ -6225,11 +6256,11 @@ function interdependent(names, bindings) {
|
|
|
6225
6256
|
}
|
|
6226
6257
|
return void 0;
|
|
6227
6258
|
}
|
|
6228
|
-
function statementSpan(source,
|
|
6229
|
-
let end =
|
|
6259
|
+
function statementSpan(source, statement2) {
|
|
6260
|
+
let end = statement2.end;
|
|
6230
6261
|
if (source[end] === "\r") end++;
|
|
6231
6262
|
if (source[end] === "\n") end++;
|
|
6232
|
-
return { start:
|
|
6263
|
+
return { start: statement2.start, end };
|
|
6233
6264
|
}
|
|
6234
6265
|
function hoistCapturedBindings(source, program, factories, bindings, imported) {
|
|
6235
6266
|
const captured = [...new Set(factories.flatMap((factory) => capturedBindings(factory, bindings)))];
|
|
@@ -6284,8 +6315,8 @@ function hoistCapturedBindings(source, program, factories, bindings, imported) {
|
|
|
6284
6315
|
`);
|
|
6285
6316
|
}
|
|
6286
6317
|
const hoistedLine = lines.join("");
|
|
6287
|
-
const edits = ordered.map(({ statement }, index) => {
|
|
6288
|
-
const span = statementSpan(source,
|
|
6318
|
+
const edits = ordered.map(({ statement: statement2 }, index) => {
|
|
6319
|
+
const span = statementSpan(source, statement2);
|
|
6289
6320
|
return { ...span, text: index === 0 ? hoistedLine : "" };
|
|
6290
6321
|
});
|
|
6291
6322
|
let rewritten = source;
|
|
@@ -7346,6 +7377,220 @@ function repointMswServer(fileName, source) {
|
|
|
7346
7377
|
return { source: rewritten, repointed: true };
|
|
7347
7378
|
}
|
|
7348
7379
|
|
|
7380
|
+
// src/roles/test/codemod/setup-static-imports.ts
|
|
7381
|
+
import { dirname as dirname7, resolve as resolve6 } from "node:path";
|
|
7382
|
+
|
|
7383
|
+
// src/core/context.ts
|
|
7384
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
7385
|
+
import { dirname as dirname6, join as join32, resolve as resolve4 } from "node:path";
|
|
7386
|
+
|
|
7387
|
+
// src/core/discover-modules.ts
|
|
7388
|
+
import { execFileSync } from "node:child_process";
|
|
7389
|
+
import { mkdtempSync, readFileSync as readFileSync25, rmSync as rmSync3 } from "node:fs";
|
|
7390
|
+
import { tmpdir } from "node:os";
|
|
7391
|
+
import { join as join31 } from "node:path";
|
|
7392
|
+
function runNx(cwd, args) {
|
|
7393
|
+
const nx = resolveBin(cwd, "nx") ?? "nx";
|
|
7394
|
+
try {
|
|
7395
|
+
return execFileSync(nx, args, {
|
|
7396
|
+
cwd,
|
|
7397
|
+
encoding: "utf8",
|
|
7398
|
+
env: { ...process.env, NX_DAEMON: "false" }
|
|
7399
|
+
});
|
|
7400
|
+
} catch (error) {
|
|
7401
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7402
|
+
throw new Error(
|
|
7403
|
+
`sentinel: could not run nx (${message}). Is nx installed in this workspace, and are you at its root?`,
|
|
7404
|
+
{ cause: error }
|
|
7405
|
+
);
|
|
7406
|
+
}
|
|
7407
|
+
}
|
|
7408
|
+
function readGraph(cwd) {
|
|
7409
|
+
const dir = mkdtempSync(join31(tmpdir(), "sentinel-nx-"));
|
|
7410
|
+
const file = join31(dir, "graph.json");
|
|
7411
|
+
try {
|
|
7412
|
+
runNx(cwd, ["graph", "--file", file]);
|
|
7413
|
+
const parsed = JSON.parse(readFileSync25(file, "utf8"));
|
|
7414
|
+
const nodes = parsed.graph?.nodes;
|
|
7415
|
+
if (!nodes || typeof nodes !== "object") {
|
|
7416
|
+
throw new Error(
|
|
7417
|
+
"sentinel: unexpected nx graph output (no graph.nodes); the installed nx version may be incompatible."
|
|
7418
|
+
);
|
|
7419
|
+
}
|
|
7420
|
+
return Object.entries(nodes).map(([name, node]) => ({
|
|
7421
|
+
name,
|
|
7422
|
+
root: join31(cwd, node.data.root)
|
|
7423
|
+
}));
|
|
7424
|
+
} finally {
|
|
7425
|
+
rmSync3(dir, { recursive: true, force: true });
|
|
7426
|
+
}
|
|
7427
|
+
}
|
|
7428
|
+
function discoverModules(cwd, options = {}) {
|
|
7429
|
+
const modules = readGraph(cwd);
|
|
7430
|
+
if (!options.affected) return modules;
|
|
7431
|
+
const affected = new Set(
|
|
7432
|
+
JSON.parse(runNx(cwd, ["show", "projects", "--affected", "--json"]))
|
|
7433
|
+
);
|
|
7434
|
+
return modules.filter((module) => affected.has(module.name));
|
|
7435
|
+
}
|
|
7436
|
+
|
|
7437
|
+
// src/core/context.ts
|
|
7438
|
+
var MODULE_MARKERS = ["package.json", "project.json"];
|
|
7439
|
+
function isModuleDir(cwd) {
|
|
7440
|
+
return MODULE_MARKERS.some((marker) => existsSync26(join32(cwd, marker)));
|
|
7441
|
+
}
|
|
7442
|
+
function findModuleRoot(startDir, workspaceRoot) {
|
|
7443
|
+
let dir = resolve4(startDir);
|
|
7444
|
+
for (; ; ) {
|
|
7445
|
+
if (workspaceRoot !== void 0 && dir === resolve4(workspaceRoot)) return void 0;
|
|
7446
|
+
if (isModuleDir(dir)) return dir;
|
|
7447
|
+
const parent = dirname6(dir);
|
|
7448
|
+
if (parent === dir) return void 0;
|
|
7449
|
+
dir = parent;
|
|
7450
|
+
}
|
|
7451
|
+
}
|
|
7452
|
+
function resolveContext(cwd, opts) {
|
|
7453
|
+
const atRoot = existsSync26(join32(cwd, WORKSPACE_ROOT_MARKER));
|
|
7454
|
+
if (!atRoot && isModuleDir(cwd)) {
|
|
7455
|
+
if (opts.module) {
|
|
7456
|
+
throw new Error(
|
|
7457
|
+
"You are in a module directory: drop --module (the context is the current module)."
|
|
7458
|
+
);
|
|
7459
|
+
}
|
|
7460
|
+
if (opts.ci) {
|
|
7461
|
+
throw new Error("--ci selects the affected set from the workspace root; run it there.");
|
|
7462
|
+
}
|
|
7463
|
+
const name = moduleName(cwd);
|
|
7464
|
+
return { modules: [{ name, root: cwd }], scope: "cwd-module" };
|
|
7465
|
+
}
|
|
7466
|
+
if (atRoot) {
|
|
7467
|
+
if (opts.module) {
|
|
7468
|
+
const found = discoverModules(cwd).find((module) => module.name === opts.module);
|
|
7469
|
+
if (!found) throw new Error(`module "${opts.module}" not found in the workspace.`);
|
|
7470
|
+
return { modules: [found], scope: "named-module" };
|
|
7471
|
+
}
|
|
7472
|
+
if (opts.ci) return { modules: discoverModules(cwd, { affected: true }), scope: "affected" };
|
|
7473
|
+
return { modules: discoverModules(cwd), scope: "all" };
|
|
7474
|
+
}
|
|
7475
|
+
throw new Error(
|
|
7476
|
+
`Run sentinel from a module directory or the workspace root (found neither ${MODULE_MARKERS.join("/")} nor ${WORKSPACE_ROOT_MARKER} here).`
|
|
7477
|
+
);
|
|
7478
|
+
}
|
|
7479
|
+
|
|
7480
|
+
// src/roles/test/codemod/workspace-specifier.ts
|
|
7481
|
+
import { existsSync as existsSync27, readFileSync as readFileSync26 } from "node:fs";
|
|
7482
|
+
import { join as join33, relative as relative5, resolve as resolve5 } from "node:path";
|
|
7483
|
+
var EXTENSIONS = ["", ".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", "/index.ts", "/index.js"];
|
|
7484
|
+
function pathEntries(workspaceRoot) {
|
|
7485
|
+
const file = join33(workspaceRoot, "tsconfig.base.json");
|
|
7486
|
+
if (!existsSync27(file)) return [];
|
|
7487
|
+
let paths;
|
|
7488
|
+
try {
|
|
7489
|
+
paths = parseJsonc(
|
|
7490
|
+
readFileSync26(file, "utf8"),
|
|
7491
|
+
"tsconfig.base.json"
|
|
7492
|
+
)?.compilerOptions?.paths;
|
|
7493
|
+
} catch {
|
|
7494
|
+
return [];
|
|
7495
|
+
}
|
|
7496
|
+
const entries = [];
|
|
7497
|
+
for (const [key, targets] of Object.entries(paths ?? {})) {
|
|
7498
|
+
const target = targets[0];
|
|
7499
|
+
if (target === void 0) continue;
|
|
7500
|
+
const wildcard = key.endsWith("/*") && target.endsWith("/*");
|
|
7501
|
+
entries.push({
|
|
7502
|
+
key: wildcard ? key.slice(0, -2) : key,
|
|
7503
|
+
target: resolve5(workspaceRoot, wildcard ? target.slice(0, -2) : target),
|
|
7504
|
+
wildcard
|
|
7505
|
+
});
|
|
7506
|
+
}
|
|
7507
|
+
return entries.sort((left, right) => right.target.length - left.target.length);
|
|
7508
|
+
}
|
|
7509
|
+
function workspaceSpecifier(workspaceRoot, file) {
|
|
7510
|
+
const absolute = resolve5(file);
|
|
7511
|
+
for (const entry of pathEntries(workspaceRoot)) {
|
|
7512
|
+
if (!entry.wildcard) {
|
|
7513
|
+
if (EXTENSIONS.some((extension) => `${entry.target}${extension}` === absolute)) {
|
|
7514
|
+
return entry.key;
|
|
7515
|
+
}
|
|
7516
|
+
continue;
|
|
7517
|
+
}
|
|
7518
|
+
const rest = relative5(entry.target, absolute);
|
|
7519
|
+
if (rest === "" || rest.startsWith("..") || rest.startsWith("/")) continue;
|
|
7520
|
+
const named = rest.replace(/\\/g, "/").replace(/\.(m|c)?(j|t)sx?$/, "");
|
|
7521
|
+
return `${entry.key}/${named}`;
|
|
7522
|
+
}
|
|
7523
|
+
return void 0;
|
|
7524
|
+
}
|
|
7525
|
+
|
|
7526
|
+
// src/roles/test/codemod/setup-static-imports.ts
|
|
7527
|
+
function requireCall(source, node) {
|
|
7528
|
+
if (node?.type !== "CallExpression") return void 0;
|
|
7529
|
+
const callee = node.callee;
|
|
7530
|
+
if (callee?.type !== "Identifier" || callee.name !== "require") return void 0;
|
|
7531
|
+
const args = node.arguments ?? [];
|
|
7532
|
+
const [only] = args;
|
|
7533
|
+
if (args.length !== 1 || only?.type !== "Literal") return void 0;
|
|
7534
|
+
const raw = source.slice(only.start, only.end);
|
|
7535
|
+
return raw.slice(1, -1);
|
|
7536
|
+
}
|
|
7537
|
+
function importable(written, fileName, workspaceRoot) {
|
|
7538
|
+
if (!written.startsWith(".")) return written;
|
|
7539
|
+
const fileDir = dirname7(fileName);
|
|
7540
|
+
const moduleRoot = findModuleRoot(fileDir, workspaceRoot) ?? fileDir;
|
|
7541
|
+
const target = resolve6(fileDir, written);
|
|
7542
|
+
if (target.startsWith(`${moduleRoot}/`)) return written;
|
|
7543
|
+
if (workspaceRoot === void 0) return void 0;
|
|
7544
|
+
return workspaceSpecifier(workspaceRoot, target);
|
|
7545
|
+
}
|
|
7546
|
+
function statement(fileName, source, node, specifier, written) {
|
|
7547
|
+
if (node.type === "ExpressionStatement") return `import '${specifier}'`;
|
|
7548
|
+
const declarations = node.declarations ?? [];
|
|
7549
|
+
const [only] = declarations;
|
|
7550
|
+
if (declarations.length !== 1 || only === void 0) return void 0;
|
|
7551
|
+
const id = only.id;
|
|
7552
|
+
if (id?.type === "Identifier") {
|
|
7553
|
+
const name = source.slice(id.start, id.end);
|
|
7554
|
+
return isCommonJsPackage(fileName, written) ? `import ${name} from '${specifier}'` : `import * as ${name} from '${specifier}'`;
|
|
7555
|
+
}
|
|
7556
|
+
if (id?.type === "ObjectPattern" && isPlainShorthand(id)) {
|
|
7557
|
+
const names = source.slice(id.start, id.end);
|
|
7558
|
+
return `import ${names} from '${specifier}'`;
|
|
7559
|
+
}
|
|
7560
|
+
return void 0;
|
|
7561
|
+
}
|
|
7562
|
+
function isPlainShorthand(pattern) {
|
|
7563
|
+
const properties = pattern.properties ?? [];
|
|
7564
|
+
if (properties.length === 0) return false;
|
|
7565
|
+
return properties.every(
|
|
7566
|
+
(property) => property.type === "Property" && property.shorthand === true && property.computed !== true && property.value?.type === "Identifier"
|
|
7567
|
+
);
|
|
7568
|
+
}
|
|
7569
|
+
function rewriteSetupStaticImports(fileName, source, program, workspaceRoot) {
|
|
7570
|
+
const body = program.body ?? [];
|
|
7571
|
+
const edits = [];
|
|
7572
|
+
const refusals = [];
|
|
7573
|
+
for (const node of body) {
|
|
7574
|
+
if (node.type === "ImportDeclaration") continue;
|
|
7575
|
+
const expression = node.type === "ExpressionStatement" ? node.expression : node.type === "VariableDeclaration" ? node.declarations?.[0]?.init : void 0;
|
|
7576
|
+
const written = requireCall(source, expression);
|
|
7577
|
+
if (written === void 0) break;
|
|
7578
|
+
const specifier = importable(written, fileName, workspaceRoot);
|
|
7579
|
+
if (specifier === void 0) {
|
|
7580
|
+
refusals.push(written);
|
|
7581
|
+
break;
|
|
7582
|
+
}
|
|
7583
|
+
const text = statement(fileName, source, node, specifier, written);
|
|
7584
|
+
if (text === void 0) break;
|
|
7585
|
+
edits.push({ start: node.start, end: node.end, text });
|
|
7586
|
+
}
|
|
7587
|
+
let result = source;
|
|
7588
|
+
for (const edit of [...edits].sort((left, right) => right.start - left.start)) {
|
|
7589
|
+
result = `${result.slice(0, edit.start)}${edit.text}${result.slice(edit.end)}`;
|
|
7590
|
+
}
|
|
7591
|
+
return { source: result, rewritten: edits.length, refusals };
|
|
7592
|
+
}
|
|
7593
|
+
|
|
7349
7594
|
// src/roles/test/codemod/spy-instance-type.ts
|
|
7350
7595
|
var SPY_TYPES = /* @__PURE__ */ new Set(["SpyInstance"]);
|
|
7351
7596
|
var QUALIFIED_ONLY = /* @__PURE__ */ new Set(["Mock"]);
|
|
@@ -7417,6 +7662,7 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
|
|
|
7417
7662
|
renamed: 0,
|
|
7418
7663
|
hoisted: [],
|
|
7419
7664
|
unhandled: [],
|
|
7665
|
+
warnings: [],
|
|
7420
7666
|
refusals: [],
|
|
7421
7667
|
rulesApplied: [],
|
|
7422
7668
|
mswRepointed: false
|
|
@@ -7430,8 +7676,17 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
|
|
|
7430
7676
|
}
|
|
7431
7677
|
]
|
|
7432
7678
|
});
|
|
7433
|
-
const
|
|
7434
|
-
if (
|
|
7679
|
+
const beforeStatic = parse2(fileName, source);
|
|
7680
|
+
if (beforeStatic === void 0) return unparsable("reading the file");
|
|
7681
|
+
const statics = rewriteSetupStaticImports(
|
|
7682
|
+
fileName,
|
|
7683
|
+
source,
|
|
7684
|
+
beforeStatic,
|
|
7685
|
+
findWorkspaceRoot(dirname8(fileName))
|
|
7686
|
+
);
|
|
7687
|
+
source = statics.source;
|
|
7688
|
+
const original = statics.rewritten === 0 ? beforeStatic : parse2(fileName, source);
|
|
7689
|
+
if (original === void 0) return unparsable("hoisting the setup requires");
|
|
7435
7690
|
const titles = rewriteEachTitles(source, original);
|
|
7436
7691
|
const tables = rewriteEachTableRows(titles.source, original);
|
|
7437
7692
|
const afterTables = parse2(fileName, tables.source);
|
|
@@ -7516,23 +7771,34 @@ function migrateTestFile(fileName, source, defaultImported = /* @__PURE__ */ new
|
|
|
7516
7771
|
...fakeTimers.translated
|
|
7517
7772
|
].sort(),
|
|
7518
7773
|
refusals: [
|
|
7774
|
+
/*
|
|
7775
|
+
* A leading require that leaves its module and that no `paths` entry covers. Reported rather
|
|
7776
|
+
* than written in a shape we know nx refuses, which is Héla's bound: we refuse only where the
|
|
7777
|
+
* constraint is beyond us, and inventing a workspace alias is. Measured on this repo: 3 such
|
|
7778
|
+
* requires across 136 setup files, all 3 covered, so this list stays empty here.
|
|
7779
|
+
*/
|
|
7780
|
+
...statics.refusals.map((specifier) => ({
|
|
7781
|
+
binding: specifier,
|
|
7782
|
+
reason: `\`require('${specifier}')\` reaches another project, and \`tsconfig.base.json\` declares no path for it. A static import of a relative path is refused by nx, and \`await import()\` would make it a dynamic dependency, which forbids every static import of that project across this module. Add a \`paths\` entry for it, or move the file inside this project.`
|
|
7783
|
+
})),
|
|
7519
7784
|
...fakeTimers.refusals,
|
|
7520
7785
|
...hoist.refusals,
|
|
7521
7786
|
...mocks.refusals.map((refusal) => ({ binding: refusal.binding, reason: refusal.reason })),
|
|
7522
7787
|
...constructible.refusals,
|
|
7523
7788
|
...isolated.refusals
|
|
7524
7789
|
],
|
|
7790
|
+
warnings: fakeTimers.warnings,
|
|
7525
7791
|
mswRepointed: repointed.repointed
|
|
7526
7792
|
};
|
|
7527
7793
|
}
|
|
7528
7794
|
|
|
7529
7795
|
// src/roles/test/codemod/test-support-files.ts
|
|
7530
|
-
import { existsSync as
|
|
7531
|
-
import { dirname as
|
|
7796
|
+
import { existsSync as existsSync28, readFileSync as readFileSync27, readdirSync as readdirSync5, statSync as statSync5 } from "node:fs";
|
|
7797
|
+
import { dirname as dirname9, join as join34, relative as relative6, resolve as resolve7 } from "node:path";
|
|
7532
7798
|
var SKIPPED = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage", ".git", ".vite"]);
|
|
7533
7799
|
var TEST_FILE2 = /\.(spec|test)\.[cm]?[jt]sx?$/;
|
|
7534
7800
|
var SOURCE_FILE = /\.[cm]?[jt]sx?$/;
|
|
7535
|
-
var
|
|
7801
|
+
var EXTENSIONS2 = [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"];
|
|
7536
7802
|
function relativeSpecifiers(source) {
|
|
7537
7803
|
const found = [];
|
|
7538
7804
|
const patterns = [
|
|
@@ -7547,14 +7813,14 @@ function relativeSpecifiers(source) {
|
|
|
7547
7813
|
return found;
|
|
7548
7814
|
}
|
|
7549
7815
|
function resolveSpecifier(fromFile, specifier) {
|
|
7550
|
-
const base =
|
|
7551
|
-
if (
|
|
7552
|
-
for (const extension of
|
|
7553
|
-
if (
|
|
7816
|
+
const base = resolve7(dirname9(fromFile), specifier);
|
|
7817
|
+
if (existsSync28(base) && statSync5(base).isFile()) return base;
|
|
7818
|
+
for (const extension of EXTENSIONS2) {
|
|
7819
|
+
if (existsSync28(`${base}${extension}`)) return `${base}${extension}`;
|
|
7554
7820
|
}
|
|
7555
|
-
for (const extension of
|
|
7556
|
-
const index =
|
|
7557
|
-
if (
|
|
7821
|
+
for (const extension of EXTENSIONS2) {
|
|
7822
|
+
const index = join34(base, `index${extension}`);
|
|
7823
|
+
if (existsSync28(index)) return index;
|
|
7558
7824
|
}
|
|
7559
7825
|
return void 0;
|
|
7560
7826
|
}
|
|
@@ -7571,9 +7837,9 @@ function moduleSources(cwd) {
|
|
|
7571
7837
|
}
|
|
7572
7838
|
for (const entry of entries) {
|
|
7573
7839
|
if (entry.isDirectory()) {
|
|
7574
|
-
if (!SKIPPED.has(entry.name)) stack.push(
|
|
7840
|
+
if (!SKIPPED.has(entry.name)) stack.push(join34(dir, entry.name));
|
|
7575
7841
|
} else if (SOURCE_FILE.test(entry.name)) {
|
|
7576
|
-
found.push(
|
|
7842
|
+
found.push(join34(dir, entry.name));
|
|
7577
7843
|
}
|
|
7578
7844
|
}
|
|
7579
7845
|
}
|
|
@@ -7581,7 +7847,7 @@ function moduleSources(cwd) {
|
|
|
7581
7847
|
}
|
|
7582
7848
|
function read(path) {
|
|
7583
7849
|
try {
|
|
7584
|
-
return
|
|
7850
|
+
return readFileSync27(path, "utf8");
|
|
7585
7851
|
} catch {
|
|
7586
7852
|
return void 0;
|
|
7587
7853
|
}
|
|
@@ -7590,10 +7856,10 @@ function setupFilesInside(cwd, named) {
|
|
|
7590
7856
|
const kept = [];
|
|
7591
7857
|
for (const raw of named) {
|
|
7592
7858
|
const withoutToken = raw.replaceAll("<rootDir>/", "").replaceAll("<rootDir>", ".");
|
|
7593
|
-
const full =
|
|
7594
|
-
const inside =
|
|
7859
|
+
const full = resolve7(cwd, withoutToken);
|
|
7860
|
+
const inside = relative6(cwd, full);
|
|
7595
7861
|
if (inside.startsWith("..") || inside === "") continue;
|
|
7596
|
-
if (!
|
|
7862
|
+
if (!existsSync28(full)) continue;
|
|
7597
7863
|
kept.push(inside.replaceAll("\\", "/"));
|
|
7598
7864
|
}
|
|
7599
7865
|
return kept;
|
|
@@ -7628,7 +7894,7 @@ function listTestSupportFiles(cwd, setupFiles = []) {
|
|
|
7628
7894
|
queue.push(target);
|
|
7629
7895
|
}
|
|
7630
7896
|
}
|
|
7631
|
-
const reachable = [...testOnly].filter((file) => /\brequire\s*\(/.test(contents.get(file) ?? "")).map((file) =>
|
|
7897
|
+
const reachable = [...testOnly].filter((file) => /\brequire\s*\(/.test(contents.get(file) ?? "")).map((file) => relative6(cwd, file).replaceAll("\\", "/"));
|
|
7632
7898
|
return [.../* @__PURE__ */ new Set([...reachable, ...setupFilesInside(cwd, setupFiles)])].sort();
|
|
7633
7899
|
}
|
|
7634
7900
|
|
|
@@ -7639,7 +7905,7 @@ function parse3(source) {
|
|
|
7639
7905
|
}
|
|
7640
7906
|
function read2(path) {
|
|
7641
7907
|
try {
|
|
7642
|
-
return
|
|
7908
|
+
return readFileSync28(path, "utf8");
|
|
7643
7909
|
} catch {
|
|
7644
7910
|
return void 0;
|
|
7645
7911
|
}
|
|
@@ -7657,7 +7923,7 @@ function defaultImportedSpecifiers(cwd) {
|
|
|
7657
7923
|
}
|
|
7658
7924
|
for (const entry of entries) {
|
|
7659
7925
|
if (SKIPPED2.has(entry)) continue;
|
|
7660
|
-
const path =
|
|
7926
|
+
const path = join35(dir, entry);
|
|
7661
7927
|
let directory;
|
|
7662
7928
|
try {
|
|
7663
7929
|
directory = statSync6(path).isDirectory();
|
|
@@ -7681,27 +7947,28 @@ function migrateModuleTests(cwd, setupFiles = []) {
|
|
|
7681
7947
|
const modulesOwnMsw = ownsMswLifecycle(cwd, setupFiles);
|
|
7682
7948
|
const operations = [];
|
|
7683
7949
|
const blockers = [];
|
|
7950
|
+
const collected = [];
|
|
7684
7951
|
let files = 0;
|
|
7685
7952
|
let renamed = 0;
|
|
7686
7953
|
let hoisted = 0;
|
|
7687
7954
|
const defaultImported = defaultImportedSpecifiers(cwd);
|
|
7688
7955
|
const asyncHelpers = /* @__PURE__ */ new Set();
|
|
7689
|
-
for (const
|
|
7690
|
-
const source = read2(
|
|
7956
|
+
for (const relative9 of listTestSupportFiles(cwd, setupFiles)) {
|
|
7957
|
+
const source = read2(join35(cwd, relative9));
|
|
7691
7958
|
if (source === void 0) {
|
|
7692
|
-
blockers.push({ file:
|
|
7959
|
+
blockers.push({ file: relative9, reason: "this file could not be read" });
|
|
7693
7960
|
continue;
|
|
7694
7961
|
}
|
|
7695
7962
|
const migrated = migrateTestFile(
|
|
7696
|
-
|
|
7963
|
+
join35(cwd, relative9),
|
|
7697
7964
|
source,
|
|
7698
7965
|
defaultImported,
|
|
7699
7966
|
/* @__PURE__ */ new Set(),
|
|
7700
7967
|
modulesOwnMsw
|
|
7701
7968
|
);
|
|
7702
|
-
const renamedTo = vitestSupportName(
|
|
7703
|
-
if (!migrated.changed && renamedTo ===
|
|
7704
|
-
if (renamedTo !==
|
|
7969
|
+
const renamedTo = vitestSupportName(relative9);
|
|
7970
|
+
if (!migrated.changed && renamedTo === relative9) continue;
|
|
7971
|
+
if (renamedTo !== relative9) operations.push({ kind: "delete", path: relative9 });
|
|
7705
7972
|
const before = parse3(source);
|
|
7706
7973
|
const after = parse3(migrated.source);
|
|
7707
7974
|
if (before !== void 0 && after !== void 0) {
|
|
@@ -7714,14 +7981,14 @@ function migrateModuleTests(cwd, setupFiles = []) {
|
|
|
7714
7981
|
renamed += migrated.renamed;
|
|
7715
7982
|
operations.push({ kind: "write", path: renamedTo, contents: migrated.source });
|
|
7716
7983
|
}
|
|
7717
|
-
for (const
|
|
7718
|
-
const source = read2(
|
|
7984
|
+
for (const relative9 of listTestFiles(cwd)) {
|
|
7985
|
+
const source = read2(join35(cwd, relative9));
|
|
7719
7986
|
if (source === void 0) {
|
|
7720
|
-
blockers.push({ file:
|
|
7987
|
+
blockers.push({ file: relative9, reason: "this file could not be read" });
|
|
7721
7988
|
continue;
|
|
7722
7989
|
}
|
|
7723
7990
|
const migrated = migrateTestFile(
|
|
7724
|
-
|
|
7991
|
+
join35(cwd, relative9),
|
|
7725
7992
|
source,
|
|
7726
7993
|
defaultImported,
|
|
7727
7994
|
asyncHelpers,
|
|
@@ -7729,25 +7996,27 @@ function migrateModuleTests(cwd, setupFiles = []) {
|
|
|
7729
7996
|
);
|
|
7730
7997
|
for (const member of migrated.unhandled) {
|
|
7731
7998
|
blockers.push({
|
|
7732
|
-
file: `${
|
|
7999
|
+
file: `${relative9}:${member.line}`,
|
|
7733
8000
|
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}`
|
|
7734
8001
|
});
|
|
7735
8002
|
}
|
|
8003
|
+
collected.push(...migrated.warnings);
|
|
7736
8004
|
for (const refusal of migrated.refusals) {
|
|
7737
|
-
blockers.push({ file:
|
|
8005
|
+
blockers.push({ file: relative9, reason: refusal.reason });
|
|
7738
8006
|
}
|
|
7739
8007
|
if (!migrated.changed) continue;
|
|
7740
8008
|
files++;
|
|
7741
8009
|
renamed += migrated.renamed;
|
|
7742
8010
|
if (migrated.hoisted.length > 0) hoisted++;
|
|
7743
|
-
operations.push({ kind: "write", path:
|
|
8011
|
+
operations.push({ kind: "write", path: relative9, contents: migrated.source });
|
|
7744
8012
|
}
|
|
7745
|
-
|
|
8013
|
+
const warnings = [...new Set(collected)];
|
|
8014
|
+
return blockers.length > 0 ? { operations: [], files, renamed, hoisted, blockers, warnings } : { operations, files, renamed, hoisted, blockers: [], warnings };
|
|
7746
8015
|
}
|
|
7747
8016
|
|
|
7748
8017
|
// src/roles/test/nest/generate-config.ts
|
|
7749
|
-
import { readdirSync as readdirSync7, readFileSync as
|
|
7750
|
-
import { join as
|
|
8018
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync29, statSync as statSync7 } from "node:fs";
|
|
8019
|
+
import { join as join36, relative as relative7 } from "node:path";
|
|
7751
8020
|
var NEST_TEST_PRESET_SPECIFIER = "@hublo/sentinel/test/nest";
|
|
7752
8021
|
var REACT_TEST_PRESET_SPECIFIER = "@hublo/sentinel/test/react";
|
|
7753
8022
|
function flavourOf(reading) {
|
|
@@ -7757,7 +8026,7 @@ var SENTINEL_TEST_SETUP = "@hublo/sentinel/test/setup/jest-parity";
|
|
|
7757
8026
|
var SENTINEL_MSW_SETUP = "@hublo/sentinel/test/setup/msw-lifecycle";
|
|
7758
8027
|
var SENTINEL_WORKSPACE_SETUP = "@hublo/sentinel/test/setup/workspace";
|
|
7759
8028
|
function hopsToRoot(cwd, workspaceRoot) {
|
|
7760
|
-
const up =
|
|
8029
|
+
const up = relative7(cwd, workspaceRoot).replaceAll("\\", "/");
|
|
7761
8030
|
return up === "" ? "." : up;
|
|
7762
8031
|
}
|
|
7763
8032
|
function inlineBlock(packages) {
|
|
@@ -7791,7 +8060,7 @@ function readModuleTraits(cwd) {
|
|
|
7791
8060
|
}
|
|
7792
8061
|
for (const entry of entries) {
|
|
7793
8062
|
if (SKIPPED3.has(entry)) continue;
|
|
7794
|
-
const path =
|
|
8063
|
+
const path = join36(dir, entry);
|
|
7795
8064
|
let directory;
|
|
7796
8065
|
try {
|
|
7797
8066
|
directory = statSync7(path).isDirectory();
|
|
@@ -7805,7 +8074,7 @@ function readModuleTraits(cwd) {
|
|
|
7805
8074
|
if (!/\.tsx?$/.test(entry)) continue;
|
|
7806
8075
|
let source;
|
|
7807
8076
|
try {
|
|
7808
|
-
source =
|
|
8077
|
+
source = readFileSync29(path, "utf8");
|
|
7809
8078
|
} catch {
|
|
7810
8079
|
continue;
|
|
7811
8080
|
}
|
|
@@ -7936,8 +8205,8 @@ ${overrides.join("\n")}
|
|
|
7936
8205
|
}
|
|
7937
8206
|
|
|
7938
8207
|
// src/roles/test/nest/read-jest-config.ts
|
|
7939
|
-
import { existsSync as
|
|
7940
|
-
import { join as
|
|
8208
|
+
import { existsSync as existsSync29, readFileSync as readFileSync30 } from "node:fs";
|
|
8209
|
+
import { join as join37 } from "node:path";
|
|
7941
8210
|
var CARRIED = [
|
|
7942
8211
|
"testEnvironment",
|
|
7943
8212
|
"coverageDirectory",
|
|
@@ -8127,7 +8396,7 @@ function extendedConfig(source, cwd) {
|
|
|
8127
8396
|
(name) => name.replace(/^\.\//, "")
|
|
8128
8397
|
);
|
|
8129
8398
|
const file = candidates.find(
|
|
8130
|
-
(name) => /^jest[\w.-]*\.(ts|js)$/.test(name) &&
|
|
8399
|
+
(name) => /^jest[\w.-]*\.(ts|js)$/.test(name) && existsSync29(join37(cwd, name))
|
|
8131
8400
|
);
|
|
8132
8401
|
if (file === void 0) continue;
|
|
8133
8402
|
if (!new RegExp(String.raw`\{\s*\.\.\.${binding}\b`).test(source)) continue;
|
|
@@ -8154,11 +8423,11 @@ function sideEffects(source, extended) {
|
|
|
8154
8423
|
return found;
|
|
8155
8424
|
}
|
|
8156
8425
|
function readJestConfig(cwd, named) {
|
|
8157
|
-
const file = named ?? JEST_CONFIG_FILES.find((name) =>
|
|
8158
|
-
if (file === void 0 || !
|
|
8426
|
+
const file = named ?? JEST_CONFIG_FILES.find((name) => existsSync29(join37(cwd, name)));
|
|
8427
|
+
if (file === void 0 || !existsSync29(join37(cwd, file))) return void 0;
|
|
8159
8428
|
let source;
|
|
8160
8429
|
try {
|
|
8161
|
-
source =
|
|
8430
|
+
source = readFileSync30(join37(cwd, file), "utf8");
|
|
8162
8431
|
} catch {
|
|
8163
8432
|
return { file, refusals: [`${file} could not be read`], notes: [] };
|
|
8164
8433
|
}
|
|
@@ -8239,8 +8508,8 @@ function readJestConfig(cwd, named) {
|
|
|
8239
8508
|
}
|
|
8240
8509
|
|
|
8241
8510
|
// src/roles/test/nest/run-modes.ts
|
|
8242
|
-
import { existsSync as
|
|
8243
|
-
import { basename as basename2, join as
|
|
8511
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
8512
|
+
import { basename as basename2, join as join38 } from "node:path";
|
|
8244
8513
|
var JEST_EXECUTOR = "@nx/jest:jest";
|
|
8245
8514
|
var PRIMARY_CONFIG_FILE = "vitest.config.mts";
|
|
8246
8515
|
var PRIMARY_TARGET = "test";
|
|
@@ -8261,7 +8530,7 @@ function runModes(cwd, fallback) {
|
|
|
8261
8530
|
const jestConfig = entry.options?.jestConfig;
|
|
8262
8531
|
if (jestConfig === void 0) continue;
|
|
8263
8532
|
const name = configName(jestConfig);
|
|
8264
|
-
if (!
|
|
8533
|
+
if (!existsSync30(join38(cwd, name))) continue;
|
|
8265
8534
|
declared.push({
|
|
8266
8535
|
target,
|
|
8267
8536
|
jestConfig: name,
|
|
@@ -8286,8 +8555,8 @@ function runModes(cwd, fallback) {
|
|
|
8286
8555
|
}
|
|
8287
8556
|
|
|
8288
8557
|
// src/roles/test/written-file-formatter.ts
|
|
8289
|
-
import { existsSync as
|
|
8290
|
-
import { dirname as
|
|
8558
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
8559
|
+
import { dirname as dirname10, join as join39 } from "node:path";
|
|
8291
8560
|
var PRETTIER_CONFIGS = [
|
|
8292
8561
|
".prettierrc",
|
|
8293
8562
|
".prettierrc.json",
|
|
@@ -8306,9 +8575,9 @@ function prettierConfigFrom(cwd) {
|
|
|
8306
8575
|
let dir = cwd;
|
|
8307
8576
|
for (; ; ) {
|
|
8308
8577
|
for (const name of PRETTIER_CONFIGS) {
|
|
8309
|
-
if (
|
|
8578
|
+
if (existsSync31(join39(dir, name))) return join39(dir, name);
|
|
8310
8579
|
}
|
|
8311
|
-
const parent =
|
|
8580
|
+
const parent = dirname10(dir);
|
|
8312
8581
|
if (parent === dir) return void 0;
|
|
8313
8582
|
dir = parent;
|
|
8314
8583
|
}
|
|
@@ -8336,12 +8605,12 @@ function writtenFileFormatter(cwd) {
|
|
|
8336
8605
|
var SPEC_TSCONFIG = "tsconfig.spec.json";
|
|
8337
8606
|
var VITEST_GLOBAL_TYPES = "vitest/globals";
|
|
8338
8607
|
function specTypesOperation(cwd) {
|
|
8339
|
-
const path =
|
|
8340
|
-
if (!
|
|
8608
|
+
const path = join40(cwd, SPEC_TSCONFIG);
|
|
8609
|
+
if (!existsSync32(path)) return void 0;
|
|
8341
8610
|
let config;
|
|
8342
8611
|
try {
|
|
8343
8612
|
config = parseJsonc(
|
|
8344
|
-
|
|
8613
|
+
readFileSync31(path, "utf8"),
|
|
8345
8614
|
SPEC_TSCONFIG
|
|
8346
8615
|
);
|
|
8347
8616
|
} catch {
|
|
@@ -8361,10 +8630,10 @@ function specTypesOperation(cwd) {
|
|
|
8361
8630
|
}
|
|
8362
8631
|
function shippedTypesPath(cwd, declaration) {
|
|
8363
8632
|
try {
|
|
8364
|
-
const manifest = createRequire6(
|
|
8365
|
-
const file =
|
|
8366
|
-
if (
|
|
8367
|
-
const path =
|
|
8633
|
+
const manifest = createRequire6(join40(cwd, "noop.cjs")).resolve("@hublo/sentinel/package.json");
|
|
8634
|
+
const file = join40(dirname11(manifest), declaration);
|
|
8635
|
+
if (existsSync32(file)) {
|
|
8636
|
+
const path = relative8(cwd, file);
|
|
8368
8637
|
return path.startsWith(".") ? path : `./${path}`;
|
|
8369
8638
|
}
|
|
8370
8639
|
} catch {
|
|
@@ -8383,7 +8652,7 @@ function tsconfigFileListOperations(cwd) {
|
|
|
8383
8652
|
let config;
|
|
8384
8653
|
try {
|
|
8385
8654
|
config = parseJsonc(
|
|
8386
|
-
|
|
8655
|
+
readFileSync31(join40(cwd, name), "utf8"),
|
|
8387
8656
|
name
|
|
8388
8657
|
);
|
|
8389
8658
|
} catch {
|
|
@@ -8511,6 +8780,10 @@ function planJestMigration(context, adoption) {
|
|
|
8511
8780
|
`${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` : ""}.`,
|
|
8512
8781
|
`${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.`)
|
|
8513
8782
|
];
|
|
8783
|
+
for (const warning of migration.warnings) notes.push(`\u26A0\uFE0F ${warning}`);
|
|
8784
|
+
notes.push(
|
|
8785
|
+
`the config files this wrote are named \`vitest.*\` where they were \`jest.*\`. An ignore written by NAME no longer covers them: if your lint or format config ignores \`jest.*\`, add the \`vitest.*\` equivalent, or accept that these files are now checked.`
|
|
8786
|
+
);
|
|
8514
8787
|
if (specTypes !== void 0) {
|
|
8515
8788
|
notes.push(
|
|
8516
8789
|
`${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.`
|
|
@@ -8670,11 +8943,11 @@ function plan3(context) {
|
|
|
8670
8943
|
}
|
|
8671
8944
|
|
|
8672
8945
|
// src/roles/test/reference.ts
|
|
8673
|
-
import { execFileSync } from "node:child_process";
|
|
8946
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
8674
8947
|
import { createHash } from "node:crypto";
|
|
8675
|
-
import { existsSync as
|
|
8676
|
-
import { tmpdir } from "node:os";
|
|
8677
|
-
import { join as
|
|
8948
|
+
import { existsSync as existsSync33, mkdirSync, mkdtempSync as mkdtempSync2, readFileSync as readFileSync32, rmSync as rmSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
8949
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
8950
|
+
import { join as join41, resolve as resolve8 } from "node:path";
|
|
8678
8951
|
|
|
8679
8952
|
// src/roles/test/baseline.ts
|
|
8680
8953
|
function readSnapshot(report) {
|
|
@@ -8718,25 +8991,25 @@ function unusableAsBaseline(snapshot) {
|
|
|
8718
8991
|
|
|
8719
8992
|
// src/roles/test/reference.ts
|
|
8720
8993
|
function referencePath(cwd) {
|
|
8721
|
-
const key = createHash("sha256").update(
|
|
8722
|
-
return
|
|
8994
|
+
const key = createHash("sha256").update(resolve8(cwd)).digest("hex").slice(0, 16);
|
|
8995
|
+
return join41(tmpdir2(), "sentinel-test-reference", `${key}.json`);
|
|
8723
8996
|
}
|
|
8724
8997
|
function runJest(cwd, config) {
|
|
8725
8998
|
const bin = resolveBin(cwd, "jest");
|
|
8726
8999
|
if (bin === void 0) return void 0;
|
|
8727
9000
|
const root = findWorkspaceRoot(cwd) ?? cwd;
|
|
8728
|
-
const out =
|
|
9001
|
+
const out = join41(mkdtempSync2(join41(tmpdir2(), "sentinel-reference-")), "jest.json");
|
|
8729
9002
|
try {
|
|
8730
|
-
|
|
9003
|
+
execFileSync2(bin, ["-c", join41(cwd, config), "--json", `--outputFile=${out}`, "--silent"], {
|
|
8731
9004
|
cwd: root,
|
|
8732
9005
|
stdio: ["ignore", "ignore", "ignore"],
|
|
8733
9006
|
maxBuffer: 1 << 28
|
|
8734
9007
|
});
|
|
8735
9008
|
} catch {
|
|
8736
9009
|
}
|
|
8737
|
-
if (!
|
|
9010
|
+
if (!existsSync33(out)) return void 0;
|
|
8738
9011
|
try {
|
|
8739
|
-
return JSON.parse(
|
|
9012
|
+
return JSON.parse(readFileSync32(out, "utf8"));
|
|
8740
9013
|
} catch {
|
|
8741
9014
|
return void 0;
|
|
8742
9015
|
}
|
|
@@ -8764,7 +9037,7 @@ function captureReference(cwd, config) {
|
|
|
8764
9037
|
snapshot
|
|
8765
9038
|
};
|
|
8766
9039
|
const path = referencePath(cwd);
|
|
8767
|
-
mkdirSync(
|
|
9040
|
+
mkdirSync(join41(path, ".."), { recursive: true });
|
|
8768
9041
|
writeFileSync3(path, `${JSON.stringify(stored, void 0, 2)}
|
|
8769
9042
|
`);
|
|
8770
9043
|
return {
|
|
@@ -8944,7 +9217,7 @@ function fromWorkspaceRoot(moduleDir, options, paths) {
|
|
|
8944
9217
|
if (root === void 0 || root === moduleDir) {
|
|
8945
9218
|
return { cwd: moduleDir, options: [...options], paths: [...paths] };
|
|
8946
9219
|
}
|
|
8947
|
-
const absolute = (value) => isAbsolute3(value) ? value :
|
|
9220
|
+
const absolute = (value) => isAbsolute3(value) ? value : resolve9(moduleDir, value);
|
|
8948
9221
|
const moved = [];
|
|
8949
9222
|
let previousWasConfig = false;
|
|
8950
9223
|
for (const option of options) {
|
|
@@ -8973,9 +9246,9 @@ function registerTest() {
|
|
|
8973
9246
|
|
|
8974
9247
|
// src/roles/typescript/adapters/tsc/tsc.adapter.ts
|
|
8975
9248
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
8976
|
-
import { existsSync as
|
|
9249
|
+
import { existsSync as existsSync34, readFileSync as readFileSync34 } from "node:fs";
|
|
8977
9250
|
import { createRequire as createRequire7 } from "node:module";
|
|
8978
|
-
import { join as
|
|
9251
|
+
import { join as join43 } from "node:path";
|
|
8979
9252
|
|
|
8980
9253
|
// src/roles/typescript/presets/nest.json
|
|
8981
9254
|
var nest_default2 = {
|
|
@@ -9156,8 +9429,8 @@ function readTsconfigAdoption(cwd) {
|
|
|
9156
9429
|
}
|
|
9157
9430
|
|
|
9158
9431
|
// src/roles/typescript/adapters/tsc/plan.ts
|
|
9159
|
-
import { readFileSync as
|
|
9160
|
-
import { join as
|
|
9432
|
+
import { readFileSync as readFileSync33 } from "node:fs";
|
|
9433
|
+
import { join as join42 } from "node:path";
|
|
9161
9434
|
|
|
9162
9435
|
// src/roles/typescript/typecheck-script.ts
|
|
9163
9436
|
var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
|
|
@@ -9250,7 +9523,7 @@ function planAdoption(context) {
|
|
|
9250
9523
|
};
|
|
9251
9524
|
}
|
|
9252
9525
|
const existing = parseJsonc(
|
|
9253
|
-
|
|
9526
|
+
readFileSync33(join42(context.cwd, target.path), "utf8"),
|
|
9254
9527
|
target.path
|
|
9255
9528
|
);
|
|
9256
9529
|
const extendsChain = composeExtends(existing.extends, preset);
|
|
@@ -9388,7 +9661,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
9388
9661
|
let chain;
|
|
9389
9662
|
try {
|
|
9390
9663
|
const parsed = parseJsonc(
|
|
9391
|
-
|
|
9664
|
+
readFileSync34(join43(cwd, target.path), "utf8"),
|
|
9392
9665
|
target.path
|
|
9393
9666
|
);
|
|
9394
9667
|
chain = parsed.extends;
|
|
@@ -9401,7 +9674,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
9401
9674
|
);
|
|
9402
9675
|
if (preset === void 0) return void 0;
|
|
9403
9676
|
try {
|
|
9404
|
-
createRequire7(
|
|
9677
|
+
createRequire7(join43(cwd, "noop.js")).resolve(preset);
|
|
9405
9678
|
return void 0;
|
|
9406
9679
|
} catch {
|
|
9407
9680
|
return preset;
|
|
@@ -9497,7 +9770,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
9497
9770
|
referencedProjects(cwd, config) {
|
|
9498
9771
|
try {
|
|
9499
9772
|
const parsed = parseJsonc(
|
|
9500
|
-
|
|
9773
|
+
readFileSync34(join43(cwd, config), "utf8"),
|
|
9501
9774
|
config
|
|
9502
9775
|
);
|
|
9503
9776
|
const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path) => typeof path === "string" && path.length > 0);
|
|
@@ -9513,7 +9786,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
9513
9786
|
* check.
|
|
9514
9787
|
*/
|
|
9515
9788
|
typecheckTarget(cwd) {
|
|
9516
|
-
if (
|
|
9789
|
+
if (existsSync34(join43(cwd, "tsconfig.json"))) return "tsconfig.json";
|
|
9517
9790
|
const target = resolveTsconfigTarget(cwd);
|
|
9518
9791
|
return target.reason === "none" ? null : target.path;
|
|
9519
9792
|
}
|
|
@@ -9718,8 +9991,8 @@ function replaceLines(current, replacements) {
|
|
|
9718
9991
|
}
|
|
9719
9992
|
|
|
9720
9993
|
// src/core/apply-plan.ts
|
|
9721
|
-
import { existsSync as
|
|
9722
|
-
import { resolve as
|
|
9994
|
+
import { existsSync as existsSync35, readFileSync as readFileSync35, renameSync, rmSync as rmSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
9995
|
+
import { resolve as resolve10, sep } from "node:path";
|
|
9723
9996
|
import { applyEdits as applyEdits3, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
|
|
9724
9997
|
|
|
9725
9998
|
// src/shared/deep-merge.ts
|
|
@@ -9729,15 +10002,15 @@ function isPlainObject(value) {
|
|
|
9729
10002
|
|
|
9730
10003
|
// src/core/apply-plan.ts
|
|
9731
10004
|
function resolveWithinRoot(cwd, relativePath) {
|
|
9732
|
-
const root =
|
|
9733
|
-
const absolutePath =
|
|
10005
|
+
const root = resolve10(cwd);
|
|
10006
|
+
const absolutePath = resolve10(root, relativePath);
|
|
9734
10007
|
if (absolutePath !== root && !absolutePath.startsWith(root + sep)) {
|
|
9735
10008
|
throw new Error(`Refusing to write outside the module root: "${relativePath}".`);
|
|
9736
10009
|
}
|
|
9737
10010
|
return absolutePath;
|
|
9738
10011
|
}
|
|
9739
10012
|
function readIfExists(absolutePath) {
|
|
9740
|
-
return
|
|
10013
|
+
return existsSync35(absolutePath) ? readFileSync35(absolutePath, "utf8") : void 0;
|
|
9741
10014
|
}
|
|
9742
10015
|
function* leaves(value, prefix = []) {
|
|
9743
10016
|
for (const [key, keyValue] of Object.entries(value)) {
|
|
@@ -9846,7 +10119,7 @@ function applyPlan(cwd, plan4) {
|
|
|
9846
10119
|
const changed = preparePlan(cwd, plan4).filter((file) => file.before !== file.after);
|
|
9847
10120
|
for (const file of changed) {
|
|
9848
10121
|
if (file.deleted) {
|
|
9849
|
-
|
|
10122
|
+
rmSync5(file.absolutePath, { force: true });
|
|
9850
10123
|
continue;
|
|
9851
10124
|
}
|
|
9852
10125
|
writeFileAtomic(file.absolutePath, file.after);
|
|
@@ -9855,8 +10128,8 @@ function applyPlan(cwd, plan4) {
|
|
|
9855
10128
|
}
|
|
9856
10129
|
|
|
9857
10130
|
// src/core/config/preset-evidence.ts
|
|
9858
|
-
import { existsSync as
|
|
9859
|
-
import { join as
|
|
10131
|
+
import { existsSync as existsSync36, readdirSync as readdirSync9, readFileSync as readFileSync36 } from "node:fs";
|
|
10132
|
+
import { join as join44 } from "node:path";
|
|
9860
10133
|
var PATH_SIGNALS = [
|
|
9861
10134
|
{
|
|
9862
10135
|
preset: "nest",
|
|
@@ -9890,12 +10163,12 @@ function sourceImports(cwd, pattern) {
|
|
|
9890
10163
|
}
|
|
9891
10164
|
for (const entry of entries) {
|
|
9892
10165
|
if (entry.isDirectory()) {
|
|
9893
|
-
if (!NOT_SCANNED.has(entry.name)) stack.push(
|
|
10166
|
+
if (!NOT_SCANNED.has(entry.name)) stack.push(join44(dir, entry.name));
|
|
9894
10167
|
continue;
|
|
9895
10168
|
}
|
|
9896
10169
|
if (!SOURCE_FILE2.test(entry.name)) continue;
|
|
9897
10170
|
try {
|
|
9898
|
-
if (pattern.test(
|
|
10171
|
+
if (pattern.test(readFileSync36(join44(dir, entry.name), "utf8"))) return true;
|
|
9899
10172
|
} catch {
|
|
9900
10173
|
continue;
|
|
9901
10174
|
}
|
|
@@ -9904,11 +10177,11 @@ function sourceImports(cwd, pattern) {
|
|
|
9904
10177
|
return false;
|
|
9905
10178
|
}
|
|
9906
10179
|
function dependencyNames(cwd) {
|
|
9907
|
-
const path =
|
|
9908
|
-
if (!
|
|
10180
|
+
const path = join44(cwd, "package.json");
|
|
10181
|
+
if (!existsSync36(path)) return [];
|
|
9909
10182
|
try {
|
|
9910
10183
|
const manifest = parseJsonc(
|
|
9911
|
-
|
|
10184
|
+
readFileSync36(path, "utf8"),
|
|
9912
10185
|
path
|
|
9913
10186
|
);
|
|
9914
10187
|
return [
|
|
@@ -9931,7 +10204,7 @@ function declaresJsx(cwd) {
|
|
|
9931
10204
|
for (const name of entries) {
|
|
9932
10205
|
try {
|
|
9933
10206
|
const config = parseJsonc(
|
|
9934
|
-
|
|
10207
|
+
readFileSync36(join44(cwd, name), "utf8"),
|
|
9935
10208
|
name
|
|
9936
10209
|
);
|
|
9937
10210
|
if (config.compilerOptions?.jsx !== void 0) return true;
|
|
@@ -10118,13 +10391,13 @@ export {
|
|
|
10118
10391
|
resolve,
|
|
10119
10392
|
buildConfigFile,
|
|
10120
10393
|
BaseAdapter,
|
|
10121
|
-
resolveBin,
|
|
10122
10394
|
VERBS,
|
|
10123
10395
|
TARGETS,
|
|
10124
10396
|
LONG_RUNNING_TARGETS,
|
|
10125
10397
|
SWEEPABLE_TARGETS,
|
|
10126
10398
|
PRESET_NAMES,
|
|
10127
10399
|
palette,
|
|
10400
|
+
resolveContext,
|
|
10128
10401
|
registerAdapters,
|
|
10129
10402
|
correctingSignal,
|
|
10130
10403
|
describeFramework,
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hublo/sentinel",
|
|
3
|
-
"version": "1.4.0-alpha.
|
|
3
|
+
"version": "1.4.0-alpha.24",
|
|
4
4
|
"description": "One CLI that guards code health across Hublo repos: shared lint/typescript/build/test presets, static & dynamic analysis, and architecture checks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/types/jest-global.d.ts
CHANGED
|
@@ -39,3 +39,39 @@
|
|
|
39
39
|
* type of what actually executes rather than the type of the runner it is leaving.
|
|
40
40
|
*/
|
|
41
41
|
declare const jest: (typeof import('vitest'))['vi']
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The same answer for TYPE positions, which the const above does not reach.
|
|
45
|
+
*
|
|
46
|
+
* `declare const jest` makes `jest.fn()` compile and says nothing about `jest.Mock`, because a
|
|
47
|
+
* const has no type members. A helper that annotates with one gets:
|
|
48
|
+
*
|
|
49
|
+
* libs/front/tests/src/mocks/libs/analytics.mock.ts(2,22)
|
|
50
|
+
* error TS2694: Namespace 'jest' has no exported member 'Mock'.
|
|
51
|
+
*
|
|
52
|
+
* Measured on `libs/front/components`, whose typecheck was green before it migrated: 8 of these,
|
|
53
|
+
* every one of them in `libs/front/tests`, a project this module reads and does not own. Repo-wide
|
|
54
|
+
* the whole vocabulary is eight names, and here is each one's Vitest counterpart.
|
|
55
|
+
*
|
|
56
|
+
* ⚠️ `Mock` and `SpyInstance` take JEST's generics here, not Vitest's, and the difference is not
|
|
57
|
+
* cosmetic: jest's first generic is the RETURN type, Vitest's is the whole SIGNATURE. These
|
|
58
|
+
* declarations serve files that are still written for jest, so they must read jest's spelling;
|
|
59
|
+
* `codemod/spy-instance-type.ts` is the other half, and it converts the spelling in the files the
|
|
60
|
+
* migration does rewrite.
|
|
61
|
+
*
|
|
62
|
+
* A type-only namespace declares no value, which is why it merges with the const above instead of
|
|
63
|
+
* colliding with it.
|
|
64
|
+
*/
|
|
65
|
+
declare namespace jest {
|
|
66
|
+
type Mock<T = any, Y extends any[] = any[]> = import('vitest').Mock<(...args: Y) => T>
|
|
67
|
+
type Mocked<T> = import('vitest').Mocked<T>
|
|
68
|
+
type MockedObject<T> = import('vitest').MockedObject<T>
|
|
69
|
+
type MockedClass<T extends abstract new (...args: any[]) => any> = import('vitest').MockedClass<T>
|
|
70
|
+
type MockedFunction<T extends (...args: any[]) => any> = import('vitest').MockedFunction<T>
|
|
71
|
+
/** jest's own alias for `MockedFunction`. */
|
|
72
|
+
type MockedFn<T extends (...args: any[]) => any> = import('vitest').MockedFunction<T>
|
|
73
|
+
type SpyInstance<T = any, Y extends any[] = any[]> = import('vitest').MockInstance<
|
|
74
|
+
(...args: Y) => T
|
|
75
|
+
>
|
|
76
|
+
type SpiedFunction<T extends (...args: any[]) => any> = import('vitest').MockInstance<T>
|
|
77
|
+
}
|