@hublo/sentinel 1.2.0-alpha.1 → 1.2.0-alpha.11
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-6VOQDDZD.js} +649 -339
- package/dist/index.js +1 -2
- package/dist/roles/build/nest/toolchain.d.ts +90 -0
- package/dist/roles/build/nest/toolchain.js +254 -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,33 @@ 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
|
+
if (buildConfigFile(cwd) !== void 0) return void 0;
|
|
660
|
+
return webpackBuildTarget(cwd) === void 0 ? void 0 : "nest";
|
|
661
|
+
}
|
|
662
|
+
if (importsSpecifier(source, BUILD_PRESET_SPECIFIERS.nest)) return "nest";
|
|
663
|
+
if (importsPreset(source)) return "react";
|
|
461
664
|
return REACT_PLUGIN_SPECIFIERS.some((specifier) => importsSpecifier(source, specifier)) ? "react" : void 0;
|
|
462
665
|
}
|
|
463
666
|
function readBuildConfigSource(cwd) {
|
|
464
667
|
const configFile = buildConfigFile(cwd);
|
|
465
668
|
if (!configFile) return void 0;
|
|
466
669
|
try {
|
|
467
|
-
return
|
|
670
|
+
return readFileSync5(join7(cwd, configFile), "utf8");
|
|
468
671
|
} catch {
|
|
469
672
|
return void 0;
|
|
470
673
|
}
|
|
@@ -474,7 +677,7 @@ function readBuildAdoption(cwd) {
|
|
|
474
677
|
if (!configFile) return NOT_ADOPTED(null);
|
|
475
678
|
let source;
|
|
476
679
|
try {
|
|
477
|
-
source =
|
|
680
|
+
source = readFileSync5(join7(cwd, configFile), "utf8");
|
|
478
681
|
} catch (error) {
|
|
479
682
|
return NOT_ADOPTED(configFile, error instanceof Error ? error.message : String(error));
|
|
480
683
|
}
|
|
@@ -486,7 +689,9 @@ function readBuildAdoption(cwd) {
|
|
|
486
689
|
);
|
|
487
690
|
return {
|
|
488
691
|
configFile,
|
|
489
|
-
|
|
692
|
+
// Read from the config rather than assumed: the two presets are adopted the same way and
|
|
693
|
+
// reporting the wrong one would make `--inspect` lie about what a module builds.
|
|
694
|
+
preset: declaredBuildPreset(cwd) ?? "react",
|
|
490
695
|
adopted: true,
|
|
491
696
|
conformant: ownDeclarations.length === 0,
|
|
492
697
|
drift: ownDeclarations.map(
|
|
@@ -502,9 +707,9 @@ function atLeast(declared, needed) {
|
|
|
502
707
|
const parse2 = (value) => value.replace(/^[\^~>=<\s]+/, "").split("-")[0].split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
503
708
|
const left = parse2(declared);
|
|
504
709
|
const right = parse2(needed);
|
|
505
|
-
for (let
|
|
506
|
-
const a = left[
|
|
507
|
-
const b = right[
|
|
710
|
+
for (let at = 0; at < Math.max(left.length, right.length); at++) {
|
|
711
|
+
const a = left[at] ?? 0;
|
|
712
|
+
const b = right[at] ?? 0;
|
|
508
713
|
if (a !== b) return a > b;
|
|
509
714
|
}
|
|
510
715
|
return true;
|
|
@@ -525,43 +730,103 @@ function describeUnmet(preset, unmet) {
|
|
|
525
730
|
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
731
|
}
|
|
527
732
|
|
|
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
|
-
|
|
733
|
+
// src/roles/build/rewrite-imports.ts
|
|
734
|
+
import { parseSync } from "oxc-parser";
|
|
735
|
+
var MOVED_SPECIFIERS = new Set(OWNED_PACKAGES.map((entry) => entry.specifier));
|
|
736
|
+
function esmBlocker(fileName, packageType) {
|
|
737
|
+
if (/\.(mts|mjs)$/.test(fileName)) return void 0;
|
|
738
|
+
if (packageType === "module") return void 0;
|
|
739
|
+
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.`;
|
|
740
|
+
}
|
|
741
|
+
var DEFAULT_EXPORT_NAMES = Object.fromEntries(
|
|
742
|
+
OWNED_PACKAGES.filter((entry) => entry.default !== void 0).map((entry) => [
|
|
743
|
+
entry.specifier,
|
|
744
|
+
entry.default
|
|
745
|
+
])
|
|
746
|
+
);
|
|
747
|
+
function renderImport(bindings) {
|
|
748
|
+
const render = (list) => list.map((b) => b.exported === b.local ? b.exported : `${b.exported} as ${b.local}`).sort((left, right) => left.localeCompare(right)).join(", ");
|
|
749
|
+
const values = bindings.filter((b) => !b.typeOnly);
|
|
750
|
+
const types = bindings.filter((b) => b.typeOnly);
|
|
751
|
+
const lines = [];
|
|
752
|
+
if (values.length > 0) lines.push(`import { ${render(values)} } from '${BUILD_PRESET_SPECIFIER}'`);
|
|
753
|
+
if (types.length > 0) {
|
|
754
|
+
lines.push(`import type { ${render(types)} } from '${BUILD_PRESET_SPECIFIER}'`);
|
|
755
|
+
}
|
|
756
|
+
return lines.join("\n");
|
|
757
|
+
}
|
|
758
|
+
function rewriteBuildImports(source, fileName = "vite.config.ts") {
|
|
759
|
+
const { program, errors } = parseSync(fileName, source, { sourceType: "module" });
|
|
760
|
+
if (errors.length > 0) {
|
|
761
|
+
return {
|
|
762
|
+
kind: "blocked",
|
|
763
|
+
why: `${fileName} does not parse: ${errors[0]?.message ?? "unknown"}`
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
const imports = program.body.filter(
|
|
767
|
+
(node) => node.type === "ImportDeclaration"
|
|
768
|
+
);
|
|
769
|
+
if (imports.some((node) => node.source.value === BUILD_PRESET_SPECIFIER)) {
|
|
770
|
+
return { kind: "unchanged", why: "this config already imports its toolchain from sentinel" };
|
|
771
|
+
}
|
|
772
|
+
const moving = imports.filter((node) => MOVED_SPECIFIERS.has(node.source.value));
|
|
773
|
+
if (moving.length === 0) {
|
|
774
|
+
return {
|
|
775
|
+
kind: "unchanged",
|
|
776
|
+
why: "this config imports none of the packages sentinel owns, so there is nothing to move"
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
const bindings = [];
|
|
780
|
+
for (const node of moving) {
|
|
781
|
+
const from = node.source.value;
|
|
782
|
+
const specifiers = node.specifiers ?? [];
|
|
783
|
+
if (specifiers.length === 0) {
|
|
784
|
+
return { kind: "blocked", why: `\`import '${from}'\` has no bindings to move` };
|
|
785
|
+
}
|
|
786
|
+
for (const spec of specifiers) {
|
|
787
|
+
const typeOnly = node.importKind === "type" || spec.importKind === "type";
|
|
788
|
+
if (spec.type === "ImportDefaultSpecifier") {
|
|
789
|
+
const exported = DEFAULT_EXPORT_NAMES[from];
|
|
790
|
+
if (!exported) {
|
|
791
|
+
return {
|
|
792
|
+
kind: "blocked",
|
|
793
|
+
why: `no sentinel export for the default import of \`${from}\``
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
bindings.push({ exported, local: spec.local.name, typeOnly });
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
if (spec.type === "ImportSpecifier") {
|
|
800
|
+
bindings.push({
|
|
801
|
+
exported: spec.imported?.name ?? spec.imported?.value ?? spec.local.name,
|
|
802
|
+
local: spec.local.name,
|
|
803
|
+
typeOnly
|
|
804
|
+
});
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
return {
|
|
808
|
+
kind: "blocked",
|
|
809
|
+
why: `\`import * as ${spec.local.name} from '${from}'\` cannot be mapped onto sentinel's named exports`
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
const spans = moving.map((node) => ({ start: node.start, end: node.end })).sort((left, right) => left.start - right.start);
|
|
814
|
+
const first = spans[0];
|
|
815
|
+
const rest = spans.slice(1);
|
|
816
|
+
let out = source;
|
|
817
|
+
for (const span of rest.slice().reverse()) {
|
|
818
|
+
const after = out.slice(span.end).match(/^\r?\n/)?.[0].length ?? 0;
|
|
819
|
+
out = out.slice(0, span.start) + out.slice(span.end + after);
|
|
820
|
+
}
|
|
821
|
+
out = out.slice(0, first.start) + renderImport(bindings) + out.slice(first.end);
|
|
822
|
+
return {
|
|
823
|
+
kind: "rewritten",
|
|
824
|
+
source: out,
|
|
825
|
+
moved: [...new Set(moving.map((node) => node.source.value))].sort()
|
|
826
|
+
};
|
|
564
827
|
}
|
|
828
|
+
|
|
829
|
+
// src/roles/build/plan.ts
|
|
565
830
|
function ownedBuildDependencies(cwd) {
|
|
566
831
|
const manifest = readProjectPackageJson(cwd);
|
|
567
832
|
const keys = [];
|
|
@@ -590,9 +855,12 @@ function buildScripts(cwd) {
|
|
|
590
855
|
};
|
|
591
856
|
const existingDev = existingCommand(cwd, DEV_SCRIPT_NAME);
|
|
592
857
|
if (existingDev !== void 0) {
|
|
858
|
+
const own2 = moduleName(cwd);
|
|
593
859
|
scripts[DEV_SCRIPT_NAME] = composeDevScript(
|
|
594
860
|
existingDev,
|
|
595
|
-
|
|
861
|
+
// Trailing `--` for the same reason as the build script: whatever the caller appends
|
|
862
|
+
// reaches Vite, not sentinel's parser. It goes after `--module`, which is sentinel's own.
|
|
863
|
+
`${selfCommand(cwd, "--run --dev") ?? SENTINEL_DEV_COMMAND} --module ${own2} --`
|
|
596
864
|
);
|
|
597
865
|
}
|
|
598
866
|
return scripts;
|
|
@@ -606,23 +874,57 @@ function plan(context) {
|
|
|
606
874
|
if (unmet.length > 0) {
|
|
607
875
|
return { operations: [], skipped: describeUnmet(context.preset, unmet) };
|
|
608
876
|
}
|
|
609
|
-
|
|
877
|
+
const declaredPreset2 = declaredBuildPreset(context.cwd);
|
|
878
|
+
const configFile = buildConfigFile(context.cwd);
|
|
879
|
+
if (declaredPreset2 === void 0 && configFile !== void 0) {
|
|
610
880
|
return {
|
|
611
881
|
operations: [],
|
|
612
|
-
skipped: `this module has a Vite config, but it
|
|
882
|
+
skipped: `this module has a Vite config, but it builds neither a React app (no @vitejs/plugin-react or @tanstack/react-start) nor a Nest service (no ${BUILD_PRESET_SPECIFIERS.nest}). Those are the two families this role serves.`
|
|
613
883
|
};
|
|
614
884
|
}
|
|
885
|
+
const webpackTarget = webpackBuildTarget(context.cwd);
|
|
886
|
+
if (declaredBuildPreset(context.cwd) === "nest" || webpackTarget !== void 0) {
|
|
887
|
+
return planNestService(context, configFile, webpackTarget);
|
|
888
|
+
}
|
|
889
|
+
if (configFile === void 0) {
|
|
890
|
+
return {
|
|
891
|
+
operations: [],
|
|
892
|
+
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.`
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
return planReactApp(context, configFile);
|
|
896
|
+
}
|
|
897
|
+
function planReactApp(context, configFile) {
|
|
615
898
|
const notes = [];
|
|
616
899
|
const operations = [];
|
|
617
|
-
const
|
|
618
|
-
if (
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
900
|
+
const notEsm = esmBlocker(configFile, readProjectPackageJson(context.cwd).type);
|
|
901
|
+
if (notEsm !== void 0) return { operations: [], skipped: notEsm };
|
|
902
|
+
const rewrite = rewriteBuildImports(readConfigSource(context.cwd, configFile), configFile);
|
|
903
|
+
if (rewrite.kind === "blocked") {
|
|
904
|
+
return {
|
|
905
|
+
operations: [],
|
|
906
|
+
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.`
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
if (rewrite.kind === "rewritten") {
|
|
910
|
+
operations.push({ kind: "write", path: configFile, contents: rewrite.source });
|
|
911
|
+
}
|
|
912
|
+
for (const name of COMPANION_CONFIG_FILES) {
|
|
913
|
+
const source = readConfigSource(context.cwd, name);
|
|
914
|
+
if (source === "") continue;
|
|
915
|
+
const companion = rewriteBuildImports(source, name);
|
|
916
|
+
if (companion.kind === "rewritten") {
|
|
917
|
+
operations.push({ kind: "write", path: name, contents: companion.source });
|
|
918
|
+
notes.push(`${name} now imports ${companion.moved.join(", ")} from sentinel too.`);
|
|
919
|
+
} else if (companion.kind === "blocked") {
|
|
920
|
+
notes.push(
|
|
921
|
+
`${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.`
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
if (rewrite.kind === "rewritten") {
|
|
624
926
|
notes.push(
|
|
625
|
-
`${configFile}
|
|
927
|
+
`${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.`
|
|
626
928
|
);
|
|
627
929
|
}
|
|
628
930
|
operations.push(manifestOperation(context.cwd, buildScripts(context.cwd)));
|
|
@@ -636,12 +938,33 @@ function plan(context) {
|
|
|
636
938
|
}
|
|
637
939
|
return { operations, notes };
|
|
638
940
|
}
|
|
639
|
-
function
|
|
640
|
-
if (
|
|
941
|
+
function planNestService(context, configFile, webpackTarget) {
|
|
942
|
+
if (webpackTarget === void 0) {
|
|
943
|
+
return {
|
|
944
|
+
operations: [],
|
|
945
|
+
skipped: `already building through sentinel: ${configFile ?? NEST_CONFIG_FILE} imports the nest preset and the nx target runs it. Nothing to change.`
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
const refusal = refusalFor(context.cwd, webpackTarget);
|
|
949
|
+
if (refusal !== void 0) return { operations: [], blocked: refusal };
|
|
950
|
+
const dropped = Object.keys(webpackTarget.configurations ?? {});
|
|
951
|
+
return {
|
|
952
|
+
operations: nestAdoptionOperations(context.cwd, webpackTarget),
|
|
953
|
+
notes: [
|
|
954
|
+
`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.`,
|
|
955
|
+
...dropped.length > 0 ? [
|
|
956
|
+
`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.`
|
|
957
|
+
] : [],
|
|
958
|
+
`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.`
|
|
959
|
+
]
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
function readConfigSource(cwd, configFile) {
|
|
963
|
+
if (!existsSync8(join8(cwd, configFile))) return "";
|
|
641
964
|
try {
|
|
642
|
-
return
|
|
965
|
+
return readFileSync6(join8(cwd, configFile), "utf8");
|
|
643
966
|
} catch {
|
|
644
|
-
return
|
|
967
|
+
return "";
|
|
645
968
|
}
|
|
646
969
|
}
|
|
647
970
|
|
|
@@ -679,11 +1002,17 @@ var ViteRoleAdapter = class extends BaseAdapter {
|
|
|
679
1002
|
* resolution let it run at all — filtering here would give "no adapter for preset svelte",
|
|
680
1003
|
* which tells nobody what to do about it.
|
|
681
1004
|
*
|
|
682
|
-
* Nest
|
|
683
|
-
*
|
|
1005
|
+
* Nest it now serves too, through `@hublo/sentinel/build/nest`. It used to be declined on the
|
|
1006
|
+
* grounds that "there is nothing to bundle", which was never true: those 36 services bundle
|
|
1007
|
+
* with webpack. What changed is that Nx v24 removes the `@nx/webpack:webpack` executor and
|
|
1008
|
+
* the `composePlugins` / `withNx` helpers their shared config is built on, and nx's own
|
|
1009
|
+
* migration generator refuses every one of them because they use `@nx/js:node`
|
|
1010
|
+
* (nrwl/nx#36389). So the way out runs through here.
|
|
1011
|
+
*
|
|
1012
|
+
* `node` and `tools` remain genuinely declined: nothing under them bundles at all.
|
|
684
1013
|
*/
|
|
685
1014
|
appliesTo(preset) {
|
|
686
|
-
return preset === "react" || preset === "svelte";
|
|
1015
|
+
return preset === "react" || preset === "svelte" || preset === "nest";
|
|
687
1016
|
}
|
|
688
1017
|
/**
|
|
689
1018
|
* `react`, read from the module's Vite config rather than from its dependencies.
|
|
@@ -773,6 +1102,26 @@ var ViteDevAdapter = class extends ViteRoleAdapter {
|
|
|
773
1102
|
get label() {
|
|
774
1103
|
return "dev";
|
|
775
1104
|
}
|
|
1105
|
+
/**
|
|
1106
|
+
* `--init --dev` does the same thing as `--init --build`, and now says so.
|
|
1107
|
+
*
|
|
1108
|
+
* The shared plan is deliberate (see `ViteRoleAdapter.plan`): one plan writes both scripts,
|
|
1109
|
+
* because adopting one without the other leaves the module half-migrated. What was missing
|
|
1110
|
+
* was only the telling: the run printed a message about the build and nothing about `serve`,
|
|
1111
|
+
* so a developer could believe the dev server had been adopted on its own. Refusing was the
|
|
1112
|
+
* wrong correction, it added friction to a command that already produced the right result.
|
|
1113
|
+
*/
|
|
1114
|
+
plan(context) {
|
|
1115
|
+
const planned = super.plan(context);
|
|
1116
|
+
if (planned.operations.length === 0) return planned;
|
|
1117
|
+
return {
|
|
1118
|
+
...planned,
|
|
1119
|
+
notes: [
|
|
1120
|
+
...planned.notes ?? [],
|
|
1121
|
+
"--init --dev adopts the build too: one plan writes both `build` and `serve`."
|
|
1122
|
+
]
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
776
1125
|
/**
|
|
777
1126
|
* Start the dev server. It does not return until stopped, so there is no verdict to report
|
|
778
1127
|
* beyond the exit code the developer's own Ctrl-C produces.
|
|
@@ -791,48 +1140,6 @@ var ViteDevAdapter = class extends ViteRoleAdapter {
|
|
|
791
1140
|
}
|
|
792
1141
|
};
|
|
793
1142
|
|
|
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
1143
|
// src/roles/build/prerequisite.ts
|
|
837
1144
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
838
1145
|
var PREBUILD_SCRIPT_NAME = `pre${BUILD_SCRIPT_NAME}`;
|
|
@@ -859,23 +1166,6 @@ function runPrerequisite(cwd, env = process.env) {
|
|
|
859
1166
|
return { kind: "ran" };
|
|
860
1167
|
}
|
|
861
1168
|
|
|
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
1169
|
// src/roles/build/adapters/vite/vite.adapter.ts
|
|
880
1170
|
var ViteAdapter = class extends ViteRoleAdapter {
|
|
881
1171
|
target = "build";
|
|
@@ -901,25 +1191,30 @@ var ViteAdapter = class extends ViteRoleAdapter {
|
|
|
901
1191
|
return this.spawnVite(ctx, ready.vite, ["build"], 1);
|
|
902
1192
|
}
|
|
903
1193
|
/**
|
|
904
|
-
* What this module builds with,
|
|
1194
|
+
* What this module builds with, WITHOUT loading its config.
|
|
905
1195
|
*
|
|
906
|
-
*
|
|
907
|
-
*
|
|
908
|
-
*
|
|
1196
|
+
* Everything reported here is read from the manifest and from the config's text: whether the
|
|
1197
|
+
* module imports its toolchain from sentinel, which Vite install would answer, what it still
|
|
1198
|
+
* declares of its own, and whether it has a `prebuild` step.
|
|
909
1199
|
*
|
|
910
|
-
*
|
|
911
|
-
*
|
|
912
|
-
*
|
|
1200
|
+
* It used to also load the config through Vite and report where it departed from a preset
|
|
1201
|
+
* and what it added on top. Both were dropped with the preset itself. Adoption changes a
|
|
1202
|
+
* config's IMPORTS and nothing else, so there is no preset the app signed up to and no
|
|
1203
|
+
* distance from it to measure; and computing it meant EXECUTING an app's config, which is
|
|
1204
|
+
* expensive and can fail on its own terms (career throws without `VITE_BRAND_ID`). Reading
|
|
1205
|
+
* a file to report on it must not be able to break.
|
|
913
1206
|
*
|
|
914
|
-
*
|
|
915
|
-
*
|
|
1207
|
+
* They come back with the shared base, when there is again something an app has chosen to
|
|
1208
|
+
* adopt and can therefore depart from.
|
|
916
1209
|
*/
|
|
917
1210
|
async inspect(ctx) {
|
|
918
1211
|
const adoption = readBuildAdoption(ctx.cwd);
|
|
919
|
-
|
|
1212
|
+
return {
|
|
920
1213
|
runner: "vite",
|
|
921
1214
|
configFile: adoption.configFile,
|
|
922
1215
|
...adoption.unreadable ? { unreadable: adoption.unreadable } : {},
|
|
1216
|
+
// The field nothing else reports: an adopted config takes its plugins from sentinel, so
|
|
1217
|
+
// a module answering `module` here is one build away from the two-copies failure.
|
|
923
1218
|
vite: viteOrigin(ctx.cwd),
|
|
924
1219
|
// Rendered as a labelled block by the shared renderer, the same shape the lint role's
|
|
925
1220
|
// parked rules and the format role's overrides already use.
|
|
@@ -929,36 +1224,6 @@ var ViteAdapter = class extends ViteRoleAdapter {
|
|
|
929
1224
|
})),
|
|
930
1225
|
prerequisite: moduleScripts(ctx.cwd)[PREBUILD_SCRIPT_NAME] ?? null
|
|
931
1226
|
};
|
|
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
1227
|
}
|
|
963
1228
|
/**
|
|
964
1229
|
* Adoption as data, WITHOUT building.
|
|
@@ -984,17 +1249,6 @@ var ViteAdapter = class extends ViteRoleAdapter {
|
|
|
984
1249
|
};
|
|
985
1250
|
}
|
|
986
1251
|
};
|
|
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
1252
|
|
|
999
1253
|
// src/roles/build/register.ts
|
|
1000
1254
|
function registerBuild() {
|
|
@@ -1006,12 +1260,12 @@ function registerBuild() {
|
|
|
1006
1260
|
|
|
1007
1261
|
// src/roles/format/adapters/oxfmt/oxfmt.adapter.ts
|
|
1008
1262
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
1009
|
-
import { existsSync as
|
|
1010
|
-
import { join as
|
|
1263
|
+
import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
|
|
1264
|
+
import { join as join15 } from "path";
|
|
1011
1265
|
|
|
1012
1266
|
// src/core/config/has-source.ts
|
|
1013
1267
|
import { readdirSync } from "fs";
|
|
1014
|
-
import { extname, join as
|
|
1268
|
+
import { extname, join as join9, relative as relative3 } from "path";
|
|
1015
1269
|
var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
1016
1270
|
"node_modules",
|
|
1017
1271
|
"dist",
|
|
@@ -1062,7 +1316,7 @@ function hasSourceFiles(cwd, extensions) {
|
|
|
1062
1316
|
}
|
|
1063
1317
|
for (const entry of entries) {
|
|
1064
1318
|
if (entry.isDirectory()) {
|
|
1065
|
-
if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(
|
|
1319
|
+
if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(join9(dir, entry.name));
|
|
1066
1320
|
continue;
|
|
1067
1321
|
}
|
|
1068
1322
|
if (wanted.has(extname(entry.name))) return true;
|
|
@@ -1083,12 +1337,12 @@ function listSourceFiles(cwd, extensions) {
|
|
|
1083
1337
|
continue;
|
|
1084
1338
|
}
|
|
1085
1339
|
for (const entry of entries) {
|
|
1086
|
-
const full =
|
|
1340
|
+
const full = join9(dir, entry.name);
|
|
1087
1341
|
if (entry.isDirectory()) {
|
|
1088
1342
|
if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(full);
|
|
1089
1343
|
continue;
|
|
1090
1344
|
}
|
|
1091
|
-
if (wanted.has(extname(entry.name))) found.push(
|
|
1345
|
+
if (wanted.has(extname(entry.name))) found.push(relative3(cwd, full));
|
|
1092
1346
|
}
|
|
1093
1347
|
}
|
|
1094
1348
|
return found;
|
|
@@ -1096,8 +1350,9 @@ function listSourceFiles(cwd, extensions) {
|
|
|
1096
1350
|
|
|
1097
1351
|
// src/core/named-scope.ts
|
|
1098
1352
|
var TOLERATE_EMPTY_FLAG = "--no-error-on-unmatched-pattern";
|
|
1099
|
-
function scopeFlags(paths) {
|
|
1100
|
-
|
|
1353
|
+
function scopeFlags(paths, options = []) {
|
|
1354
|
+
if (paths.length === 0 || options.includes(TOLERATE_EMPTY_FLAG)) return [];
|
|
1355
|
+
return [TOLERATE_EMPTY_FLAG];
|
|
1101
1356
|
}
|
|
1102
1357
|
|
|
1103
1358
|
// src/core/settings.ts
|
|
@@ -1105,8 +1360,8 @@ var WORKSPACE_ROOT_MARKER = "nx.json";
|
|
|
1105
1360
|
var DEFAULT_MAX_DIAGNOSTICS = 100;
|
|
1106
1361
|
|
|
1107
1362
|
// src/core/workspace-prep.ts
|
|
1108
|
-
import { existsSync as
|
|
1109
|
-
import { dirname as dirname3, join as
|
|
1363
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync } from "fs";
|
|
1364
|
+
import { dirname as dirname3, join as join10, relative as relative4 } from "path";
|
|
1110
1365
|
var OVERRIDE_KEY = "i18next>typescript";
|
|
1111
1366
|
var NATIVE_TS_ALIAS = "@typescript/native";
|
|
1112
1367
|
var WORKSPACE_YAML = "pnpm-workspace.yaml";
|
|
@@ -1115,7 +1370,7 @@ var LIST_ITEM = /^(\s*)-\s+(.*?)\s*$/;
|
|
|
1115
1370
|
function findWorkspaceRoot(startDir) {
|
|
1116
1371
|
let dir = startDir;
|
|
1117
1372
|
for (; ; ) {
|
|
1118
|
-
if (
|
|
1373
|
+
if (existsSync9(join10(dir, WORKSPACE_ROOT_MARKER))) return dir;
|
|
1119
1374
|
const parent = dirname3(dir);
|
|
1120
1375
|
if (parent === dir) return void 0;
|
|
1121
1376
|
dir = parent;
|
|
@@ -1128,9 +1383,9 @@ function declaredNativeTs(pkg) {
|
|
|
1128
1383
|
return version || void 0;
|
|
1129
1384
|
}
|
|
1130
1385
|
function ensureI18nextSingleton(root, dryRun) {
|
|
1131
|
-
const pkgPath =
|
|
1132
|
-
if (!
|
|
1133
|
-
const pkg = JSON.parse(
|
|
1386
|
+
const pkgPath = join10(root, "package.json");
|
|
1387
|
+
if (!existsSync9(pkgPath)) return void 0;
|
|
1388
|
+
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
1134
1389
|
const want = declaredNativeTs(pkg);
|
|
1135
1390
|
if (!want) return void 0;
|
|
1136
1391
|
const have = pkg.pnpm?.overrides?.[OVERRIDE_KEY];
|
|
@@ -1143,10 +1398,10 @@ function ensureI18nextSingleton(root, dryRun) {
|
|
|
1143
1398
|
return `pinned ${OVERRIDE_KEY} to ${want} in package.json (i18next singleton)`;
|
|
1144
1399
|
}
|
|
1145
1400
|
function ensureReleaseAgeAllowList(root, dryRun) {
|
|
1146
|
-
const yamlPath =
|
|
1147
|
-
if (!
|
|
1401
|
+
const yamlPath = join10(root, WORKSPACE_YAML);
|
|
1402
|
+
if (!existsSync9(yamlPath)) return void 0;
|
|
1148
1403
|
const own = readOwnPackage().name;
|
|
1149
|
-
const lines =
|
|
1404
|
+
const lines = readFileSync7(yamlPath, "utf8").split("\n");
|
|
1150
1405
|
const keyIdx = lines.findIndex((line) => line.replace(/\s+$/, "") === `${RELEASE_AGE_KEY}:`);
|
|
1151
1406
|
if (keyIdx === -1) return void 0;
|
|
1152
1407
|
let lastItemIdx = keyIdx;
|
|
@@ -1179,13 +1434,13 @@ var ROOT_PRETTIER_CONFIGS = [
|
|
|
1179
1434
|
"prettier.config.mjs"
|
|
1180
1435
|
];
|
|
1181
1436
|
function ensureFormatterExclusion(root, moduleDir, dryRun) {
|
|
1182
|
-
const rel =
|
|
1437
|
+
const rel = relative4(root, moduleDir).replaceAll("\\", "/");
|
|
1183
1438
|
if (rel === "" || rel.startsWith("..")) return void 0;
|
|
1184
|
-
const ignorePath =
|
|
1185
|
-
const hasPrettier =
|
|
1439
|
+
const ignorePath = join10(root, PRETTIER_IGNORE);
|
|
1440
|
+
const hasPrettier = existsSync9(ignorePath) || ROOT_PRETTIER_CONFIGS.some((name) => existsSync9(join10(root, name)));
|
|
1186
1441
|
if (!hasPrettier) return void 0;
|
|
1187
1442
|
const pattern = `/${rel}/`;
|
|
1188
|
-
const existing =
|
|
1443
|
+
const existing = existsSync9(ignorePath) ? readFileSync7(ignorePath, "utf8") : "";
|
|
1189
1444
|
const lines = existing.split("\n");
|
|
1190
1445
|
if (lines.some((line) => line.trim().replace(/\/$/, "") === pattern.replace(/\/$/, ""))) {
|
|
1191
1446
|
return void 0;
|
|
@@ -1221,10 +1476,10 @@ function ensureWorkspacePrep(opts) {
|
|
|
1221
1476
|
function inspectWorkspacePrep(root) {
|
|
1222
1477
|
const entries = [];
|
|
1223
1478
|
let pkg = {};
|
|
1224
|
-
const pkgPath =
|
|
1225
|
-
if (
|
|
1479
|
+
const pkgPath = join10(root, "package.json");
|
|
1480
|
+
if (existsSync9(pkgPath)) {
|
|
1226
1481
|
try {
|
|
1227
|
-
pkg = JSON.parse(
|
|
1482
|
+
pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
1228
1483
|
} catch {
|
|
1229
1484
|
pkg = {};
|
|
1230
1485
|
}
|
|
@@ -1238,9 +1493,9 @@ function inspectWorkspacePrep(root) {
|
|
|
1238
1493
|
});
|
|
1239
1494
|
}
|
|
1240
1495
|
const own = readOwnPackage().name;
|
|
1241
|
-
const yamlPath =
|
|
1242
|
-
if (
|
|
1243
|
-
const yaml =
|
|
1496
|
+
const yamlPath = join10(root, WORKSPACE_YAML);
|
|
1497
|
+
if (existsSync9(yamlPath)) {
|
|
1498
|
+
const yaml = readFileSync7(yamlPath, "utf8");
|
|
1244
1499
|
const listed = new RegExp(`^\\s*-\\s*['"]?${own.replace("/", "\\/")}['"]?\\s*$`, "m").test(yaml);
|
|
1245
1500
|
if (listed) {
|
|
1246
1501
|
const gated = new RegExp(`^\\s*minimumReleaseAge\\s*:`, "m").test(yaml);
|
|
@@ -1327,8 +1582,8 @@ function writesWhenRewritten(name, command) {
|
|
|
1327
1582
|
var CHECK_SCRIPT_NAMES = /* @__PURE__ */ new Set(["lint", "format", "check", "test", "typecheck", "verify"]);
|
|
1328
1583
|
|
|
1329
1584
|
// src/roles/format/inherited-ignores.ts
|
|
1330
|
-
import { existsSync as
|
|
1331
|
-
import { join as
|
|
1585
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
1586
|
+
import { join as join11 } from "path";
|
|
1332
1587
|
var ROOT_IGNORE_FILE = ".prettierignore";
|
|
1333
1588
|
function isPattern(line) {
|
|
1334
1589
|
const trimmed = line.trim();
|
|
@@ -1343,11 +1598,11 @@ function toModulePattern(pattern) {
|
|
|
1343
1598
|
}
|
|
1344
1599
|
function inheritedIgnorePatterns(workspaceRoot) {
|
|
1345
1600
|
if (!workspaceRoot) return [];
|
|
1346
|
-
const path =
|
|
1347
|
-
if (!
|
|
1601
|
+
const path = join11(workspaceRoot, ROOT_IGNORE_FILE);
|
|
1602
|
+
if (!existsSync10(path)) return [];
|
|
1348
1603
|
let contents;
|
|
1349
1604
|
try {
|
|
1350
|
-
contents =
|
|
1605
|
+
contents = readFileSync8(path, "utf8");
|
|
1351
1606
|
} catch {
|
|
1352
1607
|
return [];
|
|
1353
1608
|
}
|
|
@@ -1434,13 +1689,13 @@ function formatPresetFor(preset) {
|
|
|
1434
1689
|
}
|
|
1435
1690
|
|
|
1436
1691
|
// src/roles/format/prettier-config.ts
|
|
1437
|
-
import { existsSync as
|
|
1438
|
-
import { join as
|
|
1692
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
1693
|
+
import { join as join13 } from "path";
|
|
1439
1694
|
|
|
1440
1695
|
// src/roles/format/resolve-oxfmt.ts
|
|
1441
|
-
import { readFileSync as
|
|
1696
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
1442
1697
|
import { createRequire as createRequire2 } from "module";
|
|
1443
|
-
import { dirname as dirname4, join as
|
|
1698
|
+
import { dirname as dirname4, join as join12 } from "path";
|
|
1444
1699
|
function resolveOxfmt(cwd) {
|
|
1445
1700
|
return resolveBin(cwd, "oxfmt") ?? binFromOwnInstall("oxfmt", "oxfmt");
|
|
1446
1701
|
}
|
|
@@ -1469,8 +1724,8 @@ function configSchema() {
|
|
|
1469
1724
|
function readConfigSchema() {
|
|
1470
1725
|
try {
|
|
1471
1726
|
const manifest = createRequire2(import.meta.url).resolve("oxfmt/package.json");
|
|
1472
|
-
const schemaPath =
|
|
1473
|
-
return JSON.parse(
|
|
1727
|
+
const schemaPath = join12(dirname4(manifest), "configuration_schema.json");
|
|
1728
|
+
return JSON.parse(readFileSync9(schemaPath, "utf8"));
|
|
1474
1729
|
} catch {
|
|
1475
1730
|
return void 0;
|
|
1476
1731
|
}
|
|
@@ -1501,14 +1756,14 @@ function toOxfmtOverrides(value) {
|
|
|
1501
1756
|
return { overrides, unresolved };
|
|
1502
1757
|
}
|
|
1503
1758
|
function readPrettierSettings(cwd) {
|
|
1504
|
-
const file = PRETTIER_CONFIG_FILES.find((name) =>
|
|
1759
|
+
const file = PRETTIER_CONFIG_FILES.find((name) => existsSync11(join13(cwd, name)));
|
|
1505
1760
|
if (!file) return { options: {}, unresolved: [] };
|
|
1506
1761
|
if (/\.(js|cjs|mjs)$/.test(file)) {
|
|
1507
1762
|
return { options: {}, file, unresolved: [`${file} is JavaScript, so it was not executed`] };
|
|
1508
1763
|
}
|
|
1509
1764
|
let parsed;
|
|
1510
1765
|
try {
|
|
1511
|
-
parsed = parseJsonc(
|
|
1766
|
+
parsed = parseJsonc(readFileSync10(join13(cwd, file), "utf8"), file);
|
|
1512
1767
|
} catch {
|
|
1513
1768
|
return { options: {}, file, unresolved: [`${file} could not be parsed`] };
|
|
1514
1769
|
}
|
|
@@ -1538,8 +1793,8 @@ function readPrettierSettings(cwd) {
|
|
|
1538
1793
|
}
|
|
1539
1794
|
|
|
1540
1795
|
// src/roles/format/read-adoption.ts
|
|
1541
|
-
import { existsSync as
|
|
1542
|
-
import { join as
|
|
1796
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
1797
|
+
import { join as join14 } from "path";
|
|
1543
1798
|
var NOT_ADOPTED2 = (configFile, unreadable = null) => ({
|
|
1544
1799
|
configFile,
|
|
1545
1800
|
preset: null,
|
|
@@ -1555,11 +1810,11 @@ function sameValue(a, b) {
|
|
|
1555
1810
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
1556
1811
|
}
|
|
1557
1812
|
function readFormatAdoption(cwd) {
|
|
1558
|
-
const path =
|
|
1559
|
-
if (!
|
|
1813
|
+
const path = join14(cwd, FORMAT_CONFIG_FILE);
|
|
1814
|
+
if (!existsSync12(path)) return NOT_ADOPTED2(null);
|
|
1560
1815
|
let parsed;
|
|
1561
1816
|
try {
|
|
1562
|
-
parsed = parseJsonc(
|
|
1817
|
+
parsed = parseJsonc(readFileSync11(path, "utf8"), FORMAT_CONFIG_FILE);
|
|
1563
1818
|
} catch (error) {
|
|
1564
1819
|
const reason = error instanceof Error ? error.message : String(error);
|
|
1565
1820
|
return NOT_ADOPTED2(FORMAT_CONFIG_FILE, `${FORMAT_CONFIG_FILE} could not be parsed (${reason})`);
|
|
@@ -1684,7 +1939,7 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1684
1939
|
manifestOperation(context.cwd, this.formatScripts(context.cwd))
|
|
1685
1940
|
];
|
|
1686
1941
|
const prettierConfigs = PRETTIER_CONFIG_FILES.filter(
|
|
1687
|
-
(name) =>
|
|
1942
|
+
(name) => existsSync13(join15(context.cwd, name))
|
|
1688
1943
|
);
|
|
1689
1944
|
for (const name of prettierConfigs) operations.push({ kind: "delete", path: name });
|
|
1690
1945
|
const removableDeps = this.modulePrettierDependencies(context.cwd);
|
|
@@ -1808,7 +2063,7 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1808
2063
|
});
|
|
1809
2064
|
const result = spawnSync3(
|
|
1810
2065
|
oxfmt,
|
|
1811
|
-
[mode, ...scopeFlags(paths), ...options, ...paths.length > 0 ? paths : ["."]],
|
|
2066
|
+
[mode, ...scopeFlags(paths, options), ...options, ...paths.length > 0 ? paths : ["."]],
|
|
1812
2067
|
{
|
|
1813
2068
|
cwd: ctx.cwd,
|
|
1814
2069
|
stdio: "inherit"
|
|
@@ -1924,7 +2179,7 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1924
2179
|
modulePrettierDependencies(cwd) {
|
|
1925
2180
|
const isPrettierPackage = (name) => name === "prettier" || name.startsWith("prettier-plugin-") || name.startsWith("@prettier/");
|
|
1926
2181
|
try {
|
|
1927
|
-
const manifest = JSON.parse(
|
|
2182
|
+
const manifest = JSON.parse(readFileSync12(join15(cwd, "package.json"), "utf8"));
|
|
1928
2183
|
return Object.keys(manifest.devDependencies ?? {}).filter(isPrettierPackage).map((name) => ["devDependencies", name]);
|
|
1929
2184
|
} catch {
|
|
1930
2185
|
return [];
|
|
@@ -2006,8 +2261,8 @@ function registerFormat() {
|
|
|
2006
2261
|
|
|
2007
2262
|
// src/roles/lint/adapters/oxlint/oxlint.adapter.ts
|
|
2008
2263
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
2009
|
-
import { existsSync as
|
|
2010
|
-
import { join as
|
|
2264
|
+
import { existsSync as existsSync21, readFileSync as readFileSync20, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
2265
|
+
import { join as join25 } from "path";
|
|
2011
2266
|
|
|
2012
2267
|
// src/core/config/deferred-rules.ts
|
|
2013
2268
|
function deferredRuleNames(rules) {
|
|
@@ -2016,7 +2271,7 @@ function deferredRuleNames(rules) {
|
|
|
2016
2271
|
|
|
2017
2272
|
// src/core/fix-report.ts
|
|
2018
2273
|
import { statSync } from "fs";
|
|
2019
|
-
import { join as
|
|
2274
|
+
import { join as join16 } from "path";
|
|
2020
2275
|
var CONFIG_REFUSED = /failed to parse .*config|invalid config file/i;
|
|
2021
2276
|
function readFailure(output) {
|
|
2022
2277
|
const lines = output.split("\n").map((line2) => line2.trim());
|
|
@@ -2036,7 +2291,7 @@ function fingerprint(path) {
|
|
|
2036
2291
|
function snapshotFiles(cwd, extensions) {
|
|
2037
2292
|
const snapshot = /* @__PURE__ */ new Map();
|
|
2038
2293
|
for (const file of listSourceFiles(cwd, extensions)) {
|
|
2039
|
-
const mark = fingerprint(
|
|
2294
|
+
const mark = fingerprint(join16(cwd, file));
|
|
2040
2295
|
if (mark !== void 0) snapshot.set(file, mark);
|
|
2041
2296
|
}
|
|
2042
2297
|
return snapshot;
|
|
@@ -2044,7 +2299,7 @@ function snapshotFiles(cwd, extensions) {
|
|
|
2044
2299
|
function changedSince(cwd, before, extensions) {
|
|
2045
2300
|
const changed = [];
|
|
2046
2301
|
for (const file of listSourceFiles(cwd, extensions)) {
|
|
2047
|
-
const now = fingerprint(
|
|
2302
|
+
const now = fingerprint(join16(cwd, file));
|
|
2048
2303
|
if (now === void 0) continue;
|
|
2049
2304
|
if (before.get(file) !== now) changed.push(file);
|
|
2050
2305
|
}
|
|
@@ -2063,8 +2318,8 @@ function describeFixOutcome(outcome, toolLabel) {
|
|
|
2063
2318
|
}
|
|
2064
2319
|
|
|
2065
2320
|
// src/roles/lint/module-baseline.ts
|
|
2066
|
-
import { existsSync as
|
|
2067
|
-
import { join as
|
|
2321
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
|
|
2322
|
+
import { join as join17 } from "path";
|
|
2068
2323
|
var LINT_BASELINE_FILE = ".oxlintrc.baseline.json";
|
|
2069
2324
|
var LINT_MEASURE_FILE = ".oxlintrc.measure.json";
|
|
2070
2325
|
var LINT_BASELINE_SPECIFIER = `./${LINT_BASELINE_FILE}`;
|
|
@@ -2108,11 +2363,11 @@ function describeHolds(holds) {
|
|
|
2108
2363
|
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
2364
|
}
|
|
2110
2365
|
function heldRules(cwd) {
|
|
2111
|
-
const path =
|
|
2112
|
-
if (!
|
|
2366
|
+
const path = join17(cwd, LINT_BASELINE_FILE);
|
|
2367
|
+
if (!existsSync14(path)) return [];
|
|
2113
2368
|
try {
|
|
2114
2369
|
const parsed = parseJsonc(
|
|
2115
|
-
|
|
2370
|
+
readFileSync13(path, "utf8"),
|
|
2116
2371
|
LINT_BASELINE_FILE
|
|
2117
2372
|
);
|
|
2118
2373
|
return Object.keys(parsed.rules ?? {});
|
|
@@ -3935,14 +4190,14 @@ function downgradedRulesFor(preset) {
|
|
|
3935
4190
|
}
|
|
3936
4191
|
|
|
3937
4192
|
// src/roles/lint/extra-layers.ts
|
|
3938
|
-
import { existsSync as
|
|
3939
|
-
import { isAbsolute as isAbsolute2, join as
|
|
4193
|
+
import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
|
|
4194
|
+
import { isAbsolute as isAbsolute2, join as join18, resolve as resolve3 } from "path";
|
|
3940
4195
|
function ruleCount(cwd, specifier) {
|
|
3941
4196
|
const path = isAbsolute2(specifier) ? specifier : resolve3(cwd, specifier);
|
|
3942
|
-
if (!
|
|
4197
|
+
if (!existsSync15(path)) return void 0;
|
|
3943
4198
|
try {
|
|
3944
4199
|
const parsed = parseJsonc(
|
|
3945
|
-
|
|
4200
|
+
readFileSync14(path, "utf8"),
|
|
3946
4201
|
specifier
|
|
3947
4202
|
);
|
|
3948
4203
|
return Object.keys(parsed.rules ?? {}).length;
|
|
@@ -3962,7 +4217,7 @@ function extraLayers(cwd, extendsList) {
|
|
|
3962
4217
|
function committedExtendsList(cwd, configFile) {
|
|
3963
4218
|
try {
|
|
3964
4219
|
const parsed = parseJsonc(
|
|
3965
|
-
|
|
4220
|
+
readFileSync14(join18(cwd, configFile), "utf8"),
|
|
3966
4221
|
configFile
|
|
3967
4222
|
);
|
|
3968
4223
|
return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
|
|
@@ -4021,8 +4276,8 @@ function errorCountsByConfigRule(stdout) {
|
|
|
4021
4276
|
}
|
|
4022
4277
|
|
|
4023
4278
|
// src/core/config/read-adoption.ts
|
|
4024
|
-
import { existsSync as
|
|
4025
|
-
import { join as
|
|
4279
|
+
import { existsSync as existsSync17, readFileSync as readFileSync16 } from "fs";
|
|
4280
|
+
import { join as join20 } from "path";
|
|
4026
4281
|
|
|
4027
4282
|
// src/core/config/owned-keys.ts
|
|
4028
4283
|
function presetOwnedKeys(config, permitted, presetSets) {
|
|
@@ -4037,12 +4292,12 @@ function localOnlyKeys(config, permitted, presetSets) {
|
|
|
4037
4292
|
}
|
|
4038
4293
|
|
|
4039
4294
|
// src/core/config/resolve-config-target.ts
|
|
4040
|
-
import { existsSync as
|
|
4041
|
-
import { join as
|
|
4295
|
+
import { existsSync as existsSync16, readFileSync as readFileSync15 } from "fs";
|
|
4296
|
+
import { join as join19 } from "path";
|
|
4042
4297
|
function readExtends(absolutePath) {
|
|
4043
4298
|
let parsed;
|
|
4044
4299
|
try {
|
|
4045
|
-
parsed = parseJsonc(
|
|
4300
|
+
parsed = parseJsonc(readFileSync15(absolutePath, "utf8"), absolutePath);
|
|
4046
4301
|
} catch {
|
|
4047
4302
|
return [];
|
|
4048
4303
|
}
|
|
@@ -4056,8 +4311,8 @@ function resolveConfigTarget(moduleDir, { candidates, markers, fallback }) {
|
|
|
4056
4311
|
let existing;
|
|
4057
4312
|
let existingExtendsSomething = false;
|
|
4058
4313
|
for (const candidate of candidates) {
|
|
4059
|
-
const absolutePath =
|
|
4060
|
-
if (!
|
|
4314
|
+
const absolutePath = join19(moduleDir, candidate);
|
|
4315
|
+
if (!existsSync16(absolutePath)) continue;
|
|
4061
4316
|
const chain = readExtends(absolutePath);
|
|
4062
4317
|
if (existing === void 0) {
|
|
4063
4318
|
existing = candidate;
|
|
@@ -4090,12 +4345,12 @@ var NOT_ADOPTED3 = (configFile, unreadable = null) => ({
|
|
|
4090
4345
|
});
|
|
4091
4346
|
function readAdoption(cwd, options) {
|
|
4092
4347
|
const target = resolveConfigTarget(cwd, options);
|
|
4093
|
-
if (target.reason === "none" || !
|
|
4348
|
+
if (target.reason === "none" || !existsSync17(join20(cwd, target.path))) {
|
|
4094
4349
|
return NOT_ADOPTED3(target.reason === "none" ? null : target.path);
|
|
4095
4350
|
}
|
|
4096
4351
|
let parsed;
|
|
4097
4352
|
try {
|
|
4098
|
-
parsed = parseJsonc(
|
|
4353
|
+
parsed = parseJsonc(readFileSync16(join20(cwd, target.path), "utf8"), target.path);
|
|
4099
4354
|
} catch (error) {
|
|
4100
4355
|
const reason = error instanceof Error ? error.message : String(error);
|
|
4101
4356
|
return NOT_ADOPTED3(target.path, `${target.path} could not be parsed (${reason})`);
|
|
@@ -4129,9 +4384,9 @@ function readLintAdoption(cwd) {
|
|
|
4129
4384
|
}
|
|
4130
4385
|
|
|
4131
4386
|
// src/roles/lint/resolve-oxlint.ts
|
|
4132
|
-
import { existsSync as
|
|
4387
|
+
import { existsSync as existsSync18 } from "fs";
|
|
4133
4388
|
import { createRequire as createRequire3 } from "module";
|
|
4134
|
-
import { delimiter as delimiter2, dirname as dirname5, join as
|
|
4389
|
+
import { delimiter as delimiter2, dirname as dirname5, join as join21 } from "path";
|
|
4135
4390
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4136
4391
|
var PACKAGE_OF = {
|
|
4137
4392
|
oxlint: "oxlint",
|
|
@@ -4156,32 +4411,31 @@ function tsgolintShim(cwd) {
|
|
|
4156
4411
|
for (const owner of ["oxlint-tsgolint", "oxlint"]) {
|
|
4157
4412
|
try {
|
|
4158
4413
|
const packageDir = dirname5(require3.resolve(`${owner}/package.json`));
|
|
4159
|
-
candidates.push(
|
|
4160
|
-
candidates.push(
|
|
4414
|
+
candidates.push(join21(packageDir, "node_modules", ".bin", "tsgolint"));
|
|
4415
|
+
candidates.push(join21(packageDir, "..", ".bin", "tsgolint"));
|
|
4161
4416
|
} catch {
|
|
4162
4417
|
}
|
|
4163
4418
|
}
|
|
4164
4419
|
try {
|
|
4165
4420
|
const ownRoot = dirname5(require3.resolve("@hublo/sentinel/package.json"));
|
|
4166
|
-
candidates.push(
|
|
4421
|
+
candidates.push(join21(ownRoot, "node_modules", ".bin", "tsgolint"));
|
|
4167
4422
|
} catch {
|
|
4168
4423
|
candidates.push(resolveBin(dirname5(fileURLToPath2(import.meta.url)), "tsgolint") ?? "");
|
|
4169
4424
|
}
|
|
4170
|
-
return candidates.find((candidate) => candidate !== "" &&
|
|
4425
|
+
return candidates.find((candidate) => candidate !== "" && existsSync18(candidate));
|
|
4171
4426
|
}
|
|
4172
4427
|
function oxlintSearchPath(cwd) {
|
|
4173
4428
|
return binSearchPath(cwd);
|
|
4174
4429
|
}
|
|
4175
4430
|
|
|
4176
4431
|
// src/roles/lint/adapters/oxlint/plan.ts
|
|
4177
|
-
import { existsSync as
|
|
4178
|
-
import { join as
|
|
4432
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19 } from "fs";
|
|
4433
|
+
import { join as join24 } from "path";
|
|
4179
4434
|
|
|
4180
4435
|
// src/roles/lint/eslint-ignores.ts
|
|
4181
|
-
import { existsSync as
|
|
4182
|
-
import { join as
|
|
4183
|
-
|
|
4184
|
-
var STRING_LITERAL = /['"`]([^'"`]+)['"`]/g;
|
|
4436
|
+
import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
|
|
4437
|
+
import { join as join22 } from "path";
|
|
4438
|
+
import { parseSync as parseSync2 } from "oxc-parser";
|
|
4185
4439
|
var OPAQUE_SOURCE = /\b(includeIgnoreFile)\s*\([^)]*\)/g;
|
|
4186
4440
|
function toOxlintPattern(pattern) {
|
|
4187
4441
|
return pattern.replace(/^\.\//, "");
|
|
@@ -4192,24 +4446,71 @@ function readRootEslintIgnores(root) {
|
|
|
4192
4446
|
return { patterns: patterns.filter((pattern) => pattern.startsWith("**/")), unresolved };
|
|
4193
4447
|
}
|
|
4194
4448
|
function readEslintIgnores(cwd) {
|
|
4195
|
-
const config = ESLINT_CONFIG_FILES.map((name) =>
|
|
4449
|
+
const config = ESLINT_CONFIG_FILES.map((name) => join22(cwd, name)).find((path) => existsSync19(path));
|
|
4196
4450
|
if (!config) return { patterns: [], unresolved: [] };
|
|
4197
4451
|
let source;
|
|
4198
4452
|
try {
|
|
4199
|
-
source =
|
|
4453
|
+
source = readFileSync17(config, "utf8");
|
|
4200
4454
|
} catch {
|
|
4201
4455
|
return { patterns: [], unresolved: [] };
|
|
4202
4456
|
}
|
|
4203
4457
|
const patterns = /* @__PURE__ */ new Set();
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4458
|
+
const unresolved = new Set([...source.matchAll(OPAQUE_SOURCE)].map((match) => match[0]));
|
|
4459
|
+
const { program, errors } = parseSync2(config, source, { sourceType: "module" });
|
|
4460
|
+
if (errors.length > 0) {
|
|
4461
|
+
return { patterns: [], unresolved: [`${config} could not be parsed`] };
|
|
4462
|
+
}
|
|
4463
|
+
walk(program, (node) => {
|
|
4464
|
+
if (node.type === "CallExpression" && calleeName(node) === "globalIgnores") {
|
|
4465
|
+
for (const literal of stringLiterals(firstArrayArgument(node))) {
|
|
4466
|
+
patterns.add(toOxlintPattern(literal));
|
|
4208
4467
|
}
|
|
4468
|
+
return;
|
|
4209
4469
|
}
|
|
4470
|
+
if (node.type !== "ObjectExpression") return;
|
|
4471
|
+
const keys = objectKeys(node);
|
|
4472
|
+
if (!keys.includes("ignores")) return;
|
|
4473
|
+
const values = arrayValues(node, "ignores");
|
|
4474
|
+
if (keys.length > 1) {
|
|
4475
|
+
unresolved.add(
|
|
4476
|
+
`ignores scoped to one config block (with ${keys.filter((k) => k !== "ignores").join(", ")}): ${values.join(", ")} \u2014 exempt from those RULES, not excluded from linting`
|
|
4477
|
+
);
|
|
4478
|
+
return;
|
|
4479
|
+
}
|
|
4480
|
+
for (const literal of values) patterns.add(toOxlintPattern(literal));
|
|
4481
|
+
});
|
|
4482
|
+
return { patterns: [...patterns], unresolved: [...unresolved] };
|
|
4483
|
+
}
|
|
4484
|
+
function walk(node, visit) {
|
|
4485
|
+
if (!node || typeof node !== "object") return;
|
|
4486
|
+
const current = node;
|
|
4487
|
+
if (typeof current.type === "string") visit(current);
|
|
4488
|
+
for (const key of Object.keys(current)) {
|
|
4489
|
+
const value = current[key];
|
|
4490
|
+
if (Array.isArray(value)) for (const entry of value) walk(entry, visit);
|
|
4491
|
+
else if (value && typeof value === "object") walk(value, visit);
|
|
4210
4492
|
}
|
|
4211
|
-
|
|
4212
|
-
|
|
4493
|
+
}
|
|
4494
|
+
function calleeName(node) {
|
|
4495
|
+
const callee = node.callee;
|
|
4496
|
+
return callee?.type === "Identifier" ? callee.name : void 0;
|
|
4497
|
+
}
|
|
4498
|
+
function firstArrayArgument(node) {
|
|
4499
|
+
const args = node.arguments ?? [];
|
|
4500
|
+
return args.find((argument) => argument?.type === "ArrayExpression");
|
|
4501
|
+
}
|
|
4502
|
+
function stringLiterals(array) {
|
|
4503
|
+
const elements = array?.elements ?? [];
|
|
4504
|
+
return elements.filter((element) => element?.type === "Literal" && typeof element.value === "string").map((element) => element.value);
|
|
4505
|
+
}
|
|
4506
|
+
function objectKeys(node) {
|
|
4507
|
+
const properties = node.properties ?? [];
|
|
4508
|
+
return properties.filter((property) => property?.type === "Property" && !property.computed).map((property) => property.key?.name ?? String(property.key?.value ?? "")).filter(Boolean);
|
|
4509
|
+
}
|
|
4510
|
+
function arrayValues(node, name) {
|
|
4511
|
+
const properties = node.properties ?? [];
|
|
4512
|
+
const found = properties.find((property) => property.key?.name === name);
|
|
4513
|
+
return stringLiterals(found?.value);
|
|
4213
4514
|
}
|
|
4214
4515
|
|
|
4215
4516
|
// src/roles/lint/lint-script.ts
|
|
@@ -4250,8 +4551,8 @@ function lintPresetFor(preset) {
|
|
|
4250
4551
|
}
|
|
4251
4552
|
|
|
4252
4553
|
// src/roles/lint/rename-suppressions.ts
|
|
4253
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
4254
|
-
import { join as
|
|
4554
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync18, statSync as statSync2 } from "fs";
|
|
4555
|
+
import { join as join23, relative as relative5 } from "path";
|
|
4255
4556
|
var SOURCE_EXTENSIONS = [
|
|
4256
4557
|
".ts",
|
|
4257
4558
|
".tsx",
|
|
@@ -4298,7 +4599,7 @@ function* sourceFiles(dir) {
|
|
|
4298
4599
|
return;
|
|
4299
4600
|
}
|
|
4300
4601
|
for (const entry of entries) {
|
|
4301
|
-
const full =
|
|
4602
|
+
const full = join23(dir, entry);
|
|
4302
4603
|
let isDirectory;
|
|
4303
4604
|
try {
|
|
4304
4605
|
isDirectory = statSync2(full).isDirectory();
|
|
@@ -4318,7 +4619,7 @@ function findSuppressionRenames(cwd, renames) {
|
|
|
4318
4619
|
for (const file of sourceFiles(cwd)) {
|
|
4319
4620
|
let content;
|
|
4320
4621
|
try {
|
|
4321
|
-
content =
|
|
4622
|
+
content = readFileSync18(file, "utf8");
|
|
4322
4623
|
} catch {
|
|
4323
4624
|
continue;
|
|
4324
4625
|
}
|
|
@@ -4342,7 +4643,7 @@ function findSuppressionRenames(cwd, renames) {
|
|
|
4342
4643
|
}
|
|
4343
4644
|
if (hits.length === 0) return;
|
|
4344
4645
|
found.push({
|
|
4345
|
-
file:
|
|
4646
|
+
file: relative5(cwd, file),
|
|
4346
4647
|
line: index + 1,
|
|
4347
4648
|
from: line,
|
|
4348
4649
|
to: line.replace(rules, renamed.join(", ")),
|
|
@@ -4446,7 +4747,7 @@ function plan2(context) {
|
|
|
4446
4747
|
keys: removableDeps
|
|
4447
4748
|
});
|
|
4448
4749
|
}
|
|
4449
|
-
const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) =>
|
|
4750
|
+
const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) => existsSync20(join24(context.cwd, name)));
|
|
4450
4751
|
for (const name of eslintConfigs) operations.push({ kind: "delete", path: name });
|
|
4451
4752
|
operations.push(
|
|
4452
4753
|
...nxTargetOperations({
|
|
@@ -4544,7 +4845,7 @@ function lintScripts(cwd) {
|
|
|
4544
4845
|
function committedExtends(cwd) {
|
|
4545
4846
|
try {
|
|
4546
4847
|
const parsed = parseJsonc(
|
|
4547
|
-
|
|
4848
|
+
readFileSync19(join24(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4548
4849
|
LINT_CONFIG_FILE
|
|
4549
4850
|
);
|
|
4550
4851
|
return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
|
|
@@ -4555,7 +4856,7 @@ function committedExtends(cwd) {
|
|
|
4555
4856
|
function committedIgnorePatterns(cwd) {
|
|
4556
4857
|
try {
|
|
4557
4858
|
const parsed = parseJsonc(
|
|
4558
|
-
|
|
4859
|
+
readFileSync19(join24(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4559
4860
|
LINT_CONFIG_FILE
|
|
4560
4861
|
);
|
|
4561
4862
|
return Array.isArray(parsed.ignorePatterns) ? parsed.ignorePatterns.filter((entry) => typeof entry === "string") : [];
|
|
@@ -4567,7 +4868,7 @@ function moduleEslintDependencies(cwd) {
|
|
|
4567
4868
|
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
4869
|
let manifest;
|
|
4569
4870
|
try {
|
|
4570
|
-
manifest = JSON.parse(
|
|
4871
|
+
manifest = JSON.parse(readFileSync19(join24(cwd, "package.json"), "utf8"));
|
|
4571
4872
|
} catch {
|
|
4572
4873
|
return [];
|
|
4573
4874
|
}
|
|
@@ -4701,20 +5002,20 @@ ${result.stderr ?? ""}`);
|
|
|
4701
5002
|
* build the developer can see, rather than a silent half-adoption they cannot.
|
|
4702
5003
|
*/
|
|
4703
5004
|
writeModuleBaseline(ctx, oxlint, env) {
|
|
4704
|
-
const configPath =
|
|
4705
|
-
const baselinePath =
|
|
4706
|
-
if (!
|
|
5005
|
+
const configPath = join25(ctx.cwd, LINT_CONFIG_FILE);
|
|
5006
|
+
const baselinePath = join25(ctx.cwd, LINT_BASELINE_FILE);
|
|
5007
|
+
if (!existsSync21(configPath)) return;
|
|
4707
5008
|
let config;
|
|
4708
5009
|
try {
|
|
4709
5010
|
config = parseJsonc(
|
|
4710
|
-
|
|
5011
|
+
readFileSync20(configPath, "utf8"),
|
|
4711
5012
|
LINT_CONFIG_FILE
|
|
4712
5013
|
);
|
|
4713
5014
|
} catch {
|
|
4714
5015
|
return;
|
|
4715
5016
|
}
|
|
4716
5017
|
const current = Array.isArray(config.extends) ? config.extends : [];
|
|
4717
|
-
const measurePath =
|
|
5018
|
+
const measurePath = join25(ctx.cwd, LINT_MEASURE_FILE);
|
|
4718
5019
|
let measured;
|
|
4719
5020
|
try {
|
|
4720
5021
|
writeFileSync2(
|
|
@@ -4755,14 +5056,14 @@ ${result.stderr ?? ""}`);
|
|
|
4755
5056
|
} else {
|
|
4756
5057
|
rmSync(baselinePath, { force: true });
|
|
4757
5058
|
}
|
|
4758
|
-
if (next.length !== current.length || next.some((entry,
|
|
5059
|
+
if (next.length !== current.length || next.some((entry, at) => entry !== current[at])) {
|
|
4759
5060
|
config.extends = next;
|
|
4760
5061
|
writeFileSync2(configPath, `${JSON.stringify(config, null, 2)}
|
|
4761
5062
|
`);
|
|
4762
5063
|
}
|
|
4763
5064
|
}
|
|
4764
5065
|
async run(ctx) {
|
|
4765
|
-
if (!
|
|
5066
|
+
if (!existsSync21(join25(ctx.cwd, LINT_CONFIG_FILE))) {
|
|
4766
5067
|
process.stderr.write(
|
|
4767
5068
|
`sentinel lint(oxlint): no ${LINT_CONFIG_FILE} in this module; run \`sentinel --init --lint\` to adopt.
|
|
4768
5069
|
`
|
|
@@ -4807,7 +5108,7 @@ ${result.stderr ?? ""}`);
|
|
|
4807
5108
|
valueFlags: OXLINT_VALUE_FLAGS
|
|
4808
5109
|
});
|
|
4809
5110
|
const targets = paths.length > 0 ? paths : ["."];
|
|
4810
|
-
const emptyScope = scopeFlags(paths);
|
|
5111
|
+
const emptyScope = scopeFlags(paths, passedOptions);
|
|
4811
5112
|
const lint = (extra) => {
|
|
4812
5113
|
const passed = [...extra, ...emptyScope, ...passedOptions];
|
|
4813
5114
|
const result = spawnSync4(oxlint, ["-c", LINT_CONFIG_FILE, ...passed, ...targets], {
|
|
@@ -4910,7 +5211,7 @@ ${result.stderr ?? ""}`);
|
|
|
4910
5211
|
let stub;
|
|
4911
5212
|
try {
|
|
4912
5213
|
stub = parseJsonc(
|
|
4913
|
-
|
|
5214
|
+
readFileSync20(join25(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4914
5215
|
LINT_CONFIG_FILE
|
|
4915
5216
|
);
|
|
4916
5217
|
} catch {
|
|
@@ -4920,7 +5221,7 @@ ${result.stderr ?? ""}`);
|
|
|
4920
5221
|
for (const entry of stub.extends ?? []) {
|
|
4921
5222
|
try {
|
|
4922
5223
|
const preset = parseJsonc(
|
|
4923
|
-
|
|
5224
|
+
readFileSync20(join25(cwd, entry), "utf8"),
|
|
4924
5225
|
entry
|
|
4925
5226
|
);
|
|
4926
5227
|
for (const rule of Object.keys(preset.rules ?? {})) names.add(rule);
|
|
@@ -4990,14 +5291,14 @@ ${result.stderr ?? ""}`);
|
|
|
4990
5291
|
let parsed;
|
|
4991
5292
|
try {
|
|
4992
5293
|
parsed = parseJsonc(
|
|
4993
|
-
|
|
5294
|
+
readFileSync20(join25(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4994
5295
|
LINT_CONFIG_FILE
|
|
4995
5296
|
);
|
|
4996
5297
|
} catch {
|
|
4997
5298
|
return void 0;
|
|
4998
5299
|
}
|
|
4999
5300
|
const targets = Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : typeof parsed.extends === "string" ? [parsed.extends] : [];
|
|
5000
|
-
return targets.find((target) => !
|
|
5301
|
+
return targets.find((target) => !existsSync21(join25(cwd, target)));
|
|
5001
5302
|
}
|
|
5002
5303
|
/** Announce what is not enforced, so reduced coverage is never silent. */
|
|
5003
5304
|
announceDisabled(preset) {
|
|
@@ -5029,9 +5330,9 @@ function registerLint() {
|
|
|
5029
5330
|
|
|
5030
5331
|
// src/roles/typescript/adapters/tsc/tsc.adapter.ts
|
|
5031
5332
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
5032
|
-
import { existsSync as
|
|
5333
|
+
import { existsSync as existsSync22, readFileSync as readFileSync22 } from "fs";
|
|
5033
5334
|
import { createRequire as createRequire4 } from "module";
|
|
5034
|
-
import { join as
|
|
5335
|
+
import { join as join27 } from "path";
|
|
5035
5336
|
|
|
5036
5337
|
// src/roles/typescript/presets/nest.json
|
|
5037
5338
|
var nest_default2 = {
|
|
@@ -5212,8 +5513,8 @@ function readTsconfigAdoption(cwd) {
|
|
|
5212
5513
|
}
|
|
5213
5514
|
|
|
5214
5515
|
// src/roles/typescript/adapters/tsc/plan.ts
|
|
5215
|
-
import { readFileSync as
|
|
5216
|
-
import { join as
|
|
5516
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
5517
|
+
import { join as join26 } from "path";
|
|
5217
5518
|
|
|
5218
5519
|
// src/roles/typescript/typecheck-script.ts
|
|
5219
5520
|
var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
|
|
@@ -5306,7 +5607,7 @@ function planAdoption(context) {
|
|
|
5306
5607
|
};
|
|
5307
5608
|
}
|
|
5308
5609
|
const existing = parseJsonc(
|
|
5309
|
-
|
|
5610
|
+
readFileSync21(join26(context.cwd, target.path), "utf8"),
|
|
5310
5611
|
target.path
|
|
5311
5612
|
);
|
|
5312
5613
|
const extendsChain = composeExtends(existing.extends, preset);
|
|
@@ -5444,7 +5745,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
5444
5745
|
let chain;
|
|
5445
5746
|
try {
|
|
5446
5747
|
const parsed = parseJsonc(
|
|
5447
|
-
|
|
5748
|
+
readFileSync22(join27(cwd, target.path), "utf8"),
|
|
5448
5749
|
target.path
|
|
5449
5750
|
);
|
|
5450
5751
|
chain = parsed.extends;
|
|
@@ -5457,7 +5758,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
5457
5758
|
);
|
|
5458
5759
|
if (preset === void 0) return void 0;
|
|
5459
5760
|
try {
|
|
5460
|
-
createRequire4(
|
|
5761
|
+
createRequire4(join27(cwd, "noop.js")).resolve(preset);
|
|
5461
5762
|
return void 0;
|
|
5462
5763
|
} catch {
|
|
5463
5764
|
return preset;
|
|
@@ -5553,7 +5854,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
5553
5854
|
referencedProjects(cwd, config) {
|
|
5554
5855
|
try {
|
|
5555
5856
|
const parsed = parseJsonc(
|
|
5556
|
-
|
|
5857
|
+
readFileSync22(join27(cwd, config), "utf8"),
|
|
5557
5858
|
config
|
|
5558
5859
|
);
|
|
5559
5860
|
const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path) => typeof path === "string" && path.length > 0);
|
|
@@ -5569,7 +5870,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
5569
5870
|
* check.
|
|
5570
5871
|
*/
|
|
5571
5872
|
typecheckTarget(cwd) {
|
|
5572
|
-
if (
|
|
5873
|
+
if (existsSync22(join27(cwd, "tsconfig.json"))) return "tsconfig.json";
|
|
5573
5874
|
const target = resolveTsconfigTarget(cwd);
|
|
5574
5875
|
return target.reason === "none" ? null : target.path;
|
|
5575
5876
|
}
|
|
@@ -5717,7 +6018,7 @@ function detectFramework(packageJson) {
|
|
|
5717
6018
|
}
|
|
5718
6019
|
|
|
5719
6020
|
// src/core/dispatch.ts
|
|
5720
|
-
import { basename as
|
|
6021
|
+
import { basename as basename3 } from "path";
|
|
5721
6022
|
|
|
5722
6023
|
// src/shared/text.ts
|
|
5723
6024
|
function ensureLines(current, lines) {
|
|
@@ -5773,9 +6074,16 @@ function replaceLines(current, replacements) {
|
|
|
5773
6074
|
}
|
|
5774
6075
|
|
|
5775
6076
|
// src/core/apply-plan.ts
|
|
5776
|
-
import { existsSync as
|
|
6077
|
+
import { existsSync as existsSync23, readFileSync as readFileSync23, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
5777
6078
|
import { resolve as resolve4, sep } from "path";
|
|
5778
6079
|
import { applyEdits, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
|
|
6080
|
+
|
|
6081
|
+
// src/shared/deep-merge.ts
|
|
6082
|
+
function isPlainObject(value) {
|
|
6083
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6084
|
+
}
|
|
6085
|
+
|
|
6086
|
+
// src/core/apply-plan.ts
|
|
5779
6087
|
function resolveWithinRoot(cwd, relativePath) {
|
|
5780
6088
|
const root = resolve4(cwd);
|
|
5781
6089
|
const absolutePath = resolve4(root, relativePath);
|
|
@@ -5785,7 +6093,7 @@ function resolveWithinRoot(cwd, relativePath) {
|
|
|
5785
6093
|
return absolutePath;
|
|
5786
6094
|
}
|
|
5787
6095
|
function readIfExists(absolutePath) {
|
|
5788
|
-
return
|
|
6096
|
+
return existsSync23(absolutePath) ? readFileSync23(absolutePath, "utf8") : void 0;
|
|
5789
6097
|
}
|
|
5790
6098
|
function* leaves(value, prefix = []) {
|
|
5791
6099
|
for (const [key, keyValue] of Object.entries(value)) {
|
|
@@ -5903,8 +6211,8 @@ function applyPlan(cwd, plan3) {
|
|
|
5903
6211
|
}
|
|
5904
6212
|
|
|
5905
6213
|
// src/core/config/preset-evidence.ts
|
|
5906
|
-
import { existsSync as
|
|
5907
|
-
import { join as
|
|
6214
|
+
import { existsSync as existsSync24, readdirSync as readdirSync3, readFileSync as readFileSync24 } from "fs";
|
|
6215
|
+
import { join as join28 } from "path";
|
|
5908
6216
|
var PATH_SIGNALS = [
|
|
5909
6217
|
{
|
|
5910
6218
|
preset: "nest",
|
|
@@ -5918,11 +6226,11 @@ var DEPENDENCY_SIGNALS = [
|
|
|
5918
6226
|
{ preset: "nest", pattern: /^@nestjs\// }
|
|
5919
6227
|
];
|
|
5920
6228
|
function dependencyNames(cwd) {
|
|
5921
|
-
const path =
|
|
5922
|
-
if (!
|
|
6229
|
+
const path = join28(cwd, "package.json");
|
|
6230
|
+
if (!existsSync24(path)) return [];
|
|
5923
6231
|
try {
|
|
5924
6232
|
const manifest = parseJsonc(
|
|
5925
|
-
|
|
6233
|
+
readFileSync24(path, "utf8"),
|
|
5926
6234
|
path
|
|
5927
6235
|
);
|
|
5928
6236
|
return [
|
|
@@ -5945,7 +6253,7 @@ function declaresJsx(cwd) {
|
|
|
5945
6253
|
for (const name of entries) {
|
|
5946
6254
|
try {
|
|
5947
6255
|
const config = parseJsonc(
|
|
5948
|
-
|
|
6256
|
+
readFileSync24(join28(cwd, name), "utf8"),
|
|
5949
6257
|
name
|
|
5950
6258
|
);
|
|
5951
6259
|
if (config.compilerOptions?.jsx !== void 0) return true;
|
|
@@ -6086,7 +6394,7 @@ async function dispatch(opts) {
|
|
|
6086
6394
|
process.stderr.write(` fixing what ${opts.target} can fix automatically...
|
|
6087
6395
|
`);
|
|
6088
6396
|
await adapter.afterInit({
|
|
6089
|
-
module:
|
|
6397
|
+
module: basename3(opts.cwd),
|
|
6090
6398
|
cwd: opts.cwd,
|
|
6091
6399
|
preset,
|
|
6092
6400
|
ci: false,
|
|
@@ -6108,9 +6416,11 @@ export {
|
|
|
6108
6416
|
resolveBin,
|
|
6109
6417
|
readOwnVersion,
|
|
6110
6418
|
readProjectPackageJson,
|
|
6111
|
-
|
|
6419
|
+
moduleName,
|
|
6420
|
+
buildConfigFile,
|
|
6112
6421
|
VERBS,
|
|
6113
6422
|
TARGETS,
|
|
6423
|
+
LONG_RUNNING_TARGETS,
|
|
6114
6424
|
SWEEPABLE_TARGETS,
|
|
6115
6425
|
PRESET_NAMES,
|
|
6116
6426
|
WORKSPACE_ROOT_MARKER,
|
|
@@ -6123,4 +6433,4 @@ export {
|
|
|
6123
6433
|
detectFramework,
|
|
6124
6434
|
dispatch
|
|
6125
6435
|
};
|
|
6126
|
-
//# sourceMappingURL=chunk-
|
|
6436
|
+
//# sourceMappingURL=chunk-6VOQDDZD.js.map
|