@hublo/sentinel 1.2.0-alpha.1 → 1.2.0-alpha.10
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 +2 -2
- package/dist/bin/sentinel.d.ts +5 -0
- package/dist/bin/sentinel.js +57 -8
- package/dist/{chunk-FLJ2QNFF.js → chunk-7WB7MQHD.js} +641 -337
- package/dist/index.js +1 -2
- package/dist/roles/build/nest/toolchain.d.ts +90 -0
- package/dist/roles/build/nest/toolchain.js +253 -0
- package/dist/roles/build/toolchain.d.ts +5 -0
- package/dist/roles/build/toolchain.js +16 -0
- package/package.json +21 -3
- package/dist/chunk-XVDOQ3G3.js +0 -39
- package/dist/roles/build/react-app.d.ts +0 -112
- package/dist/roles/build/react-app.js +0 -126
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
REACT_APP_DEFAULTS,
|
|
3
|
-
REQUIREMENTS,
|
|
4
|
-
isPlainObject
|
|
5
|
-
} from "./chunk-XVDOQ3G3.js";
|
|
6
|
-
|
|
7
1
|
// src/core/registry.ts
|
|
8
2
|
var adapters = [];
|
|
9
3
|
var PresetUnsupportedError = class extends Error {
|
|
@@ -88,7 +82,7 @@ var BaseAdapter = class {
|
|
|
88
82
|
|
|
89
83
|
// src/shared/package-json.ts
|
|
90
84
|
import { existsSync, readFileSync } from "fs";
|
|
91
|
-
import { dirname, join } from "path";
|
|
85
|
+
import { basename, dirname, join } from "path";
|
|
92
86
|
import { fileURLToPath } from "url";
|
|
93
87
|
function readOwnPackage() {
|
|
94
88
|
let dir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -133,6 +127,10 @@ function readNxProjectName(dir) {
|
|
|
133
127
|
return void 0;
|
|
134
128
|
}
|
|
135
129
|
}
|
|
130
|
+
function moduleName(dir) {
|
|
131
|
+
const declared = readProjectPackageJson(dir).name;
|
|
132
|
+
return readNxProjectName(dir) ?? (declared === void 0 ? basename(dir) : declared);
|
|
133
|
+
}
|
|
136
134
|
|
|
137
135
|
// src/core/domain.ts
|
|
138
136
|
var VERBS = ["run", "inspect", "init", "migrate", "report", "status"];
|
|
@@ -207,9 +205,9 @@ function binFromOwnInstall(packageName, binName) {
|
|
|
207
205
|
try {
|
|
208
206
|
const manifest = require2.resolve(`${packageName}/package.json`);
|
|
209
207
|
const bin = require2(manifest).bin;
|
|
210
|
-
const
|
|
211
|
-
if (!
|
|
212
|
-
const executable = join2(dirname2(manifest),
|
|
208
|
+
const relative6 = typeof bin === "string" ? bin : bin?.[binName];
|
|
209
|
+
if (!relative6) return void 0;
|
|
210
|
+
const executable = join2(dirname2(manifest), relative6);
|
|
213
211
|
return existsSync3(executable) ? executable : void 0;
|
|
214
212
|
} catch {
|
|
215
213
|
return void 0;
|
|
@@ -222,8 +220,8 @@ function binSearchPath(cwd) {
|
|
|
222
220
|
}
|
|
223
221
|
|
|
224
222
|
// src/roles/build/plan.ts
|
|
225
|
-
import { existsSync as
|
|
226
|
-
import { join as
|
|
223
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
224
|
+
import { join as join8 } from "path";
|
|
227
225
|
|
|
228
226
|
// src/core/config/existing-command.ts
|
|
229
227
|
import { readFileSync as readFileSync3 } from "fs";
|
|
@@ -243,7 +241,7 @@ function parseJsonc(text, source = "config") {
|
|
|
243
241
|
|
|
244
242
|
// src/core/config/manifest.ts
|
|
245
243
|
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
246
|
-
import { basename, join as join3 } from "path";
|
|
244
|
+
import { basename as basename2, join as join3 } from "path";
|
|
247
245
|
function moduleScripts(cwd) {
|
|
248
246
|
try {
|
|
249
247
|
const pkg = JSON.parse(readFileSync2(join3(cwd, "package.json"), "utf8"));
|
|
@@ -252,7 +250,7 @@ function moduleScripts(cwd) {
|
|
|
252
250
|
return {};
|
|
253
251
|
}
|
|
254
252
|
}
|
|
255
|
-
function
|
|
253
|
+
function moduleName2(cwd) {
|
|
256
254
|
try {
|
|
257
255
|
const pkg = JSON.parse(readFileSync2(join3(cwd, "package.json"), "utf8"));
|
|
258
256
|
return pkg.name;
|
|
@@ -261,7 +259,7 @@ function moduleName(cwd) {
|
|
|
261
259
|
}
|
|
262
260
|
}
|
|
263
261
|
function isSelfAdoption(cwd) {
|
|
264
|
-
return
|
|
262
|
+
return moduleName2(cwd) === readOwnPackage().name;
|
|
265
263
|
}
|
|
266
264
|
function selfCommand(cwd, flags) {
|
|
267
265
|
const own = readOwnPackage();
|
|
@@ -280,7 +278,7 @@ function manifestOperation(cwd, scripts) {
|
|
|
280
278
|
return {
|
|
281
279
|
kind: "merge-json",
|
|
282
280
|
path: "package.json",
|
|
283
|
-
value: { name: readNxProjectName(cwd) ??
|
|
281
|
+
value: { name: readNxProjectName(cwd) ?? basename2(cwd), private: true, ...value }
|
|
284
282
|
};
|
|
285
283
|
}
|
|
286
284
|
|
|
@@ -384,16 +382,98 @@ function keepsOtherCommands(script, sentinelCommand) {
|
|
|
384
382
|
return script.split(SEGMENT_SEPARATOR).filter((_, index) => !isSeparatorAt(index)).some((segment) => segment.trim() !== "" && segment.trim() !== sentinelCommand);
|
|
385
383
|
}
|
|
386
384
|
|
|
385
|
+
// src/roles/build/presets/requirements.json
|
|
386
|
+
var requirements_default = {
|
|
387
|
+
svelte: {
|
|
388
|
+
why: "sentinel ships Vite 8, and @sveltejs/vite-plugin-svelte caps at Vite 6 until its major 7",
|
|
389
|
+
requires: {
|
|
390
|
+
"@sveltejs/vite-plugin-svelte": "7.0.0",
|
|
391
|
+
svelte: "5.46.4"
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
// src/roles/build/presets/toolchain.json
|
|
397
|
+
var toolchain_default = {
|
|
398
|
+
$comment: "The build toolchain sentinel owns. One table, because three places used to hold parts of it: the list to remove from a module's package.json, the list of import specifiers to rewrite, and the map from a default import to the name sentinel re-exports it under. A package added to one and not the others gives a removal with no rewrite, or an import pointing at a package the module no longer declares.",
|
|
399
|
+
owned: [
|
|
400
|
+
{
|
|
401
|
+
package: "vite",
|
|
402
|
+
specifier: "vite",
|
|
403
|
+
named: ["defineConfig", "loadEnv", "mergeConfig"],
|
|
404
|
+
types: [
|
|
405
|
+
"Plugin",
|
|
406
|
+
"PluginOption",
|
|
407
|
+
"SassPreprocessorOptions",
|
|
408
|
+
"UserConfig",
|
|
409
|
+
"UserConfigExport"
|
|
410
|
+
],
|
|
411
|
+
why: "the bundler itself. `defineConfig`, `loadEnv` and `mergeConfig` are what every config in this repo imports from it, and the types travel with them: a config importing PluginOption from a package it no longer declares builds but does not typecheck."
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
package: "@vitejs/plugin-react",
|
|
415
|
+
specifier: "@vitejs/plugin-react",
|
|
416
|
+
default: "react",
|
|
417
|
+
why: "the React transform. Bound to the Vite that runs it, so it has to come from the same install."
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
package: "@tailwindcss/vite",
|
|
421
|
+
specifier: "@tailwindcss/vite",
|
|
422
|
+
default: "tailwindcss",
|
|
423
|
+
why: "the Tailwind pipeline, identical in all three front apps."
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
package: "vite-plugin-svgr",
|
|
427
|
+
specifier: "vite-plugin-svgr",
|
|
428
|
+
default: "svgr",
|
|
429
|
+
why: "SVG as components, identical in all three front apps."
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
package: "nitro",
|
|
433
|
+
specifier: "nitro/vite",
|
|
434
|
+
named: ["nitro"],
|
|
435
|
+
why: "the SSR server. Note the package and the import specifier differ, which is exactly the kind of split that made three separate lists disagree."
|
|
436
|
+
}
|
|
437
|
+
],
|
|
438
|
+
add: [
|
|
439
|
+
{
|
|
440
|
+
package: "@hublo/sentinel",
|
|
441
|
+
why: "the module now gets its toolchain from here, so it declares it. Pinned by the installer rather than by us: `--init` writes the dependency, `pnpm install` resolves it."
|
|
442
|
+
}
|
|
443
|
+
],
|
|
444
|
+
$notOwned: {
|
|
445
|
+
"@rolldown/plugin-babel": "declared by career and host-admin themselves, not at the root. It supports LocatorJS, a development convenience gated on LOCATORJS=true, not shared build infrastructure. Removing it would remove a feature from two teams, which is their call and must not ride along inside an adoption.",
|
|
446
|
+
"@locator/babel-jsx": "same, and same owners.",
|
|
447
|
+
"@tanstack/react-start": "a framework the apps code against: 48 source files import it directly.",
|
|
448
|
+
"@tanstack/router-plugin": "generates the app's own route tree.",
|
|
449
|
+
"vite-bundle-analyzer": "at the root at 1.3.7 and imported nowhere, the only mention being a commented-out line in host-admin. Not a candidate to re-export, a candidate to delete from the root."
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
// src/roles/build/preset-data.ts
|
|
454
|
+
var REQUIREMENTS = requirements_default;
|
|
455
|
+
var OWNED_PACKAGES = toolchain_default.owned;
|
|
456
|
+
|
|
387
457
|
// src/roles/build/config-policy.ts
|
|
458
|
+
var COMPANION_CONFIG_FILES = [
|
|
459
|
+
"vitest.config.ts",
|
|
460
|
+
"vitest.config.mts",
|
|
461
|
+
"vitest.config.js",
|
|
462
|
+
"vitest.config.mjs"
|
|
463
|
+
];
|
|
388
464
|
var BUILD_CONFIG_FILES = [
|
|
389
465
|
"vite.config.ts",
|
|
390
466
|
"vite.config.mts",
|
|
391
467
|
"vite.config.js",
|
|
392
468
|
"vite.config.mjs"
|
|
393
469
|
];
|
|
394
|
-
var
|
|
470
|
+
var BUILD_PRESET_SPECIFIERS = {
|
|
471
|
+
react: "@hublo/sentinel/build/react",
|
|
472
|
+
nest: "@hublo/sentinel/build/nest"
|
|
473
|
+
};
|
|
474
|
+
var BUILD_PRESET_SPECIFIER = BUILD_PRESET_SPECIFIERS.react;
|
|
395
475
|
var BUILD_SCRIPT_NAME = "build";
|
|
396
|
-
var SENTINEL_BUILD_COMMAND = "sentinel --run --build";
|
|
476
|
+
var SENTINEL_BUILD_COMMAND = "sentinel --run --build --";
|
|
397
477
|
var DEV_SCRIPT_NAME = "serve";
|
|
398
478
|
var SENTINEL_DEV_COMMAND = "sentinel --run --dev";
|
|
399
479
|
function buildTarget() {
|
|
@@ -404,13 +484,9 @@ function buildTarget() {
|
|
|
404
484
|
}
|
|
405
485
|
};
|
|
406
486
|
}
|
|
407
|
-
var SENTINEL_OWNED_BUILD_PACKAGES =
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
"@tailwindcss/vite",
|
|
411
|
-
"vite-plugin-svgr",
|
|
412
|
-
"nitro"
|
|
413
|
-
];
|
|
487
|
+
var SENTINEL_OWNED_BUILD_PACKAGES = OWNED_PACKAGES.map(
|
|
488
|
+
(entry) => entry.package
|
|
489
|
+
);
|
|
414
490
|
|
|
415
491
|
// src/roles/build/dev-script.ts
|
|
416
492
|
var CHAIN = " -- ";
|
|
@@ -418,23 +494,143 @@ var VITE_TOKEN = /(^|\s|\/)vite(\s|$)/;
|
|
|
418
494
|
var DEV_SUBCOMMANDS = /* @__PURE__ */ new Set(["dev", "serve"]);
|
|
419
495
|
function appOptions(rest) {
|
|
420
496
|
const tokens = (rest ?? "").trim().split(/\s+/).filter(Boolean);
|
|
421
|
-
const kept = tokens.filter((token,
|
|
497
|
+
const kept = tokens.filter((token, at) => !(at === 0 && DEV_SUBCOMMANDS.has(token)));
|
|
422
498
|
return kept.join(" ");
|
|
423
499
|
}
|
|
424
500
|
function composeDevScript(existing, command) {
|
|
425
501
|
if (existing === void 0 || existing.trim() === "") return command;
|
|
426
502
|
const chunks = existing.trim().split(CHAIN);
|
|
427
|
-
const
|
|
428
|
-
if (
|
|
429
|
-
const rest = (chunks[
|
|
503
|
+
const at = chunks.map((chunk) => VITE_TOKEN.test(chunk)).lastIndexOf(true);
|
|
504
|
+
if (at === -1) return existing;
|
|
505
|
+
const rest = (chunks[at] ?? "").replace(/^.*?(^|\s|\/)vite(\s|$)/, "");
|
|
430
506
|
const options = appOptions(rest);
|
|
431
|
-
chunks[
|
|
507
|
+
chunks[at] = options === "" ? command : `${command} -- ${options}`;
|
|
432
508
|
return chunks.join(CHAIN);
|
|
433
509
|
}
|
|
434
510
|
|
|
435
|
-
// src/roles/build/
|
|
511
|
+
// src/roles/build/nest/adopt.ts
|
|
512
|
+
import { relative as relative2 } from "path";
|
|
513
|
+
|
|
514
|
+
// src/roles/build/nest/nx-target.ts
|
|
436
515
|
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
437
|
-
import { join as join6 } from "path";
|
|
516
|
+
import { join as join6, relative } from "path";
|
|
517
|
+
var DEPRECATED_NX_BUILDER = "@nx/webpack:webpack";
|
|
518
|
+
var TRANSLATABLE_WEBPACK_CONFIG = "tools/webpack-configs/update.webpack.config.js";
|
|
519
|
+
var readProjectJson = (cwd) => {
|
|
520
|
+
const path = join6(cwd, "project.json");
|
|
521
|
+
if (!existsSync6(path)) return void 0;
|
|
522
|
+
try {
|
|
523
|
+
return JSON.parse(readFileSync4(path, "utf8"));
|
|
524
|
+
} catch {
|
|
525
|
+
return void 0;
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
function webpackBuildTarget(cwd) {
|
|
529
|
+
const project = readProjectJson(cwd);
|
|
530
|
+
const target = project?.targets?.build;
|
|
531
|
+
if (target?.executor !== DEPRECATED_NX_BUILDER) return void 0;
|
|
532
|
+
const { outputPath, main, webpackConfig } = target.options ?? {};
|
|
533
|
+
if (typeof project?.name !== "string" || typeof outputPath !== "string" || typeof main !== "string" || typeof webpackConfig !== "string") {
|
|
534
|
+
return void 0;
|
|
535
|
+
}
|
|
536
|
+
return {
|
|
537
|
+
project: project.name,
|
|
538
|
+
outputPath,
|
|
539
|
+
main,
|
|
540
|
+
webpackConfig,
|
|
541
|
+
configurations: target.configurations
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
function refusalFor(cwd, target) {
|
|
545
|
+
if (workspaceRootFor(cwd, target) === void 0) {
|
|
546
|
+
return `this service's build target declares \`main: ${target.main}\`, which does not place this directory inside a workspace. Every value the config needs is derived from that path, so a wrong one would resolve \`workspaceRoot\` to the service itself and leave the alias table resolving nothing. Nothing was written.`;
|
|
547
|
+
}
|
|
548
|
+
if (target.webpackConfig !== TRANSLATABLE_WEBPACK_CONFIG) {
|
|
549
|
+
return `this service builds with ${target.webpackConfig}, not the shared ${TRANSLATABLE_WEBPACK_CONFIG} this preset reproduces. Its own config adds loaders or rules the preset has no equivalent for, and translating it anyway would produce a build that succeeds and a service that fails later. Read that file, decide what the Vite config needs, and write it by hand. Nothing was written.`;
|
|
550
|
+
}
|
|
551
|
+
const live = liveFileReplacements(cwd, target);
|
|
552
|
+
if (live.length > 0) {
|
|
553
|
+
return `this service's production configuration replaces ${live.join(", ")}, and those files exist. The preset has no equivalent, so translating the target would drop a real substitution. Nothing was written.`;
|
|
554
|
+
}
|
|
555
|
+
return void 0;
|
|
556
|
+
}
|
|
557
|
+
function liveFileReplacements(cwd, target) {
|
|
558
|
+
const workspaceRoot = workspaceRootFor(cwd, target);
|
|
559
|
+
if (workspaceRoot === void 0) return [];
|
|
560
|
+
const configurations = target.configurations ?? {};
|
|
561
|
+
const live = [];
|
|
562
|
+
for (const configuration of Object.values(configurations)) {
|
|
563
|
+
for (const replacement of configuration.fileReplacements ?? []) {
|
|
564
|
+
const exists = existsSync6(join6(workspaceRoot, replacement.replace)) || existsSync6(join6(workspaceRoot, replacement.with));
|
|
565
|
+
if (exists) live.push(replacement.replace);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return live;
|
|
569
|
+
}
|
|
570
|
+
function workspaceRootFor(cwd, target) {
|
|
571
|
+
const moduleRoot = target.main.replace(/\/src\/.*$/, "");
|
|
572
|
+
if (moduleRoot === target.main || !cwd.endsWith(moduleRoot)) return void 0;
|
|
573
|
+
return cwd.slice(0, cwd.length - moduleRoot.length - 1);
|
|
574
|
+
}
|
|
575
|
+
function workspaceRootHops(cwd, target) {
|
|
576
|
+
const root = workspaceRootFor(cwd, target);
|
|
577
|
+
if (root === void 0) {
|
|
578
|
+
throw new Error(`sentinel build(nest): cannot place ${cwd} in a workspace from ${target.main}`);
|
|
579
|
+
}
|
|
580
|
+
const rel = relative(root, cwd);
|
|
581
|
+
return rel === "" ? 0 : rel.split(/[\\/]/).length;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/roles/build/nest/adopt.ts
|
|
585
|
+
var NEST_CONFIG_FILE = "vite.config.mts";
|
|
586
|
+
function nestConfigSource(cwd, target) {
|
|
587
|
+
const hops = workspaceRootHops(cwd, target);
|
|
588
|
+
const up = Array.from({ length: hops }, () => "..").join("/");
|
|
589
|
+
return `import path from 'node:path'
|
|
590
|
+
|
|
591
|
+
import { nestService } from '${BUILD_PRESET_SPECIFIERS.nest}'
|
|
592
|
+
|
|
593
|
+
export default nestService({
|
|
594
|
+
project: '${target.project}',
|
|
595
|
+
root: __dirname,
|
|
596
|
+
workspaceRoot: path.resolve(__dirname, '${up}'),
|
|
597
|
+
outDir: '${target.outputPath}',
|
|
598
|
+
})
|
|
599
|
+
`;
|
|
600
|
+
}
|
|
601
|
+
function nestBuildTarget(cwd, target) {
|
|
602
|
+
const workspaceRoot = workspaceRootFor(cwd, target);
|
|
603
|
+
if (workspaceRoot === void 0) {
|
|
604
|
+
throw new Error(
|
|
605
|
+
`sentinel build(nest): cannot place ${cwd} inside a workspace from ${target.main}`
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
const moduleRoot = relative2(workspaceRoot, cwd);
|
|
609
|
+
return {
|
|
610
|
+
executor: "nx:run-commands",
|
|
611
|
+
outputs: [`{workspaceRoot}/${target.outputPath}`],
|
|
612
|
+
options: {
|
|
613
|
+
cwd: moduleRoot,
|
|
614
|
+
command: "pnpm exec sentinel --run --build",
|
|
615
|
+
forwardAllArgs: false
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
function nestAdoptionOperations(cwd, target) {
|
|
620
|
+
return [
|
|
621
|
+
{ kind: "write", path: NEST_CONFIG_FILE, contents: nestConfigSource(cwd, target) },
|
|
622
|
+
{ kind: "remove-json-keys", path: "project.json", keys: [["targets", "build"]] },
|
|
623
|
+
{
|
|
624
|
+
kind: "merge-json",
|
|
625
|
+
path: "project.json",
|
|
626
|
+
value: { targets: { build: nestBuildTarget(cwd, target) } }
|
|
627
|
+
}
|
|
628
|
+
];
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// src/roles/build/read-adoption.ts
|
|
632
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
|
|
633
|
+
import { join as join7 } from "path";
|
|
438
634
|
var NOT_ADOPTED = (configFile, unreadable = null) => ({
|
|
439
635
|
configFile,
|
|
440
636
|
preset: null,
|
|
@@ -445,26 +641,32 @@ var NOT_ADOPTED = (configFile, unreadable = null) => ({
|
|
|
445
641
|
unreadable
|
|
446
642
|
});
|
|
447
643
|
function buildConfigFile(cwd) {
|
|
448
|
-
return BUILD_CONFIG_FILES.find((name) =>
|
|
644
|
+
return BUILD_CONFIG_FILES.find((name) => existsSync7(join7(cwd, name)));
|
|
449
645
|
}
|
|
450
646
|
function importsSpecifier(source, specifier) {
|
|
451
647
|
const escaped = specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
452
648
|
return new RegExp(`(?:from|import)\\s*\\(?\\s*['"\`]${escaped}(?:/[^'"\`]*)?['"\`]`).test(source);
|
|
453
649
|
}
|
|
454
650
|
function importsPreset(source) {
|
|
455
|
-
return
|
|
651
|
+
return Object.values(BUILD_PRESET_SPECIFIERS).some(
|
|
652
|
+
(specifier) => importsSpecifier(source, specifier)
|
|
653
|
+
);
|
|
456
654
|
}
|
|
457
655
|
var REACT_PLUGIN_SPECIFIERS = ["@vitejs/plugin-react", "@tanstack/react-start"];
|
|
458
656
|
function declaredBuildPreset(cwd) {
|
|
459
657
|
const source = readBuildConfigSource(cwd);
|
|
460
|
-
if (source === void 0)
|
|
658
|
+
if (source === void 0) {
|
|
659
|
+
return webpackBuildTarget(cwd) === void 0 ? void 0 : "nest";
|
|
660
|
+
}
|
|
661
|
+
if (importsSpecifier(source, BUILD_PRESET_SPECIFIERS.nest)) return "nest";
|
|
662
|
+
if (importsPreset(source)) return "react";
|
|
461
663
|
return REACT_PLUGIN_SPECIFIERS.some((specifier) => importsSpecifier(source, specifier)) ? "react" : void 0;
|
|
462
664
|
}
|
|
463
665
|
function readBuildConfigSource(cwd) {
|
|
464
666
|
const configFile = buildConfigFile(cwd);
|
|
465
667
|
if (!configFile) return void 0;
|
|
466
668
|
try {
|
|
467
|
-
return
|
|
669
|
+
return readFileSync5(join7(cwd, configFile), "utf8");
|
|
468
670
|
} catch {
|
|
469
671
|
return void 0;
|
|
470
672
|
}
|
|
@@ -474,7 +676,7 @@ function readBuildAdoption(cwd) {
|
|
|
474
676
|
if (!configFile) return NOT_ADOPTED(null);
|
|
475
677
|
let source;
|
|
476
678
|
try {
|
|
477
|
-
source =
|
|
679
|
+
source = readFileSync5(join7(cwd, configFile), "utf8");
|
|
478
680
|
} catch (error) {
|
|
479
681
|
return NOT_ADOPTED(configFile, error instanceof Error ? error.message : String(error));
|
|
480
682
|
}
|
|
@@ -486,7 +688,9 @@ function readBuildAdoption(cwd) {
|
|
|
486
688
|
);
|
|
487
689
|
return {
|
|
488
690
|
configFile,
|
|
489
|
-
|
|
691
|
+
// Read from the config rather than assumed: the two presets are adopted the same way and
|
|
692
|
+
// reporting the wrong one would make `--inspect` lie about what a module builds.
|
|
693
|
+
preset: declaredBuildPreset(cwd) ?? "react",
|
|
490
694
|
adopted: true,
|
|
491
695
|
conformant: ownDeclarations.length === 0,
|
|
492
696
|
drift: ownDeclarations.map(
|
|
@@ -502,9 +706,9 @@ function atLeast(declared, needed) {
|
|
|
502
706
|
const parse2 = (value) => value.replace(/^[\^~>=<\s]+/, "").split("-")[0].split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
503
707
|
const left = parse2(declared);
|
|
504
708
|
const right = parse2(needed);
|
|
505
|
-
for (let
|
|
506
|
-
const a = left[
|
|
507
|
-
const b = right[
|
|
709
|
+
for (let at = 0; at < Math.max(left.length, right.length); at++) {
|
|
710
|
+
const a = left[at] ?? 0;
|
|
711
|
+
const b = right[at] ?? 0;
|
|
508
712
|
if (a !== b) return a > b;
|
|
509
713
|
}
|
|
510
714
|
return true;
|
|
@@ -525,43 +729,103 @@ function describeUnmet(preset, unmet) {
|
|
|
525
729
|
return `the build role does not apply to this module yet: ${list}. ${entry?.why ?? ""}. Nothing was written. Raise those versions, check the module still builds and its tests still pass, then re-run \`sentinel --init --build\`.`;
|
|
526
730
|
}
|
|
527
731
|
|
|
528
|
-
// src/roles/build/
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
732
|
+
// src/roles/build/rewrite-imports.ts
|
|
733
|
+
import { parseSync } from "oxc-parser";
|
|
734
|
+
var MOVED_SPECIFIERS = new Set(OWNED_PACKAGES.map((entry) => entry.specifier));
|
|
735
|
+
function esmBlocker(fileName, packageType) {
|
|
736
|
+
if (/\.(mts|mjs)$/.test(fileName)) return void 0;
|
|
737
|
+
if (packageType === "module") return void 0;
|
|
738
|
+
return `sentinel is ESM only, and this module does not declare \`"type": "module"\`, so Vite would load ${fileName} as CommonJS and fail to require it. Add \`"type": "module"\` to this module's package.json, or rename the config to \`${fileName.replace(/\.(ts|js)$/, ".m$1")}\`, then run this again.`;
|
|
739
|
+
}
|
|
740
|
+
var DEFAULT_EXPORT_NAMES = Object.fromEntries(
|
|
741
|
+
OWNED_PACKAGES.filter((entry) => entry.default !== void 0).map((entry) => [
|
|
742
|
+
entry.specifier,
|
|
743
|
+
entry.default
|
|
744
|
+
])
|
|
745
|
+
);
|
|
746
|
+
function renderImport(bindings) {
|
|
747
|
+
const render = (list) => list.map((b) => b.exported === b.local ? b.exported : `${b.exported} as ${b.local}`).sort((left, right) => left.localeCompare(right)).join(", ");
|
|
748
|
+
const values = bindings.filter((b) => !b.typeOnly);
|
|
749
|
+
const types = bindings.filter((b) => b.typeOnly);
|
|
750
|
+
const lines = [];
|
|
751
|
+
if (values.length > 0) lines.push(`import { ${render(values)} } from '${BUILD_PRESET_SPECIFIER}'`);
|
|
752
|
+
if (types.length > 0) {
|
|
753
|
+
lines.push(`import type { ${render(types)} } from '${BUILD_PRESET_SPECIFIER}'`);
|
|
754
|
+
}
|
|
755
|
+
return lines.join("\n");
|
|
756
|
+
}
|
|
757
|
+
function rewriteBuildImports(source, fileName = "vite.config.ts") {
|
|
758
|
+
const { program, errors } = parseSync(fileName, source, { sourceType: "module" });
|
|
759
|
+
if (errors.length > 0) {
|
|
760
|
+
return {
|
|
761
|
+
kind: "blocked",
|
|
762
|
+
why: `${fileName} does not parse: ${errors[0]?.message ?? "unknown"}`
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
const imports = program.body.filter(
|
|
766
|
+
(node) => node.type === "ImportDeclaration"
|
|
767
|
+
);
|
|
768
|
+
if (imports.some((node) => node.source.value === BUILD_PRESET_SPECIFIER)) {
|
|
769
|
+
return { kind: "unchanged", why: "this config already imports its toolchain from sentinel" };
|
|
770
|
+
}
|
|
771
|
+
const moving = imports.filter((node) => MOVED_SPECIFIERS.has(node.source.value));
|
|
772
|
+
if (moving.length === 0) {
|
|
773
|
+
return {
|
|
774
|
+
kind: "unchanged",
|
|
775
|
+
why: "this config imports none of the packages sentinel owns, so there is nothing to move"
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
const bindings = [];
|
|
779
|
+
for (const node of moving) {
|
|
780
|
+
const from = node.source.value;
|
|
781
|
+
const specifiers = node.specifiers ?? [];
|
|
782
|
+
if (specifiers.length === 0) {
|
|
783
|
+
return { kind: "blocked", why: `\`import '${from}'\` has no bindings to move` };
|
|
784
|
+
}
|
|
785
|
+
for (const spec of specifiers) {
|
|
786
|
+
const typeOnly = node.importKind === "type" || spec.importKind === "type";
|
|
787
|
+
if (spec.type === "ImportDefaultSpecifier") {
|
|
788
|
+
const exported = DEFAULT_EXPORT_NAMES[from];
|
|
789
|
+
if (!exported) {
|
|
790
|
+
return {
|
|
791
|
+
kind: "blocked",
|
|
792
|
+
why: `no sentinel export for the default import of \`${from}\``
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
bindings.push({ exported, local: spec.local.name, typeOnly });
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
if (spec.type === "ImportSpecifier") {
|
|
799
|
+
bindings.push({
|
|
800
|
+
exported: spec.imported?.name ?? spec.imported?.value ?? spec.local.name,
|
|
801
|
+
local: spec.local.name,
|
|
802
|
+
typeOnly
|
|
803
|
+
});
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
return {
|
|
807
|
+
kind: "blocked",
|
|
808
|
+
why: `\`import * as ${spec.local.name} from '${from}'\` cannot be mapped onto sentinel's named exports`
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
const spans = moving.map((node) => ({ start: node.start, end: node.end })).sort((left, right) => left.start - right.start);
|
|
813
|
+
const first = spans[0];
|
|
814
|
+
const rest = spans.slice(1);
|
|
815
|
+
let out = source;
|
|
816
|
+
for (const span of rest.slice().reverse()) {
|
|
817
|
+
const after = out.slice(span.end).match(/^\r?\n/)?.[0].length ?? 0;
|
|
818
|
+
out = out.slice(0, span.start) + out.slice(span.end + after);
|
|
819
|
+
}
|
|
820
|
+
out = out.slice(0, first.start) + renderImport(bindings) + out.slice(first.end);
|
|
821
|
+
return {
|
|
822
|
+
kind: "rewritten",
|
|
823
|
+
source: out,
|
|
824
|
+
moved: [...new Set(moving.map((node) => node.source.value))].sort()
|
|
825
|
+
};
|
|
564
826
|
}
|
|
827
|
+
|
|
828
|
+
// src/roles/build/plan.ts
|
|
565
829
|
function ownedBuildDependencies(cwd) {
|
|
566
830
|
const manifest = readProjectPackageJson(cwd);
|
|
567
831
|
const keys = [];
|
|
@@ -590,9 +854,12 @@ function buildScripts(cwd) {
|
|
|
590
854
|
};
|
|
591
855
|
const existingDev = existingCommand(cwd, DEV_SCRIPT_NAME);
|
|
592
856
|
if (existingDev !== void 0) {
|
|
857
|
+
const own2 = moduleName(cwd);
|
|
593
858
|
scripts[DEV_SCRIPT_NAME] = composeDevScript(
|
|
594
859
|
existingDev,
|
|
595
|
-
|
|
860
|
+
// Trailing `--` for the same reason as the build script: whatever the caller appends
|
|
861
|
+
// reaches Vite, not sentinel's parser. It goes after `--module`, which is sentinel's own.
|
|
862
|
+
`${selfCommand(cwd, "--run --dev") ?? SENTINEL_DEV_COMMAND} --module ${own2} --`
|
|
596
863
|
);
|
|
597
864
|
}
|
|
598
865
|
return scripts;
|
|
@@ -615,15 +882,65 @@ function plan(context) {
|
|
|
615
882
|
const notes = [];
|
|
616
883
|
const operations = [];
|
|
617
884
|
const configFile = buildConfigFile(context.cwd);
|
|
885
|
+
const webpackTarget = webpackBuildTarget(context.cwd);
|
|
886
|
+
const isNest = declaredBuildPreset(context.cwd) === "nest" || webpackTarget !== void 0;
|
|
887
|
+
if (isNest) {
|
|
888
|
+
if (webpackTarget === void 0) {
|
|
889
|
+
return {
|
|
890
|
+
operations: [],
|
|
891
|
+
skipped: `already building through sentinel: ${configFile ?? NEST_CONFIG_FILE} imports the nest preset and the nx target runs it. Nothing to change.`
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
const refusal = refusalFor(context.cwd, webpackTarget);
|
|
895
|
+
if (refusal !== void 0) return { operations: [], blocked: refusal };
|
|
896
|
+
const dropped = Object.keys(webpackTarget.configurations ?? {});
|
|
897
|
+
return {
|
|
898
|
+
operations: nestAdoptionOperations(context.cwd, webpackTarget),
|
|
899
|
+
notes: [
|
|
900
|
+
`wrote ${NEST_CONFIG_FILE} and pointed the nx build target at sentinel. The target stays \`nx:run-commands\` with \`forwardAllArgs: false\`, so the image's \`--generatePackageJson\` still works and the Dockerfile needs no change.`,
|
|
901
|
+
...dropped.length > 0 ? [
|
|
902
|
+
`dropped the \`${dropped.join("`, `")}\` configuration${dropped.length > 1 ? "s" : ""} from the build target: its fileReplacements point at files that do not exist, so it replaced nothing. A live one would have blocked this instead.`
|
|
903
|
+
] : [],
|
|
904
|
+
`verify with \`nx run ${webpackTarget.project}:generate-swagger-file\` and \`git diff --exit-code\`: it runs the built bundle, so it proves the service boots AND that its published contract did not move.`
|
|
905
|
+
]
|
|
906
|
+
};
|
|
907
|
+
}
|
|
618
908
|
if (configFile === void 0) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
`
|
|
622
|
-
|
|
623
|
-
} else
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
);
|
|
909
|
+
return {
|
|
910
|
+
operations: [],
|
|
911
|
+
skipped: `no Vite config in this module, so there is nothing to point at sentinel. This role moves an existing config's imports; it does not introduce a bundler. If this module builds another way (36 services here build through \`@nx/webpack\`), the build role does not apply to it.`
|
|
912
|
+
};
|
|
913
|
+
} else {
|
|
914
|
+
const notEsm = esmBlocker(configFile, readProjectPackageJson(context.cwd).type);
|
|
915
|
+
if (notEsm !== void 0) return { operations: [], skipped: notEsm };
|
|
916
|
+
const rewrite = rewriteBuildImports(readConfigSource(context.cwd, configFile), configFile);
|
|
917
|
+
if (rewrite.kind === "blocked") {
|
|
918
|
+
return {
|
|
919
|
+
operations: [],
|
|
920
|
+
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.`
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
if (rewrite.kind === "rewritten") {
|
|
924
|
+
operations.push({ kind: "write", path: configFile, contents: rewrite.source });
|
|
925
|
+
}
|
|
926
|
+
for (const name of COMPANION_CONFIG_FILES) {
|
|
927
|
+
const source = readConfigSource(context.cwd, name);
|
|
928
|
+
if (source === "") continue;
|
|
929
|
+
const companion = rewriteBuildImports(source, name);
|
|
930
|
+
if (companion.kind === "rewritten") {
|
|
931
|
+
operations.push({ kind: "write", path: name, contents: companion.source });
|
|
932
|
+
notes.push(`${name} now imports ${companion.moved.join(", ")} from sentinel too.`);
|
|
933
|
+
} else if (companion.kind === "blocked") {
|
|
934
|
+
notes.push(
|
|
935
|
+
`${name} imports the toolchain and could NOT be pointed at sentinel: ${companion.why}. It will stop resolving when the workspace root drops these packages, and it will fail your TESTS rather than your build.`
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
if (rewrite.kind === "rewritten") {
|
|
940
|
+
notes.push(
|
|
941
|
+
`${configFile} now imports ${rewrite.moved.join(", ")} from \`${BUILD_PRESET_SPECIFIER}\`. Only the import lines changed: every attribute, alias and path is untouched, so this build produces what it produced before.`
|
|
942
|
+
);
|
|
943
|
+
}
|
|
627
944
|
}
|
|
628
945
|
operations.push(manifestOperation(context.cwd, buildScripts(context.cwd)));
|
|
629
946
|
operations.push(...nxTargetOperations({ cwd: context.cwd, targets: buildTarget() }));
|
|
@@ -636,12 +953,12 @@ function plan(context) {
|
|
|
636
953
|
}
|
|
637
954
|
return { operations, notes };
|
|
638
955
|
}
|
|
639
|
-
function
|
|
640
|
-
if (!
|
|
956
|
+
function readConfigSource(cwd, configFile) {
|
|
957
|
+
if (!existsSync8(join8(cwd, configFile))) return "";
|
|
641
958
|
try {
|
|
642
|
-
return
|
|
959
|
+
return readFileSync6(join8(cwd, configFile), "utf8");
|
|
643
960
|
} catch {
|
|
644
|
-
return
|
|
961
|
+
return "";
|
|
645
962
|
}
|
|
646
963
|
}
|
|
647
964
|
|
|
@@ -679,11 +996,17 @@ var ViteRoleAdapter = class extends BaseAdapter {
|
|
|
679
996
|
* resolution let it run at all — filtering here would give "no adapter for preset svelte",
|
|
680
997
|
* which tells nobody what to do about it.
|
|
681
998
|
*
|
|
682
|
-
* Nest
|
|
683
|
-
*
|
|
999
|
+
* Nest it now serves too, through `@hublo/sentinel/build/nest`. It used to be declined on the
|
|
1000
|
+
* grounds that "there is nothing to bundle", which was never true: those 36 services bundle
|
|
1001
|
+
* with webpack. What changed is that Nx v24 removes the `@nx/webpack:webpack` executor and
|
|
1002
|
+
* the `composePlugins` / `withNx` helpers their shared config is built on, and nx's own
|
|
1003
|
+
* migration generator refuses every one of them because they use `@nx/js:node`
|
|
1004
|
+
* (nrwl/nx#36389). So the way out runs through here.
|
|
1005
|
+
*
|
|
1006
|
+
* `node` and `tools` remain genuinely declined: nothing under them bundles at all.
|
|
684
1007
|
*/
|
|
685
1008
|
appliesTo(preset) {
|
|
686
|
-
return preset === "react" || preset === "svelte";
|
|
1009
|
+
return preset === "react" || preset === "svelte" || preset === "nest";
|
|
687
1010
|
}
|
|
688
1011
|
/**
|
|
689
1012
|
* `react`, read from the module's Vite config rather than from its dependencies.
|
|
@@ -773,6 +1096,26 @@ var ViteDevAdapter = class extends ViteRoleAdapter {
|
|
|
773
1096
|
get label() {
|
|
774
1097
|
return "dev";
|
|
775
1098
|
}
|
|
1099
|
+
/**
|
|
1100
|
+
* `--init --dev` does the same thing as `--init --build`, and now says so.
|
|
1101
|
+
*
|
|
1102
|
+
* The shared plan is deliberate (see `ViteRoleAdapter.plan`): one plan writes both scripts,
|
|
1103
|
+
* because adopting one without the other leaves the module half-migrated. What was missing
|
|
1104
|
+
* was only the telling: the run printed a message about the build and nothing about `serve`,
|
|
1105
|
+
* so a developer could believe the dev server had been adopted on its own. Refusing was the
|
|
1106
|
+
* wrong correction, it added friction to a command that already produced the right result.
|
|
1107
|
+
*/
|
|
1108
|
+
plan(context) {
|
|
1109
|
+
const planned = super.plan(context);
|
|
1110
|
+
if (planned.operations.length === 0) return planned;
|
|
1111
|
+
return {
|
|
1112
|
+
...planned,
|
|
1113
|
+
notes: [
|
|
1114
|
+
...planned.notes ?? [],
|
|
1115
|
+
"--init --dev adopts the build too: one plan writes both `build` and `serve`."
|
|
1116
|
+
]
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
776
1119
|
/**
|
|
777
1120
|
* Start the dev server. It does not return until stopped, so there is no verdict to report
|
|
778
1121
|
* beyond the exit code the developer's own Ctrl-C produces.
|
|
@@ -791,48 +1134,6 @@ var ViteDevAdapter = class extends ViteRoleAdapter {
|
|
|
791
1134
|
}
|
|
792
1135
|
};
|
|
793
1136
|
|
|
794
|
-
// src/roles/build/owned-paths.ts
|
|
795
|
-
var PRESET_OWNED_KEYS = [
|
|
796
|
-
"base",
|
|
797
|
-
"root",
|
|
798
|
-
"define",
|
|
799
|
-
"server",
|
|
800
|
-
"build",
|
|
801
|
-
"nitro",
|
|
802
|
-
"resolve",
|
|
803
|
-
"plugins"
|
|
804
|
-
];
|
|
805
|
-
function describeDepartures(config, defaults) {
|
|
806
|
-
const departures = [];
|
|
807
|
-
const build = asRecord(config.build);
|
|
808
|
-
const target = build?.target;
|
|
809
|
-
if (typeof target === "string" && target !== defaults.target) {
|
|
810
|
-
departures.push({
|
|
811
|
-
rule: "build.target",
|
|
812
|
-
reason: `${target}, where the convention is ${defaults.target}`
|
|
813
|
-
});
|
|
814
|
-
}
|
|
815
|
-
const server = asRecord(config.server);
|
|
816
|
-
const hosts = server?.allowedHosts;
|
|
817
|
-
if (Array.isArray(hosts) && !sameStrings(hosts, defaults.allowedHosts)) {
|
|
818
|
-
departures.push({
|
|
819
|
-
rule: "server.allowedHosts",
|
|
820
|
-
reason: `${JSON.stringify(hosts)}, where the convention is ${JSON.stringify(defaults.allowedHosts)}`
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
|
-
return departures;
|
|
824
|
-
}
|
|
825
|
-
function appAdditions(config) {
|
|
826
|
-
const owned = new Set(PRESET_OWNED_KEYS);
|
|
827
|
-
return Object.keys(config).filter((key) => !owned.has(key)).sort();
|
|
828
|
-
}
|
|
829
|
-
function asRecord(value) {
|
|
830
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
831
|
-
}
|
|
832
|
-
function sameStrings(a, b) {
|
|
833
|
-
return a.length === b.length && a.every((value, at2) => value === b[at2]);
|
|
834
|
-
}
|
|
835
|
-
|
|
836
1137
|
// src/roles/build/prerequisite.ts
|
|
837
1138
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
838
1139
|
var PREBUILD_SCRIPT_NAME = `pre${BUILD_SCRIPT_NAME}`;
|
|
@@ -859,23 +1160,6 @@ function runPrerequisite(cwd, env = process.env) {
|
|
|
859
1160
|
return { kind: "ran" };
|
|
860
1161
|
}
|
|
861
1162
|
|
|
862
|
-
// src/roles/build/resolve-config.ts
|
|
863
|
-
async function resolveBuildConfig(cwd) {
|
|
864
|
-
try {
|
|
865
|
-
const { loadConfigFromFile } = await import("vite");
|
|
866
|
-
const loaded = await loadConfigFromFile(
|
|
867
|
-
{ command: "build", mode: "production" },
|
|
868
|
-
void 0,
|
|
869
|
-
cwd,
|
|
870
|
-
"silent"
|
|
871
|
-
);
|
|
872
|
-
if (!loaded) return { kind: "failed", reason: "Vite found no config file in this module" };
|
|
873
|
-
return { kind: "loaded", config: loaded.config, from: loaded.path };
|
|
874
|
-
} catch (error) {
|
|
875
|
-
return { kind: "failed", reason: error instanceof Error ? error.message : String(error) };
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
|
|
879
1163
|
// src/roles/build/adapters/vite/vite.adapter.ts
|
|
880
1164
|
var ViteAdapter = class extends ViteRoleAdapter {
|
|
881
1165
|
target = "build";
|
|
@@ -901,25 +1185,30 @@ var ViteAdapter = class extends ViteRoleAdapter {
|
|
|
901
1185
|
return this.spawnVite(ctx, ready.vite, ["build"], 1);
|
|
902
1186
|
}
|
|
903
1187
|
/**
|
|
904
|
-
* What this module builds with,
|
|
1188
|
+
* What this module builds with, WITHOUT loading its config.
|
|
905
1189
|
*
|
|
906
|
-
*
|
|
907
|
-
*
|
|
908
|
-
*
|
|
1190
|
+
* Everything reported here is read from the manifest and from the config's text: whether the
|
|
1191
|
+
* module imports its toolchain from sentinel, which Vite install would answer, what it still
|
|
1192
|
+
* declares of its own, and whether it has a `prebuild` step.
|
|
909
1193
|
*
|
|
910
|
-
*
|
|
911
|
-
*
|
|
912
|
-
*
|
|
1194
|
+
* It used to also load the config through Vite and report where it departed from a preset
|
|
1195
|
+
* and what it added on top. Both were dropped with the preset itself. Adoption changes a
|
|
1196
|
+
* config's IMPORTS and nothing else, so there is no preset the app signed up to and no
|
|
1197
|
+
* distance from it to measure; and computing it meant EXECUTING an app's config, which is
|
|
1198
|
+
* expensive and can fail on its own terms (career throws without `VITE_BRAND_ID`). Reading
|
|
1199
|
+
* a file to report on it must not be able to break.
|
|
913
1200
|
*
|
|
914
|
-
*
|
|
915
|
-
*
|
|
1201
|
+
* They come back with the shared base, when there is again something an app has chosen to
|
|
1202
|
+
* adopt and can therefore depart from.
|
|
916
1203
|
*/
|
|
917
1204
|
async inspect(ctx) {
|
|
918
1205
|
const adoption = readBuildAdoption(ctx.cwd);
|
|
919
|
-
|
|
1206
|
+
return {
|
|
920
1207
|
runner: "vite",
|
|
921
1208
|
configFile: adoption.configFile,
|
|
922
1209
|
...adoption.unreadable ? { unreadable: adoption.unreadable } : {},
|
|
1210
|
+
// The field nothing else reports: an adopted config takes its plugins from sentinel, so
|
|
1211
|
+
// a module answering `module` here is one build away from the two-copies failure.
|
|
923
1212
|
vite: viteOrigin(ctx.cwd),
|
|
924
1213
|
// Rendered as a labelled block by the shared renderer, the same shape the lint role's
|
|
925
1214
|
// parked rules and the format role's overrides already use.
|
|
@@ -929,36 +1218,6 @@ var ViteAdapter = class extends ViteRoleAdapter {
|
|
|
929
1218
|
})),
|
|
930
1219
|
prerequisite: moduleScripts(ctx.cwd)[PREBUILD_SCRIPT_NAME] ?? null
|
|
931
1220
|
};
|
|
932
|
-
if (adoption.configFile === null) return base;
|
|
933
|
-
const resolved = await resolveBuildConfig(ctx.cwd);
|
|
934
|
-
if (resolved.kind === "failed") {
|
|
935
|
-
return { ...base, configError: resolved.reason };
|
|
936
|
-
}
|
|
937
|
-
const config = resolved.config;
|
|
938
|
-
return {
|
|
939
|
-
...base,
|
|
940
|
-
// What the app departs from, and what it adds, are different facts. A DEPARTURE is a
|
|
941
|
-
// disagreement with an opinion the preset holds; an ADDITION is the app needing
|
|
942
|
-
// something the preset never claimed, which is the preset working rather than being
|
|
943
|
-
// worked around.
|
|
944
|
-
departures: describeDepartures(config, REACT_APP_DEFAULTS),
|
|
945
|
-
additions: appAdditions(config),
|
|
946
|
-
resolved: {
|
|
947
|
-
base: at(config, "base") ?? null,
|
|
948
|
-
target: at(config, "build", "target") ?? null,
|
|
949
|
-
sourcemap: at(config, "build", "sourcemap") ?? null,
|
|
950
|
-
port: at(config, "server", "port") ?? null,
|
|
951
|
-
// The one number nothing else reports and that no formula predicts: career goes DOWN
|
|
952
|
-
// to 9998 where the others go up, which is why it is data rather than `port + 1`.
|
|
953
|
-
hmrPort: at(config, "server", "hmr", "port") ?? null,
|
|
954
|
-
// Counted, not listed. It is 56-67% of every config today, and printing 296 entries
|
|
955
|
-
// would bury everything above it. The count is what tells you whether it moved.
|
|
956
|
-
aliases: countOf(at(config, "resolve", "alias")),
|
|
957
|
-
// Flattened without a depth limit: Vite lets a plugin be an arbitrarily nested array,
|
|
958
|
-
// and a magic number here would silently undercount the day someone nests one deeper.
|
|
959
|
-
plugins: countOf(at(config, "plugins"))
|
|
960
|
-
}
|
|
961
|
-
};
|
|
962
1221
|
}
|
|
963
1222
|
/**
|
|
964
1223
|
* Adoption as data, WITHOUT building.
|
|
@@ -984,17 +1243,6 @@ var ViteAdapter = class extends ViteRoleAdapter {
|
|
|
984
1243
|
};
|
|
985
1244
|
}
|
|
986
1245
|
};
|
|
987
|
-
function at(config, ...path) {
|
|
988
|
-
let value = config;
|
|
989
|
-
for (const key of path) {
|
|
990
|
-
if (typeof value !== "object" || value === null) return void 0;
|
|
991
|
-
value = value[key];
|
|
992
|
-
}
|
|
993
|
-
return value;
|
|
994
|
-
}
|
|
995
|
-
function countOf(value) {
|
|
996
|
-
return Array.isArray(value) ? value.flat(Number.POSITIVE_INFINITY).length : 0;
|
|
997
|
-
}
|
|
998
1246
|
|
|
999
1247
|
// src/roles/build/register.ts
|
|
1000
1248
|
function registerBuild() {
|
|
@@ -1006,12 +1254,12 @@ function registerBuild() {
|
|
|
1006
1254
|
|
|
1007
1255
|
// src/roles/format/adapters/oxfmt/oxfmt.adapter.ts
|
|
1008
1256
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
1009
|
-
import { existsSync as
|
|
1010
|
-
import { join as
|
|
1257
|
+
import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
|
|
1258
|
+
import { join as join15 } from "path";
|
|
1011
1259
|
|
|
1012
1260
|
// src/core/config/has-source.ts
|
|
1013
1261
|
import { readdirSync } from "fs";
|
|
1014
|
-
import { extname, join as
|
|
1262
|
+
import { extname, join as join9, relative as relative3 } from "path";
|
|
1015
1263
|
var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
1016
1264
|
"node_modules",
|
|
1017
1265
|
"dist",
|
|
@@ -1062,7 +1310,7 @@ function hasSourceFiles(cwd, extensions) {
|
|
|
1062
1310
|
}
|
|
1063
1311
|
for (const entry of entries) {
|
|
1064
1312
|
if (entry.isDirectory()) {
|
|
1065
|
-
if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(
|
|
1313
|
+
if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(join9(dir, entry.name));
|
|
1066
1314
|
continue;
|
|
1067
1315
|
}
|
|
1068
1316
|
if (wanted.has(extname(entry.name))) return true;
|
|
@@ -1083,12 +1331,12 @@ function listSourceFiles(cwd, extensions) {
|
|
|
1083
1331
|
continue;
|
|
1084
1332
|
}
|
|
1085
1333
|
for (const entry of entries) {
|
|
1086
|
-
const full =
|
|
1334
|
+
const full = join9(dir, entry.name);
|
|
1087
1335
|
if (entry.isDirectory()) {
|
|
1088
1336
|
if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(full);
|
|
1089
1337
|
continue;
|
|
1090
1338
|
}
|
|
1091
|
-
if (wanted.has(extname(entry.name))) found.push(
|
|
1339
|
+
if (wanted.has(extname(entry.name))) found.push(relative3(cwd, full));
|
|
1092
1340
|
}
|
|
1093
1341
|
}
|
|
1094
1342
|
return found;
|
|
@@ -1096,8 +1344,9 @@ function listSourceFiles(cwd, extensions) {
|
|
|
1096
1344
|
|
|
1097
1345
|
// src/core/named-scope.ts
|
|
1098
1346
|
var TOLERATE_EMPTY_FLAG = "--no-error-on-unmatched-pattern";
|
|
1099
|
-
function scopeFlags(paths) {
|
|
1100
|
-
|
|
1347
|
+
function scopeFlags(paths, options = []) {
|
|
1348
|
+
if (paths.length === 0 || options.includes(TOLERATE_EMPTY_FLAG)) return [];
|
|
1349
|
+
return [TOLERATE_EMPTY_FLAG];
|
|
1101
1350
|
}
|
|
1102
1351
|
|
|
1103
1352
|
// src/core/settings.ts
|
|
@@ -1105,8 +1354,8 @@ var WORKSPACE_ROOT_MARKER = "nx.json";
|
|
|
1105
1354
|
var DEFAULT_MAX_DIAGNOSTICS = 100;
|
|
1106
1355
|
|
|
1107
1356
|
// src/core/workspace-prep.ts
|
|
1108
|
-
import { existsSync as
|
|
1109
|
-
import { dirname as dirname3, join as
|
|
1357
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync } from "fs";
|
|
1358
|
+
import { dirname as dirname3, join as join10, relative as relative4 } from "path";
|
|
1110
1359
|
var OVERRIDE_KEY = "i18next>typescript";
|
|
1111
1360
|
var NATIVE_TS_ALIAS = "@typescript/native";
|
|
1112
1361
|
var WORKSPACE_YAML = "pnpm-workspace.yaml";
|
|
@@ -1115,7 +1364,7 @@ var LIST_ITEM = /^(\s*)-\s+(.*?)\s*$/;
|
|
|
1115
1364
|
function findWorkspaceRoot(startDir) {
|
|
1116
1365
|
let dir = startDir;
|
|
1117
1366
|
for (; ; ) {
|
|
1118
|
-
if (
|
|
1367
|
+
if (existsSync9(join10(dir, WORKSPACE_ROOT_MARKER))) return dir;
|
|
1119
1368
|
const parent = dirname3(dir);
|
|
1120
1369
|
if (parent === dir) return void 0;
|
|
1121
1370
|
dir = parent;
|
|
@@ -1128,9 +1377,9 @@ function declaredNativeTs(pkg) {
|
|
|
1128
1377
|
return version || void 0;
|
|
1129
1378
|
}
|
|
1130
1379
|
function ensureI18nextSingleton(root, dryRun) {
|
|
1131
|
-
const pkgPath =
|
|
1132
|
-
if (!
|
|
1133
|
-
const pkg = JSON.parse(
|
|
1380
|
+
const pkgPath = join10(root, "package.json");
|
|
1381
|
+
if (!existsSync9(pkgPath)) return void 0;
|
|
1382
|
+
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
1134
1383
|
const want = declaredNativeTs(pkg);
|
|
1135
1384
|
if (!want) return void 0;
|
|
1136
1385
|
const have = pkg.pnpm?.overrides?.[OVERRIDE_KEY];
|
|
@@ -1143,10 +1392,10 @@ function ensureI18nextSingleton(root, dryRun) {
|
|
|
1143
1392
|
return `pinned ${OVERRIDE_KEY} to ${want} in package.json (i18next singleton)`;
|
|
1144
1393
|
}
|
|
1145
1394
|
function ensureReleaseAgeAllowList(root, dryRun) {
|
|
1146
|
-
const yamlPath =
|
|
1147
|
-
if (!
|
|
1395
|
+
const yamlPath = join10(root, WORKSPACE_YAML);
|
|
1396
|
+
if (!existsSync9(yamlPath)) return void 0;
|
|
1148
1397
|
const own = readOwnPackage().name;
|
|
1149
|
-
const lines =
|
|
1398
|
+
const lines = readFileSync7(yamlPath, "utf8").split("\n");
|
|
1150
1399
|
const keyIdx = lines.findIndex((line) => line.replace(/\s+$/, "") === `${RELEASE_AGE_KEY}:`);
|
|
1151
1400
|
if (keyIdx === -1) return void 0;
|
|
1152
1401
|
let lastItemIdx = keyIdx;
|
|
@@ -1179,13 +1428,13 @@ var ROOT_PRETTIER_CONFIGS = [
|
|
|
1179
1428
|
"prettier.config.mjs"
|
|
1180
1429
|
];
|
|
1181
1430
|
function ensureFormatterExclusion(root, moduleDir, dryRun) {
|
|
1182
|
-
const rel =
|
|
1431
|
+
const rel = relative4(root, moduleDir).replaceAll("\\", "/");
|
|
1183
1432
|
if (rel === "" || rel.startsWith("..")) return void 0;
|
|
1184
|
-
const ignorePath =
|
|
1185
|
-
const hasPrettier =
|
|
1433
|
+
const ignorePath = join10(root, PRETTIER_IGNORE);
|
|
1434
|
+
const hasPrettier = existsSync9(ignorePath) || ROOT_PRETTIER_CONFIGS.some((name) => existsSync9(join10(root, name)));
|
|
1186
1435
|
if (!hasPrettier) return void 0;
|
|
1187
1436
|
const pattern = `/${rel}/`;
|
|
1188
|
-
const existing =
|
|
1437
|
+
const existing = existsSync9(ignorePath) ? readFileSync7(ignorePath, "utf8") : "";
|
|
1189
1438
|
const lines = existing.split("\n");
|
|
1190
1439
|
if (lines.some((line) => line.trim().replace(/\/$/, "") === pattern.replace(/\/$/, ""))) {
|
|
1191
1440
|
return void 0;
|
|
@@ -1221,10 +1470,10 @@ function ensureWorkspacePrep(opts) {
|
|
|
1221
1470
|
function inspectWorkspacePrep(root) {
|
|
1222
1471
|
const entries = [];
|
|
1223
1472
|
let pkg = {};
|
|
1224
|
-
const pkgPath =
|
|
1225
|
-
if (
|
|
1473
|
+
const pkgPath = join10(root, "package.json");
|
|
1474
|
+
if (existsSync9(pkgPath)) {
|
|
1226
1475
|
try {
|
|
1227
|
-
pkg = JSON.parse(
|
|
1476
|
+
pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
1228
1477
|
} catch {
|
|
1229
1478
|
pkg = {};
|
|
1230
1479
|
}
|
|
@@ -1238,9 +1487,9 @@ function inspectWorkspacePrep(root) {
|
|
|
1238
1487
|
});
|
|
1239
1488
|
}
|
|
1240
1489
|
const own = readOwnPackage().name;
|
|
1241
|
-
const yamlPath =
|
|
1242
|
-
if (
|
|
1243
|
-
const yaml =
|
|
1490
|
+
const yamlPath = join10(root, WORKSPACE_YAML);
|
|
1491
|
+
if (existsSync9(yamlPath)) {
|
|
1492
|
+
const yaml = readFileSync7(yamlPath, "utf8");
|
|
1244
1493
|
const listed = new RegExp(`^\\s*-\\s*['"]?${own.replace("/", "\\/")}['"]?\\s*$`, "m").test(yaml);
|
|
1245
1494
|
if (listed) {
|
|
1246
1495
|
const gated = new RegExp(`^\\s*minimumReleaseAge\\s*:`, "m").test(yaml);
|
|
@@ -1327,8 +1576,8 @@ function writesWhenRewritten(name, command) {
|
|
|
1327
1576
|
var CHECK_SCRIPT_NAMES = /* @__PURE__ */ new Set(["lint", "format", "check", "test", "typecheck", "verify"]);
|
|
1328
1577
|
|
|
1329
1578
|
// src/roles/format/inherited-ignores.ts
|
|
1330
|
-
import { existsSync as
|
|
1331
|
-
import { join as
|
|
1579
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
1580
|
+
import { join as join11 } from "path";
|
|
1332
1581
|
var ROOT_IGNORE_FILE = ".prettierignore";
|
|
1333
1582
|
function isPattern(line) {
|
|
1334
1583
|
const trimmed = line.trim();
|
|
@@ -1343,11 +1592,11 @@ function toModulePattern(pattern) {
|
|
|
1343
1592
|
}
|
|
1344
1593
|
function inheritedIgnorePatterns(workspaceRoot) {
|
|
1345
1594
|
if (!workspaceRoot) return [];
|
|
1346
|
-
const path =
|
|
1347
|
-
if (!
|
|
1595
|
+
const path = join11(workspaceRoot, ROOT_IGNORE_FILE);
|
|
1596
|
+
if (!existsSync10(path)) return [];
|
|
1348
1597
|
let contents;
|
|
1349
1598
|
try {
|
|
1350
|
-
contents =
|
|
1599
|
+
contents = readFileSync8(path, "utf8");
|
|
1351
1600
|
} catch {
|
|
1352
1601
|
return [];
|
|
1353
1602
|
}
|
|
@@ -1434,13 +1683,13 @@ function formatPresetFor(preset) {
|
|
|
1434
1683
|
}
|
|
1435
1684
|
|
|
1436
1685
|
// src/roles/format/prettier-config.ts
|
|
1437
|
-
import { existsSync as
|
|
1438
|
-
import { join as
|
|
1686
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
1687
|
+
import { join as join13 } from "path";
|
|
1439
1688
|
|
|
1440
1689
|
// src/roles/format/resolve-oxfmt.ts
|
|
1441
|
-
import { readFileSync as
|
|
1690
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
1442
1691
|
import { createRequire as createRequire2 } from "module";
|
|
1443
|
-
import { dirname as dirname4, join as
|
|
1692
|
+
import { dirname as dirname4, join as join12 } from "path";
|
|
1444
1693
|
function resolveOxfmt(cwd) {
|
|
1445
1694
|
return resolveBin(cwd, "oxfmt") ?? binFromOwnInstall("oxfmt", "oxfmt");
|
|
1446
1695
|
}
|
|
@@ -1469,8 +1718,8 @@ function configSchema() {
|
|
|
1469
1718
|
function readConfigSchema() {
|
|
1470
1719
|
try {
|
|
1471
1720
|
const manifest = createRequire2(import.meta.url).resolve("oxfmt/package.json");
|
|
1472
|
-
const schemaPath =
|
|
1473
|
-
return JSON.parse(
|
|
1721
|
+
const schemaPath = join12(dirname4(manifest), "configuration_schema.json");
|
|
1722
|
+
return JSON.parse(readFileSync9(schemaPath, "utf8"));
|
|
1474
1723
|
} catch {
|
|
1475
1724
|
return void 0;
|
|
1476
1725
|
}
|
|
@@ -1501,14 +1750,14 @@ function toOxfmtOverrides(value) {
|
|
|
1501
1750
|
return { overrides, unresolved };
|
|
1502
1751
|
}
|
|
1503
1752
|
function readPrettierSettings(cwd) {
|
|
1504
|
-
const file = PRETTIER_CONFIG_FILES.find((name) =>
|
|
1753
|
+
const file = PRETTIER_CONFIG_FILES.find((name) => existsSync11(join13(cwd, name)));
|
|
1505
1754
|
if (!file) return { options: {}, unresolved: [] };
|
|
1506
1755
|
if (/\.(js|cjs|mjs)$/.test(file)) {
|
|
1507
1756
|
return { options: {}, file, unresolved: [`${file} is JavaScript, so it was not executed`] };
|
|
1508
1757
|
}
|
|
1509
1758
|
let parsed;
|
|
1510
1759
|
try {
|
|
1511
|
-
parsed = parseJsonc(
|
|
1760
|
+
parsed = parseJsonc(readFileSync10(join13(cwd, file), "utf8"), file);
|
|
1512
1761
|
} catch {
|
|
1513
1762
|
return { options: {}, file, unresolved: [`${file} could not be parsed`] };
|
|
1514
1763
|
}
|
|
@@ -1538,8 +1787,8 @@ function readPrettierSettings(cwd) {
|
|
|
1538
1787
|
}
|
|
1539
1788
|
|
|
1540
1789
|
// src/roles/format/read-adoption.ts
|
|
1541
|
-
import { existsSync as
|
|
1542
|
-
import { join as
|
|
1790
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
1791
|
+
import { join as join14 } from "path";
|
|
1543
1792
|
var NOT_ADOPTED2 = (configFile, unreadable = null) => ({
|
|
1544
1793
|
configFile,
|
|
1545
1794
|
preset: null,
|
|
@@ -1555,11 +1804,11 @@ function sameValue(a, b) {
|
|
|
1555
1804
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
1556
1805
|
}
|
|
1557
1806
|
function readFormatAdoption(cwd) {
|
|
1558
|
-
const path =
|
|
1559
|
-
if (!
|
|
1807
|
+
const path = join14(cwd, FORMAT_CONFIG_FILE);
|
|
1808
|
+
if (!existsSync12(path)) return NOT_ADOPTED2(null);
|
|
1560
1809
|
let parsed;
|
|
1561
1810
|
try {
|
|
1562
|
-
parsed = parseJsonc(
|
|
1811
|
+
parsed = parseJsonc(readFileSync11(path, "utf8"), FORMAT_CONFIG_FILE);
|
|
1563
1812
|
} catch (error) {
|
|
1564
1813
|
const reason = error instanceof Error ? error.message : String(error);
|
|
1565
1814
|
return NOT_ADOPTED2(FORMAT_CONFIG_FILE, `${FORMAT_CONFIG_FILE} could not be parsed (${reason})`);
|
|
@@ -1684,7 +1933,7 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1684
1933
|
manifestOperation(context.cwd, this.formatScripts(context.cwd))
|
|
1685
1934
|
];
|
|
1686
1935
|
const prettierConfigs = PRETTIER_CONFIG_FILES.filter(
|
|
1687
|
-
(name) =>
|
|
1936
|
+
(name) => existsSync13(join15(context.cwd, name))
|
|
1688
1937
|
);
|
|
1689
1938
|
for (const name of prettierConfigs) operations.push({ kind: "delete", path: name });
|
|
1690
1939
|
const removableDeps = this.modulePrettierDependencies(context.cwd);
|
|
@@ -1808,7 +2057,7 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1808
2057
|
});
|
|
1809
2058
|
const result = spawnSync3(
|
|
1810
2059
|
oxfmt,
|
|
1811
|
-
[mode, ...scopeFlags(paths), ...options, ...paths.length > 0 ? paths : ["."]],
|
|
2060
|
+
[mode, ...scopeFlags(paths, options), ...options, ...paths.length > 0 ? paths : ["."]],
|
|
1812
2061
|
{
|
|
1813
2062
|
cwd: ctx.cwd,
|
|
1814
2063
|
stdio: "inherit"
|
|
@@ -1924,7 +2173,7 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1924
2173
|
modulePrettierDependencies(cwd) {
|
|
1925
2174
|
const isPrettierPackage = (name) => name === "prettier" || name.startsWith("prettier-plugin-") || name.startsWith("@prettier/");
|
|
1926
2175
|
try {
|
|
1927
|
-
const manifest = JSON.parse(
|
|
2176
|
+
const manifest = JSON.parse(readFileSync12(join15(cwd, "package.json"), "utf8"));
|
|
1928
2177
|
return Object.keys(manifest.devDependencies ?? {}).filter(isPrettierPackage).map((name) => ["devDependencies", name]);
|
|
1929
2178
|
} catch {
|
|
1930
2179
|
return [];
|
|
@@ -2006,8 +2255,8 @@ function registerFormat() {
|
|
|
2006
2255
|
|
|
2007
2256
|
// src/roles/lint/adapters/oxlint/oxlint.adapter.ts
|
|
2008
2257
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
2009
|
-
import { existsSync as
|
|
2010
|
-
import { join as
|
|
2258
|
+
import { existsSync as existsSync21, readFileSync as readFileSync20, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
2259
|
+
import { join as join25 } from "path";
|
|
2011
2260
|
|
|
2012
2261
|
// src/core/config/deferred-rules.ts
|
|
2013
2262
|
function deferredRuleNames(rules) {
|
|
@@ -2016,7 +2265,7 @@ function deferredRuleNames(rules) {
|
|
|
2016
2265
|
|
|
2017
2266
|
// src/core/fix-report.ts
|
|
2018
2267
|
import { statSync } from "fs";
|
|
2019
|
-
import { join as
|
|
2268
|
+
import { join as join16 } from "path";
|
|
2020
2269
|
var CONFIG_REFUSED = /failed to parse .*config|invalid config file/i;
|
|
2021
2270
|
function readFailure(output) {
|
|
2022
2271
|
const lines = output.split("\n").map((line2) => line2.trim());
|
|
@@ -2036,7 +2285,7 @@ function fingerprint(path) {
|
|
|
2036
2285
|
function snapshotFiles(cwd, extensions) {
|
|
2037
2286
|
const snapshot = /* @__PURE__ */ new Map();
|
|
2038
2287
|
for (const file of listSourceFiles(cwd, extensions)) {
|
|
2039
|
-
const mark = fingerprint(
|
|
2288
|
+
const mark = fingerprint(join16(cwd, file));
|
|
2040
2289
|
if (mark !== void 0) snapshot.set(file, mark);
|
|
2041
2290
|
}
|
|
2042
2291
|
return snapshot;
|
|
@@ -2044,7 +2293,7 @@ function snapshotFiles(cwd, extensions) {
|
|
|
2044
2293
|
function changedSince(cwd, before, extensions) {
|
|
2045
2294
|
const changed = [];
|
|
2046
2295
|
for (const file of listSourceFiles(cwd, extensions)) {
|
|
2047
|
-
const now = fingerprint(
|
|
2296
|
+
const now = fingerprint(join16(cwd, file));
|
|
2048
2297
|
if (now === void 0) continue;
|
|
2049
2298
|
if (before.get(file) !== now) changed.push(file);
|
|
2050
2299
|
}
|
|
@@ -2063,8 +2312,8 @@ function describeFixOutcome(outcome, toolLabel) {
|
|
|
2063
2312
|
}
|
|
2064
2313
|
|
|
2065
2314
|
// src/roles/lint/module-baseline.ts
|
|
2066
|
-
import { existsSync as
|
|
2067
|
-
import { join as
|
|
2315
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
|
|
2316
|
+
import { join as join17 } from "path";
|
|
2068
2317
|
var LINT_BASELINE_FILE = ".oxlintrc.baseline.json";
|
|
2069
2318
|
var LINT_MEASURE_FILE = ".oxlintrc.measure.json";
|
|
2070
2319
|
var LINT_BASELINE_SPECIFIER = `./${LINT_BASELINE_FILE}`;
|
|
@@ -2108,11 +2357,11 @@ function describeHolds(holds) {
|
|
|
2108
2357
|
return `held ${holds.length} rule(s) at \`warn\` for this module, covering ${total} violation(s) that were already there: ${listed}${rest}. They are written to ${LINT_BASELINE_FILE}, they still report, and they return to \`error\` once fixed and re-initialized`;
|
|
2109
2358
|
}
|
|
2110
2359
|
function heldRules(cwd) {
|
|
2111
|
-
const path =
|
|
2112
|
-
if (!
|
|
2360
|
+
const path = join17(cwd, LINT_BASELINE_FILE);
|
|
2361
|
+
if (!existsSync14(path)) return [];
|
|
2113
2362
|
try {
|
|
2114
2363
|
const parsed = parseJsonc(
|
|
2115
|
-
|
|
2364
|
+
readFileSync13(path, "utf8"),
|
|
2116
2365
|
LINT_BASELINE_FILE
|
|
2117
2366
|
);
|
|
2118
2367
|
return Object.keys(parsed.rules ?? {});
|
|
@@ -3935,14 +4184,14 @@ function downgradedRulesFor(preset) {
|
|
|
3935
4184
|
}
|
|
3936
4185
|
|
|
3937
4186
|
// src/roles/lint/extra-layers.ts
|
|
3938
|
-
import { existsSync as
|
|
3939
|
-
import { isAbsolute as isAbsolute2, join as
|
|
4187
|
+
import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
|
|
4188
|
+
import { isAbsolute as isAbsolute2, join as join18, resolve as resolve3 } from "path";
|
|
3940
4189
|
function ruleCount(cwd, specifier) {
|
|
3941
4190
|
const path = isAbsolute2(specifier) ? specifier : resolve3(cwd, specifier);
|
|
3942
|
-
if (!
|
|
4191
|
+
if (!existsSync15(path)) return void 0;
|
|
3943
4192
|
try {
|
|
3944
4193
|
const parsed = parseJsonc(
|
|
3945
|
-
|
|
4194
|
+
readFileSync14(path, "utf8"),
|
|
3946
4195
|
specifier
|
|
3947
4196
|
);
|
|
3948
4197
|
return Object.keys(parsed.rules ?? {}).length;
|
|
@@ -3962,7 +4211,7 @@ function extraLayers(cwd, extendsList) {
|
|
|
3962
4211
|
function committedExtendsList(cwd, configFile) {
|
|
3963
4212
|
try {
|
|
3964
4213
|
const parsed = parseJsonc(
|
|
3965
|
-
|
|
4214
|
+
readFileSync14(join18(cwd, configFile), "utf8"),
|
|
3966
4215
|
configFile
|
|
3967
4216
|
);
|
|
3968
4217
|
return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
|
|
@@ -4021,8 +4270,8 @@ function errorCountsByConfigRule(stdout) {
|
|
|
4021
4270
|
}
|
|
4022
4271
|
|
|
4023
4272
|
// src/core/config/read-adoption.ts
|
|
4024
|
-
import { existsSync as
|
|
4025
|
-
import { join as
|
|
4273
|
+
import { existsSync as existsSync17, readFileSync as readFileSync16 } from "fs";
|
|
4274
|
+
import { join as join20 } from "path";
|
|
4026
4275
|
|
|
4027
4276
|
// src/core/config/owned-keys.ts
|
|
4028
4277
|
function presetOwnedKeys(config, permitted, presetSets) {
|
|
@@ -4037,12 +4286,12 @@ function localOnlyKeys(config, permitted, presetSets) {
|
|
|
4037
4286
|
}
|
|
4038
4287
|
|
|
4039
4288
|
// src/core/config/resolve-config-target.ts
|
|
4040
|
-
import { existsSync as
|
|
4041
|
-
import { join as
|
|
4289
|
+
import { existsSync as existsSync16, readFileSync as readFileSync15 } from "fs";
|
|
4290
|
+
import { join as join19 } from "path";
|
|
4042
4291
|
function readExtends(absolutePath) {
|
|
4043
4292
|
let parsed;
|
|
4044
4293
|
try {
|
|
4045
|
-
parsed = parseJsonc(
|
|
4294
|
+
parsed = parseJsonc(readFileSync15(absolutePath, "utf8"), absolutePath);
|
|
4046
4295
|
} catch {
|
|
4047
4296
|
return [];
|
|
4048
4297
|
}
|
|
@@ -4056,8 +4305,8 @@ function resolveConfigTarget(moduleDir, { candidates, markers, fallback }) {
|
|
|
4056
4305
|
let existing;
|
|
4057
4306
|
let existingExtendsSomething = false;
|
|
4058
4307
|
for (const candidate of candidates) {
|
|
4059
|
-
const absolutePath =
|
|
4060
|
-
if (!
|
|
4308
|
+
const absolutePath = join19(moduleDir, candidate);
|
|
4309
|
+
if (!existsSync16(absolutePath)) continue;
|
|
4061
4310
|
const chain = readExtends(absolutePath);
|
|
4062
4311
|
if (existing === void 0) {
|
|
4063
4312
|
existing = candidate;
|
|
@@ -4090,12 +4339,12 @@ var NOT_ADOPTED3 = (configFile, unreadable = null) => ({
|
|
|
4090
4339
|
});
|
|
4091
4340
|
function readAdoption(cwd, options) {
|
|
4092
4341
|
const target = resolveConfigTarget(cwd, options);
|
|
4093
|
-
if (target.reason === "none" || !
|
|
4342
|
+
if (target.reason === "none" || !existsSync17(join20(cwd, target.path))) {
|
|
4094
4343
|
return NOT_ADOPTED3(target.reason === "none" ? null : target.path);
|
|
4095
4344
|
}
|
|
4096
4345
|
let parsed;
|
|
4097
4346
|
try {
|
|
4098
|
-
parsed = parseJsonc(
|
|
4347
|
+
parsed = parseJsonc(readFileSync16(join20(cwd, target.path), "utf8"), target.path);
|
|
4099
4348
|
} catch (error) {
|
|
4100
4349
|
const reason = error instanceof Error ? error.message : String(error);
|
|
4101
4350
|
return NOT_ADOPTED3(target.path, `${target.path} could not be parsed (${reason})`);
|
|
@@ -4129,9 +4378,9 @@ function readLintAdoption(cwd) {
|
|
|
4129
4378
|
}
|
|
4130
4379
|
|
|
4131
4380
|
// src/roles/lint/resolve-oxlint.ts
|
|
4132
|
-
import { existsSync as
|
|
4381
|
+
import { existsSync as existsSync18 } from "fs";
|
|
4133
4382
|
import { createRequire as createRequire3 } from "module";
|
|
4134
|
-
import { delimiter as delimiter2, dirname as dirname5, join as
|
|
4383
|
+
import { delimiter as delimiter2, dirname as dirname5, join as join21 } from "path";
|
|
4135
4384
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4136
4385
|
var PACKAGE_OF = {
|
|
4137
4386
|
oxlint: "oxlint",
|
|
@@ -4156,32 +4405,31 @@ function tsgolintShim(cwd) {
|
|
|
4156
4405
|
for (const owner of ["oxlint-tsgolint", "oxlint"]) {
|
|
4157
4406
|
try {
|
|
4158
4407
|
const packageDir = dirname5(require3.resolve(`${owner}/package.json`));
|
|
4159
|
-
candidates.push(
|
|
4160
|
-
candidates.push(
|
|
4408
|
+
candidates.push(join21(packageDir, "node_modules", ".bin", "tsgolint"));
|
|
4409
|
+
candidates.push(join21(packageDir, "..", ".bin", "tsgolint"));
|
|
4161
4410
|
} catch {
|
|
4162
4411
|
}
|
|
4163
4412
|
}
|
|
4164
4413
|
try {
|
|
4165
4414
|
const ownRoot = dirname5(require3.resolve("@hublo/sentinel/package.json"));
|
|
4166
|
-
candidates.push(
|
|
4415
|
+
candidates.push(join21(ownRoot, "node_modules", ".bin", "tsgolint"));
|
|
4167
4416
|
} catch {
|
|
4168
4417
|
candidates.push(resolveBin(dirname5(fileURLToPath2(import.meta.url)), "tsgolint") ?? "");
|
|
4169
4418
|
}
|
|
4170
|
-
return candidates.find((candidate) => candidate !== "" &&
|
|
4419
|
+
return candidates.find((candidate) => candidate !== "" && existsSync18(candidate));
|
|
4171
4420
|
}
|
|
4172
4421
|
function oxlintSearchPath(cwd) {
|
|
4173
4422
|
return binSearchPath(cwd);
|
|
4174
4423
|
}
|
|
4175
4424
|
|
|
4176
4425
|
// src/roles/lint/adapters/oxlint/plan.ts
|
|
4177
|
-
import { existsSync as
|
|
4178
|
-
import { join as
|
|
4426
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19 } from "fs";
|
|
4427
|
+
import { join as join24 } from "path";
|
|
4179
4428
|
|
|
4180
4429
|
// src/roles/lint/eslint-ignores.ts
|
|
4181
|
-
import { existsSync as
|
|
4182
|
-
import { join as
|
|
4183
|
-
|
|
4184
|
-
var STRING_LITERAL = /['"`]([^'"`]+)['"`]/g;
|
|
4430
|
+
import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
|
|
4431
|
+
import { join as join22 } from "path";
|
|
4432
|
+
import { parseSync as parseSync2 } from "oxc-parser";
|
|
4185
4433
|
var OPAQUE_SOURCE = /\b(includeIgnoreFile)\s*\([^)]*\)/g;
|
|
4186
4434
|
function toOxlintPattern(pattern) {
|
|
4187
4435
|
return pattern.replace(/^\.\//, "");
|
|
@@ -4192,24 +4440,71 @@ function readRootEslintIgnores(root) {
|
|
|
4192
4440
|
return { patterns: patterns.filter((pattern) => pattern.startsWith("**/")), unresolved };
|
|
4193
4441
|
}
|
|
4194
4442
|
function readEslintIgnores(cwd) {
|
|
4195
|
-
const config = ESLINT_CONFIG_FILES.map((name) =>
|
|
4443
|
+
const config = ESLINT_CONFIG_FILES.map((name) => join22(cwd, name)).find((path) => existsSync19(path));
|
|
4196
4444
|
if (!config) return { patterns: [], unresolved: [] };
|
|
4197
4445
|
let source;
|
|
4198
4446
|
try {
|
|
4199
|
-
source =
|
|
4447
|
+
source = readFileSync17(config, "utf8");
|
|
4200
4448
|
} catch {
|
|
4201
4449
|
return { patterns: [], unresolved: [] };
|
|
4202
4450
|
}
|
|
4203
4451
|
const patterns = /* @__PURE__ */ new Set();
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4452
|
+
const unresolved = new Set([...source.matchAll(OPAQUE_SOURCE)].map((match) => match[0]));
|
|
4453
|
+
const { program, errors } = parseSync2(config, source, { sourceType: "module" });
|
|
4454
|
+
if (errors.length > 0) {
|
|
4455
|
+
return { patterns: [], unresolved: [`${config} could not be parsed`] };
|
|
4456
|
+
}
|
|
4457
|
+
walk(program, (node) => {
|
|
4458
|
+
if (node.type === "CallExpression" && calleeName(node) === "globalIgnores") {
|
|
4459
|
+
for (const literal of stringLiterals(firstArrayArgument(node))) {
|
|
4460
|
+
patterns.add(toOxlintPattern(literal));
|
|
4208
4461
|
}
|
|
4462
|
+
return;
|
|
4209
4463
|
}
|
|
4464
|
+
if (node.type !== "ObjectExpression") return;
|
|
4465
|
+
const keys = objectKeys(node);
|
|
4466
|
+
if (!keys.includes("ignores")) return;
|
|
4467
|
+
const values = arrayValues(node, "ignores");
|
|
4468
|
+
if (keys.length > 1) {
|
|
4469
|
+
unresolved.add(
|
|
4470
|
+
`ignores scoped to one config block (with ${keys.filter((k) => k !== "ignores").join(", ")}): ${values.join(", ")} \u2014 exempt from those RULES, not excluded from linting`
|
|
4471
|
+
);
|
|
4472
|
+
return;
|
|
4473
|
+
}
|
|
4474
|
+
for (const literal of values) patterns.add(toOxlintPattern(literal));
|
|
4475
|
+
});
|
|
4476
|
+
return { patterns: [...patterns], unresolved: [...unresolved] };
|
|
4477
|
+
}
|
|
4478
|
+
function walk(node, visit) {
|
|
4479
|
+
if (!node || typeof node !== "object") return;
|
|
4480
|
+
const current = node;
|
|
4481
|
+
if (typeof current.type === "string") visit(current);
|
|
4482
|
+
for (const key of Object.keys(current)) {
|
|
4483
|
+
const value = current[key];
|
|
4484
|
+
if (Array.isArray(value)) for (const entry of value) walk(entry, visit);
|
|
4485
|
+
else if (value && typeof value === "object") walk(value, visit);
|
|
4210
4486
|
}
|
|
4211
|
-
|
|
4212
|
-
|
|
4487
|
+
}
|
|
4488
|
+
function calleeName(node) {
|
|
4489
|
+
const callee = node.callee;
|
|
4490
|
+
return callee?.type === "Identifier" ? callee.name : void 0;
|
|
4491
|
+
}
|
|
4492
|
+
function firstArrayArgument(node) {
|
|
4493
|
+
const args = node.arguments ?? [];
|
|
4494
|
+
return args.find((argument) => argument?.type === "ArrayExpression");
|
|
4495
|
+
}
|
|
4496
|
+
function stringLiterals(array) {
|
|
4497
|
+
const elements = array?.elements ?? [];
|
|
4498
|
+
return elements.filter((element) => element?.type === "Literal" && typeof element.value === "string").map((element) => element.value);
|
|
4499
|
+
}
|
|
4500
|
+
function objectKeys(node) {
|
|
4501
|
+
const properties = node.properties ?? [];
|
|
4502
|
+
return properties.filter((property) => property?.type === "Property" && !property.computed).map((property) => property.key?.name ?? String(property.key?.value ?? "")).filter(Boolean);
|
|
4503
|
+
}
|
|
4504
|
+
function arrayValues(node, name) {
|
|
4505
|
+
const properties = node.properties ?? [];
|
|
4506
|
+
const found = properties.find((property) => property.key?.name === name);
|
|
4507
|
+
return stringLiterals(found?.value);
|
|
4213
4508
|
}
|
|
4214
4509
|
|
|
4215
4510
|
// src/roles/lint/lint-script.ts
|
|
@@ -4250,8 +4545,8 @@ function lintPresetFor(preset) {
|
|
|
4250
4545
|
}
|
|
4251
4546
|
|
|
4252
4547
|
// src/roles/lint/rename-suppressions.ts
|
|
4253
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
4254
|
-
import { join as
|
|
4548
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync18, statSync as statSync2 } from "fs";
|
|
4549
|
+
import { join as join23, relative as relative5 } from "path";
|
|
4255
4550
|
var SOURCE_EXTENSIONS = [
|
|
4256
4551
|
".ts",
|
|
4257
4552
|
".tsx",
|
|
@@ -4298,7 +4593,7 @@ function* sourceFiles(dir) {
|
|
|
4298
4593
|
return;
|
|
4299
4594
|
}
|
|
4300
4595
|
for (const entry of entries) {
|
|
4301
|
-
const full =
|
|
4596
|
+
const full = join23(dir, entry);
|
|
4302
4597
|
let isDirectory;
|
|
4303
4598
|
try {
|
|
4304
4599
|
isDirectory = statSync2(full).isDirectory();
|
|
@@ -4318,7 +4613,7 @@ function findSuppressionRenames(cwd, renames) {
|
|
|
4318
4613
|
for (const file of sourceFiles(cwd)) {
|
|
4319
4614
|
let content;
|
|
4320
4615
|
try {
|
|
4321
|
-
content =
|
|
4616
|
+
content = readFileSync18(file, "utf8");
|
|
4322
4617
|
} catch {
|
|
4323
4618
|
continue;
|
|
4324
4619
|
}
|
|
@@ -4342,7 +4637,7 @@ function findSuppressionRenames(cwd, renames) {
|
|
|
4342
4637
|
}
|
|
4343
4638
|
if (hits.length === 0) return;
|
|
4344
4639
|
found.push({
|
|
4345
|
-
file:
|
|
4640
|
+
file: relative5(cwd, file),
|
|
4346
4641
|
line: index + 1,
|
|
4347
4642
|
from: line,
|
|
4348
4643
|
to: line.replace(rules, renamed.join(", ")),
|
|
@@ -4446,7 +4741,7 @@ function plan2(context) {
|
|
|
4446
4741
|
keys: removableDeps
|
|
4447
4742
|
});
|
|
4448
4743
|
}
|
|
4449
|
-
const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) =>
|
|
4744
|
+
const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) => existsSync20(join24(context.cwd, name)));
|
|
4450
4745
|
for (const name of eslintConfigs) operations.push({ kind: "delete", path: name });
|
|
4451
4746
|
operations.push(
|
|
4452
4747
|
...nxTargetOperations({
|
|
@@ -4544,7 +4839,7 @@ function lintScripts(cwd) {
|
|
|
4544
4839
|
function committedExtends(cwd) {
|
|
4545
4840
|
try {
|
|
4546
4841
|
const parsed = parseJsonc(
|
|
4547
|
-
|
|
4842
|
+
readFileSync19(join24(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4548
4843
|
LINT_CONFIG_FILE
|
|
4549
4844
|
);
|
|
4550
4845
|
return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
|
|
@@ -4555,7 +4850,7 @@ function committedExtends(cwd) {
|
|
|
4555
4850
|
function committedIgnorePatterns(cwd) {
|
|
4556
4851
|
try {
|
|
4557
4852
|
const parsed = parseJsonc(
|
|
4558
|
-
|
|
4853
|
+
readFileSync19(join24(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4559
4854
|
LINT_CONFIG_FILE
|
|
4560
4855
|
);
|
|
4561
4856
|
return Array.isArray(parsed.ignorePatterns) ? parsed.ignorePatterns.filter((entry) => typeof entry === "string") : [];
|
|
@@ -4567,7 +4862,7 @@ function moduleEslintDependencies(cwd) {
|
|
|
4567
4862
|
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/");
|
|
4568
4863
|
let manifest;
|
|
4569
4864
|
try {
|
|
4570
|
-
manifest = JSON.parse(
|
|
4865
|
+
manifest = JSON.parse(readFileSync19(join24(cwd, "package.json"), "utf8"));
|
|
4571
4866
|
} catch {
|
|
4572
4867
|
return [];
|
|
4573
4868
|
}
|
|
@@ -4701,20 +4996,20 @@ ${result.stderr ?? ""}`);
|
|
|
4701
4996
|
* build the developer can see, rather than a silent half-adoption they cannot.
|
|
4702
4997
|
*/
|
|
4703
4998
|
writeModuleBaseline(ctx, oxlint, env) {
|
|
4704
|
-
const configPath =
|
|
4705
|
-
const baselinePath =
|
|
4706
|
-
if (!
|
|
4999
|
+
const configPath = join25(ctx.cwd, LINT_CONFIG_FILE);
|
|
5000
|
+
const baselinePath = join25(ctx.cwd, LINT_BASELINE_FILE);
|
|
5001
|
+
if (!existsSync21(configPath)) return;
|
|
4707
5002
|
let config;
|
|
4708
5003
|
try {
|
|
4709
5004
|
config = parseJsonc(
|
|
4710
|
-
|
|
5005
|
+
readFileSync20(configPath, "utf8"),
|
|
4711
5006
|
LINT_CONFIG_FILE
|
|
4712
5007
|
);
|
|
4713
5008
|
} catch {
|
|
4714
5009
|
return;
|
|
4715
5010
|
}
|
|
4716
5011
|
const current = Array.isArray(config.extends) ? config.extends : [];
|
|
4717
|
-
const measurePath =
|
|
5012
|
+
const measurePath = join25(ctx.cwd, LINT_MEASURE_FILE);
|
|
4718
5013
|
let measured;
|
|
4719
5014
|
try {
|
|
4720
5015
|
writeFileSync2(
|
|
@@ -4755,14 +5050,14 @@ ${result.stderr ?? ""}`);
|
|
|
4755
5050
|
} else {
|
|
4756
5051
|
rmSync(baselinePath, { force: true });
|
|
4757
5052
|
}
|
|
4758
|
-
if (next.length !== current.length || next.some((entry,
|
|
5053
|
+
if (next.length !== current.length || next.some((entry, at) => entry !== current[at])) {
|
|
4759
5054
|
config.extends = next;
|
|
4760
5055
|
writeFileSync2(configPath, `${JSON.stringify(config, null, 2)}
|
|
4761
5056
|
`);
|
|
4762
5057
|
}
|
|
4763
5058
|
}
|
|
4764
5059
|
async run(ctx) {
|
|
4765
|
-
if (!
|
|
5060
|
+
if (!existsSync21(join25(ctx.cwd, LINT_CONFIG_FILE))) {
|
|
4766
5061
|
process.stderr.write(
|
|
4767
5062
|
`sentinel lint(oxlint): no ${LINT_CONFIG_FILE} in this module; run \`sentinel --init --lint\` to adopt.
|
|
4768
5063
|
`
|
|
@@ -4807,7 +5102,7 @@ ${result.stderr ?? ""}`);
|
|
|
4807
5102
|
valueFlags: OXLINT_VALUE_FLAGS
|
|
4808
5103
|
});
|
|
4809
5104
|
const targets = paths.length > 0 ? paths : ["."];
|
|
4810
|
-
const emptyScope = scopeFlags(paths);
|
|
5105
|
+
const emptyScope = scopeFlags(paths, passedOptions);
|
|
4811
5106
|
const lint = (extra) => {
|
|
4812
5107
|
const passed = [...extra, ...emptyScope, ...passedOptions];
|
|
4813
5108
|
const result = spawnSync4(oxlint, ["-c", LINT_CONFIG_FILE, ...passed, ...targets], {
|
|
@@ -4910,7 +5205,7 @@ ${result.stderr ?? ""}`);
|
|
|
4910
5205
|
let stub;
|
|
4911
5206
|
try {
|
|
4912
5207
|
stub = parseJsonc(
|
|
4913
|
-
|
|
5208
|
+
readFileSync20(join25(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4914
5209
|
LINT_CONFIG_FILE
|
|
4915
5210
|
);
|
|
4916
5211
|
} catch {
|
|
@@ -4920,7 +5215,7 @@ ${result.stderr ?? ""}`);
|
|
|
4920
5215
|
for (const entry of stub.extends ?? []) {
|
|
4921
5216
|
try {
|
|
4922
5217
|
const preset = parseJsonc(
|
|
4923
|
-
|
|
5218
|
+
readFileSync20(join25(cwd, entry), "utf8"),
|
|
4924
5219
|
entry
|
|
4925
5220
|
);
|
|
4926
5221
|
for (const rule of Object.keys(preset.rules ?? {})) names.add(rule);
|
|
@@ -4990,14 +5285,14 @@ ${result.stderr ?? ""}`);
|
|
|
4990
5285
|
let parsed;
|
|
4991
5286
|
try {
|
|
4992
5287
|
parsed = parseJsonc(
|
|
4993
|
-
|
|
5288
|
+
readFileSync20(join25(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4994
5289
|
LINT_CONFIG_FILE
|
|
4995
5290
|
);
|
|
4996
5291
|
} catch {
|
|
4997
5292
|
return void 0;
|
|
4998
5293
|
}
|
|
4999
5294
|
const targets = Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : typeof parsed.extends === "string" ? [parsed.extends] : [];
|
|
5000
|
-
return targets.find((target) => !
|
|
5295
|
+
return targets.find((target) => !existsSync21(join25(cwd, target)));
|
|
5001
5296
|
}
|
|
5002
5297
|
/** Announce what is not enforced, so reduced coverage is never silent. */
|
|
5003
5298
|
announceDisabled(preset) {
|
|
@@ -5029,9 +5324,9 @@ function registerLint() {
|
|
|
5029
5324
|
|
|
5030
5325
|
// src/roles/typescript/adapters/tsc/tsc.adapter.ts
|
|
5031
5326
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
5032
|
-
import { existsSync as
|
|
5327
|
+
import { existsSync as existsSync22, readFileSync as readFileSync22 } from "fs";
|
|
5033
5328
|
import { createRequire as createRequire4 } from "module";
|
|
5034
|
-
import { join as
|
|
5329
|
+
import { join as join27 } from "path";
|
|
5035
5330
|
|
|
5036
5331
|
// src/roles/typescript/presets/nest.json
|
|
5037
5332
|
var nest_default2 = {
|
|
@@ -5212,8 +5507,8 @@ function readTsconfigAdoption(cwd) {
|
|
|
5212
5507
|
}
|
|
5213
5508
|
|
|
5214
5509
|
// src/roles/typescript/adapters/tsc/plan.ts
|
|
5215
|
-
import { readFileSync as
|
|
5216
|
-
import { join as
|
|
5510
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
5511
|
+
import { join as join26 } from "path";
|
|
5217
5512
|
|
|
5218
5513
|
// src/roles/typescript/typecheck-script.ts
|
|
5219
5514
|
var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
|
|
@@ -5306,7 +5601,7 @@ function planAdoption(context) {
|
|
|
5306
5601
|
};
|
|
5307
5602
|
}
|
|
5308
5603
|
const existing = parseJsonc(
|
|
5309
|
-
|
|
5604
|
+
readFileSync21(join26(context.cwd, target.path), "utf8"),
|
|
5310
5605
|
target.path
|
|
5311
5606
|
);
|
|
5312
5607
|
const extendsChain = composeExtends(existing.extends, preset);
|
|
@@ -5444,7 +5739,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
5444
5739
|
let chain;
|
|
5445
5740
|
try {
|
|
5446
5741
|
const parsed = parseJsonc(
|
|
5447
|
-
|
|
5742
|
+
readFileSync22(join27(cwd, target.path), "utf8"),
|
|
5448
5743
|
target.path
|
|
5449
5744
|
);
|
|
5450
5745
|
chain = parsed.extends;
|
|
@@ -5457,7 +5752,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
5457
5752
|
);
|
|
5458
5753
|
if (preset === void 0) return void 0;
|
|
5459
5754
|
try {
|
|
5460
|
-
createRequire4(
|
|
5755
|
+
createRequire4(join27(cwd, "noop.js")).resolve(preset);
|
|
5461
5756
|
return void 0;
|
|
5462
5757
|
} catch {
|
|
5463
5758
|
return preset;
|
|
@@ -5553,7 +5848,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
5553
5848
|
referencedProjects(cwd, config) {
|
|
5554
5849
|
try {
|
|
5555
5850
|
const parsed = parseJsonc(
|
|
5556
|
-
|
|
5851
|
+
readFileSync22(join27(cwd, config), "utf8"),
|
|
5557
5852
|
config
|
|
5558
5853
|
);
|
|
5559
5854
|
const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path) => typeof path === "string" && path.length > 0);
|
|
@@ -5569,7 +5864,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
5569
5864
|
* check.
|
|
5570
5865
|
*/
|
|
5571
5866
|
typecheckTarget(cwd) {
|
|
5572
|
-
if (
|
|
5867
|
+
if (existsSync22(join27(cwd, "tsconfig.json"))) return "tsconfig.json";
|
|
5573
5868
|
const target = resolveTsconfigTarget(cwd);
|
|
5574
5869
|
return target.reason === "none" ? null : target.path;
|
|
5575
5870
|
}
|
|
@@ -5717,7 +6012,7 @@ function detectFramework(packageJson) {
|
|
|
5717
6012
|
}
|
|
5718
6013
|
|
|
5719
6014
|
// src/core/dispatch.ts
|
|
5720
|
-
import { basename as
|
|
6015
|
+
import { basename as basename3 } from "path";
|
|
5721
6016
|
|
|
5722
6017
|
// src/shared/text.ts
|
|
5723
6018
|
function ensureLines(current, lines) {
|
|
@@ -5773,9 +6068,16 @@ function replaceLines(current, replacements) {
|
|
|
5773
6068
|
}
|
|
5774
6069
|
|
|
5775
6070
|
// src/core/apply-plan.ts
|
|
5776
|
-
import { existsSync as
|
|
6071
|
+
import { existsSync as existsSync23, readFileSync as readFileSync23, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
5777
6072
|
import { resolve as resolve4, sep } from "path";
|
|
5778
6073
|
import { applyEdits, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
|
|
6074
|
+
|
|
6075
|
+
// src/shared/deep-merge.ts
|
|
6076
|
+
function isPlainObject(value) {
|
|
6077
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6078
|
+
}
|
|
6079
|
+
|
|
6080
|
+
// src/core/apply-plan.ts
|
|
5779
6081
|
function resolveWithinRoot(cwd, relativePath) {
|
|
5780
6082
|
const root = resolve4(cwd);
|
|
5781
6083
|
const absolutePath = resolve4(root, relativePath);
|
|
@@ -5785,7 +6087,7 @@ function resolveWithinRoot(cwd, relativePath) {
|
|
|
5785
6087
|
return absolutePath;
|
|
5786
6088
|
}
|
|
5787
6089
|
function readIfExists(absolutePath) {
|
|
5788
|
-
return
|
|
6090
|
+
return existsSync23(absolutePath) ? readFileSync23(absolutePath, "utf8") : void 0;
|
|
5789
6091
|
}
|
|
5790
6092
|
function* leaves(value, prefix = []) {
|
|
5791
6093
|
for (const [key, keyValue] of Object.entries(value)) {
|
|
@@ -5903,8 +6205,8 @@ function applyPlan(cwd, plan3) {
|
|
|
5903
6205
|
}
|
|
5904
6206
|
|
|
5905
6207
|
// src/core/config/preset-evidence.ts
|
|
5906
|
-
import { existsSync as
|
|
5907
|
-
import { join as
|
|
6208
|
+
import { existsSync as existsSync24, readdirSync as readdirSync3, readFileSync as readFileSync24 } from "fs";
|
|
6209
|
+
import { join as join28 } from "path";
|
|
5908
6210
|
var PATH_SIGNALS = [
|
|
5909
6211
|
{
|
|
5910
6212
|
preset: "nest",
|
|
@@ -5918,11 +6220,11 @@ var DEPENDENCY_SIGNALS = [
|
|
|
5918
6220
|
{ preset: "nest", pattern: /^@nestjs\// }
|
|
5919
6221
|
];
|
|
5920
6222
|
function dependencyNames(cwd) {
|
|
5921
|
-
const path =
|
|
5922
|
-
if (!
|
|
6223
|
+
const path = join28(cwd, "package.json");
|
|
6224
|
+
if (!existsSync24(path)) return [];
|
|
5923
6225
|
try {
|
|
5924
6226
|
const manifest = parseJsonc(
|
|
5925
|
-
|
|
6227
|
+
readFileSync24(path, "utf8"),
|
|
5926
6228
|
path
|
|
5927
6229
|
);
|
|
5928
6230
|
return [
|
|
@@ -5945,7 +6247,7 @@ function declaresJsx(cwd) {
|
|
|
5945
6247
|
for (const name of entries) {
|
|
5946
6248
|
try {
|
|
5947
6249
|
const config = parseJsonc(
|
|
5948
|
-
|
|
6250
|
+
readFileSync24(join28(cwd, name), "utf8"),
|
|
5949
6251
|
name
|
|
5950
6252
|
);
|
|
5951
6253
|
if (config.compilerOptions?.jsx !== void 0) return true;
|
|
@@ -6086,7 +6388,7 @@ async function dispatch(opts) {
|
|
|
6086
6388
|
process.stderr.write(` fixing what ${opts.target} can fix automatically...
|
|
6087
6389
|
`);
|
|
6088
6390
|
await adapter.afterInit({
|
|
6089
|
-
module:
|
|
6391
|
+
module: basename3(opts.cwd),
|
|
6090
6392
|
cwd: opts.cwd,
|
|
6091
6393
|
preset,
|
|
6092
6394
|
ci: false,
|
|
@@ -6108,9 +6410,11 @@ export {
|
|
|
6108
6410
|
resolveBin,
|
|
6109
6411
|
readOwnVersion,
|
|
6110
6412
|
readProjectPackageJson,
|
|
6111
|
-
|
|
6413
|
+
moduleName,
|
|
6414
|
+
buildConfigFile,
|
|
6112
6415
|
VERBS,
|
|
6113
6416
|
TARGETS,
|
|
6417
|
+
LONG_RUNNING_TARGETS,
|
|
6114
6418
|
SWEEPABLE_TARGETS,
|
|
6115
6419
|
PRESET_NAMES,
|
|
6116
6420
|
WORKSPACE_ROOT_MARKER,
|
|
@@ -6123,4 +6427,4 @@ export {
|
|
|
6123
6427
|
detectFramework,
|
|
6124
6428
|
dispatch
|
|
6125
6429
|
};
|
|
6126
|
-
//# sourceMappingURL=chunk-
|
|
6430
|
+
//# sourceMappingURL=chunk-7WB7MQHD.js.map
|