@hublo/sentinel 1.5.0 → 1.5.1-alpha.1
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/README.md +9 -4
- package/dist/bin/sentinel.js +366 -156
- package/dist/{chunk-MOELAQPC.js → chunk-64H5HBPH.js} +580 -158
- package/dist/{chunk-43HQ6BVJ.js → chunk-J2USE3KV.js} +69 -19
- package/dist/{chunk-IL5PPLXE.js → chunk-JRZI5IIN.js} +1 -1
- package/dist/{chunk-IL5PPLXE.js.map → chunk-JRZI5IIN.js.map} +1 -1
- package/dist/{chunk-Y57KA4QS.js → chunk-LFNOBQZU.js} +2 -2
- package/dist/index.d.ts +48 -0
- package/dist/index.js +3 -3
- package/dist/roles/test/setup/jest-parity.js +2 -2
- package/dist/roles/test/setup/workspace-entry.js +2 -2
- package/dist/{validate-FRMENKHM.js → validate-TIPIECG6.js} +2 -2
- package/docs/format-adoption.md +40 -10
- package/docs/lint-adoption.md +73 -10
- package/docs/typescript-adoption.md +1 -0
- package/docs/typescript-traces.md +249 -0
- package/docs/using-sentinel.md +15 -3
- package/docs/validating-a-change.md +130 -0
- package/package.json +30 -23
- /package/dist/{chunk-Y57KA4QS.js.map → chunk-LFNOBQZU.js.map} +0 -0
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
LINT_CONFIG_FILE,
|
|
11
11
|
LINT_MEASURE_FILE,
|
|
12
12
|
LINT_SCRIPT_NAME,
|
|
13
|
+
OWNED_WHEN_SET_KEYS,
|
|
13
14
|
PERMITTED_LOCAL_KEYS,
|
|
14
15
|
PRETTIER_CONFIG_FILES,
|
|
15
16
|
PROVENANCE_KEY,
|
|
@@ -51,11 +52,12 @@ import {
|
|
|
51
52
|
resolveOxfmt,
|
|
52
53
|
resolveOxlint,
|
|
53
54
|
rewriteToolchainImports,
|
|
55
|
+
scriptPath,
|
|
54
56
|
selfCommand,
|
|
55
57
|
splitToolArgs,
|
|
56
58
|
walk,
|
|
57
59
|
writtenFileFormatter
|
|
58
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-J2USE3KV.js";
|
|
59
61
|
import {
|
|
60
62
|
DEFAULT_MAX_DIAGNOSTICS,
|
|
61
63
|
WORKSPACE_ROOT_MARKER,
|
|
@@ -64,7 +66,7 @@ import {
|
|
|
64
66
|
ownPackageDir,
|
|
65
67
|
readOwnPackage,
|
|
66
68
|
readProjectPackageJson
|
|
67
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-JRZI5IIN.js";
|
|
68
70
|
import {
|
|
69
71
|
PRESET_NAMES,
|
|
70
72
|
REPORT_DIR,
|
|
@@ -77,6 +79,9 @@ import {
|
|
|
77
79
|
isPlainObject,
|
|
78
80
|
overridableSettings
|
|
79
81
|
} from "./chunk-WBDBRGLV.js";
|
|
82
|
+
import {
|
|
83
|
+
globToRegExp
|
|
84
|
+
} from "./chunk-O7REVMOC.js";
|
|
80
85
|
import {
|
|
81
86
|
parseJsonc
|
|
82
87
|
} from "./chunk-QXFCZON7.js";
|
|
@@ -153,7 +158,7 @@ function resolve(target, preset, runner) {
|
|
|
153
158
|
// src/shared/color.ts
|
|
154
159
|
import { styleText } from "node:util";
|
|
155
160
|
function palette(stream) {
|
|
156
|
-
const paint = (
|
|
161
|
+
const paint = (format2, text) => styleText(format2, text, { stream });
|
|
157
162
|
return {
|
|
158
163
|
ok: (text) => paint(["green", "bold"], text),
|
|
159
164
|
fail: (text) => paint(["red", "bold"], text),
|
|
@@ -581,6 +586,19 @@ function composeToolScript(existing, options) {
|
|
|
581
586
|
function keepsOtherCommands(script, sentinelCommand) {
|
|
582
587
|
return script.split(SEGMENT_SEPARATOR).filter((_, index) => !isSeparatorAt(index)).some((segment) => segment.trim() !== "" && segment.trim() !== sentinelCommand);
|
|
583
588
|
}
|
|
589
|
+
function leadingCommands(script) {
|
|
590
|
+
if (script === void 0) return void 0;
|
|
591
|
+
const parts = script.split(SEGMENT_SEPARATOR);
|
|
592
|
+
const leading = [];
|
|
593
|
+
for (let index = 0; index < parts.length; index += 2) {
|
|
594
|
+
const segment = (parts[index] ?? "").trim();
|
|
595
|
+
if (invokes(segment, "sentinel") || segment.includes("bin/sentinel.js")) break;
|
|
596
|
+
if (index + 1 >= parts.length) return void 0;
|
|
597
|
+
if ((parts[index + 1] ?? "").trim() !== "&&") return void 0;
|
|
598
|
+
leading.push(segment);
|
|
599
|
+
}
|
|
600
|
+
return leading.length > 0 ? leading.join(" && ") : void 0;
|
|
601
|
+
}
|
|
584
602
|
|
|
585
603
|
// src/roles/build/dev-script.ts
|
|
586
604
|
var CHAIN = " -- ";
|
|
@@ -819,8 +837,10 @@ function planReactApp(context2, configFile) {
|
|
|
819
837
|
skipped: `${configFile} could not be pointed at sentinel, so nothing was written: ${rewrite.why}. Nothing here is broken; this needs a look before the role applies.`
|
|
820
838
|
};
|
|
821
839
|
}
|
|
840
|
+
const rewritten = [];
|
|
822
841
|
if (rewrite.kind === "rewritten") {
|
|
823
842
|
operations.push({ kind: "write", path: configFile, contents: rewrite.source });
|
|
843
|
+
rewritten.push(configFile);
|
|
824
844
|
}
|
|
825
845
|
for (const name of COMPANION_CONFIG_FILES) {
|
|
826
846
|
const source = readConfigSource(context2.cwd, name);
|
|
@@ -828,6 +848,7 @@ function planReactApp(context2, configFile) {
|
|
|
828
848
|
const companion = rewriteBuildImports(source, name);
|
|
829
849
|
if (companion.kind === "rewritten") {
|
|
830
850
|
operations.push({ kind: "write", path: name, contents: companion.source });
|
|
851
|
+
rewritten.push(name);
|
|
831
852
|
notes.push(`${name} now imports ${companion.moved.join(", ")} from sentinel too.`);
|
|
832
853
|
} else if (companion.kind === "blocked") {
|
|
833
854
|
notes.push(
|
|
@@ -861,10 +882,12 @@ function planReactApp(context2, configFile) {
|
|
|
861
882
|
`removed ${owned.map(([, name]) => name).join(", ")} from this module: sentinel owns them now, and two copies of Vite in one build give the plugins a different Vite than the one running them.`
|
|
862
883
|
);
|
|
863
884
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
885
|
+
if (rewritten.length > 0) {
|
|
886
|
+
const files = rewritten.join(", ");
|
|
887
|
+
notes.push(
|
|
888
|
+
readFormatAdoption(context2.cwd).adopted ? `run \`sentinel --run --format --fix\` on this module: adoption rewrote ${files}, and a rewritten import can be longer than your formatter wraps at.` : `this module is formatted by the workspace, not by sentinel, so format ${files} with whatever owns it before committing: a rewritten import can be longer than that formatter wraps at, and the check would fail on a file nobody typed.`
|
|
889
|
+
);
|
|
890
|
+
}
|
|
868
891
|
notes.push(
|
|
869
892
|
`this config is code, and it runs before the build does: any environment variable it reads has to be present wherever you build, including a fresh clone, another CI job or an image. Adoption does not change what it reads, but a failure there arrives early and names this role rather than the variable.`
|
|
870
893
|
);
|
|
@@ -1266,8 +1289,9 @@ function registerBuild() {
|
|
|
1266
1289
|
|
|
1267
1290
|
// src/roles/format/adapters/oxfmt/oxfmt.adapter.ts
|
|
1268
1291
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
1269
|
-
import { existsSync as existsSync8, readFileSync as
|
|
1270
|
-
import { join as
|
|
1292
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9 } from "node:fs";
|
|
1293
|
+
import { join as join11 } from "node:path";
|
|
1294
|
+
import { isDeepStrictEqual } from "node:util";
|
|
1271
1295
|
|
|
1272
1296
|
// src/core/config/has-source.ts
|
|
1273
1297
|
import { readdirSync } from "node:fs";
|
|
@@ -1361,6 +1385,160 @@ function scopeFlags(paths, options = []) {
|
|
|
1361
1385
|
return [TOLERATE_EMPTY_FLAG];
|
|
1362
1386
|
}
|
|
1363
1387
|
|
|
1388
|
+
// src/roles/format/deliberate-import-order.ts
|
|
1389
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1390
|
+
import { join as join8 } from "node:path";
|
|
1391
|
+
import { parseSync } from "oxc-parser";
|
|
1392
|
+
import { format } from "oxfmt";
|
|
1393
|
+
|
|
1394
|
+
// src/roles/format/presets/deliberate-import-order.json
|
|
1395
|
+
var deliberate_import_order_default = {
|
|
1396
|
+
rules: [
|
|
1397
|
+
"import/order",
|
|
1398
|
+
"import/first",
|
|
1399
|
+
"import-x/order",
|
|
1400
|
+
"import-x/first",
|
|
1401
|
+
"simple-import-sort/imports",
|
|
1402
|
+
"simple-import-sort/exports"
|
|
1403
|
+
],
|
|
1404
|
+
modules: [
|
|
1405
|
+
"dd-trace",
|
|
1406
|
+
"dd-trace/**",
|
|
1407
|
+
"reflect-metadata",
|
|
1408
|
+
"source-map-support/register",
|
|
1409
|
+
"@opentelemetry/sdk-**",
|
|
1410
|
+
"@opentelemetry/auto-instrumentations-**",
|
|
1411
|
+
"core-js",
|
|
1412
|
+
"core-js/**",
|
|
1413
|
+
"regenerator-runtime",
|
|
1414
|
+
"regenerator-runtime/**"
|
|
1415
|
+
]
|
|
1416
|
+
};
|
|
1417
|
+
|
|
1418
|
+
// src/roles/format/deliberate-import-order.ts
|
|
1419
|
+
var PIN_COMMENT = "// oxfmt-ignore";
|
|
1420
|
+
var CODE_EXTENSIONS = [".js", ".mjs", ".cjs", ".jsx", ".ts", ".mts", ".cts", ".tsx"];
|
|
1421
|
+
var ORDER_RULES = deliberate_import_order_default.rules;
|
|
1422
|
+
var LOAD_ORDER_MODULES = deliberate_import_order_default.modules.map((glob) => globToRegExp(glob));
|
|
1423
|
+
var MODULE_NAMES = [
|
|
1424
|
+
...new Set(deliberate_import_order_default.modules.map((glob) => glob.split("*")[0] ?? glob))
|
|
1425
|
+
];
|
|
1426
|
+
function disabledRules(comment, directive) {
|
|
1427
|
+
const match = new RegExp(String.raw`^\s*${directive}(?:\s+(.*?))?\s*(?:--.*)?$`).exec(comment);
|
|
1428
|
+
if (match === null) return void 0;
|
|
1429
|
+
return (match[1] ?? "").split(/[\s,]+/).filter(Boolean);
|
|
1430
|
+
}
|
|
1431
|
+
function namesOrderRule(rules) {
|
|
1432
|
+
return rules !== void 0 && rules.some((rule) => ORDER_RULES.includes(rule));
|
|
1433
|
+
}
|
|
1434
|
+
function lineOf(starts, offset) {
|
|
1435
|
+
let low = 0;
|
|
1436
|
+
let high = starts.length - 1;
|
|
1437
|
+
while (low < high) {
|
|
1438
|
+
const middle = Math.ceil((low + high) / 2);
|
|
1439
|
+
if ((starts[middle] ?? 0) <= offset) low = middle;
|
|
1440
|
+
else high = middle - 1;
|
|
1441
|
+
}
|
|
1442
|
+
return low;
|
|
1443
|
+
}
|
|
1444
|
+
function readImports(fileName, text) {
|
|
1445
|
+
const { program, comments, errors } = parseSync(fileName, text, { sourceType: "module" });
|
|
1446
|
+
if (errors.length > 0) return [];
|
|
1447
|
+
const starts = [0];
|
|
1448
|
+
for (let index = 0; index < text.length; index++) if (text[index] === "\n") starts.push(index + 1);
|
|
1449
|
+
const nextLine = /* @__PURE__ */ new Set();
|
|
1450
|
+
const sameLine = /* @__PURE__ */ new Set();
|
|
1451
|
+
const regions = [];
|
|
1452
|
+
let regionStart;
|
|
1453
|
+
for (const comment of comments) {
|
|
1454
|
+
const line = lineOf(starts, comment.start);
|
|
1455
|
+
if (comment.type === "Line") {
|
|
1456
|
+
if (namesOrderRule(disabledRules(comment.value, "eslint-disable-next-line"))) {
|
|
1457
|
+
nextLine.add(lineOf(starts, comment.end) + 1);
|
|
1458
|
+
}
|
|
1459
|
+
if (namesOrderRule(disabledRules(comment.value, "eslint-disable-line"))) sameLine.add(line);
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
if (regionStart === void 0 && namesOrderRule(disabledRules(comment.value, "eslint-disable"))) {
|
|
1463
|
+
regionStart = comment.end;
|
|
1464
|
+
continue;
|
|
1465
|
+
}
|
|
1466
|
+
const enabled = disabledRules(comment.value, "eslint-enable");
|
|
1467
|
+
if (regionStart !== void 0 && enabled !== void 0) {
|
|
1468
|
+
if (enabled.length === 0 || namesOrderRule(enabled)) {
|
|
1469
|
+
regions.push([regionStart, comment.start]);
|
|
1470
|
+
regionStart = void 0;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
if (regionStart !== void 0) regions.push([regionStart, text.length]);
|
|
1475
|
+
const imports = [];
|
|
1476
|
+
for (const node of program.body) {
|
|
1477
|
+
if (node.type !== "ImportDeclaration") continue;
|
|
1478
|
+
const line = lineOf(starts, node.start);
|
|
1479
|
+
const source = node.source.value;
|
|
1480
|
+
const typeOnly = node.importKind === "type";
|
|
1481
|
+
const signalled = !typeOnly && (LOAD_ORDER_MODULES.some((pattern) => pattern.test(source)) || nextLine.has(line) || sameLine.has(line) || regions.some(([from, to]) => node.start >= from && node.start < to));
|
|
1482
|
+
imports.push({ line, source, signalled });
|
|
1483
|
+
}
|
|
1484
|
+
return imports;
|
|
1485
|
+
}
|
|
1486
|
+
function wouldMove(before, after) {
|
|
1487
|
+
const order = (list) => list.map((entry) => entry.source);
|
|
1488
|
+
const was = order(before);
|
|
1489
|
+
const now = order(after);
|
|
1490
|
+
return before.some((entry, at) => {
|
|
1491
|
+
if (!entry.signalled) return false;
|
|
1492
|
+
const earlier = new Set(was.slice(0, at));
|
|
1493
|
+
const index = now.indexOf(entry.source);
|
|
1494
|
+
const nowEarlier = new Set(now.slice(0, index));
|
|
1495
|
+
return earlier.size !== nowEarlier.size || [...earlier].some((name) => !nowEarlier.has(name));
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
function pin(text, imports) {
|
|
1499
|
+
const lines = text.split("\n");
|
|
1500
|
+
const insertAt = [];
|
|
1501
|
+
for (const { line, signalled } of imports) {
|
|
1502
|
+
if (!signalled) continue;
|
|
1503
|
+
let top = line;
|
|
1504
|
+
while (top > 0 && /^\s*\/\//.test(lines[top - 1] ?? "")) top -= 1;
|
|
1505
|
+
if (lines.slice(top, line).some((above) => above.trim() === PIN_COMMENT)) continue;
|
|
1506
|
+
insertAt.push(top);
|
|
1507
|
+
}
|
|
1508
|
+
for (const at of [...new Set(insertAt)].sort((a, b) => b - a)) {
|
|
1509
|
+
const indent = /^\s*/.exec(lines[at] ?? "")?.[0] ?? "";
|
|
1510
|
+
lines.splice(at, 0, `${indent}${PIN_COMMENT}`);
|
|
1511
|
+
}
|
|
1512
|
+
return lines.join("\n");
|
|
1513
|
+
}
|
|
1514
|
+
function formatOptions(config) {
|
|
1515
|
+
const {
|
|
1516
|
+
$sentinel: _provenance,
|
|
1517
|
+
ignorePatterns: _ignored,
|
|
1518
|
+
overrides: _overrides,
|
|
1519
|
+
...options
|
|
1520
|
+
} = config;
|
|
1521
|
+
return options;
|
|
1522
|
+
}
|
|
1523
|
+
async function pinDeliberateImportOrder(cwd, options) {
|
|
1524
|
+
if (!options.sortImports) return [];
|
|
1525
|
+
const pinned = [];
|
|
1526
|
+
for (const file of listSourceFiles(cwd, CODE_EXTENSIONS).sort()) {
|
|
1527
|
+
const path2 = join8(cwd, file);
|
|
1528
|
+
const text = readFileSync6(path2, "utf8");
|
|
1529
|
+
if (!text.includes("eslint-disable") && !MODULE_NAMES.some((name) => text.includes(name)))
|
|
1530
|
+
continue;
|
|
1531
|
+
const before = readImports(file, text);
|
|
1532
|
+
if (!before.some((entry) => entry.signalled)) continue;
|
|
1533
|
+
const sorted = await format(file, text, options);
|
|
1534
|
+
if (sorted.errors.length > 0) continue;
|
|
1535
|
+
if (!wouldMove(before, readImports(file, sorted.code))) continue;
|
|
1536
|
+
writeFileSync2(path2, pin(text, before));
|
|
1537
|
+
pinned.push(file);
|
|
1538
|
+
}
|
|
1539
|
+
return pinned;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1364
1542
|
// src/roles/format/format-script.ts
|
|
1365
1543
|
var SENTINEL_FORMAT_COMMAND = "sentinel --run --format";
|
|
1366
1544
|
function isPrettierSegment(segment) {
|
|
@@ -1389,8 +1567,8 @@ function writesWhenRewritten(name, command) {
|
|
|
1389
1567
|
var CHECK_SCRIPT_NAMES = /* @__PURE__ */ new Set(["lint", "format", "check", "test", "typecheck", "verify"]);
|
|
1390
1568
|
|
|
1391
1569
|
// src/roles/format/inherited-ignores.ts
|
|
1392
|
-
import { existsSync as existsSync6, readFileSync as
|
|
1393
|
-
import { join as
|
|
1570
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "node:fs";
|
|
1571
|
+
import { join as join9 } from "node:path";
|
|
1394
1572
|
var ROOT_IGNORE_FILE = ".prettierignore";
|
|
1395
1573
|
function isPattern(line) {
|
|
1396
1574
|
const trimmed = line.trim();
|
|
@@ -1406,11 +1584,11 @@ function toModulePattern(pattern) {
|
|
|
1406
1584
|
}
|
|
1407
1585
|
function inheritedIgnorePatterns(workspaceRoot) {
|
|
1408
1586
|
if (!workspaceRoot) return [];
|
|
1409
|
-
const path2 =
|
|
1587
|
+
const path2 = join9(workspaceRoot, ROOT_IGNORE_FILE);
|
|
1410
1588
|
if (!existsSync6(path2)) return [];
|
|
1411
1589
|
let contents;
|
|
1412
1590
|
try {
|
|
1413
|
-
contents =
|
|
1591
|
+
contents = readFileSync7(path2, "utf8");
|
|
1414
1592
|
} catch {
|
|
1415
1593
|
return [];
|
|
1416
1594
|
}
|
|
@@ -1455,8 +1633,8 @@ function needsSvelteCompiler(output) {
|
|
|
1455
1633
|
}
|
|
1456
1634
|
|
|
1457
1635
|
// src/roles/format/prettier-config.ts
|
|
1458
|
-
import { existsSync as existsSync7, readFileSync as
|
|
1459
|
-
import { join as
|
|
1636
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "node:fs";
|
|
1637
|
+
import { join as join10 } from "node:path";
|
|
1460
1638
|
var UNSUPPORTED = /* @__PURE__ */ new Set(["plugins", "parser", "filepath", "rangeStart", "rangeEnd"]);
|
|
1461
1639
|
function toOxfmtOverrides(value) {
|
|
1462
1640
|
if (!Array.isArray(value)) return { overrides: [], unresolved: [] };
|
|
@@ -1481,14 +1659,14 @@ function toOxfmtOverrides(value) {
|
|
|
1481
1659
|
return { overrides, unresolved };
|
|
1482
1660
|
}
|
|
1483
1661
|
function readPrettierSettings(cwd) {
|
|
1484
|
-
const file = PRETTIER_CONFIG_FILES.find((name) => existsSync7(
|
|
1662
|
+
const file = PRETTIER_CONFIG_FILES.find((name) => existsSync7(join10(cwd, name)));
|
|
1485
1663
|
if (!file) return { options: {}, unresolved: [] };
|
|
1486
1664
|
if (/\.(js|cjs|mjs)$/.test(file)) {
|
|
1487
1665
|
return { options: {}, file, unresolved: [`${file} is JavaScript, so it was not executed`] };
|
|
1488
1666
|
}
|
|
1489
1667
|
let parsed;
|
|
1490
1668
|
try {
|
|
1491
|
-
parsed = parseJsonc(
|
|
1669
|
+
parsed = parseJsonc(readFileSync8(join10(cwd, file), "utf8"), file);
|
|
1492
1670
|
} catch {
|
|
1493
1671
|
return { options: {}, file, unresolved: [`${file} could not be parsed`] };
|
|
1494
1672
|
}
|
|
@@ -1570,6 +1748,11 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1570
1748
|
const existing = readFormatAdoption(context2.cwd);
|
|
1571
1749
|
const prettier = readPrettierSettings(context2.cwd);
|
|
1572
1750
|
const local = existing.adopted ? { ...existing.local } : { ...prettier.options };
|
|
1751
|
+
const recorded = existing.preset === null ? void 0 : formatPresetFor(existing.preset);
|
|
1752
|
+
const ownedWhenSet = existing.adopted ? OWNED_WHEN_SET_KEYS.filter(
|
|
1753
|
+
(key) => !(key in local) && key in existing.effective && !isDeepStrictEqual(existing.effective[key], preset[key]) && !isDeepStrictEqual(existing.effective[key], recorded?.[key])
|
|
1754
|
+
) : [];
|
|
1755
|
+
for (const key of ownedWhenSet) local[key] = existing.effective[key];
|
|
1573
1756
|
for (const [key, value] of Object.entries(local)) {
|
|
1574
1757
|
if (ALWAYS_LOCAL_KEYS.includes(key)) continue;
|
|
1575
1758
|
if (key in preset && JSON.stringify(preset[key]) === JSON.stringify(value)) delete local[key];
|
|
@@ -1581,17 +1764,19 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1581
1764
|
.../* @__PURE__ */ new Set([...declaredIgnores, ...inherited, ...FORMAT_DEFAULT_IGNORES])
|
|
1582
1765
|
].sort();
|
|
1583
1766
|
if (ignorePatterns.length > 0) local.ignorePatterns = ignorePatterns;
|
|
1584
|
-
const
|
|
1585
|
-
|
|
1586
|
-
const config = {
|
|
1587
|
-
[PROVENANCE_KEY]: { preset: presetName, version, local: localKeys },
|
|
1767
|
+
const configStamped = (version2) => ({
|
|
1768
|
+
[PROVENANCE_KEY]: { preset: presetName, version: version2, local: localKeys },
|
|
1588
1769
|
...preset,
|
|
1589
1770
|
...Object.fromEntries(
|
|
1590
1771
|
[...localKeys, ...ALWAYS_LOCAL_KEYS].flatMap(
|
|
1591
1772
|
(key) => key in local ? [[key, local[key]]] : []
|
|
1592
1773
|
)
|
|
1593
1774
|
)
|
|
1594
|
-
};
|
|
1775
|
+
});
|
|
1776
|
+
const previous = existing.adopted ? existing.presetVersion : null;
|
|
1777
|
+
const unchanged = previous !== null && isDeepStrictEqual(configStamped(previous), existing.effective);
|
|
1778
|
+
const version = unchanged ? previous : own.version;
|
|
1779
|
+
const config = configStamped(version);
|
|
1595
1780
|
const operations = [
|
|
1596
1781
|
// Written pre-formatted (2-space, trailing newline): oxfmt formats JSON too, and until
|
|
1597
1782
|
// the format pass runs this file is the one config nothing has formatted yet.
|
|
@@ -1599,7 +1784,7 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1599
1784
|
manifestOperation(context2.cwd, this.formatScripts(context2.cwd))
|
|
1600
1785
|
];
|
|
1601
1786
|
const prettierConfigs = PRETTIER_CONFIG_FILES.filter(
|
|
1602
|
-
(name) => existsSync8(
|
|
1787
|
+
(name) => existsSync8(join11(context2.cwd, name))
|
|
1603
1788
|
);
|
|
1604
1789
|
for (const name of prettierConfigs) operations.push({ kind: "delete", path: name });
|
|
1605
1790
|
const removableDeps = this.modulePrettierDependencies(context2.cwd);
|
|
@@ -1608,8 +1793,13 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1608
1793
|
}
|
|
1609
1794
|
operations.push(...nxTargetOperations({ cwd: context2.cwd, targets: formatTargets() }));
|
|
1610
1795
|
const notes = [
|
|
1611
|
-
`materialized the ${presetName} format preset into ${FORMAT_CONFIG_FILE} (${own.name}@${own.version}). oxfmt has no \`extends\`, so the values live here; \`sentinel --status --format\` reports modules left behind by a preset change`
|
|
1796
|
+
`materialized the ${presetName} format preset into ${FORMAT_CONFIG_FILE} (${own.name}@${version}${unchanged && version !== own.version ? ", unchanged since" : ""}). oxfmt has no \`extends\`, so the values live here; \`sentinel --status --format\` reports modules left behind by a preset change`
|
|
1612
1797
|
];
|
|
1798
|
+
if (ownedWhenSet.length > 0) {
|
|
1799
|
+
notes.push(
|
|
1800
|
+
`kept this module's own ${ownedWhenSet.join(", ")} and declared it in \`${PROVENANCE_KEY}.local\`: the module set it, so a re-init does not take it back`
|
|
1801
|
+
);
|
|
1802
|
+
}
|
|
1613
1803
|
if (localKeys.length > 0) {
|
|
1614
1804
|
notes.push(
|
|
1615
1805
|
`kept this module's own formatting (${localKeys.join(", ")})${existing.adopted ? "" : ` from ${prettier.file}`}: the preset is the repo's, these are yours`
|
|
@@ -1655,9 +1845,41 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1655
1845
|
* walks targets in order), so what the linter rewrote gets formatted rather than the
|
|
1656
1846
|
* other way round.
|
|
1657
1847
|
*/
|
|
1848
|
+
/**
|
|
1849
|
+
* The same `oxfmt --write .`, in the copy `--init --dry-run` hands it, and silent. See
|
|
1850
|
+
* `Adapter.rehearseAfterInit`. It runs after the lint rehearsal for the reason `afterInit` runs
|
|
1851
|
+
* after the lint fix, so what the linter rewrote is what gets formatted.
|
|
1852
|
+
*
|
|
1853
|
+
* The copy sits where the module sits in a stand-in workspace, and that matters to this tool in
|
|
1854
|
+
* particular: oxfmt reads the nearest `.editorconfig` upwards (verified: an `indent_style = tab`
|
|
1855
|
+
* two levels up reindented a file), and the root of this repo has one.
|
|
1856
|
+
*/
|
|
1857
|
+
async rehearseAfterInit(ctx, origin) {
|
|
1858
|
+
const oxfmt = resolveOxfmt(origin);
|
|
1859
|
+
if (!oxfmt) return "oxfmt is not installed in this module yet";
|
|
1860
|
+
await this.keepDeliberateImportOrder(ctx.cwd);
|
|
1861
|
+
const pass = spawnSync3(oxfmt, ["--write", "."], { cwd: ctx.cwd, encoding: "utf8" });
|
|
1862
|
+
if (pass.error) return pass.error.message;
|
|
1863
|
+
return pass.status === 0 ? void 0 : oxfmtSaid(`${pass.stdout ?? ""}${pass.stderr ?? ""}`);
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* Pin, before the formatter sorts, the imports whose order is deliberate. See
|
|
1867
|
+
* `deliberate-import-order.ts`. Returns the line to print, or undefined when nothing was pinned.
|
|
1868
|
+
*/
|
|
1869
|
+
async keepDeliberateImportOrder(cwd) {
|
|
1870
|
+
const pinned = await pinDeliberateImportOrder(
|
|
1871
|
+
cwd,
|
|
1872
|
+
formatOptions(readFormatAdoption(cwd).effective)
|
|
1873
|
+
);
|
|
1874
|
+
if (pinned.length === 0) return void 0;
|
|
1875
|
+
return `kept the import order of ${pinned.length} file(s) where it is deliberate (an eslint-disable naming an import-order rule, or an import that has to load first, such as dd-trace): ${pinned.join(", ")}. Each such import now carries \`${PIN_COMMENT}\`, so the import sort leaves it where it is`;
|
|
1876
|
+
}
|
|
1658
1877
|
async afterInit(ctx) {
|
|
1659
1878
|
const oxfmt = resolveOxfmt(ctx.cwd);
|
|
1660
1879
|
if (!oxfmt) return;
|
|
1880
|
+
const kept = await this.keepDeliberateImportOrder(ctx.cwd);
|
|
1881
|
+
if (kept !== void 0) process.stderr.write(` ${palette(process.stderr).warn(kept)}
|
|
1882
|
+
`);
|
|
1661
1883
|
const pass = spawnSync3(oxfmt, ["--write", "."], { cwd: ctx.cwd, encoding: "utf8" });
|
|
1662
1884
|
process.stderr.write(` ${palette(process.stderr).dim(FORMATTER_DIFFERENCES)}
|
|
1663
1885
|
`);
|
|
@@ -1839,7 +2061,7 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1839
2061
|
modulePrettierDependencies(cwd) {
|
|
1840
2062
|
const isPrettierPackage = (name) => name === "prettier" || name.startsWith("prettier-plugin-") || name.startsWith("@prettier/");
|
|
1841
2063
|
try {
|
|
1842
|
-
const manifest = JSON.parse(
|
|
2064
|
+
const manifest = JSON.parse(readFileSync9(join11(cwd, "package.json"), "utf8"));
|
|
1843
2065
|
return Object.keys(manifest.devDependencies ?? {}).filter(isPrettierPackage).map((name) => ["devDependencies", name]);
|
|
1844
2066
|
} catch {
|
|
1845
2067
|
return [];
|
|
@@ -1921,8 +2143,8 @@ function registerFormat() {
|
|
|
1921
2143
|
|
|
1922
2144
|
// src/roles/lint/adapters/oxlint/oxlint.adapter.ts
|
|
1923
2145
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
1924
|
-
import { existsSync as existsSync12, readFileSync as
|
|
1925
|
-
import { join as
|
|
2146
|
+
import { existsSync as existsSync12, readFileSync as readFileSync14, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2147
|
+
import { join as join17 } from "node:path";
|
|
1926
2148
|
|
|
1927
2149
|
// src/core/config/deferred-rules.ts
|
|
1928
2150
|
function deferredRuleNames(rules) {
|
|
@@ -1931,7 +2153,7 @@ function deferredRuleNames(rules) {
|
|
|
1931
2153
|
|
|
1932
2154
|
// src/core/fix-report.ts
|
|
1933
2155
|
import { statSync as statSync2 } from "node:fs";
|
|
1934
|
-
import { join as
|
|
2156
|
+
import { join as join12 } from "node:path";
|
|
1935
2157
|
var CONFIG_REFUSED = /failed to parse .*config|invalid config file/i;
|
|
1936
2158
|
function readFailure(output) {
|
|
1937
2159
|
const lines = output.split("\n").map((line2) => line2.trim());
|
|
@@ -1951,7 +2173,7 @@ function fingerprint(path2) {
|
|
|
1951
2173
|
function snapshotFiles(cwd, extensions) {
|
|
1952
2174
|
const snapshot = /* @__PURE__ */ new Map();
|
|
1953
2175
|
for (const file of listSourceFiles(cwd, extensions)) {
|
|
1954
|
-
const mark = fingerprint(
|
|
2176
|
+
const mark = fingerprint(join12(cwd, file));
|
|
1955
2177
|
if (mark !== void 0) snapshot.set(file, mark);
|
|
1956
2178
|
}
|
|
1957
2179
|
return snapshot;
|
|
@@ -1959,7 +2181,7 @@ function snapshotFiles(cwd, extensions) {
|
|
|
1959
2181
|
function changedSince(cwd, before, extensions) {
|
|
1960
2182
|
const changed = [];
|
|
1961
2183
|
for (const file of listSourceFiles(cwd, extensions)) {
|
|
1962
|
-
const now = fingerprint(
|
|
2184
|
+
const now = fingerprint(join12(cwd, file));
|
|
1963
2185
|
if (now === void 0) continue;
|
|
1964
2186
|
if (before.get(file) !== now) changed.push(file);
|
|
1965
2187
|
}
|
|
@@ -1967,6 +2189,9 @@ function changedSince(cwd, before, extensions) {
|
|
|
1967
2189
|
}
|
|
1968
2190
|
var NAMED_IN_REPORT = 5;
|
|
1969
2191
|
var NOT_INSTALLED_YET = /invalid config file[\s\S]*node_modules/i;
|
|
2192
|
+
function presetNotInstalledYet(failure) {
|
|
2193
|
+
return NOT_INSTALLED_YET.test(failure);
|
|
2194
|
+
}
|
|
1970
2195
|
function describeFixOutcome(outcome, toolLabel) {
|
|
1971
2196
|
if (outcome.failure !== void 0) {
|
|
1972
2197
|
if (NOT_INSTALLED_YET.test(outcome.failure)) {
|
|
@@ -4402,14 +4627,14 @@ function downgradedRulesFor(preset) {
|
|
|
4402
4627
|
}
|
|
4403
4628
|
|
|
4404
4629
|
// src/roles/lint/extra-layers.ts
|
|
4405
|
-
import { existsSync as existsSync9, readFileSync as
|
|
4406
|
-
import { isAbsolute, join as
|
|
4630
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10 } from "node:fs";
|
|
4631
|
+
import { isAbsolute, join as join13, resolve as resolve2 } from "node:path";
|
|
4407
4632
|
function ruleCount(cwd, specifier) {
|
|
4408
4633
|
const path2 = isAbsolute(specifier) ? specifier : resolve2(cwd, specifier);
|
|
4409
4634
|
if (!existsSync9(path2)) return void 0;
|
|
4410
4635
|
try {
|
|
4411
4636
|
const parsed = parseJsonc(
|
|
4412
|
-
|
|
4637
|
+
readFileSync10(path2, "utf8"),
|
|
4413
4638
|
specifier
|
|
4414
4639
|
);
|
|
4415
4640
|
return Object.keys(parsed.rules ?? {}).length;
|
|
@@ -4429,7 +4654,7 @@ function extraLayers(cwd, extendsList) {
|
|
|
4429
4654
|
function committedExtendsList(cwd, configFile) {
|
|
4430
4655
|
try {
|
|
4431
4656
|
const parsed = parseJsonc(
|
|
4432
|
-
|
|
4657
|
+
readFileSync10(join13(cwd, configFile), "utf8"),
|
|
4433
4658
|
configFile
|
|
4434
4659
|
);
|
|
4435
4660
|
return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
|
|
@@ -4488,13 +4713,13 @@ function errorCountsByConfigRule(stdout) {
|
|
|
4488
4713
|
}
|
|
4489
4714
|
|
|
4490
4715
|
// src/roles/lint/adapters/oxlint/plan.ts
|
|
4491
|
-
import { existsSync as existsSync11, readFileSync as
|
|
4492
|
-
import { join as
|
|
4716
|
+
import { existsSync as existsSync11, readFileSync as readFileSync13 } from "node:fs";
|
|
4717
|
+
import { join as join16 } from "node:path";
|
|
4493
4718
|
|
|
4494
4719
|
// src/roles/lint/eslint-ignores.ts
|
|
4495
|
-
import { existsSync as existsSync10, readFileSync as
|
|
4496
|
-
import { join as
|
|
4497
|
-
import { parseSync } from "oxc-parser";
|
|
4720
|
+
import { existsSync as existsSync10, readFileSync as readFileSync11 } from "node:fs";
|
|
4721
|
+
import { join as join14 } from "node:path";
|
|
4722
|
+
import { parseSync as parseSync2 } from "oxc-parser";
|
|
4498
4723
|
var OPAQUE_SOURCE = /\b(includeIgnoreFile)\s*\([^)]*\)/g;
|
|
4499
4724
|
function toOxlintPattern(pattern) {
|
|
4500
4725
|
return pattern.replace(/^\.\//, "");
|
|
@@ -4505,17 +4730,17 @@ function readRootEslintIgnores(root) {
|
|
|
4505
4730
|
return { patterns: patterns.filter((pattern) => pattern.startsWith("**/")), unresolved };
|
|
4506
4731
|
}
|
|
4507
4732
|
function readEslintIgnores(cwd) {
|
|
4508
|
-
const config = ESLINT_CONFIG_FILES.map((name) =>
|
|
4733
|
+
const config = ESLINT_CONFIG_FILES.map((name) => join14(cwd, name)).find((path2) => existsSync10(path2));
|
|
4509
4734
|
if (!config) return { patterns: [], unresolved: [] };
|
|
4510
4735
|
let source;
|
|
4511
4736
|
try {
|
|
4512
|
-
source =
|
|
4737
|
+
source = readFileSync11(config, "utf8");
|
|
4513
4738
|
} catch {
|
|
4514
4739
|
return { patterns: [], unresolved: [] };
|
|
4515
4740
|
}
|
|
4516
4741
|
const patterns = /* @__PURE__ */ new Set();
|
|
4517
4742
|
const unresolved = new Set([...source.matchAll(OPAQUE_SOURCE)].map((match) => match[0]));
|
|
4518
|
-
const { program, errors } =
|
|
4743
|
+
const { program, errors } = parseSync2(config, source, { sourceType: "module" });
|
|
4519
4744
|
if (errors.length > 0) {
|
|
4520
4745
|
return { patterns: [], unresolved: [`${config} could not be parsed`] };
|
|
4521
4746
|
}
|
|
@@ -4579,6 +4804,98 @@ function keepsOtherCommands3(script) {
|
|
|
4579
4804
|
return keepsOtherCommands(script, SENTINEL_LINT_COMMAND);
|
|
4580
4805
|
}
|
|
4581
4806
|
|
|
4807
|
+
// src/roles/lint/module-own-config.ts
|
|
4808
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4809
|
+
function pluginKey(entry) {
|
|
4810
|
+
if (typeof entry === "string") return entry;
|
|
4811
|
+
if (isRecord(entry) && typeof entry.name === "string") return entry.name;
|
|
4812
|
+
if (isRecord(entry) && typeof entry.specifier === "string") return entry.specifier;
|
|
4813
|
+
return void 0;
|
|
4814
|
+
}
|
|
4815
|
+
function governedRuleNames(preset) {
|
|
4816
|
+
return /* @__PURE__ */ new Set([
|
|
4817
|
+
...Object.keys(preset.rules),
|
|
4818
|
+
...(preset.overrides ?? []).flatMap((override) => Object.keys(override.rules))
|
|
4819
|
+
]);
|
|
4820
|
+
}
|
|
4821
|
+
function moduleOwnBlocks(committed, preset) {
|
|
4822
|
+
const blocks = {};
|
|
4823
|
+
const kept = [];
|
|
4824
|
+
const dropped = [];
|
|
4825
|
+
if (Array.isArray(committed.plugins)) {
|
|
4826
|
+
const own = committed.plugins.filter(
|
|
4827
|
+
(name) => typeof name === "string" && !preset.plugins.includes(name)
|
|
4828
|
+
);
|
|
4829
|
+
if (own.length > 0) {
|
|
4830
|
+
blocks.plugins = own;
|
|
4831
|
+
kept.push(
|
|
4832
|
+
`kept this module's own plugins (${own.join(", ")}): the preset does not enable them`
|
|
4833
|
+
);
|
|
4834
|
+
}
|
|
4835
|
+
}
|
|
4836
|
+
if (Array.isArray(committed.jsPlugins)) {
|
|
4837
|
+
const presetKeys = new Set(preset.jsPlugins.map((entry) => pluginKey(entry)));
|
|
4838
|
+
const own = [];
|
|
4839
|
+
for (const entry of committed.jsPlugins) {
|
|
4840
|
+
const key = pluginKey(entry);
|
|
4841
|
+
if (key !== void 0 && presetKeys.has(key)) dropped.push(`jsPlugins ${key}`);
|
|
4842
|
+
else own.push(entry);
|
|
4843
|
+
}
|
|
4844
|
+
if (own.length > 0) {
|
|
4845
|
+
blocks.jsPlugins = own;
|
|
4846
|
+
kept.push(
|
|
4847
|
+
`kept ${own.length} jsPlugin(s) this module added (${own.map((entry) => pluginKey(entry) ?? "?").join(", ")})`
|
|
4848
|
+
);
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
if (isRecord(committed.settings)) {
|
|
4852
|
+
const presetSettings = preset.settings ?? {};
|
|
4853
|
+
const own = {};
|
|
4854
|
+
for (const [key, value] of Object.entries(committed.settings)) {
|
|
4855
|
+
if (key in presetSettings) dropped.push(`settings.${key}`);
|
|
4856
|
+
else own[key] = value;
|
|
4857
|
+
}
|
|
4858
|
+
if (Object.keys(own).length > 0) {
|
|
4859
|
+
blocks.settings = own;
|
|
4860
|
+
kept.push(`kept this module's own settings (${Object.keys(own).join(", ")})`);
|
|
4861
|
+
}
|
|
4862
|
+
}
|
|
4863
|
+
if (Array.isArray(committed.overrides)) {
|
|
4864
|
+
const governed = governedRuleNames(preset);
|
|
4865
|
+
const byFiles = /* @__PURE__ */ new Map();
|
|
4866
|
+
for (const block of committed.overrides) {
|
|
4867
|
+
if (!isRecord(block) || !Array.isArray(block.files)) continue;
|
|
4868
|
+
const files = JSON.stringify([...block.files].map(String).sort());
|
|
4869
|
+
const rules = {};
|
|
4870
|
+
for (const [rule, value] of Object.entries(isRecord(block.rules) ? block.rules : {})) {
|
|
4871
|
+
if (governed.has(rule)) dropped.push(`overrides[${block.files.join(", ")}] ${rule}`);
|
|
4872
|
+
else rules[rule] = value;
|
|
4873
|
+
}
|
|
4874
|
+
const { rules: _rules, ...rest } = block;
|
|
4875
|
+
const merged = byFiles.get(files);
|
|
4876
|
+
if (merged === void 0) {
|
|
4877
|
+
byFiles.set(files, { ...rest, ...Object.keys(rules).length > 0 ? { rules } : {} });
|
|
4878
|
+
continue;
|
|
4879
|
+
}
|
|
4880
|
+
const { files: _sameFiles, ...others } = rest;
|
|
4881
|
+
const mergedRules = { ...isRecord(merged.rules) ? merged.rules : {}, ...rules };
|
|
4882
|
+
Object.assign(
|
|
4883
|
+
merged,
|
|
4884
|
+
others,
|
|
4885
|
+
Object.keys(mergedRules).length > 0 ? { rules: mergedRules } : {}
|
|
4886
|
+
);
|
|
4887
|
+
}
|
|
4888
|
+
const own = [...byFiles.values()].filter((block) => Object.keys(block).length > 1);
|
|
4889
|
+
if (own.length > 0) {
|
|
4890
|
+
blocks.overrides = own;
|
|
4891
|
+
kept.push(
|
|
4892
|
+
`kept ${own.length} override block(s) this module set (${own.map((block) => block.files.join(", ")).join("; ")})`
|
|
4893
|
+
);
|
|
4894
|
+
}
|
|
4895
|
+
}
|
|
4896
|
+
return { blocks, kept, dropped };
|
|
4897
|
+
}
|
|
4898
|
+
|
|
4582
4899
|
// src/roles/lint/presets.ts
|
|
4583
4900
|
var hasRuleData = (name) => GENERATED_LINT_PRESETS.includes(name);
|
|
4584
4901
|
function lintPresetFor(preset) {
|
|
@@ -4600,8 +4917,8 @@ function lintPresetFor(preset) {
|
|
|
4600
4917
|
}
|
|
4601
4918
|
|
|
4602
4919
|
// src/roles/lint/rename-suppressions.ts
|
|
4603
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
4604
|
-
import { join as
|
|
4920
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync12, statSync as statSync3 } from "node:fs";
|
|
4921
|
+
import { join as join15, relative as relative3 } from "node:path";
|
|
4605
4922
|
var SOURCE_EXTENSIONS = [
|
|
4606
4923
|
".ts",
|
|
4607
4924
|
".tsx",
|
|
@@ -4648,7 +4965,7 @@ function* sourceFiles(dir) {
|
|
|
4648
4965
|
return;
|
|
4649
4966
|
}
|
|
4650
4967
|
for (const entry of entries) {
|
|
4651
|
-
const full =
|
|
4968
|
+
const full = join15(dir, entry);
|
|
4652
4969
|
let isDirectory;
|
|
4653
4970
|
try {
|
|
4654
4971
|
isDirectory = statSync3(full).isDirectory();
|
|
@@ -4668,7 +4985,7 @@ function findSuppressionRenames(cwd, renames) {
|
|
|
4668
4985
|
for (const file of sourceFiles(cwd)) {
|
|
4669
4986
|
let content;
|
|
4670
4987
|
try {
|
|
4671
|
-
content =
|
|
4988
|
+
content = readFileSync12(file, "utf8");
|
|
4672
4989
|
} catch {
|
|
4673
4990
|
continue;
|
|
4674
4991
|
}
|
|
@@ -4788,8 +5105,18 @@ function plan2(context2) {
|
|
|
4788
5105
|
...rootIgnores.patterns
|
|
4789
5106
|
].filter((pattern) => !defaults2.has(bare(pattern)));
|
|
4790
5107
|
const carried = [.../* @__PURE__ */ new Set([...DEFAULT_IGNORE_PATTERNS, ...moduleOwn])];
|
|
5108
|
+
const committed = committedConfig(context2.cwd);
|
|
5109
|
+
const { kept: teamRules, governed: droppedRules } = moduleOwnRules(
|
|
5110
|
+
isRecord2(committed.rules) ? committed.rules : {},
|
|
5111
|
+
lintPresetFor(variant)
|
|
5112
|
+
);
|
|
5113
|
+
const teamBlocks = moduleOwnBlocks(committed, lintPresetFor(variant));
|
|
5114
|
+
const { overrides: teamOverrides, ...teamTopLevel } = teamBlocks.blocks;
|
|
4791
5115
|
const stub = {
|
|
4792
5116
|
extends: extendsWithPreset(committedExtends(context2.cwd), variant),
|
|
5117
|
+
...teamTopLevel,
|
|
5118
|
+
...Object.keys(teamRules).length > 0 ? { rules: teamRules } : {},
|
|
5119
|
+
...teamOverrides !== void 0 ? { overrides: teamOverrides } : {},
|
|
4793
5120
|
...carried.length > 0 ? { ignorePatterns: carried } : {}
|
|
4794
5121
|
};
|
|
4795
5122
|
const operations = [
|
|
@@ -4804,7 +5131,7 @@ function plan2(context2) {
|
|
|
4804
5131
|
keys: removableDeps
|
|
4805
5132
|
});
|
|
4806
5133
|
}
|
|
4807
|
-
const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) => existsSync11(
|
|
5134
|
+
const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) => existsSync11(join16(context2.cwd, name)));
|
|
4808
5135
|
for (const name of eslintConfigs) operations.push({ kind: "delete", path: name });
|
|
4809
5136
|
operations.push(
|
|
4810
5137
|
...nxTargetOperations({
|
|
@@ -4838,6 +5165,22 @@ function plan2(context2) {
|
|
|
4838
5165
|
if (eslintConfigs.length > 0) {
|
|
4839
5166
|
notes.push(`removed ${eslintConfigs.join(", ")} and pointed the nx lint target at oxlint`);
|
|
4840
5167
|
}
|
|
5168
|
+
if (Object.keys(teamRules).length > 0) {
|
|
5169
|
+
notes.push(
|
|
5170
|
+
`kept ${Object.keys(teamRules).length} rule(s) this module set itself (${Object.keys(teamRules).join(", ")}): the preset does not configure them`
|
|
5171
|
+
);
|
|
5172
|
+
}
|
|
5173
|
+
if (droppedRules.length > 0) {
|
|
5174
|
+
notes.push(
|
|
5175
|
+
`dropped ${droppedRules.length} rule(s) this module set over the preset (${droppedRules.join(", ")}): the preset governs them`
|
|
5176
|
+
);
|
|
5177
|
+
}
|
|
5178
|
+
notes.push(...teamBlocks.kept);
|
|
5179
|
+
if (teamBlocks.dropped.length > 0) {
|
|
5180
|
+
notes.push(
|
|
5181
|
+
`dropped ${teamBlocks.dropped.length} entr(ies) this module set over the preset (${teamBlocks.dropped.join(", ")}): the preset governs them`
|
|
5182
|
+
);
|
|
5183
|
+
}
|
|
4841
5184
|
if (rootIgnores.patterns.length > 0) {
|
|
4842
5185
|
notes.push(
|
|
4843
5186
|
`carried ${rootIgnores.patterns.length} ignore pattern(s) from the workspace eslint config (${rootIgnores.patterns.join(", ")}): this module is linted by that config today, so without them adoption would start linting what the repo has always excluded`
|
|
@@ -4867,7 +5210,7 @@ function plan2(context2) {
|
|
|
4867
5210
|
if (keepsOtherCommands3(script)) {
|
|
4868
5211
|
const stillPrettier = /(^|[\s/])prettier(\s|$)/.test(script);
|
|
4869
5212
|
notes.push(
|
|
4870
|
-
`kept the rest of the lint script (\`${script}\`): only the eslint part is sentinel's` + (stillPrettier ? "
|
|
5213
|
+
`kept the rest of the lint script (\`${script}\`): only the eslint part is sentinel's` + (stillPrettier ? "; the format role, adopted with it, moves the prettier part" : "")
|
|
4871
5214
|
);
|
|
4872
5215
|
}
|
|
4873
5216
|
notes.push(
|
|
@@ -4902,7 +5245,7 @@ function lintScripts(cwd) {
|
|
|
4902
5245
|
function committedExtends(cwd) {
|
|
4903
5246
|
try {
|
|
4904
5247
|
const parsed = parseJsonc(
|
|
4905
|
-
|
|
5248
|
+
readFileSync13(join16(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4906
5249
|
LINT_CONFIG_FILE
|
|
4907
5250
|
);
|
|
4908
5251
|
return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
|
|
@@ -4910,10 +5253,32 @@ function committedExtends(cwd) {
|
|
|
4910
5253
|
return [];
|
|
4911
5254
|
}
|
|
4912
5255
|
}
|
|
5256
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5257
|
+
function committedConfig(cwd) {
|
|
5258
|
+
try {
|
|
5259
|
+
const parsed = parseJsonc(
|
|
5260
|
+
readFileSync13(join16(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
5261
|
+
LINT_CONFIG_FILE
|
|
5262
|
+
);
|
|
5263
|
+
return isRecord2(parsed) ? parsed : {};
|
|
5264
|
+
} catch {
|
|
5265
|
+
return {};
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
function moduleOwnRules(committed, preset) {
|
|
5269
|
+
const governedNames = governedRuleNames(preset);
|
|
5270
|
+
const kept = {};
|
|
5271
|
+
const governed = [];
|
|
5272
|
+
for (const [rule, value] of Object.entries(committed)) {
|
|
5273
|
+
if (governedNames.has(rule)) governed.push(rule);
|
|
5274
|
+
else kept[rule] = value;
|
|
5275
|
+
}
|
|
5276
|
+
return { kept, governed };
|
|
5277
|
+
}
|
|
4913
5278
|
function committedIgnorePatterns(cwd) {
|
|
4914
5279
|
try {
|
|
4915
5280
|
const parsed = parseJsonc(
|
|
4916
|
-
|
|
5281
|
+
readFileSync13(join16(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4917
5282
|
LINT_CONFIG_FILE
|
|
4918
5283
|
);
|
|
4919
5284
|
return Array.isArray(parsed.ignorePatterns) ? parsed.ignorePatterns.filter((entry) => typeof entry === "string") : [];
|
|
@@ -4925,7 +5290,7 @@ function moduleEslintDependencies(cwd) {
|
|
|
4925
5290
|
const isEslintPackage = (name) => name === "eslint" || name === "@types/eslint" || name === "typescript-eslint" || name.startsWith("@typescript-eslint/") || name.startsWith("eslint-plugin-") || name.startsWith("eslint-config-") || name.startsWith("@eslint/");
|
|
4926
5291
|
let manifest;
|
|
4927
5292
|
try {
|
|
4928
|
-
manifest = JSON.parse(
|
|
5293
|
+
manifest = JSON.parse(readFileSync13(join16(cwd, "package.json"), "utf8"));
|
|
4929
5294
|
} catch {
|
|
4930
5295
|
return [];
|
|
4931
5296
|
}
|
|
@@ -5039,7 +5404,54 @@ ${result.stderr ?? ""}`);
|
|
|
5039
5404
|
const env = { ...process.env, PATH: oxlintPath(ctx.cwd, process.env) };
|
|
5040
5405
|
process.stderr.write(` ${describeFixOutcome(this.fixPass(ctx, oxlint, env), "lint")}
|
|
5041
5406
|
`);
|
|
5042
|
-
this.writeModuleBaseline(ctx, oxlint, env);
|
|
5407
|
+
if (this.runLeadingCommands(ctx, env)) this.writeModuleBaseline(ctx, oxlint, env);
|
|
5408
|
+
}
|
|
5409
|
+
/**
|
|
5410
|
+
* Run what the module's `lint` script runs before sentinel, so the baseline measures the module
|
|
5411
|
+
* `pnpm run lint` lints. See `leadingCommands`: on hr-management that is `svelte-kit sync`, and
|
|
5412
|
+
* without it the measurement met a tsconfig extending a file that did not exist yet and held
|
|
5413
|
+
* `typescript/tsconfig-error`, a hold a fresh clone would commit.
|
|
5414
|
+
*
|
|
5415
|
+
* Shelled the way a package manager runs a script, with every `node_modules/.bin` up the tree
|
|
5416
|
+
* on PATH. False when the command failed: a baseline measured without it is the wrong module,
|
|
5417
|
+
* and recording that is worse than recording nothing, so the existing baseline is left alone
|
|
5418
|
+
* and the developer is told what to run.
|
|
5419
|
+
*/
|
|
5420
|
+
runLeadingCommands(ctx, env) {
|
|
5421
|
+
const leading = leadingCommands(moduleScripts(ctx.cwd)[LINT_SCRIPT_NAME]);
|
|
5422
|
+
if (leading === void 0) return true;
|
|
5423
|
+
const result = spawnSync4(leading, {
|
|
5424
|
+
cwd: ctx.cwd,
|
|
5425
|
+
shell: true,
|
|
5426
|
+
encoding: "utf8",
|
|
5427
|
+
env: { ...env, PATH: scriptPath(ctx.cwd, env.PATH) },
|
|
5428
|
+
maxBuffer: CAPTURE_MAX_BUFFER
|
|
5429
|
+
});
|
|
5430
|
+
if (!result.error && result.status === 0) {
|
|
5431
|
+
process.stderr.write(
|
|
5432
|
+
` ran \`${leading}\` before measuring, as the lint script runs it before sentinel
|
|
5433
|
+
`
|
|
5434
|
+
);
|
|
5435
|
+
return true;
|
|
5436
|
+
}
|
|
5437
|
+
const said = (result.error?.message ?? `${result.stderr ?? ""}${result.stdout ?? ""}`).trim().split("\n").slice(-1)[0];
|
|
5438
|
+
process.stderr.write(
|
|
5439
|
+
` ${palette(process.stderr).warn(
|
|
5440
|
+
`the lint script runs \`${leading}\` before sentinel, and it ${result.error ? "could not start" : `exited ${result.status ?? 1}`} here${said ? ` (${said})` : ""}, so this module's existing violations were not measured and ${LINT_BASELINE_FILE} was left as it was. Fix that, then run \`sentinel --init --lint\` again.`
|
|
5441
|
+
)}
|
|
5442
|
+
`
|
|
5443
|
+
);
|
|
5444
|
+
return false;
|
|
5445
|
+
}
|
|
5446
|
+
/**
|
|
5447
|
+
* The fix pass alone, in the copy `--init --dry-run` hands it, without the baseline measurement
|
|
5448
|
+
* that follows it for real. See `Adapter.rehearseAfterInit`.
|
|
5449
|
+
*/
|
|
5450
|
+
async rehearseAfterInit(ctx, origin) {
|
|
5451
|
+
const oxlint = resolveOxlint(origin);
|
|
5452
|
+
if (!oxlint) return "oxlint is not installed in this module yet";
|
|
5453
|
+
const env = { ...process.env, PATH: oxlintPath(origin, process.env) };
|
|
5454
|
+
return this.fixPass(ctx, oxlint, env).failure;
|
|
5043
5455
|
}
|
|
5044
5456
|
/**
|
|
5045
5457
|
* Measure what THIS module already violates, and hold exactly those rules at `warn`.
|
|
@@ -5059,23 +5471,23 @@ ${result.stderr ?? ""}`);
|
|
|
5059
5471
|
* build the developer can see, rather than a silent half-adoption they cannot.
|
|
5060
5472
|
*/
|
|
5061
5473
|
writeModuleBaseline(ctx, oxlint, env) {
|
|
5062
|
-
const configPath =
|
|
5063
|
-
const baselinePath =
|
|
5474
|
+
const configPath = join17(ctx.cwd, LINT_CONFIG_FILE);
|
|
5475
|
+
const baselinePath = join17(ctx.cwd, LINT_BASELINE_FILE);
|
|
5064
5476
|
if (!existsSync12(configPath)) return;
|
|
5065
5477
|
let config;
|
|
5066
5478
|
try {
|
|
5067
5479
|
config = parseJsonc(
|
|
5068
|
-
|
|
5480
|
+
readFileSync14(configPath, "utf8"),
|
|
5069
5481
|
LINT_CONFIG_FILE
|
|
5070
5482
|
);
|
|
5071
5483
|
} catch {
|
|
5072
5484
|
return;
|
|
5073
5485
|
}
|
|
5074
5486
|
const current = Array.isArray(config.extends) ? config.extends : [];
|
|
5075
|
-
const measurePath =
|
|
5487
|
+
const measurePath = join17(ctx.cwd, LINT_MEASURE_FILE);
|
|
5076
5488
|
let measured;
|
|
5077
5489
|
try {
|
|
5078
|
-
|
|
5490
|
+
writeFileSync3(
|
|
5079
5491
|
measurePath,
|
|
5080
5492
|
`${JSON.stringify({ ...config, extends: extendsWithBaseline(current, false) }, null, 2)}
|
|
5081
5493
|
`
|
|
@@ -5107,7 +5519,7 @@ ${result.stderr ?? ""}`);
|
|
|
5107
5519
|
const holds = holdsFrom(errorCountsByConfigRule(measured.stdout ?? ""));
|
|
5108
5520
|
const next = extendsWithBaseline(current, holds.length > 0);
|
|
5109
5521
|
if (holds.length > 0) {
|
|
5110
|
-
|
|
5522
|
+
writeFileSync3(baselinePath, renderBaseline(holds, readOwnPackage().version));
|
|
5111
5523
|
process.stderr.write(` ${palette(process.stderr).warn(describeHolds(holds))}
|
|
5112
5524
|
`);
|
|
5113
5525
|
} else {
|
|
@@ -5115,12 +5527,12 @@ ${result.stderr ?? ""}`);
|
|
|
5115
5527
|
}
|
|
5116
5528
|
if (next.length !== current.length || next.some((entry, at) => entry !== current[at])) {
|
|
5117
5529
|
config.extends = next;
|
|
5118
|
-
|
|
5530
|
+
writeFileSync3(configPath, `${JSON.stringify(config, null, 2)}
|
|
5119
5531
|
`);
|
|
5120
5532
|
}
|
|
5121
5533
|
}
|
|
5122
5534
|
async run(ctx) {
|
|
5123
|
-
if (!existsSync12(
|
|
5535
|
+
if (!existsSync12(join17(ctx.cwd, LINT_CONFIG_FILE))) {
|
|
5124
5536
|
process.stderr.write(
|
|
5125
5537
|
`sentinel lint(oxlint): no ${LINT_CONFIG_FILE} in this module; run \`sentinel --init --lint\` to adopt.
|
|
5126
5538
|
`
|
|
@@ -5268,7 +5680,7 @@ ${result.stderr ?? ""}`);
|
|
|
5268
5680
|
let stub;
|
|
5269
5681
|
try {
|
|
5270
5682
|
stub = parseJsonc(
|
|
5271
|
-
|
|
5683
|
+
readFileSync14(join17(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
5272
5684
|
LINT_CONFIG_FILE
|
|
5273
5685
|
);
|
|
5274
5686
|
} catch {
|
|
@@ -5278,7 +5690,7 @@ ${result.stderr ?? ""}`);
|
|
|
5278
5690
|
for (const entry of stub.extends ?? []) {
|
|
5279
5691
|
try {
|
|
5280
5692
|
const preset = parseJsonc(
|
|
5281
|
-
|
|
5693
|
+
readFileSync14(join17(cwd, entry), "utf8"),
|
|
5282
5694
|
entry
|
|
5283
5695
|
);
|
|
5284
5696
|
for (const rule of Object.keys(preset.rules ?? {})) names.add(rule);
|
|
@@ -5348,14 +5760,14 @@ ${result.stderr ?? ""}`);
|
|
|
5348
5760
|
let parsed;
|
|
5349
5761
|
try {
|
|
5350
5762
|
parsed = parseJsonc(
|
|
5351
|
-
|
|
5763
|
+
readFileSync14(join17(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
5352
5764
|
LINT_CONFIG_FILE
|
|
5353
5765
|
);
|
|
5354
5766
|
} catch {
|
|
5355
5767
|
return void 0;
|
|
5356
5768
|
}
|
|
5357
5769
|
const targets2 = Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : typeof parsed.extends === "string" ? [parsed.extends] : [];
|
|
5358
|
-
return targets2.find((target) => !existsSync12(
|
|
5770
|
+
return targets2.find((target) => !existsSync12(join17(cwd, target)));
|
|
5359
5771
|
}
|
|
5360
5772
|
/** Announce what is not enforced, so reduced coverage is never silent. */
|
|
5361
5773
|
announceDisabled(preset) {
|
|
@@ -5387,8 +5799,8 @@ function registerLint() {
|
|
|
5387
5799
|
|
|
5388
5800
|
// src/roles/static-analysis/adapters/jscpd/jscpd.adapter.ts
|
|
5389
5801
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
5390
|
-
import { existsSync as existsSync18, mkdirSync as mkdirSync2, readFileSync as
|
|
5391
|
-
import { join as
|
|
5802
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync2, readFileSync as readFileSync20, unlinkSync, writeFileSync as writeFileSync7 } from "node:fs";
|
|
5803
|
+
import { join as join27 } from "node:path";
|
|
5392
5804
|
import { globSync as globSync5 } from "tinyglobby";
|
|
5393
5805
|
|
|
5394
5806
|
// src/roles/static-analysis/bundle/budgets.ts
|
|
@@ -5417,21 +5829,21 @@ function bandFor(valueKb, referenceKb) {
|
|
|
5417
5829
|
}
|
|
5418
5830
|
|
|
5419
5831
|
// src/roles/static-analysis/bundle/scan.ts
|
|
5420
|
-
import { existsSync as existsSync14, readFileSync as
|
|
5421
|
-
import { basename, join as
|
|
5832
|
+
import { existsSync as existsSync14, readFileSync as readFileSync16 } from "node:fs";
|
|
5833
|
+
import { basename, join as join19 } from "node:path";
|
|
5422
5834
|
import { pathToFileURL } from "node:url";
|
|
5423
5835
|
import { gzipSync } from "node:zlib";
|
|
5424
5836
|
import { globSync as globSync2 } from "tinyglobby";
|
|
5425
5837
|
|
|
5426
5838
|
// src/roles/static-analysis/bundle/find-build.ts
|
|
5427
|
-
import { existsSync as existsSync13, readFileSync as
|
|
5428
|
-
import { join as
|
|
5839
|
+
import { existsSync as existsSync13, readFileSync as readFileSync15, statSync as statSync4 } from "node:fs";
|
|
5840
|
+
import { join as join18 } from "node:path";
|
|
5429
5841
|
import { globSync } from "tinyglobby";
|
|
5430
5842
|
var MANIFEST_GLOB = "server/_tanstack-start-manifest*.mjs";
|
|
5431
5843
|
function candidateDirs(cwd) {
|
|
5432
5844
|
const declared = readProjectPackageJson(cwd).nx?.targets?.build?.["outputs"];
|
|
5433
|
-
const fromDeclaration = Array.isArray(declared) ? declared.filter((token) => typeof token === "string").map((token) =>
|
|
5434
|
-
return [...fromDeclaration,
|
|
5845
|
+
const fromDeclaration = Array.isArray(declared) ? declared.filter((token) => typeof token === "string").map((token) => join18(cwd, token.replace(/^\{projectRoot\}\//, ""))) : [];
|
|
5846
|
+
return [...fromDeclaration, join18(cwd, ".output"), join18(cwd, "dist"), join18(cwd, "build")];
|
|
5435
5847
|
}
|
|
5436
5848
|
function findBuildOutput(cwd) {
|
|
5437
5849
|
const lookedIn = [];
|
|
@@ -5441,7 +5853,7 @@ function findBuildOutput(cwd) {
|
|
|
5441
5853
|
const found = globSync(MANIFEST_GLOB, { cwd: dir, absolute: true, dot: false });
|
|
5442
5854
|
if (found.length === 0) continue;
|
|
5443
5855
|
const manifestPath = found[0];
|
|
5444
|
-
const publicDir =
|
|
5856
|
+
const publicDir = join18(dir, "public");
|
|
5445
5857
|
if (!existsSync13(publicDir)) continue;
|
|
5446
5858
|
return { manifestPath, publicDir, ...dateOf(dir, manifestPath) };
|
|
5447
5859
|
}
|
|
@@ -5464,10 +5876,10 @@ function findBuildOutput(cwd) {
|
|
|
5464
5876
|
};
|
|
5465
5877
|
}
|
|
5466
5878
|
function dateOf(dir, manifestPath) {
|
|
5467
|
-
const record =
|
|
5879
|
+
const record = join18(dir, "nitro.json");
|
|
5468
5880
|
if (existsSync13(record)) {
|
|
5469
5881
|
try {
|
|
5470
|
-
const parsed = JSON.parse(
|
|
5882
|
+
const parsed = JSON.parse(readFileSync15(record, "utf8"));
|
|
5471
5883
|
if (typeof parsed.date === "string") {
|
|
5472
5884
|
const date = new Date(parsed.date);
|
|
5473
5885
|
if (!Number.isNaN(date.getTime())) return { builtAt: date, builtAtFrom: "build record" };
|
|
@@ -5493,9 +5905,9 @@ function kindOf(path2) {
|
|
|
5493
5905
|
return "other";
|
|
5494
5906
|
}
|
|
5495
5907
|
function resolveAnnounced(publicDir, announced) {
|
|
5496
|
-
const direct =
|
|
5908
|
+
const direct = join19(publicDir, announced);
|
|
5497
5909
|
if (existsSync14(direct)) return direct;
|
|
5498
|
-
const stripped =
|
|
5910
|
+
const stripped = join19(publicDir, announced.replace(/^\/[^/]+\//, "/"));
|
|
5499
5911
|
return existsSync14(stripped) ? stripped : void 0;
|
|
5500
5912
|
}
|
|
5501
5913
|
function emptyWeight() {
|
|
@@ -5532,7 +5944,7 @@ async function scanBundle(cwd) {
|
|
|
5532
5944
|
unresolved.push(announced);
|
|
5533
5945
|
measured.set(announced, void 0);
|
|
5534
5946
|
} else {
|
|
5535
|
-
const buffer =
|
|
5947
|
+
const buffer = readFileSync16(path2);
|
|
5536
5948
|
measured.set(announced, {
|
|
5537
5949
|
parsed: buffer.length,
|
|
5538
5950
|
transfer: gzipSync(buffer, { level: 9 }).length
|
|
@@ -5619,7 +6031,7 @@ function scanDynamicLayer(publicDir, references) {
|
|
|
5619
6031
|
})) {
|
|
5620
6032
|
const name = basename(path2);
|
|
5621
6033
|
if (referenced.has(name)) continue;
|
|
5622
|
-
const buffer =
|
|
6034
|
+
const buffer = readFileSync16(path2);
|
|
5623
6035
|
const size = { parsed: buffer.length, transfer: gzipSync(buffer, { level: 9 }).length };
|
|
5624
6036
|
add(all2, size);
|
|
5625
6037
|
const group = groupOf(name);
|
|
@@ -5647,23 +6059,23 @@ function scanDynamicLayer(publicDir, references) {
|
|
|
5647
6059
|
}
|
|
5648
6060
|
|
|
5649
6061
|
// src/roles/static-analysis/sources.ts
|
|
5650
|
-
import { readFileSync as
|
|
5651
|
-
import { join as
|
|
5652
|
-
import { parseSync as
|
|
6062
|
+
import { readFileSync as readFileSync17 } from "node:fs";
|
|
6063
|
+
import { join as join22 } from "node:path";
|
|
6064
|
+
import { parseSync as parseSync3 } from "oxc-parser";
|
|
5653
6065
|
import { globSync as globSync3 } from "tinyglobby";
|
|
5654
6066
|
|
|
5655
6067
|
// src/roles/static-analysis/adapters/jscpd/tool-config.ts
|
|
5656
|
-
import { existsSync as existsSync16, mkdtempSync, rmSync as rmSync3, writeFileSync as
|
|
6068
|
+
import { existsSync as existsSync16, mkdtempSync, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
5657
6069
|
import { tmpdir } from "node:os";
|
|
5658
|
-
import { join as
|
|
6070
|
+
import { join as join21 } from "node:path";
|
|
5659
6071
|
|
|
5660
6072
|
// src/roles/static-analysis/configs/resolve.ts
|
|
5661
6073
|
import { existsSync as existsSync15 } from "node:fs";
|
|
5662
|
-
import { join as
|
|
6074
|
+
import { join as join20 } from "node:path";
|
|
5663
6075
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
5664
6076
|
var STATIC_CONFIG_FILE = "static-analysis.config.mjs";
|
|
5665
6077
|
async function loadStaticConfig(cwd) {
|
|
5666
|
-
const path2 =
|
|
6078
|
+
const path2 = join20(cwd, STATIC_CONFIG_FILE);
|
|
5667
6079
|
if (!existsSync15(path2)) {
|
|
5668
6080
|
return { config: defineStaticAnalysis(), from: null, unreadable: null };
|
|
5669
6081
|
}
|
|
@@ -5710,7 +6122,7 @@ function exclusions(config, scope = "tests") {
|
|
|
5710
6122
|
return [.../* @__PURE__ */ new Set([...config.ignore, ...OWN_ARTEFACTS, ...tests])];
|
|
5711
6123
|
}
|
|
5712
6124
|
function measuredRoots(cwd) {
|
|
5713
|
-
return existsSync16(
|
|
6125
|
+
return existsSync16(join21(cwd, "src")) ? ["src"] : ["."];
|
|
5714
6126
|
}
|
|
5715
6127
|
function fileSettings(config, scope, extraEntries = []) {
|
|
5716
6128
|
return {
|
|
@@ -5740,9 +6152,9 @@ function fileSettings(config, scope, extraEntries = []) {
|
|
|
5740
6152
|
};
|
|
5741
6153
|
}
|
|
5742
6154
|
function duplicationRun(config, paths, scope) {
|
|
5743
|
-
const dir = mkdtempSync(
|
|
5744
|
-
const configPath =
|
|
5745
|
-
|
|
6155
|
+
const dir = mkdtempSync(join21(tmpdir(), "sentinel-static-"));
|
|
6156
|
+
const configPath = join21(dir, "jscpd.json");
|
|
6157
|
+
writeFileSync4(configPath, `${JSON.stringify(fileSettings(config, scope), null, 2)}
|
|
5746
6158
|
`, "utf8");
|
|
5747
6159
|
const args = [
|
|
5748
6160
|
"--config",
|
|
@@ -5767,9 +6179,9 @@ function duplicationRun(config, paths, scope) {
|
|
|
5767
6179
|
return { args, rawReportDir: dir, dispose: () => rmSync3(dir, { force: true, recursive: true }) };
|
|
5768
6180
|
}
|
|
5769
6181
|
function deadCodeRun(config, paths, extraEntries = []) {
|
|
5770
|
-
const dir = mkdtempSync(
|
|
5771
|
-
const configPath =
|
|
5772
|
-
|
|
6182
|
+
const dir = mkdtempSync(join21(tmpdir(), "sentinel-static-"));
|
|
6183
|
+
const configPath = join21(dir, "jscpd.json");
|
|
6184
|
+
writeFileSync4(
|
|
5773
6185
|
configPath,
|
|
5774
6186
|
`${JSON.stringify(fileSettings(config, "dead-code", extraEntries), null, 2)}
|
|
5775
6187
|
`,
|
|
@@ -5820,12 +6232,12 @@ function readParsed(cwd, files, passes) {
|
|
|
5820
6232
|
function parsedSource(cwd, path2) {
|
|
5821
6233
|
let text;
|
|
5822
6234
|
try {
|
|
5823
|
-
text =
|
|
6235
|
+
text = readFileSync17(join22(cwd, path2), "utf8");
|
|
5824
6236
|
} catch {
|
|
5825
6237
|
return void 0;
|
|
5826
6238
|
}
|
|
5827
6239
|
try {
|
|
5828
|
-
const parsed =
|
|
6240
|
+
const parsed = parseSync3(path2, text);
|
|
5829
6241
|
if (parsed.errors.length > 0) return void 0;
|
|
5830
6242
|
return { path: path2, text, program: parsed.program };
|
|
5831
6243
|
} catch {
|
|
@@ -5834,7 +6246,7 @@ function parsedSource(cwd, path2) {
|
|
|
5834
6246
|
}
|
|
5835
6247
|
|
|
5836
6248
|
// src/roles/static-analysis/complexity/cyclomatic.ts
|
|
5837
|
-
import { parseSync as
|
|
6249
|
+
import { parseSync as parseSync4 } from "oxc-parser";
|
|
5838
6250
|
var FUNCTION_NODES = /* @__PURE__ */ new Set([
|
|
5839
6251
|
"FunctionDeclaration",
|
|
5840
6252
|
"FunctionExpression",
|
|
@@ -6007,13 +6419,13 @@ function unreadableNote(coverage) {
|
|
|
6007
6419
|
}
|
|
6008
6420
|
|
|
6009
6421
|
// src/roles/static-analysis/excerpt.ts
|
|
6010
|
-
import { readFileSync as
|
|
6011
|
-
import { join as
|
|
6422
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
6423
|
+
import { join as join24 } from "node:path";
|
|
6012
6424
|
|
|
6013
6425
|
// src/roles/static-analysis/maintainability/size-rules.ts
|
|
6014
|
-
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as
|
|
6426
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6015
6427
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
6016
|
-
import { join as
|
|
6428
|
+
import { join as join23 } from "node:path";
|
|
6017
6429
|
var GAUGE_THRESHOLDS = {
|
|
6018
6430
|
// McCabe 1976 says 10, our p90 says 11. The only indicator where two independent candidates agree
|
|
6019
6431
|
// within one unit, and 94.8% of our functions already sit in the 1-10 band SEI calls low risk.
|
|
@@ -6042,7 +6454,7 @@ var GAUGE_THRESHOLDS = {
|
|
|
6042
6454
|
"cognitive-complexity": 7
|
|
6043
6455
|
};
|
|
6044
6456
|
var COGNITIVE = "cognitive-complexity";
|
|
6045
|
-
var cognitivePlugin = () =>
|
|
6457
|
+
var cognitivePlugin = () => join23(ownPackageDir(), "plugins", "sonar-cognitive.js");
|
|
6046
6458
|
var CATEGORIES = [
|
|
6047
6459
|
"correctness",
|
|
6048
6460
|
"suspicious",
|
|
@@ -6074,9 +6486,9 @@ function sizeRulesConfig() {
|
|
|
6074
6486
|
};
|
|
6075
6487
|
}
|
|
6076
6488
|
function sizeRulesRun(paths, ignore) {
|
|
6077
|
-
const dir = mkdtempSync2(
|
|
6078
|
-
const configPath =
|
|
6079
|
-
|
|
6489
|
+
const dir = mkdtempSync2(join23(tmpdir2(), "sentinel-size-"));
|
|
6490
|
+
const configPath = join23(dir, "oxlintrc.json");
|
|
6491
|
+
writeFileSync5(configPath, `${JSON.stringify(sizeRulesConfig(), null, 2)}
|
|
6080
6492
|
`, "utf8");
|
|
6081
6493
|
return {
|
|
6082
6494
|
// `--format json` because the console shape is rendered by this role from the figures: a
|
|
@@ -7533,7 +7945,7 @@ function linesOf(cache, cwd, relative5) {
|
|
|
7533
7945
|
if (cache.has(relative5)) return cache.get(relative5);
|
|
7534
7946
|
let lines;
|
|
7535
7947
|
try {
|
|
7536
|
-
lines =
|
|
7948
|
+
lines = readFileSync18(join24(cwd, relative5), "utf8").split("\n");
|
|
7537
7949
|
} catch {
|
|
7538
7950
|
lines = void 0;
|
|
7539
7951
|
}
|
|
@@ -7765,7 +8177,7 @@ var SCRIPT = `(function(){
|
|
|
7765
8177
|
})();`;
|
|
7766
8178
|
|
|
7767
8179
|
// src/roles/static-analysis/graph/imports.ts
|
|
7768
|
-
import { dirname, join as
|
|
8180
|
+
import { dirname, join as join25 } from "node:path";
|
|
7769
8181
|
var TS_BEHIND_JS = {
|
|
7770
8182
|
".js": [".ts", ".tsx"],
|
|
7771
8183
|
".mjs": [".mts"],
|
|
@@ -7818,7 +8230,7 @@ function specifiersOf(program) {
|
|
|
7818
8230
|
}
|
|
7819
8231
|
function resolveSpecifier(from, specifier, known) {
|
|
7820
8232
|
if (!specifier.startsWith(".")) return void 0;
|
|
7821
|
-
const base =
|
|
8233
|
+
const base = join25(dirname(from), specifier);
|
|
7822
8234
|
if (known.has(base)) return base;
|
|
7823
8235
|
for (const extension of RESOLVABLE_EXTENSIONS) {
|
|
7824
8236
|
if (known.has(base + extension)) return base + extension;
|
|
@@ -7829,7 +8241,7 @@ function resolveSpecifier(from, specifier, known) {
|
|
|
7829
8241
|
if (known.has(swapped)) return swapped;
|
|
7830
8242
|
}
|
|
7831
8243
|
for (const extension of RESOLVABLE_EXTENSIONS) {
|
|
7832
|
-
const index =
|
|
8244
|
+
const index = join25(base, `index${extension}`);
|
|
7833
8245
|
if (known.has(index)) return index;
|
|
7834
8246
|
}
|
|
7835
8247
|
return void 0;
|
|
@@ -7942,26 +8354,26 @@ function scanSyntax(cwd, config) {
|
|
|
7942
8354
|
|
|
7943
8355
|
// src/roles/static-analysis/workspace/consumers.ts
|
|
7944
8356
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
7945
|
-
import { existsSync as existsSync17, mkdirSync, readFileSync as
|
|
7946
|
-
import { dirname as dirname2, join as
|
|
7947
|
-
var CACHE_DIR =
|
|
8357
|
+
import { existsSync as existsSync17, mkdirSync, readFileSync as readFileSync19, statSync as statSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
8358
|
+
import { dirname as dirname2, join as join26, relative as relative4, resolve as resolve3, sep } from "node:path";
|
|
8359
|
+
var CACHE_DIR = join26("node_modules", ".cache", "sentinel");
|
|
7948
8360
|
var CACHE_FILE = "consumer-index.json";
|
|
7949
8361
|
var SHAPE = 4;
|
|
7950
8362
|
function workspaceRootFrom(cwd) {
|
|
7951
8363
|
let dir = resolve3(cwd);
|
|
7952
8364
|
for (; ; ) {
|
|
7953
|
-
if (existsSync17(
|
|
8365
|
+
if (existsSync17(join26(dir, WORKSPACE_ROOT_MARKER))) return dir;
|
|
7954
8366
|
const parent = dirname2(dir);
|
|
7955
8367
|
if (parent === dir) return void 0;
|
|
7956
8368
|
dir = parent;
|
|
7957
8369
|
}
|
|
7958
8370
|
}
|
|
7959
8371
|
function aliasTable(workspaceRoot) {
|
|
7960
|
-
const file =
|
|
8372
|
+
const file = join26(workspaceRoot, "tsconfig.base.json");
|
|
7961
8373
|
if (!existsSync17(file)) return /* @__PURE__ */ new Map();
|
|
7962
8374
|
let paths;
|
|
7963
8375
|
try {
|
|
7964
|
-
const parsed = JSON.parse(
|
|
8376
|
+
const parsed = JSON.parse(readFileSync19(file, "utf8"));
|
|
7965
8377
|
paths = parsed.compilerOptions?.paths ?? {};
|
|
7966
8378
|
} catch {
|
|
7967
8379
|
return /* @__PURE__ */ new Map();
|
|
@@ -7979,7 +8391,7 @@ function aliasTable(workspaceRoot) {
|
|
|
7979
8391
|
function resolveSpecifier2(specifier, table, workspaceRoot) {
|
|
7980
8392
|
const isFile = (candidate) => {
|
|
7981
8393
|
try {
|
|
7982
|
-
return statSync5(
|
|
8394
|
+
return statSync5(join26(workspaceRoot, candidate)).isFile();
|
|
7983
8395
|
} catch {
|
|
7984
8396
|
return false;
|
|
7985
8397
|
}
|
|
@@ -8016,11 +8428,11 @@ function resolveSpecifier2(specifier, table, workspaceRoot) {
|
|
|
8016
8428
|
return void 0;
|
|
8017
8429
|
}
|
|
8018
8430
|
function resolveRelative(importer, specifier, workspaceRoot) {
|
|
8019
|
-
const base =
|
|
8431
|
+
const base = join26(dirname2(importer), specifier).split(sep).join("/");
|
|
8020
8432
|
if (base.startsWith("..")) return void 0;
|
|
8021
8433
|
const isFile = (candidate) => {
|
|
8022
8434
|
try {
|
|
8023
|
-
return statSync5(
|
|
8435
|
+
return statSync5(join26(workspaceRoot, candidate)).isFile();
|
|
8024
8436
|
} catch {
|
|
8025
8437
|
return false;
|
|
8026
8438
|
}
|
|
@@ -8118,10 +8530,10 @@ function hash(text) {
|
|
|
8118
8530
|
}
|
|
8119
8531
|
function consumerIndex(workspaceRoot, moduleRoots) {
|
|
8120
8532
|
const key = cacheKey(workspaceRoot);
|
|
8121
|
-
const file =
|
|
8533
|
+
const file = join26(workspaceRoot, CACHE_DIR, CACHE_FILE);
|
|
8122
8534
|
if (key && existsSync17(file)) {
|
|
8123
8535
|
try {
|
|
8124
|
-
const cached = JSON.parse(
|
|
8536
|
+
const cached = JSON.parse(readFileSync19(file, "utf8"));
|
|
8125
8537
|
if (cached.key === key) {
|
|
8126
8538
|
return {
|
|
8127
8539
|
imported: new Set(cached.imported),
|
|
@@ -8135,14 +8547,14 @@ function consumerIndex(workspaceRoot, moduleRoots) {
|
|
|
8135
8547
|
const index = buildConsumerIndex(workspaceRoot, moduleRoots());
|
|
8136
8548
|
if (key) {
|
|
8137
8549
|
try {
|
|
8138
|
-
mkdirSync(
|
|
8550
|
+
mkdirSync(join26(workspaceRoot, CACHE_DIR), { recursive: true });
|
|
8139
8551
|
const payload = {
|
|
8140
8552
|
key,
|
|
8141
8553
|
imported: [...index.imported],
|
|
8142
8554
|
testOnly: [...index.testOnly],
|
|
8143
8555
|
builtFrom: index.builtFrom
|
|
8144
8556
|
};
|
|
8145
|
-
|
|
8557
|
+
writeFileSync6(file, JSON.stringify(payload), "utf8");
|
|
8146
8558
|
} catch {
|
|
8147
8559
|
}
|
|
8148
8560
|
}
|
|
@@ -8239,7 +8651,7 @@ var JscpdAdapter = class extends BaseAdapter {
|
|
|
8239
8651
|
operations: [
|
|
8240
8652
|
// `write` and not `merge-json`: this file is code, and the engine's in-place merge is for
|
|
8241
8653
|
// JSON. A re-init therefore RESTORES the stub, so an existing one is left alone below.
|
|
8242
|
-
...existsSync18(
|
|
8654
|
+
...existsSync18(join27(context2.cwd, STATIC_CONFIG_FILE)) ? [] : [{ kind: "write", path: STATIC_CONFIG_FILE, contents: configStub() }],
|
|
8243
8655
|
manifestOperation(context2.cwd, scripts()),
|
|
8244
8656
|
...nxTargetOperations({ cwd: context2.cwd, targets: targets() })
|
|
8245
8657
|
// The report directory is ignored ONCE at the workspace root, by `--init`'s workspace prep,
|
|
@@ -8489,7 +8901,7 @@ var JscpdAdapter = class extends BaseAdapter {
|
|
|
8489
8901
|
const declared = moduleScripts(ctx.cwd);
|
|
8490
8902
|
const missing = Object.keys(scripts()).filter((name) => !(name in declared));
|
|
8491
8903
|
const adopted = missing.length < Object.keys(scripts()).length;
|
|
8492
|
-
const hasConfig = existsSync18(
|
|
8904
|
+
const hasConfig = existsSync18(join27(ctx.cwd, STATIC_CONFIG_FILE));
|
|
8493
8905
|
return {
|
|
8494
8906
|
adopted,
|
|
8495
8907
|
preset: adopted ? ctx.preset : null,
|
|
@@ -8581,7 +8993,7 @@ function runDeadCode(jscpd, config, paths, cwd, extraEntries = []) {
|
|
|
8581
8993
|
try {
|
|
8582
8994
|
const ran = runTool(jscpd, args, cwd, "dead-code");
|
|
8583
8995
|
if (ran.kind !== "ran") return ran;
|
|
8584
|
-
const path2 =
|
|
8996
|
+
const path2 = join27(rawReportDir, RAW_REPORT["dead-code"]);
|
|
8585
8997
|
if (!existsSync18(path2)) {
|
|
8586
8998
|
process.stderr.write(
|
|
8587
8999
|
`sentinel static-analysis(jscpd): the dead-code pass wrote no ${RAW_REPORT["dead-code"]}
|
|
@@ -8589,7 +9001,7 @@ function runDeadCode(jscpd, config, paths, cwd, extraEntries = []) {
|
|
|
8589
9001
|
);
|
|
8590
9002
|
return { kind: "failed", code: 1 };
|
|
8591
9003
|
}
|
|
8592
|
-
const parsed = safeParseDeadCode(
|
|
9004
|
+
const parsed = safeParseDeadCode(readFileSync20(path2, "utf8"));
|
|
8593
9005
|
const statistics = parsed?.statistics;
|
|
8594
9006
|
if (!statistics) {
|
|
8595
9007
|
process.stderr.write(
|
|
@@ -8612,11 +9024,11 @@ function runDeadCode(jscpd, config, paths, cwd, extraEntries = []) {
|
|
|
8612
9024
|
}
|
|
8613
9025
|
}
|
|
8614
9026
|
function moduleRelative(name, roots, cwd) {
|
|
8615
|
-
if (existsSync18(
|
|
9027
|
+
if (existsSync18(join27(cwd, name))) return name;
|
|
8616
9028
|
for (const root of roots) {
|
|
8617
9029
|
if (root === ".") continue;
|
|
8618
9030
|
const candidate = `${root}/${name}`;
|
|
8619
|
-
if (existsSync18(
|
|
9031
|
+
if (existsSync18(join27(cwd, candidate))) return candidate;
|
|
8620
9032
|
}
|
|
8621
9033
|
return name;
|
|
8622
9034
|
}
|
|
@@ -8625,8 +9037,8 @@ function runDuplication(jscpd, config, paths, scope, cwd) {
|
|
|
8625
9037
|
try {
|
|
8626
9038
|
const ran = runTool(jscpd, args, cwd, scope);
|
|
8627
9039
|
if (ran.kind !== "ran") return ran;
|
|
8628
|
-
const path2 =
|
|
8629
|
-
const parsed = existsSync18(path2) ? safeParse(
|
|
9040
|
+
const path2 = join27(rawReportDir, RAW_REPORT.duplication);
|
|
9041
|
+
const parsed = existsSync18(path2) ? safeParse(readFileSync20(path2, "utf8")) : void 0;
|
|
8630
9042
|
const total = parsed?.statistics?.total;
|
|
8631
9043
|
if (!total) {
|
|
8632
9044
|
process.stderr.write(
|
|
@@ -8795,7 +9207,7 @@ function outsideOf(cwd) {
|
|
|
8795
9207
|
function moduleRootsOf(workspaceRoot) {
|
|
8796
9208
|
const roots = [];
|
|
8797
9209
|
for (const area of ["apps", "libs"]) {
|
|
8798
|
-
const base =
|
|
9210
|
+
const base = join27(workspaceRoot, area);
|
|
8799
9211
|
if (!existsSync18(base)) continue;
|
|
8800
9212
|
for (const entry of globSync5([`${area}/**/package.json`, `${area}/**/project.json`], {
|
|
8801
9213
|
cwd: workspaceRoot,
|
|
@@ -9138,7 +9550,7 @@ var PICTURE_FILE = `${TARGET}-graph.svg`;
|
|
|
9138
9550
|
function clearStaleOutputs(cwd, keeping) {
|
|
9139
9551
|
for (const name of [...Object.values(SHAPE_FILE), PICTURE_FILE]) {
|
|
9140
9552
|
if (keeping.has(name)) continue;
|
|
9141
|
-
const path2 =
|
|
9553
|
+
const path2 = join27(cwd, REPORT_DIR, name);
|
|
9142
9554
|
if (existsSync18(path2)) unlinkSync(path2);
|
|
9143
9555
|
}
|
|
9144
9556
|
}
|
|
@@ -9157,11 +9569,11 @@ function drawsPicture(graph, coverage) {
|
|
|
9157
9569
|
async function emit(report, shape, cwd, graph, coverage) {
|
|
9158
9570
|
if (shape === "console") return [];
|
|
9159
9571
|
const detailed = shape === "markdown" ? report : withExcerpts(report, cwd);
|
|
9160
|
-
mkdirSync2(
|
|
9572
|
+
mkdirSync2(join27(cwd, REPORT_DIR), { recursive: true });
|
|
9161
9573
|
const written = [];
|
|
9162
9574
|
if (shape === "html" && drawsPicture(graph, coverage)) {
|
|
9163
9575
|
const drawn = layOut(graph.graph, graph.communityOf);
|
|
9164
|
-
const path2 =
|
|
9576
|
+
const path2 = join27(cwd, REPORT_DIR, PICTURE_FILE);
|
|
9165
9577
|
await writeGraphPicture(drawn, path2);
|
|
9166
9578
|
written.push(`${REPORT_DIR}/${PICTURE_FILE}`);
|
|
9167
9579
|
detailed.picture = buildExplorer(drawn, graph.communityOf).markup;
|
|
@@ -9169,7 +9581,7 @@ async function emit(report, shape, cwd, graph, coverage) {
|
|
|
9169
9581
|
const RENDER = { json: renderJson, html: renderHtml, markdown: renderMarkdown };
|
|
9170
9582
|
const name = SHAPE_FILE[shape] ?? `${TARGET}.${shape}`;
|
|
9171
9583
|
const body = RENDER[shape](detailed);
|
|
9172
|
-
|
|
9584
|
+
writeFileSync7(join27(cwd, REPORT_DIR, name), body, "utf8");
|
|
9173
9585
|
written.unshift(`${REPORT_DIR}/${name}`);
|
|
9174
9586
|
return written;
|
|
9175
9587
|
}
|
|
@@ -9203,9 +9615,9 @@ function registerTest() {
|
|
|
9203
9615
|
|
|
9204
9616
|
// src/roles/typescript/adapters/tsc/tsc.adapter.ts
|
|
9205
9617
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
9206
|
-
import { existsSync as existsSync19, readFileSync as
|
|
9618
|
+
import { existsSync as existsSync19, readFileSync as readFileSync22 } from "node:fs";
|
|
9207
9619
|
import { createRequire } from "node:module";
|
|
9208
|
-
import { join as
|
|
9620
|
+
import { join as join29 } from "node:path";
|
|
9209
9621
|
|
|
9210
9622
|
// src/roles/typescript/presets/nest.json
|
|
9211
9623
|
var nest_default2 = {
|
|
@@ -9390,8 +9802,8 @@ function readTsconfigAdoption(cwd) {
|
|
|
9390
9802
|
}
|
|
9391
9803
|
|
|
9392
9804
|
// src/roles/typescript/adapters/tsc/plan.ts
|
|
9393
|
-
import { readFileSync as
|
|
9394
|
-
import { join as
|
|
9805
|
+
import { readFileSync as readFileSync21 } from "node:fs";
|
|
9806
|
+
import { join as join28 } from "node:path";
|
|
9395
9807
|
|
|
9396
9808
|
// src/roles/typescript/typecheck-script.ts
|
|
9397
9809
|
var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
|
|
@@ -9484,7 +9896,7 @@ function planAdoption(context2) {
|
|
|
9484
9896
|
};
|
|
9485
9897
|
}
|
|
9486
9898
|
const existing = parseJsonc(
|
|
9487
|
-
|
|
9899
|
+
readFileSync21(join28(context2.cwd, target.path), "utf8"),
|
|
9488
9900
|
target.path
|
|
9489
9901
|
);
|
|
9490
9902
|
const extendsChain = composeExtends(existing.extends, preset);
|
|
@@ -9622,7 +10034,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
9622
10034
|
let chain;
|
|
9623
10035
|
try {
|
|
9624
10036
|
const parsed = parseJsonc(
|
|
9625
|
-
|
|
10037
|
+
readFileSync22(join29(cwd, target.path), "utf8"),
|
|
9626
10038
|
target.path
|
|
9627
10039
|
);
|
|
9628
10040
|
chain = parsed.extends;
|
|
@@ -9635,7 +10047,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
9635
10047
|
);
|
|
9636
10048
|
if (preset === void 0) return void 0;
|
|
9637
10049
|
try {
|
|
9638
|
-
createRequire(
|
|
10050
|
+
createRequire(join29(cwd, "noop.js")).resolve(preset);
|
|
9639
10051
|
return void 0;
|
|
9640
10052
|
} catch {
|
|
9641
10053
|
return preset;
|
|
@@ -9731,7 +10143,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
9731
10143
|
referencedProjects(cwd, config) {
|
|
9732
10144
|
try {
|
|
9733
10145
|
const parsed = parseJsonc(
|
|
9734
|
-
|
|
10146
|
+
readFileSync22(join29(cwd, config), "utf8"),
|
|
9735
10147
|
config
|
|
9736
10148
|
);
|
|
9737
10149
|
const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path2) => typeof path2 === "string" && path2.length > 0);
|
|
@@ -9747,7 +10159,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
9747
10159
|
* check.
|
|
9748
10160
|
*/
|
|
9749
10161
|
typecheckTarget(cwd) {
|
|
9750
|
-
if (existsSync19(
|
|
10162
|
+
if (existsSync19(join29(cwd, "tsconfig.json"))) return "tsconfig.json";
|
|
9751
10163
|
const target = resolveTsconfigTarget(cwd);
|
|
9752
10164
|
return target.reason === "none" ? null : target.path;
|
|
9753
10165
|
}
|
|
@@ -9953,7 +10365,7 @@ function replaceLines(current, replacements) {
|
|
|
9953
10365
|
}
|
|
9954
10366
|
|
|
9955
10367
|
// src/core/apply-plan.ts
|
|
9956
|
-
import { existsSync as existsSync20, readFileSync as
|
|
10368
|
+
import { existsSync as existsSync20, readFileSync as readFileSync23, renameSync, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "node:fs";
|
|
9957
10369
|
import { resolve as resolve4, sep as sep2 } from "node:path";
|
|
9958
10370
|
import { applyEdits, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
|
|
9959
10371
|
function resolveWithinRoot(cwd, relativePath) {
|
|
@@ -9965,7 +10377,7 @@ function resolveWithinRoot(cwd, relativePath) {
|
|
|
9965
10377
|
return absolutePath;
|
|
9966
10378
|
}
|
|
9967
10379
|
function readIfExists(absolutePath) {
|
|
9968
|
-
return existsSync20(absolutePath) ?
|
|
10380
|
+
return existsSync20(absolutePath) ? readFileSync23(absolutePath, "utf8") : void 0;
|
|
9969
10381
|
}
|
|
9970
10382
|
function* leaves(value, prefix = []) {
|
|
9971
10383
|
for (const [key, keyValue] of Object.entries(value)) {
|
|
@@ -10067,7 +10479,7 @@ function preparePlan(cwd, plan3) {
|
|
|
10067
10479
|
}
|
|
10068
10480
|
function writeFileAtomic(absolutePath, contents) {
|
|
10069
10481
|
const tempPath = `${absolutePath}.sentinel-${process.pid}.tmp`;
|
|
10070
|
-
|
|
10482
|
+
writeFileSync8(tempPath, contents);
|
|
10071
10483
|
renameSync(tempPath, absolutePath);
|
|
10072
10484
|
}
|
|
10073
10485
|
function applyPlan(cwd, plan3) {
|
|
@@ -10083,8 +10495,8 @@ function applyPlan(cwd, plan3) {
|
|
|
10083
10495
|
}
|
|
10084
10496
|
|
|
10085
10497
|
// src/core/config/preset-evidence.ts
|
|
10086
|
-
import { existsSync as existsSync21, readdirSync as readdirSync3, readFileSync as
|
|
10087
|
-
import { join as
|
|
10498
|
+
import { existsSync as existsSync21, readdirSync as readdirSync3, readFileSync as readFileSync24 } from "node:fs";
|
|
10499
|
+
import { join as join30 } from "node:path";
|
|
10088
10500
|
var PATH_SIGNALS = [
|
|
10089
10501
|
{
|
|
10090
10502
|
preset: "nest",
|
|
@@ -10118,12 +10530,12 @@ function sourceImports(cwd, pattern) {
|
|
|
10118
10530
|
}
|
|
10119
10531
|
for (const entry of entries) {
|
|
10120
10532
|
if (entry.isDirectory()) {
|
|
10121
|
-
if (!NOT_SCANNED.has(entry.name)) stack.push(
|
|
10533
|
+
if (!NOT_SCANNED.has(entry.name)) stack.push(join30(dir, entry.name));
|
|
10122
10534
|
continue;
|
|
10123
10535
|
}
|
|
10124
10536
|
if (!SOURCE_FILE.test(entry.name)) continue;
|
|
10125
10537
|
try {
|
|
10126
|
-
if (pattern.test(
|
|
10538
|
+
if (pattern.test(readFileSync24(join30(dir, entry.name), "utf8"))) return true;
|
|
10127
10539
|
} catch {
|
|
10128
10540
|
continue;
|
|
10129
10541
|
}
|
|
@@ -10132,11 +10544,11 @@ function sourceImports(cwd, pattern) {
|
|
|
10132
10544
|
return false;
|
|
10133
10545
|
}
|
|
10134
10546
|
function dependencyNames(cwd) {
|
|
10135
|
-
const path2 =
|
|
10547
|
+
const path2 = join30(cwd, "package.json");
|
|
10136
10548
|
if (!existsSync21(path2)) return [];
|
|
10137
10549
|
try {
|
|
10138
10550
|
const manifest = parseJsonc(
|
|
10139
|
-
|
|
10551
|
+
readFileSync24(path2, "utf8"),
|
|
10140
10552
|
path2
|
|
10141
10553
|
);
|
|
10142
10554
|
return [
|
|
@@ -10159,7 +10571,7 @@ function reactTsconfigEvidence(cwd) {
|
|
|
10159
10571
|
}
|
|
10160
10572
|
for (const name of entries) {
|
|
10161
10573
|
try {
|
|
10162
|
-
const config = parseJsonc(
|
|
10574
|
+
const config = parseJsonc(readFileSync24(join30(cwd, name), "utf8"), name);
|
|
10163
10575
|
if (config.compilerOptions?.jsx !== void 0) return 'its tsconfig sets "jsx"';
|
|
10164
10576
|
const extended = Array.isArray(config.extends) ? config.extends : config.extends === void 0 ? [] : [config.extends];
|
|
10165
10577
|
if (extended.some((entry) => entry === REACT_TYPESCRIPT_PRESET)) {
|
|
@@ -10221,8 +10633,8 @@ function resolveFlavour(opts) {
|
|
|
10221
10633
|
}
|
|
10222
10634
|
return detection.preset;
|
|
10223
10635
|
}
|
|
10224
|
-
function previewPlan(opts, plan3) {
|
|
10225
|
-
const changed =
|
|
10636
|
+
function previewPlan(opts, plan3, prepared) {
|
|
10637
|
+
const changed = prepared.filter((file) => file.before !== file.after);
|
|
10226
10638
|
if (opts.json) {
|
|
10227
10639
|
opts.record?.({
|
|
10228
10640
|
role: opts.target,
|
|
@@ -10260,8 +10672,10 @@ async function dispatch(opts) {
|
|
|
10260
10672
|
}
|
|
10261
10673
|
const detected = resolveFlavour(opts);
|
|
10262
10674
|
const resolutionPreset = opts.preset ?? declaredPresetFor(opts.target, opts.cwd) ?? detected;
|
|
10263
|
-
|
|
10264
|
-
|
|
10675
|
+
const declaredTools = opts.preset === void 0 ? availableTargets().find((target) => declaredPresetFor(target, opts.cwd) === "tools") : void 0;
|
|
10676
|
+
if (resolutionPreset === "tools" || declaredTools !== void 0) {
|
|
10677
|
+
const declaredBy = declaredTools === void 0 || declaredTools === opts.target ? "" : `this module's ${declaredTools} config declares the tools preset, and `;
|
|
10678
|
+
const message = `${declaredBy}tools modules are not adopted one at a time: they sometimes hold configuration shared with the workspace root, so they move WITH the root, as one reviewed change owned by the tooling team rather than by the module's team.`;
|
|
10265
10679
|
process.stderr.write(` ${palette(process.stderr).dim(`${opts.target}: ${message}`)}
|
|
10266
10680
|
`);
|
|
10267
10681
|
opts.record?.({ role: opts.target, status: "deferred", reason: message });
|
|
@@ -10304,7 +10718,14 @@ async function dispatch(opts) {
|
|
|
10304
10718
|
return 0;
|
|
10305
10719
|
}
|
|
10306
10720
|
if (opts.dryRun) {
|
|
10307
|
-
|
|
10721
|
+
const prepared = preparePlan(opts.cwd, plan3);
|
|
10722
|
+
opts.planned?.({
|
|
10723
|
+
target: opts.target,
|
|
10724
|
+
adapter,
|
|
10725
|
+
preset,
|
|
10726
|
+
files: prepared.filter((file) => file.before !== file.after)
|
|
10727
|
+
});
|
|
10728
|
+
return previewPlan(opts, plan3, prepared);
|
|
10308
10729
|
}
|
|
10309
10730
|
if (adapter.beforeInit) {
|
|
10310
10731
|
await adapter.beforeInit({
|
|
@@ -10357,6 +10778,7 @@ export {
|
|
|
10357
10778
|
resolve,
|
|
10358
10779
|
buildConfigFile,
|
|
10359
10780
|
palette,
|
|
10781
|
+
presetNotInstalledYet,
|
|
10360
10782
|
registerAdapters,
|
|
10361
10783
|
correctingSignal,
|
|
10362
10784
|
describeFramework,
|