@hublo/sentinel 1.1.4 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/sentinel.js +28 -2
- package/dist/{chunk-HNE774F7.js → chunk-RGIEWANO.js} +342 -175
- package/dist/index.js +1 -1
- package/oxlint/nest.json +1 -1
- package/oxlint/react-lib.json +1 -1
- package/oxlint/react.json +1 -1
- package/package.json +1 -1
package/dist/bin/sentinel.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
dispatch,
|
|
10
10
|
ensureWorkspacePrep,
|
|
11
11
|
findWorkspaceRoot,
|
|
12
|
+
inspectWorkspacePrep,
|
|
12
13
|
palette,
|
|
13
14
|
readNxProjectName,
|
|
14
15
|
readOwnVersion,
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
registerAdapters,
|
|
17
18
|
resolve,
|
|
18
19
|
resolveBin
|
|
19
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-RGIEWANO.js";
|
|
20
21
|
|
|
21
22
|
// bin/sentinel.ts
|
|
22
23
|
import { program } from "commander";
|
|
@@ -438,6 +439,10 @@ function renderStatusSummary(items, p) {
|
|
|
438
439
|
}
|
|
439
440
|
|
|
440
441
|
// src/cli/run-verb.ts
|
|
442
|
+
function workspacePrep(cwd2) {
|
|
443
|
+
const root = findWorkspaceRoot(cwd2);
|
|
444
|
+
return root ? inspectWorkspacePrep(root) : [];
|
|
445
|
+
}
|
|
441
446
|
async function runVerb(ctx) {
|
|
442
447
|
const { modules, scope } = resolveContext(ctx.cwd, { module: ctx.module, ci: ctx.ci });
|
|
443
448
|
const out = palette(process.stdout);
|
|
@@ -482,7 +487,15 @@ async function runVerb(ctx) {
|
|
|
482
487
|
});
|
|
483
488
|
const summary = generateSummaries(results, ctx.targets);
|
|
484
489
|
if (ctx.json) {
|
|
485
|
-
const base = ctx.verb === "status" || ctx.verb === "inspect" ? {
|
|
490
|
+
const base = ctx.verb === "status" || ctx.verb === "inspect" ? {
|
|
491
|
+
...summary,
|
|
492
|
+
coverage: statusCoverage(summary.results),
|
|
493
|
+
// The root-level changes sentinel maintains, each with its justification checked
|
|
494
|
+
// NOW rather than described in a comment. They are the only thing sentinel does
|
|
495
|
+
// that nobody can see afterwards, and every one of them is temporary. See
|
|
496
|
+
// `inspectWorkspacePrep`.
|
|
497
|
+
...ctx.verb === "inspect" ? { workspace: workspacePrep(ctx.cwd) } : {}
|
|
498
|
+
} : summary;
|
|
486
499
|
const payload = ctx.toolArgs.length > 0 ? { ...base, toolArgs: ctx.toolArgs } : base;
|
|
487
500
|
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
488
501
|
} else if (ctx.verb === "status" || ctx.verb === "inspect" && modules.length > 1) {
|
|
@@ -493,6 +506,19 @@ async function runVerb(ctx) {
|
|
|
493
506
|
process.stdout.write(renderSummary(item, out) + "\n");
|
|
494
507
|
}
|
|
495
508
|
}
|
|
509
|
+
if (!ctx.json && ctx.verb === "inspect") {
|
|
510
|
+
const prep = workspacePrep(ctx.cwd);
|
|
511
|
+
if (prep.length > 0) {
|
|
512
|
+
process.stdout.write(`
|
|
513
|
+
sentinel maintains these at the workspace root:
|
|
514
|
+
`);
|
|
515
|
+
for (const entry of prep) {
|
|
516
|
+
process.stdout.write(` ${out.strong(entry.rule)}
|
|
517
|
+
${entry.reason}
|
|
518
|
+
`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
496
522
|
process.stderr.write(
|
|
497
523
|
err.dim(` ${modules.length} module(s) [${scope}] in ${Date.now() - started}ms
|
|
498
524
|
`)
|
|
@@ -140,12 +140,106 @@ var PRESET_NAMES = ["react", "nest", "svelte", "node", "tools"];
|
|
|
140
140
|
|
|
141
141
|
// src/roles/format/adapters/oxfmt/oxfmt.adapter.ts
|
|
142
142
|
import { spawnSync } from "child_process";
|
|
143
|
-
import { existsSync as existsSync10, readFileSync as
|
|
144
|
-
import { join as
|
|
143
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
144
|
+
import { join as join12 } from "path";
|
|
145
|
+
|
|
146
|
+
// src/core/config/existing-command.ts
|
|
147
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
148
|
+
import { join as join3 } from "path";
|
|
149
|
+
|
|
150
|
+
// src/shared/jsonc.ts
|
|
151
|
+
import { parse, printParseErrorCode } from "jsonc-parser";
|
|
152
|
+
function parseJsonc(text, source = "config") {
|
|
153
|
+
const errors = [];
|
|
154
|
+
const value = parse(text, errors, { allowTrailingComma: true });
|
|
155
|
+
if (errors.length > 0) {
|
|
156
|
+
const details = errors.map((error) => printParseErrorCode(error.error)).join(", ");
|
|
157
|
+
throw new Error(`${source}: malformed JSONC (${details}).`);
|
|
158
|
+
}
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/core/config/manifest.ts
|
|
163
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
164
|
+
import { basename, join as join2 } from "path";
|
|
165
|
+
function moduleScripts(cwd) {
|
|
166
|
+
try {
|
|
167
|
+
const pkg = JSON.parse(readFileSync2(join2(cwd, "package.json"), "utf8"));
|
|
168
|
+
return pkg.scripts ?? {};
|
|
169
|
+
} catch {
|
|
170
|
+
return {};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function moduleName(cwd) {
|
|
174
|
+
try {
|
|
175
|
+
const pkg = JSON.parse(readFileSync2(join2(cwd, "package.json"), "utf8"));
|
|
176
|
+
return pkg.name;
|
|
177
|
+
} catch {
|
|
178
|
+
return void 0;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function isSelfAdoption(cwd) {
|
|
182
|
+
return moduleName(cwd) === readOwnPackage().name;
|
|
183
|
+
}
|
|
184
|
+
function selfCommand(cwd, flags) {
|
|
185
|
+
const own = readOwnPackage();
|
|
186
|
+
if (!isSelfAdoption(cwd) || !own.bin) return void 0;
|
|
187
|
+
return `node ${own.bin.replace(/^\.\//, "")} ${flags}`;
|
|
188
|
+
}
|
|
189
|
+
function manifestOperation(cwd, scripts) {
|
|
190
|
+
const own = readOwnPackage();
|
|
191
|
+
const value = {
|
|
192
|
+
scripts,
|
|
193
|
+
devDependencies: { [own.name]: isSelfAdoption(cwd) ? "link:." : own.version }
|
|
194
|
+
};
|
|
195
|
+
if (existsSync2(join2(cwd, "package.json"))) {
|
|
196
|
+
return { kind: "merge-json", path: "package.json", value };
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
kind: "merge-json",
|
|
200
|
+
path: "package.json",
|
|
201
|
+
value: { name: readNxProjectName(cwd) ?? basename(cwd), private: true, ...value }
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/core/config/existing-command.ts
|
|
206
|
+
function nxTargetCommand(cwd, target) {
|
|
207
|
+
let project;
|
|
208
|
+
try {
|
|
209
|
+
project = parseJsonc(
|
|
210
|
+
readFileSync3(join3(cwd, "project.json"), "utf8"),
|
|
211
|
+
"project.json"
|
|
212
|
+
);
|
|
213
|
+
} catch {
|
|
214
|
+
return void 0;
|
|
215
|
+
}
|
|
216
|
+
const entry = project.targets?.[target];
|
|
217
|
+
if (!entry || entry.executor !== "nx:run-commands") return void 0;
|
|
218
|
+
const { command, commands, cwd: targetCwd } = entry.options ?? {};
|
|
219
|
+
const ranInModule = typeof targetCwd === "string" && targetCwd.trim() !== "";
|
|
220
|
+
if (typeof command === "string" && command.trim() !== "") {
|
|
221
|
+
return { command: command.trim(), ranInModule };
|
|
222
|
+
}
|
|
223
|
+
if (Array.isArray(commands)) {
|
|
224
|
+
const parts = commands.filter((c) => typeof c === "string" && c.trim() !== "");
|
|
225
|
+
if (parts.length > 0) return { command: parts.map((c) => c.trim()).join(" && "), ranInModule };
|
|
226
|
+
}
|
|
227
|
+
return void 0;
|
|
228
|
+
}
|
|
229
|
+
function rootedForScript(command) {
|
|
230
|
+
return command.replace(/(^|&&\s*)pnpm exec /g, "$1pnpm --workspace-root exec ");
|
|
231
|
+
}
|
|
232
|
+
function existingCommand(cwd, target) {
|
|
233
|
+
const script = moduleScripts(cwd)[target];
|
|
234
|
+
if (script !== void 0 && script.trim() !== "") return script;
|
|
235
|
+
const fromTarget = nxTargetCommand(cwd, target);
|
|
236
|
+
if (fromTarget === void 0) return void 0;
|
|
237
|
+
return fromTarget.ranInModule ? fromTarget.command : rootedForScript(fromTarget.command);
|
|
238
|
+
}
|
|
145
239
|
|
|
146
240
|
// src/core/config/has-source.ts
|
|
147
241
|
import { readdirSync } from "fs";
|
|
148
|
-
import { extname, join as
|
|
242
|
+
import { extname, join as join4 } from "path";
|
|
149
243
|
var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
150
244
|
"node_modules",
|
|
151
245
|
"dist",
|
|
@@ -196,7 +290,7 @@ function hasSourceFiles(cwd, extensions) {
|
|
|
196
290
|
}
|
|
197
291
|
for (const entry of entries) {
|
|
198
292
|
if (entry.isDirectory()) {
|
|
199
|
-
if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(
|
|
293
|
+
if (!SKIP_DIRECTORIES.has(entry.name)) queue.push(join4(dir, entry.name));
|
|
200
294
|
continue;
|
|
201
295
|
}
|
|
202
296
|
if (wanted.has(extname(entry.name))) return true;
|
|
@@ -205,58 +299,15 @@ function hasSourceFiles(cwd, extensions) {
|
|
|
205
299
|
return false;
|
|
206
300
|
}
|
|
207
301
|
|
|
208
|
-
// src/core/config/manifest.ts
|
|
209
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
210
|
-
import { basename, join as join3 } from "path";
|
|
211
|
-
function moduleScripts(cwd) {
|
|
212
|
-
try {
|
|
213
|
-
const pkg = JSON.parse(readFileSync2(join3(cwd, "package.json"), "utf8"));
|
|
214
|
-
return pkg.scripts ?? {};
|
|
215
|
-
} catch {
|
|
216
|
-
return {};
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
function moduleName(cwd) {
|
|
220
|
-
try {
|
|
221
|
-
const pkg = JSON.parse(readFileSync2(join3(cwd, "package.json"), "utf8"));
|
|
222
|
-
return pkg.name;
|
|
223
|
-
} catch {
|
|
224
|
-
return void 0;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
function isSelfAdoption(cwd) {
|
|
228
|
-
return moduleName(cwd) === readOwnPackage().name;
|
|
229
|
-
}
|
|
230
|
-
function selfCommand(cwd, flags) {
|
|
231
|
-
const own = readOwnPackage();
|
|
232
|
-
if (!isSelfAdoption(cwd) || !own.bin) return void 0;
|
|
233
|
-
return `node ${own.bin.replace(/^\.\//, "")} ${flags}`;
|
|
234
|
-
}
|
|
235
|
-
function manifestOperation(cwd, scripts) {
|
|
236
|
-
const own = readOwnPackage();
|
|
237
|
-
const value = {
|
|
238
|
-
scripts,
|
|
239
|
-
devDependencies: { [own.name]: isSelfAdoption(cwd) ? "link:." : own.version }
|
|
240
|
-
};
|
|
241
|
-
if (existsSync2(join3(cwd, "package.json"))) {
|
|
242
|
-
return { kind: "merge-json", path: "package.json", value };
|
|
243
|
-
}
|
|
244
|
-
return {
|
|
245
|
-
kind: "merge-json",
|
|
246
|
-
path: "package.json",
|
|
247
|
-
value: { name: readNxProjectName(cwd) ?? basename(cwd), private: true, ...value }
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
302
|
// src/core/config/nx-target.ts
|
|
252
303
|
import { existsSync as existsSync3 } from "fs";
|
|
253
|
-
import { join as
|
|
304
|
+
import { join as join5 } from "path";
|
|
254
305
|
function nxTargetOperations(options) {
|
|
255
306
|
const { cwd, targets } = options;
|
|
256
307
|
const names = Object.keys(targets);
|
|
257
308
|
if (names.length === 0) return [];
|
|
258
309
|
const operations = [];
|
|
259
|
-
if (existsSync3(
|
|
310
|
+
if (existsSync3(join5(cwd, "project.json"))) {
|
|
260
311
|
operations.push({
|
|
261
312
|
kind: "remove-json-keys",
|
|
262
313
|
path: "project.json",
|
|
@@ -294,8 +345,8 @@ var WORKSPACE_ROOT_MARKER = "nx.json";
|
|
|
294
345
|
var DEFAULT_MAX_DIAGNOSTICS = 100;
|
|
295
346
|
|
|
296
347
|
// src/core/workspace-prep.ts
|
|
297
|
-
import { existsSync as existsSync5, readFileSync as
|
|
298
|
-
import { dirname as dirname2, join as
|
|
348
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
349
|
+
import { dirname as dirname2, join as join6, relative } from "path";
|
|
299
350
|
var OVERRIDE_KEY = "i18next>typescript";
|
|
300
351
|
var NATIVE_TS_ALIAS = "@typescript/native";
|
|
301
352
|
var WORKSPACE_YAML = "pnpm-workspace.yaml";
|
|
@@ -304,7 +355,7 @@ var LIST_ITEM = /^(\s*)-\s+(.*?)\s*$/;
|
|
|
304
355
|
function findWorkspaceRoot(startDir) {
|
|
305
356
|
let dir = startDir;
|
|
306
357
|
for (; ; ) {
|
|
307
|
-
if (existsSync5(
|
|
358
|
+
if (existsSync5(join6(dir, WORKSPACE_ROOT_MARKER))) return dir;
|
|
308
359
|
const parent = dirname2(dir);
|
|
309
360
|
if (parent === dir) return void 0;
|
|
310
361
|
dir = parent;
|
|
@@ -317,9 +368,9 @@ function declaredNativeTs(pkg) {
|
|
|
317
368
|
return version || void 0;
|
|
318
369
|
}
|
|
319
370
|
function ensureI18nextSingleton(root, dryRun) {
|
|
320
|
-
const pkgPath =
|
|
371
|
+
const pkgPath = join6(root, "package.json");
|
|
321
372
|
if (!existsSync5(pkgPath)) return void 0;
|
|
322
|
-
const pkg = JSON.parse(
|
|
373
|
+
const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
|
|
323
374
|
const want = declaredNativeTs(pkg);
|
|
324
375
|
if (!want) return void 0;
|
|
325
376
|
const have = pkg.pnpm?.overrides?.[OVERRIDE_KEY];
|
|
@@ -332,10 +383,10 @@ function ensureI18nextSingleton(root, dryRun) {
|
|
|
332
383
|
return `pinned ${OVERRIDE_KEY} to ${want} in package.json (i18next singleton)`;
|
|
333
384
|
}
|
|
334
385
|
function ensureReleaseAgeAllowList(root, dryRun) {
|
|
335
|
-
const yamlPath =
|
|
386
|
+
const yamlPath = join6(root, WORKSPACE_YAML);
|
|
336
387
|
if (!existsSync5(yamlPath)) return void 0;
|
|
337
388
|
const own = readOwnPackage().name;
|
|
338
|
-
const lines =
|
|
389
|
+
const lines = readFileSync4(yamlPath, "utf8").split("\n");
|
|
339
390
|
const keyIdx = lines.findIndex((line) => line.replace(/\s+$/, "") === `${RELEASE_AGE_KEY}:`);
|
|
340
391
|
if (keyIdx === -1) return void 0;
|
|
341
392
|
let lastItemIdx = keyIdx;
|
|
@@ -370,11 +421,11 @@ var ROOT_PRETTIER_CONFIGS = [
|
|
|
370
421
|
function ensureFormatterExclusion(root, moduleDir, dryRun) {
|
|
371
422
|
const rel = relative(root, moduleDir).replaceAll("\\", "/");
|
|
372
423
|
if (rel === "" || rel.startsWith("..")) return void 0;
|
|
373
|
-
const ignorePath =
|
|
374
|
-
const hasPrettier = existsSync5(ignorePath) || ROOT_PRETTIER_CONFIGS.some((name) => existsSync5(
|
|
424
|
+
const ignorePath = join6(root, PRETTIER_IGNORE);
|
|
425
|
+
const hasPrettier = existsSync5(ignorePath) || ROOT_PRETTIER_CONFIGS.some((name) => existsSync5(join6(root, name)));
|
|
375
426
|
if (!hasPrettier) return void 0;
|
|
376
427
|
const pattern = `/${rel}/`;
|
|
377
|
-
const existing = existsSync5(ignorePath) ?
|
|
428
|
+
const existing = existsSync5(ignorePath) ? readFileSync4(ignorePath, "utf8") : "";
|
|
378
429
|
const lines = existing.split("\n");
|
|
379
430
|
if (lines.some((line) => line.trim().replace(/\/$/, "") === pattern.replace(/\/$/, ""))) {
|
|
380
431
|
return void 0;
|
|
@@ -407,16 +458,50 @@ function ensureWorkspacePrep(opts) {
|
|
|
407
458
|
opts.formattedModule === void 0 ? void 0 : ensureFormatterExclusion(opts.root, opts.formattedModule, dryRun)
|
|
408
459
|
].filter((message) => message !== void 0);
|
|
409
460
|
}
|
|
461
|
+
function inspectWorkspacePrep(root) {
|
|
462
|
+
const entries = [];
|
|
463
|
+
let pkg = {};
|
|
464
|
+
const pkgPath = join6(root, "package.json");
|
|
465
|
+
if (existsSync5(pkgPath)) {
|
|
466
|
+
try {
|
|
467
|
+
pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
|
|
468
|
+
} catch {
|
|
469
|
+
pkg = {};
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
const pinned = pkg.pnpm?.overrides?.[OVERRIDE_KEY];
|
|
473
|
+
if (pinned !== void 0) {
|
|
474
|
+
const stillForks = declaredNativeTs(pkg);
|
|
475
|
+
entries.push({
|
|
476
|
+
rule: `package.json \u2192 pnpm.overrides.${OVERRIDE_KEY}`,
|
|
477
|
+
reason: stillForks === void 0 ? `pinned to ${pinned}, but the root no longer declares ${NATIVE_TS_ALIAS}, so nothing forks any more. This override is now dead config and can be removed.` : `pinned to ${pinned}, matching the second TypeScript the root declares (${NATIVE_TS_ALIAS}). It re-resolves i18next for EVERY module that depends on it, not only adopted ones. It retires with the second TypeScript.`
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
const own = readOwnPackage().name;
|
|
481
|
+
const yamlPath = join6(root, WORKSPACE_YAML);
|
|
482
|
+
if (existsSync5(yamlPath)) {
|
|
483
|
+
const yaml = readFileSync4(yamlPath, "utf8");
|
|
484
|
+
const listed = new RegExp(`^\\s*-\\s*['"]?${own.replace("/", "\\/")}['"]?\\s*$`, "m").test(yaml);
|
|
485
|
+
if (listed) {
|
|
486
|
+
const gated = new RegExp(`^\\s*minimumReleaseAge\\s*:`, "m").test(yaml);
|
|
487
|
+
entries.push({
|
|
488
|
+
rule: `${WORKSPACE_YAML} \u2192 ${RELEASE_AGE_KEY}: ${own}`,
|
|
489
|
+
reason: gated ? `${own} is exempt from this workspace's release-age gate, so a freshly published version installs instead of being held. That is a standing exemption from a supply-chain control: the exposure is one bump wide, since the version is pinned and the lockfile carries its integrity, so a re-publish of the same version does not pass. Retire it once a bump can simply wait out the window.` : `${own} is allow-listed, but this workspace no longer sets minimumReleaseAge, so there is no gate to be exempt from. This entry is now dead config and can be removed.`
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return entries;
|
|
494
|
+
}
|
|
410
495
|
|
|
411
496
|
// src/shared/resolve-bin.ts
|
|
412
497
|
import { existsSync as existsSync6 } from "fs";
|
|
413
498
|
import { createRequire } from "module";
|
|
414
|
-
import { delimiter, dirname as dirname3, join as
|
|
499
|
+
import { delimiter, dirname as dirname3, join as join7 } from "path";
|
|
415
500
|
var require2 = createRequire(import.meta.url);
|
|
416
501
|
function resolveBin(fromDir, name) {
|
|
417
502
|
let dir = fromDir;
|
|
418
503
|
for (; ; ) {
|
|
419
|
-
const candidate =
|
|
504
|
+
const candidate = join7(dir, "node_modules", ".bin", name);
|
|
420
505
|
if (existsSync6(candidate)) return candidate;
|
|
421
506
|
const parent = dirname3(dir);
|
|
422
507
|
if (parent === dir) return void 0;
|
|
@@ -429,7 +514,7 @@ function binFromOwnInstall(packageName, binName) {
|
|
|
429
514
|
const bin = require2(manifest).bin;
|
|
430
515
|
const relative3 = typeof bin === "string" ? bin : bin?.[binName];
|
|
431
516
|
if (!relative3) return void 0;
|
|
432
|
-
const executable =
|
|
517
|
+
const executable = join7(dirname3(manifest), relative3);
|
|
433
518
|
return existsSync6(executable) ? executable : void 0;
|
|
434
519
|
} catch {
|
|
435
520
|
return void 0;
|
|
@@ -463,6 +548,7 @@ var PERMITTED_LOCAL_KEYS = [
|
|
|
463
548
|
"experimentalOperatorPosition"
|
|
464
549
|
];
|
|
465
550
|
var ALWAYS_LOCAL_KEYS = ["ignorePatterns", "overrides"];
|
|
551
|
+
var FORMAT_DEFAULT_IGNORES = ["**/*.toml"];
|
|
466
552
|
var PRETTIER_CONFIG_FILES = [
|
|
467
553
|
".prettierrc",
|
|
468
554
|
".prettierrc.json",
|
|
@@ -556,8 +642,8 @@ function writesWhenRewritten(name, command) {
|
|
|
556
642
|
var CHECK_SCRIPT_NAMES = /* @__PURE__ */ new Set(["lint", "format", "check", "test", "typecheck", "verify"]);
|
|
557
643
|
|
|
558
644
|
// src/roles/format/inherited-ignores.ts
|
|
559
|
-
import { existsSync as existsSync7, readFileSync as
|
|
560
|
-
import { join as
|
|
645
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
|
|
646
|
+
import { join as join8 } from "path";
|
|
561
647
|
var ROOT_IGNORE_FILE = ".prettierignore";
|
|
562
648
|
function isPattern(line) {
|
|
563
649
|
const trimmed = line.trim();
|
|
@@ -572,11 +658,11 @@ function toModulePattern(pattern) {
|
|
|
572
658
|
}
|
|
573
659
|
function inheritedIgnorePatterns(workspaceRoot) {
|
|
574
660
|
if (!workspaceRoot) return [];
|
|
575
|
-
const path =
|
|
661
|
+
const path = join8(workspaceRoot, ROOT_IGNORE_FILE);
|
|
576
662
|
if (!existsSync7(path)) return [];
|
|
577
663
|
let contents;
|
|
578
664
|
try {
|
|
579
|
-
contents =
|
|
665
|
+
contents = readFileSync5(path, "utf8");
|
|
580
666
|
} catch {
|
|
581
667
|
return [];
|
|
582
668
|
}
|
|
@@ -663,25 +749,13 @@ function formatPresetFor(preset) {
|
|
|
663
749
|
}
|
|
664
750
|
|
|
665
751
|
// src/roles/format/prettier-config.ts
|
|
666
|
-
import { existsSync as existsSync8, readFileSync as
|
|
667
|
-
import { join as
|
|
668
|
-
|
|
669
|
-
// src/shared/jsonc.ts
|
|
670
|
-
import { parse, printParseErrorCode } from "jsonc-parser";
|
|
671
|
-
function parseJsonc(text, source = "config") {
|
|
672
|
-
const errors = [];
|
|
673
|
-
const value = parse(text, errors, { allowTrailingComma: true });
|
|
674
|
-
if (errors.length > 0) {
|
|
675
|
-
const details = errors.map((error) => printParseErrorCode(error.error)).join(", ");
|
|
676
|
-
throw new Error(`${source}: malformed JSONC (${details}).`);
|
|
677
|
-
}
|
|
678
|
-
return value;
|
|
679
|
-
}
|
|
752
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
|
|
753
|
+
import { join as join10 } from "path";
|
|
680
754
|
|
|
681
755
|
// src/roles/format/resolve-oxfmt.ts
|
|
682
|
-
import { readFileSync as
|
|
756
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
683
757
|
import { createRequire as createRequire2 } from "module";
|
|
684
|
-
import { dirname as dirname4, join as
|
|
758
|
+
import { dirname as dirname4, join as join9 } from "path";
|
|
685
759
|
function resolveOxfmt(cwd) {
|
|
686
760
|
return resolveBin(cwd, "oxfmt") ?? binFromOwnInstall("oxfmt", "oxfmt");
|
|
687
761
|
}
|
|
@@ -710,8 +784,8 @@ function configSchema() {
|
|
|
710
784
|
function readConfigSchema() {
|
|
711
785
|
try {
|
|
712
786
|
const manifest = createRequire2(import.meta.url).resolve("oxfmt/package.json");
|
|
713
|
-
const schemaPath =
|
|
714
|
-
return JSON.parse(
|
|
787
|
+
const schemaPath = join9(dirname4(manifest), "configuration_schema.json");
|
|
788
|
+
return JSON.parse(readFileSync6(schemaPath, "utf8"));
|
|
715
789
|
} catch {
|
|
716
790
|
return void 0;
|
|
717
791
|
}
|
|
@@ -742,14 +816,14 @@ function toOxfmtOverrides(value) {
|
|
|
742
816
|
return { overrides, unresolved };
|
|
743
817
|
}
|
|
744
818
|
function readPrettierSettings(cwd) {
|
|
745
|
-
const file = PRETTIER_CONFIG_FILES.find((name) => existsSync8(
|
|
819
|
+
const file = PRETTIER_CONFIG_FILES.find((name) => existsSync8(join10(cwd, name)));
|
|
746
820
|
if (!file) return { options: {}, unresolved: [] };
|
|
747
821
|
if (/\.(js|cjs|mjs)$/.test(file)) {
|
|
748
822
|
return { options: {}, file, unresolved: [`${file} is JavaScript, so it was not executed`] };
|
|
749
823
|
}
|
|
750
824
|
let parsed;
|
|
751
825
|
try {
|
|
752
|
-
parsed = parseJsonc(
|
|
826
|
+
parsed = parseJsonc(readFileSync7(join10(cwd, file), "utf8"), file);
|
|
753
827
|
} catch {
|
|
754
828
|
return { options: {}, file, unresolved: [`${file} could not be parsed`] };
|
|
755
829
|
}
|
|
@@ -779,8 +853,8 @@ function readPrettierSettings(cwd) {
|
|
|
779
853
|
}
|
|
780
854
|
|
|
781
855
|
// src/roles/format/read-adoption.ts
|
|
782
|
-
import { existsSync as existsSync9, readFileSync as
|
|
783
|
-
import { join as
|
|
856
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
857
|
+
import { join as join11 } from "path";
|
|
784
858
|
var NOT_ADOPTED = (configFile, unreadable = null) => ({
|
|
785
859
|
configFile,
|
|
786
860
|
preset: null,
|
|
@@ -796,11 +870,11 @@ function sameValue(a, b) {
|
|
|
796
870
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
797
871
|
}
|
|
798
872
|
function readFormatAdoption(cwd) {
|
|
799
|
-
const path =
|
|
873
|
+
const path = join11(cwd, FORMAT_CONFIG_FILE);
|
|
800
874
|
if (!existsSync9(path)) return NOT_ADOPTED(null);
|
|
801
875
|
let parsed;
|
|
802
876
|
try {
|
|
803
|
-
parsed = parseJsonc(
|
|
877
|
+
parsed = parseJsonc(readFileSync8(path, "utf8"), FORMAT_CONFIG_FILE);
|
|
804
878
|
} catch (error) {
|
|
805
879
|
const reason = error instanceof Error ? error.message : String(error);
|
|
806
880
|
return NOT_ADOPTED(FORMAT_CONFIG_FILE, `${FORMAT_CONFIG_FILE} could not be parsed (${reason})`);
|
|
@@ -887,7 +961,9 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
887
961
|
const localKeys = Object.keys(local).filter((key) => !ALWAYS_LOCAL_KEYS.includes(key)).sort();
|
|
888
962
|
const inherited = inheritedIgnorePatterns(findWorkspaceRoot(context.cwd));
|
|
889
963
|
const declaredIgnores = Array.isArray(local.ignorePatterns) ? local.ignorePatterns.filter((v) => typeof v === "string") : [];
|
|
890
|
-
const ignorePatterns = [
|
|
964
|
+
const ignorePatterns = [
|
|
965
|
+
.../* @__PURE__ */ new Set([...declaredIgnores, ...inherited, ...FORMAT_DEFAULT_IGNORES])
|
|
966
|
+
].sort();
|
|
891
967
|
if (ignorePatterns.length > 0) local.ignorePatterns = ignorePatterns;
|
|
892
968
|
const unchanged = existing.adopted && existing.conformant;
|
|
893
969
|
const version = unchanged && existing.presetVersion ? existing.presetVersion : own.version;
|
|
@@ -907,7 +983,7 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
907
983
|
manifestOperation(context.cwd, this.formatScripts(context.cwd))
|
|
908
984
|
];
|
|
909
985
|
const prettierConfigs = PRETTIER_CONFIG_FILES.filter(
|
|
910
|
-
(name) => existsSync10(
|
|
986
|
+
(name) => existsSync10(join12(context.cwd, name))
|
|
911
987
|
);
|
|
912
988
|
for (const name of prettierConfigs) operations.push({ kind: "delete", path: name });
|
|
913
989
|
const removableDeps = this.modulePrettierDependencies(context.cwd);
|
|
@@ -1143,7 +1219,7 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1143
1219
|
modulePrettierDependencies(cwd) {
|
|
1144
1220
|
const isPrettierPackage = (name) => name === "prettier" || name.startsWith("prettier-plugin-") || name.startsWith("@prettier/");
|
|
1145
1221
|
try {
|
|
1146
|
-
const manifest = JSON.parse(
|
|
1222
|
+
const manifest = JSON.parse(readFileSync9(join12(cwd, "package.json"), "utf8"));
|
|
1147
1223
|
return Object.keys(manifest.devDependencies ?? {}).filter(isPrettierPackage).map((name) => ["devDependencies", name]);
|
|
1148
1224
|
} catch {
|
|
1149
1225
|
return [];
|
|
@@ -1166,7 +1242,9 @@ var OxfmtAdapter = class extends BaseAdapter {
|
|
|
1166
1242
|
const ownCommand = selfCommand(cwd, "--run --format");
|
|
1167
1243
|
const compose = (existing, keepFix) => composeFormatScript(existing, { keepFix, command: ownCommand });
|
|
1168
1244
|
const rewritten = {
|
|
1169
|
-
|
|
1245
|
+
// `existingCommand` for the role's own script: the command can live in an
|
|
1246
|
+
// `nx:run-commands` target that adoption deletes, taking anything else it ran with it.
|
|
1247
|
+
[FORMAT_SCRIPT_NAME]: compose(existingCommand(cwd, FORMAT_SCRIPT_NAME), false),
|
|
1170
1248
|
[`${FORMAT_SCRIPT_NAME}:fix`]: compose(scripts[`${FORMAT_SCRIPT_NAME}:fix`], true)
|
|
1171
1249
|
};
|
|
1172
1250
|
for (const [name, command] of Object.entries(scripts)) {
|
|
@@ -1223,8 +1301,8 @@ function registerFormat() {
|
|
|
1223
1301
|
|
|
1224
1302
|
// src/roles/lint/adapters/oxlint/oxlint.adapter.ts
|
|
1225
1303
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1226
|
-
import { existsSync as
|
|
1227
|
-
import { join as
|
|
1304
|
+
import { existsSync as existsSync17, readFileSync as readFileSync16, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1305
|
+
import { join as join20 } from "path";
|
|
1228
1306
|
|
|
1229
1307
|
// src/core/config/deferred-rules.ts
|
|
1230
1308
|
function deferredRuleNames(rules) {
|
|
@@ -1238,6 +1316,14 @@ var PRESET_DIR = "./node_modules/@hublo/sentinel/oxlint";
|
|
|
1238
1316
|
function presetPath(preset) {
|
|
1239
1317
|
return `${PRESET_DIR}/${preset}.json`;
|
|
1240
1318
|
}
|
|
1319
|
+
function isSentinelPreset(entry) {
|
|
1320
|
+
return entry.includes("@hublo/sentinel/oxlint/");
|
|
1321
|
+
}
|
|
1322
|
+
function extendsWithPreset(current, variant) {
|
|
1323
|
+
const own = presetPath(variant);
|
|
1324
|
+
const others = current.filter((entry) => entry !== own && !isSentinelPreset(entry));
|
|
1325
|
+
return [own, ...new Set(others)];
|
|
1326
|
+
}
|
|
1241
1327
|
function presetVariant(preset, cwd) {
|
|
1242
1328
|
const normalised = cwd.replaceAll("\\", "/");
|
|
1243
1329
|
return preset === "react" && /(^|\/)libs\//.test(normalised) ? "react-lib" : preset;
|
|
@@ -1257,7 +1343,7 @@ var ESLINT_CONFIG_FILES = [
|
|
|
1257
1343
|
"eslint.config.ts"
|
|
1258
1344
|
];
|
|
1259
1345
|
function lintTarget() {
|
|
1260
|
-
return { cache: true, inputs: ["default", `{projectRoot}/${LINT_CONFIG_FILE}`] };
|
|
1346
|
+
return { cache: true, inputs: ["default", "^default", `{projectRoot}/${LINT_CONFIG_FILE}`] };
|
|
1261
1347
|
}
|
|
1262
1348
|
|
|
1263
1349
|
// src/roles/lint/presets/base.json
|
|
@@ -1601,8 +1687,8 @@ var nest_default = {
|
|
|
1601
1687
|
},
|
|
1602
1688
|
"typescript/return-await": {
|
|
1603
1689
|
severity: "warn",
|
|
1604
|
-
reason: "replaces the deprecated core `no-return-await`, which both modules set to error but which was never actually reporting: the nx lint target expanded to 2 files of 2888.
|
|
1605
|
-
options: ["
|
|
1690
|
+
reason: "replaces the deprecated core `no-return-await`, which both modules set to error but which was never actually reporting: the nx lint target expanded to 2 files of 2888. `in-try-catch` rather than `never`, and the planning adoption is what settled it. `never` forces `return await f()` to become `const x = await f(); return x` even inside a `try`, where the await is REQUIRED for the catch to fire: it produced 10 such edits across 15 modules, every one of them inside a try/catch, and every one of them a rewrite that made the code longer without making it safer. Worse, the obvious reading of the rule is to delete the `await`, which silently stops the catch from running. `in-try-catch` is what typescript-eslint recommends and flags none of those 10. Held at warn so adoption does not turn a green module red, and raised to error once the remaining findings are fixed.",
|
|
1691
|
+
options: ["in-try-catch"]
|
|
1606
1692
|
},
|
|
1607
1693
|
"typescript/switch-exhaustiveness-check": [
|
|
1608
1694
|
"error",
|
|
@@ -1993,8 +2079,8 @@ var react_lib_default = {
|
|
|
1993
2079
|
},
|
|
1994
2080
|
"typescript/return-await": {
|
|
1995
2081
|
severity: "warn",
|
|
1996
|
-
reason: "replaces the deprecated core `no-return-await
|
|
1997
|
-
options: ["
|
|
2082
|
+
reason: "replaces the deprecated core `no-return-await`, which both modules set to error but which was never actually reporting: the nx lint target expanded to 2 files of 2888. `in-try-catch` rather than `never`, and the planning adoption is what settled it. `never` forces `return await f()` to become `const x = await f(); return x` even inside a `try`, where the await is REQUIRED for the catch to fire: it produced 10 such edits across 15 modules, every one of them inside a try/catch, and every one of them a rewrite that made the code longer without making it safer. Worse, the obvious reading of the rule is to delete the `await`, which silently stops the catch from running. `in-try-catch` is what typescript-eslint recommends and flags none of those 10. Held at warn so adoption does not turn a green module red, and raised to error once the remaining findings are fixed.",
|
|
2083
|
+
options: ["in-try-catch"]
|
|
1998
2084
|
},
|
|
1999
2085
|
"use-isnan": [
|
|
2000
2086
|
"error",
|
|
@@ -2567,8 +2653,8 @@ var react_default = {
|
|
|
2567
2653
|
"typescript/restrict-template-expressions": "error",
|
|
2568
2654
|
"typescript/return-await": {
|
|
2569
2655
|
severity: "warn",
|
|
2570
|
-
reason: "replaces the deprecated core `no-return-await`, which both modules set to error but which was never actually reporting: the nx lint target expanded to 2 files of 2888.
|
|
2571
|
-
options: ["
|
|
2656
|
+
reason: "replaces the deprecated core `no-return-await`, which both modules set to error but which was never actually reporting: the nx lint target expanded to 2 files of 2888. `in-try-catch` rather than `never`, and the planning adoption is what settled it. `never` forces `return await f()` to become `const x = await f(); return x` even inside a `try`, where the await is REQUIRED for the catch to fire: it produced 10 such edits across 15 modules, every one of them inside a try/catch, and every one of them a rewrite that made the code longer without making it safer. Worse, the obvious reading of the rule is to delete the `await`, which silently stops the catch from running. `in-try-catch` is what typescript-eslint recommends and flags none of those 10. Held at warn so adoption does not turn a green module red, and raised to error once the remaining findings are fixed.",
|
|
2657
|
+
options: ["in-try-catch"]
|
|
2572
2658
|
},
|
|
2573
2659
|
"typescript/switch-exhaustiveness-check": {
|
|
2574
2660
|
severity: "warn",
|
|
@@ -3029,6 +3115,10 @@ function downgradedRulesFor(preset) {
|
|
|
3029
3115
|
return downgradedFor(preset);
|
|
3030
3116
|
}
|
|
3031
3117
|
|
|
3118
|
+
// src/roles/lint/extra-layers.ts
|
|
3119
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
3120
|
+
import { isAbsolute as isAbsolute2, join as join13, resolve as resolve3 } from "path";
|
|
3121
|
+
|
|
3032
3122
|
// src/roles/lint/module-baseline.ts
|
|
3033
3123
|
var LINT_BASELINE_FILE = ".oxlintrc.baseline.json";
|
|
3034
3124
|
var LINT_MEASURE_FILE = ".oxlintrc.measure.json";
|
|
@@ -3037,10 +3127,7 @@ function holdsFrom(errorCounts) {
|
|
|
3037
3127
|
return [...errorCounts].map(([rule, count]) => ({ rule, count })).sort((left, right) => right.count - left.count || left.rule.localeCompare(right.rule));
|
|
3038
3128
|
}
|
|
3039
3129
|
function renderBaseline(holds, version) {
|
|
3040
|
-
const entries = holds.map(
|
|
3041
|
-
({ rule, count }) => ` // ${count} violation${count === 1 ? "" : "s"} when this module adopted
|
|
3042
|
-
${JSON.stringify(rule)}: "warn"`
|
|
3043
|
-
).join(",\n");
|
|
3130
|
+
const entries = holds.map(({ rule }) => ` ${JSON.stringify(rule)}: "warn"`).join(",\n");
|
|
3044
3131
|
return [
|
|
3045
3132
|
"{",
|
|
3046
3133
|
` // Written by @hublo/sentinel@${version}. Do not edit by hand: \`sentinel --init --lint\``,
|
|
@@ -3048,9 +3135,15 @@ function renderBaseline(holds, version) {
|
|
|
3048
3135
|
" // and returns to `error` on its own.",
|
|
3049
3136
|
" //",
|
|
3050
3137
|
" // These rules are held at `warn` because this module ALREADY violated them when it",
|
|
3051
|
-
" // adopted. They still run and still report
|
|
3052
|
-
" //
|
|
3053
|
-
" //
|
|
3138
|
+
" // adopted. They still run and still report, so the build cannot fail on them.",
|
|
3139
|
+
" //",
|
|
3140
|
+
" // The hold is per RULE, not per occurrence: oxlint cannot freeze a specific list of",
|
|
3141
|
+
" // violations, so a NEW violation of one of these rules is covered too, and will only",
|
|
3142
|
+
" // warn. That is the cost of adopting without a red build, and the reason to clear this",
|
|
3143
|
+
" // file rather than live with it.",
|
|
3144
|
+
" //",
|
|
3145
|
+
" // `sentinel --inspect --lint` counts what is left. Fix them and re-run `--init --lint`",
|
|
3146
|
+
" // to get the preset severity back.",
|
|
3054
3147
|
' "rules": {',
|
|
3055
3148
|
entries,
|
|
3056
3149
|
" }",
|
|
@@ -3070,6 +3163,44 @@ function describeHolds(holds) {
|
|
|
3070
3163
|
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`;
|
|
3071
3164
|
}
|
|
3072
3165
|
|
|
3166
|
+
// src/roles/lint/extra-layers.ts
|
|
3167
|
+
function isSentinelPreset2(entry) {
|
|
3168
|
+
return entry.includes("@hublo/sentinel/oxlint/");
|
|
3169
|
+
}
|
|
3170
|
+
function ruleCount(cwd, specifier) {
|
|
3171
|
+
const path = isAbsolute2(specifier) ? specifier : resolve3(cwd, specifier);
|
|
3172
|
+
if (!existsSync11(path)) return void 0;
|
|
3173
|
+
try {
|
|
3174
|
+
const parsed = parseJsonc(
|
|
3175
|
+
readFileSync10(path, "utf8"),
|
|
3176
|
+
specifier
|
|
3177
|
+
);
|
|
3178
|
+
return Object.keys(parsed.rules ?? {}).length;
|
|
3179
|
+
} catch {
|
|
3180
|
+
return void 0;
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
function extraLayers(cwd, extendsList) {
|
|
3184
|
+
return extendsList.filter((entry) => !isSentinelPreset2(entry) && entry !== LINT_BASELINE_SPECIFIER).map((entry) => {
|
|
3185
|
+
const count = ruleCount(cwd, entry);
|
|
3186
|
+
return {
|
|
3187
|
+
rule: entry,
|
|
3188
|
+
reason: count === void 0 ? "this module extends it, but it could not be read from here" : `${count} rule(s) on top of the preset, applied after it`
|
|
3189
|
+
};
|
|
3190
|
+
});
|
|
3191
|
+
}
|
|
3192
|
+
function committedExtendsList(cwd, configFile) {
|
|
3193
|
+
try {
|
|
3194
|
+
const parsed = parseJsonc(
|
|
3195
|
+
readFileSync10(join13(cwd, configFile), "utf8"),
|
|
3196
|
+
configFile
|
|
3197
|
+
);
|
|
3198
|
+
return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
|
|
3199
|
+
} catch {
|
|
3200
|
+
return [];
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3073
3204
|
// src/roles/lint/parse-diagnostics.ts
|
|
3074
3205
|
function ruleId(code) {
|
|
3075
3206
|
const inner = /\(([^)]+)\)/.exec(code);
|
|
@@ -3120,8 +3251,8 @@ function errorCountsByConfigRule(stdout) {
|
|
|
3120
3251
|
}
|
|
3121
3252
|
|
|
3122
3253
|
// src/core/config/read-adoption.ts
|
|
3123
|
-
import { existsSync as
|
|
3124
|
-
import { join as
|
|
3254
|
+
import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
|
|
3255
|
+
import { join as join15 } from "path";
|
|
3125
3256
|
|
|
3126
3257
|
// src/core/config/owned-keys.ts
|
|
3127
3258
|
function presetOwnedKeys(config, permitted, presetSets) {
|
|
@@ -3136,12 +3267,12 @@ function localOnlyKeys(config, permitted, presetSets) {
|
|
|
3136
3267
|
}
|
|
3137
3268
|
|
|
3138
3269
|
// src/core/config/resolve-config-target.ts
|
|
3139
|
-
import { existsSync as
|
|
3140
|
-
import { join as
|
|
3270
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
3271
|
+
import { join as join14 } from "path";
|
|
3141
3272
|
function readExtends(absolutePath) {
|
|
3142
3273
|
let parsed;
|
|
3143
3274
|
try {
|
|
3144
|
-
parsed = parseJsonc(
|
|
3275
|
+
parsed = parseJsonc(readFileSync11(absolutePath, "utf8"), absolutePath);
|
|
3145
3276
|
} catch {
|
|
3146
3277
|
return [];
|
|
3147
3278
|
}
|
|
@@ -3155,8 +3286,8 @@ function resolveConfigTarget(moduleDir, { candidates, markers, fallback }) {
|
|
|
3155
3286
|
let existing;
|
|
3156
3287
|
let existingExtendsSomething = false;
|
|
3157
3288
|
for (const candidate of candidates) {
|
|
3158
|
-
const absolutePath =
|
|
3159
|
-
if (!
|
|
3289
|
+
const absolutePath = join14(moduleDir, candidate);
|
|
3290
|
+
if (!existsSync12(absolutePath)) continue;
|
|
3160
3291
|
const chain = readExtends(absolutePath);
|
|
3161
3292
|
if (existing === void 0) {
|
|
3162
3293
|
existing = candidate;
|
|
@@ -3189,12 +3320,12 @@ var NOT_ADOPTED2 = (configFile, unreadable = null) => ({
|
|
|
3189
3320
|
});
|
|
3190
3321
|
function readAdoption(cwd, options) {
|
|
3191
3322
|
const target = resolveConfigTarget(cwd, options);
|
|
3192
|
-
if (target.reason === "none" || !
|
|
3323
|
+
if (target.reason === "none" || !existsSync13(join15(cwd, target.path))) {
|
|
3193
3324
|
return NOT_ADOPTED2(target.reason === "none" ? null : target.path);
|
|
3194
3325
|
}
|
|
3195
3326
|
let parsed;
|
|
3196
3327
|
try {
|
|
3197
|
-
parsed = parseJsonc(
|
|
3328
|
+
parsed = parseJsonc(readFileSync12(join15(cwd, target.path), "utf8"), target.path);
|
|
3198
3329
|
} catch (error) {
|
|
3199
3330
|
const reason = error instanceof Error ? error.message : String(error);
|
|
3200
3331
|
return NOT_ADOPTED2(target.path, `${target.path} could not be parsed (${reason})`);
|
|
@@ -3228,9 +3359,9 @@ function readLintAdoption(cwd) {
|
|
|
3228
3359
|
}
|
|
3229
3360
|
|
|
3230
3361
|
// src/roles/lint/resolve-oxlint.ts
|
|
3231
|
-
import { existsSync as
|
|
3362
|
+
import { existsSync as existsSync14 } from "fs";
|
|
3232
3363
|
import { createRequire as createRequire3 } from "module";
|
|
3233
|
-
import { delimiter as delimiter2, dirname as dirname5, join as
|
|
3364
|
+
import { delimiter as delimiter2, dirname as dirname5, join as join16 } from "path";
|
|
3234
3365
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3235
3366
|
var PACKAGE_OF = {
|
|
3236
3367
|
oxlint: "oxlint",
|
|
@@ -3255,30 +3386,30 @@ function tsgolintShim(cwd) {
|
|
|
3255
3386
|
for (const owner of ["oxlint-tsgolint", "oxlint"]) {
|
|
3256
3387
|
try {
|
|
3257
3388
|
const packageDir = dirname5(require3.resolve(`${owner}/package.json`));
|
|
3258
|
-
candidates.push(
|
|
3259
|
-
candidates.push(
|
|
3389
|
+
candidates.push(join16(packageDir, "node_modules", ".bin", "tsgolint"));
|
|
3390
|
+
candidates.push(join16(packageDir, "..", ".bin", "tsgolint"));
|
|
3260
3391
|
} catch {
|
|
3261
3392
|
}
|
|
3262
3393
|
}
|
|
3263
3394
|
try {
|
|
3264
3395
|
const ownRoot = dirname5(require3.resolve("@hublo/sentinel/package.json"));
|
|
3265
|
-
candidates.push(
|
|
3396
|
+
candidates.push(join16(ownRoot, "node_modules", ".bin", "tsgolint"));
|
|
3266
3397
|
} catch {
|
|
3267
3398
|
candidates.push(resolveBin(dirname5(fileURLToPath2(import.meta.url)), "tsgolint") ?? "");
|
|
3268
3399
|
}
|
|
3269
|
-
return candidates.find((candidate) => candidate !== "" &&
|
|
3400
|
+
return candidates.find((candidate) => candidate !== "" && existsSync14(candidate));
|
|
3270
3401
|
}
|
|
3271
3402
|
function oxlintSearchPath(cwd) {
|
|
3272
3403
|
return binSearchPath(cwd);
|
|
3273
3404
|
}
|
|
3274
3405
|
|
|
3275
3406
|
// src/roles/lint/adapters/oxlint/plan.ts
|
|
3276
|
-
import { existsSync as
|
|
3277
|
-
import { join as
|
|
3407
|
+
import { existsSync as existsSync16, readFileSync as readFileSync15 } from "fs";
|
|
3408
|
+
import { join as join19 } from "path";
|
|
3278
3409
|
|
|
3279
3410
|
// src/roles/lint/eslint-ignores.ts
|
|
3280
|
-
import { existsSync as
|
|
3281
|
-
import { join as
|
|
3411
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
3412
|
+
import { join as join17 } from "path";
|
|
3282
3413
|
var IGNORE_BLOCKS = [/\bignores\s*:\s*\[([^\]]*)\]/g, /\bglobalIgnores\s*\(\s*\[([^\]]*)\]/g];
|
|
3283
3414
|
var STRING_LITERAL = /['"`]([^'"`]+)['"`]/g;
|
|
3284
3415
|
var OPAQUE_SOURCE = /\b(includeIgnoreFile)\s*\([^)]*\)/g;
|
|
@@ -3291,11 +3422,11 @@ function readRootEslintIgnores(root) {
|
|
|
3291
3422
|
return { patterns: patterns.filter((pattern) => pattern.startsWith("**/")), unresolved };
|
|
3292
3423
|
}
|
|
3293
3424
|
function readEslintIgnores(cwd) {
|
|
3294
|
-
const config = ESLINT_CONFIG_FILES.map((name) =>
|
|
3425
|
+
const config = ESLINT_CONFIG_FILES.map((name) => join17(cwd, name)).find((path) => existsSync15(path));
|
|
3295
3426
|
if (!config) return { patterns: [], unresolved: [] };
|
|
3296
3427
|
let source;
|
|
3297
3428
|
try {
|
|
3298
|
-
source =
|
|
3429
|
+
source = readFileSync13(config, "utf8");
|
|
3299
3430
|
} catch {
|
|
3300
3431
|
return { patterns: [], unresolved: [] };
|
|
3301
3432
|
}
|
|
@@ -3349,8 +3480,8 @@ function lintPresetFor(preset) {
|
|
|
3349
3480
|
}
|
|
3350
3481
|
|
|
3351
3482
|
// src/roles/lint/rename-suppressions.ts
|
|
3352
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
3353
|
-
import { join as
|
|
3483
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync14, statSync } from "fs";
|
|
3484
|
+
import { join as join18, relative as relative2 } from "path";
|
|
3354
3485
|
var SOURCE_EXTENSIONS = [
|
|
3355
3486
|
".ts",
|
|
3356
3487
|
".tsx",
|
|
@@ -3397,7 +3528,7 @@ function* sourceFiles(dir) {
|
|
|
3397
3528
|
return;
|
|
3398
3529
|
}
|
|
3399
3530
|
for (const entry of entries) {
|
|
3400
|
-
const full =
|
|
3531
|
+
const full = join18(dir, entry);
|
|
3401
3532
|
let isDirectory;
|
|
3402
3533
|
try {
|
|
3403
3534
|
isDirectory = statSync(full).isDirectory();
|
|
@@ -3417,7 +3548,7 @@ function findSuppressionRenames(cwd, renames) {
|
|
|
3417
3548
|
for (const file of sourceFiles(cwd)) {
|
|
3418
3549
|
let content;
|
|
3419
3550
|
try {
|
|
3420
|
-
content =
|
|
3551
|
+
content = readFileSync14(file, "utf8");
|
|
3421
3552
|
} catch {
|
|
3422
3553
|
continue;
|
|
3423
3554
|
}
|
|
@@ -3457,7 +3588,17 @@ var DEFAULT_IGNORE_PATTERNS = [
|
|
|
3457
3588
|
"**/dist/**",
|
|
3458
3589
|
"**/node_modules/**",
|
|
3459
3590
|
"**/coverage/**",
|
|
3460
|
-
|
|
3591
|
+
// `**/*.mock.ts` used to sit here and has been removed. It was the only entry in this list
|
|
3592
|
+
// with no reason written next to it, and it does not meet the bar the paragraph above sets:
|
|
3593
|
+
// a mock is not build output, not vendored and not generated. It is hand-written TypeScript
|
|
3594
|
+
// that ships with the tests, and lint rules have as much to say about it as about any other
|
|
3595
|
+
// source file. Ignoring it quietly narrowed what an adopted module checks — career lost
|
|
3596
|
+
// `src/test/router.mock.ts`, which its previous ESLint config did lint.
|
|
3597
|
+
//
|
|
3598
|
+
// Measured before removing, because this is the one change that can turn a green module red:
|
|
3599
|
+
// career has 1 such file and it reports nothing; the 15 planning modules have none. 222 exist
|
|
3600
|
+
// repo-wide, and modules adopting later meet them through the baseline, which is exactly what
|
|
3601
|
+
// the baseline is for.
|
|
3461
3602
|
// Tool configuration is not source. A repo's shared ESLint config ignores these, and every
|
|
3462
3603
|
// module inherits that; adoption replaces the config chain, so without them the first thing
|
|
3463
3604
|
// an adopted module lints is its own jest setup file. Found on libs/front/components, where
|
|
@@ -3520,7 +3661,7 @@ function plan(context) {
|
|
|
3520
3661
|
].filter((pattern) => !defaults.has(bare(pattern)));
|
|
3521
3662
|
const carried = [.../* @__PURE__ */ new Set([...DEFAULT_IGNORE_PATTERNS, ...moduleOwn])];
|
|
3522
3663
|
const stub = {
|
|
3523
|
-
extends:
|
|
3664
|
+
extends: extendsWithPreset(committedExtends(context.cwd), variant),
|
|
3524
3665
|
...carried.length > 0 ? { ignorePatterns: carried } : {}
|
|
3525
3666
|
};
|
|
3526
3667
|
const operations = [
|
|
@@ -3535,7 +3676,7 @@ function plan(context) {
|
|
|
3535
3676
|
keys: removableDeps
|
|
3536
3677
|
});
|
|
3537
3678
|
}
|
|
3538
|
-
const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) =>
|
|
3679
|
+
const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) => existsSync16(join19(context.cwd, name)));
|
|
3539
3680
|
for (const name of eslintConfigs) operations.push({ kind: "delete", path: name });
|
|
3540
3681
|
operations.push(
|
|
3541
3682
|
...nxTargetOperations({
|
|
@@ -3611,7 +3752,10 @@ function plan(context) {
|
|
|
3611
3752
|
function lintScripts(cwd) {
|
|
3612
3753
|
const scripts = moduleScripts(cwd);
|
|
3613
3754
|
const rewritten = {
|
|
3614
|
-
|
|
3755
|
+
// `existingCommand`, not `scripts[...]`: the command can live in an `nx:run-commands`
|
|
3756
|
+
// target instead of a script, and adoption deletes that target. Composing against only
|
|
3757
|
+
// the script threw away whatever else the target ran.
|
|
3758
|
+
[LINT_SCRIPT_NAME]: composeLintScript(existingCommand(cwd, LINT_SCRIPT_NAME), {
|
|
3615
3759
|
command: selfCommand(cwd, "--run --lint")
|
|
3616
3760
|
})
|
|
3617
3761
|
};
|
|
@@ -3627,10 +3771,21 @@ function lintScripts(cwd) {
|
|
|
3627
3771
|
}
|
|
3628
3772
|
return rewritten;
|
|
3629
3773
|
}
|
|
3774
|
+
function committedExtends(cwd) {
|
|
3775
|
+
try {
|
|
3776
|
+
const parsed = parseJsonc(
|
|
3777
|
+
readFileSync15(join19(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
3778
|
+
LINT_CONFIG_FILE
|
|
3779
|
+
);
|
|
3780
|
+
return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
|
|
3781
|
+
} catch {
|
|
3782
|
+
return [];
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3630
3785
|
function committedIgnorePatterns(cwd) {
|
|
3631
3786
|
try {
|
|
3632
3787
|
const parsed = parseJsonc(
|
|
3633
|
-
|
|
3788
|
+
readFileSync15(join19(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
3634
3789
|
LINT_CONFIG_FILE
|
|
3635
3790
|
);
|
|
3636
3791
|
return Array.isArray(parsed.ignorePatterns) ? parsed.ignorePatterns.filter((entry) => typeof entry === "string") : [];
|
|
@@ -3642,7 +3797,7 @@ function moduleEslintDependencies(cwd) {
|
|
|
3642
3797
|
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/");
|
|
3643
3798
|
let manifest;
|
|
3644
3799
|
try {
|
|
3645
|
-
manifest = JSON.parse(
|
|
3800
|
+
manifest = JSON.parse(readFileSync15(join19(cwd, "package.json"), "utf8"));
|
|
3646
3801
|
} catch {
|
|
3647
3802
|
return [];
|
|
3648
3803
|
}
|
|
@@ -3761,20 +3916,20 @@ var OxlintAdapter = class extends BaseAdapter {
|
|
|
3761
3916
|
* build the developer can see, rather than a silent half-adoption they cannot.
|
|
3762
3917
|
*/
|
|
3763
3918
|
writeModuleBaseline(ctx, oxlint, env) {
|
|
3764
|
-
const configPath =
|
|
3765
|
-
const baselinePath =
|
|
3766
|
-
if (!
|
|
3919
|
+
const configPath = join20(ctx.cwd, LINT_CONFIG_FILE);
|
|
3920
|
+
const baselinePath = join20(ctx.cwd, LINT_BASELINE_FILE);
|
|
3921
|
+
if (!existsSync17(configPath)) return;
|
|
3767
3922
|
let config;
|
|
3768
3923
|
try {
|
|
3769
3924
|
config = parseJsonc(
|
|
3770
|
-
|
|
3925
|
+
readFileSync16(configPath, "utf8"),
|
|
3771
3926
|
LINT_CONFIG_FILE
|
|
3772
3927
|
);
|
|
3773
3928
|
} catch {
|
|
3774
3929
|
return;
|
|
3775
3930
|
}
|
|
3776
3931
|
const current = Array.isArray(config.extends) ? config.extends : [];
|
|
3777
|
-
const measurePath =
|
|
3932
|
+
const measurePath = join20(ctx.cwd, LINT_MEASURE_FILE);
|
|
3778
3933
|
let measured;
|
|
3779
3934
|
try {
|
|
3780
3935
|
writeFileSync2(
|
|
@@ -3822,7 +3977,7 @@ var OxlintAdapter = class extends BaseAdapter {
|
|
|
3822
3977
|
}
|
|
3823
3978
|
}
|
|
3824
3979
|
async run(ctx) {
|
|
3825
|
-
if (!
|
|
3980
|
+
if (!existsSync17(join20(ctx.cwd, LINT_CONFIG_FILE))) {
|
|
3826
3981
|
process.stderr.write(
|
|
3827
3982
|
`sentinel lint(oxlint): no ${LINT_CONFIG_FILE} in this module; run \`sentinel --init --lint\` to adopt.
|
|
3828
3983
|
`
|
|
@@ -3906,7 +4061,12 @@ var OxlintAdapter = class extends BaseAdapter {
|
|
|
3906
4061
|
downgraded: downgradedRulesFor(presetVariant(ctx.preset, ctx.cwd)).map((entry) => ({
|
|
3907
4062
|
rule: entry.rule,
|
|
3908
4063
|
reason: entry.reason
|
|
3909
|
-
}))
|
|
4064
|
+
})),
|
|
4065
|
+
// What this module enforces BEYOND the preset. Not drift, and not covered by anything
|
|
4066
|
+
// else here: the stub still holds only `extends` and `ignorePatterns`, so a module with
|
|
4067
|
+
// a team layer reports `conformant=true drift=[]` and used to say nothing at all about
|
|
4068
|
+
// the extra rules it runs. See `extra-layers` for why that state is legitimate.
|
|
4069
|
+
layers: extraLayers(ctx.cwd, committedExtendsList(ctx.cwd, LINT_CONFIG_FILE))
|
|
3910
4070
|
};
|
|
3911
4071
|
}
|
|
3912
4072
|
/** How many rules the extended preset enforces, read from the preset on disk. */
|
|
@@ -3937,7 +4097,7 @@ var OxlintAdapter = class extends BaseAdapter {
|
|
|
3937
4097
|
let stub;
|
|
3938
4098
|
try {
|
|
3939
4099
|
stub = parseJsonc(
|
|
3940
|
-
|
|
4100
|
+
readFileSync16(join20(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
3941
4101
|
LINT_CONFIG_FILE
|
|
3942
4102
|
);
|
|
3943
4103
|
} catch {
|
|
@@ -3947,7 +4107,7 @@ var OxlintAdapter = class extends BaseAdapter {
|
|
|
3947
4107
|
for (const entry of stub.extends ?? []) {
|
|
3948
4108
|
try {
|
|
3949
4109
|
const preset = parseJsonc(
|
|
3950
|
-
|
|
4110
|
+
readFileSync16(join20(cwd, entry), "utf8"),
|
|
3951
4111
|
entry
|
|
3952
4112
|
);
|
|
3953
4113
|
for (const rule of Object.keys(preset.rules ?? {})) names.add(rule);
|
|
@@ -4017,14 +4177,14 @@ var OxlintAdapter = class extends BaseAdapter {
|
|
|
4017
4177
|
let parsed;
|
|
4018
4178
|
try {
|
|
4019
4179
|
parsed = parseJsonc(
|
|
4020
|
-
|
|
4180
|
+
readFileSync16(join20(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4021
4181
|
LINT_CONFIG_FILE
|
|
4022
4182
|
);
|
|
4023
4183
|
} catch {
|
|
4024
4184
|
return void 0;
|
|
4025
4185
|
}
|
|
4026
4186
|
const targets = Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : typeof parsed.extends === "string" ? [parsed.extends] : [];
|
|
4027
|
-
return targets.find((target) => !
|
|
4187
|
+
return targets.find((target) => !existsSync17(join20(cwd, target)));
|
|
4028
4188
|
}
|
|
4029
4189
|
/** Announce what is not enforced, so reduced coverage is never silent. */
|
|
4030
4190
|
announceDisabled(preset) {
|
|
@@ -4056,9 +4216,9 @@ function registerLint() {
|
|
|
4056
4216
|
|
|
4057
4217
|
// src/roles/typescript/adapters/tsc/tsc.adapter.ts
|
|
4058
4218
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
4059
|
-
import { existsSync as
|
|
4219
|
+
import { existsSync as existsSync18, readFileSync as readFileSync18 } from "fs";
|
|
4060
4220
|
import { createRequire as createRequire4 } from "module";
|
|
4061
|
-
import { join as
|
|
4221
|
+
import { join as join22 } from "path";
|
|
4062
4222
|
|
|
4063
4223
|
// src/roles/typescript/presets/base.json
|
|
4064
4224
|
var base_default3 = {
|
|
@@ -4239,8 +4399,8 @@ function readTsconfigAdoption(cwd) {
|
|
|
4239
4399
|
}
|
|
4240
4400
|
|
|
4241
4401
|
// src/roles/typescript/adapters/tsc/plan.ts
|
|
4242
|
-
import { readFileSync as
|
|
4243
|
-
import { join as
|
|
4402
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
4403
|
+
import { join as join21 } from "path";
|
|
4244
4404
|
|
|
4245
4405
|
// src/roles/typescript/typecheck-script.ts
|
|
4246
4406
|
var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
|
|
@@ -4262,10 +4422,16 @@ function keepsForeignChecker(existing) {
|
|
|
4262
4422
|
|
|
4263
4423
|
// src/roles/typescript/adapters/tsc/plan.ts
|
|
4264
4424
|
var INSTALL_NOTE = "run `pnpm install` to fetch @hublo/sentinel (added to the module devDependencies) so `extends` and the typecheck script resolve";
|
|
4265
|
-
var TYPECHECK_TARGET = {
|
|
4425
|
+
var TYPECHECK_TARGET = {
|
|
4426
|
+
cache: true,
|
|
4427
|
+
inputs: ["default", "^default", "{projectRoot}/tsconfig.json"]
|
|
4428
|
+
};
|
|
4266
4429
|
function typecheckScripts(cwd) {
|
|
4267
4430
|
return {
|
|
4268
|
-
|
|
4431
|
+
// Reads the nx target too, not only the npm script: five modules in this repo keep a
|
|
4432
|
+
// real prerequisite (the brand-token generator) in an `nx:run-commands` typecheck
|
|
4433
|
+
// target, and adoption used to delete it along with the target.
|
|
4434
|
+
[TYPECHECK_SCRIPT_NAME]: composeTypecheckScript(existingCommand(cwd, TYPECHECK_SCRIPT_NAME))
|
|
4269
4435
|
};
|
|
4270
4436
|
}
|
|
4271
4437
|
function composeExtends(current, preset) {
|
|
@@ -4325,7 +4491,7 @@ function planAdoption(context) {
|
|
|
4325
4491
|
};
|
|
4326
4492
|
}
|
|
4327
4493
|
const existing = parseJsonc(
|
|
4328
|
-
|
|
4494
|
+
readFileSync17(join21(context.cwd, target.path), "utf8"),
|
|
4329
4495
|
target.path
|
|
4330
4496
|
);
|
|
4331
4497
|
const extendsChain = composeExtends(existing.extends, preset);
|
|
@@ -4463,7 +4629,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
4463
4629
|
let chain;
|
|
4464
4630
|
try {
|
|
4465
4631
|
const parsed = parseJsonc(
|
|
4466
|
-
|
|
4632
|
+
readFileSync18(join22(cwd, target.path), "utf8"),
|
|
4467
4633
|
target.path
|
|
4468
4634
|
);
|
|
4469
4635
|
chain = parsed.extends;
|
|
@@ -4476,7 +4642,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
4476
4642
|
);
|
|
4477
4643
|
if (preset === void 0) return void 0;
|
|
4478
4644
|
try {
|
|
4479
|
-
createRequire4(
|
|
4645
|
+
createRequire4(join22(cwd, "noop.js")).resolve(preset);
|
|
4480
4646
|
return void 0;
|
|
4481
4647
|
} catch {
|
|
4482
4648
|
return preset;
|
|
@@ -4572,7 +4738,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
4572
4738
|
referencedProjects(cwd, config) {
|
|
4573
4739
|
try {
|
|
4574
4740
|
const parsed = parseJsonc(
|
|
4575
|
-
|
|
4741
|
+
readFileSync18(join22(cwd, config), "utf8"),
|
|
4576
4742
|
config
|
|
4577
4743
|
);
|
|
4578
4744
|
const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path) => typeof path === "string" && path.length > 0);
|
|
@@ -4588,7 +4754,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
4588
4754
|
* check.
|
|
4589
4755
|
*/
|
|
4590
4756
|
typecheckTarget(cwd) {
|
|
4591
|
-
if (
|
|
4757
|
+
if (existsSync18(join22(cwd, "tsconfig.json"))) return "tsconfig.json";
|
|
4592
4758
|
const target = resolveTsconfigTarget(cwd);
|
|
4593
4759
|
return target.reason === "none" ? null : target.path;
|
|
4594
4760
|
}
|
|
@@ -4791,8 +4957,8 @@ function replaceLines(current, replacements) {
|
|
|
4791
4957
|
}
|
|
4792
4958
|
|
|
4793
4959
|
// src/core/apply-plan.ts
|
|
4794
|
-
import { existsSync as
|
|
4795
|
-
import { resolve as
|
|
4960
|
+
import { existsSync as existsSync19, readFileSync as readFileSync19, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
4961
|
+
import { resolve as resolve4, sep } from "path";
|
|
4796
4962
|
import { applyEdits, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
|
|
4797
4963
|
|
|
4798
4964
|
// src/shared/deep-merge.ts
|
|
@@ -4802,15 +4968,15 @@ function isPlainObject(value) {
|
|
|
4802
4968
|
|
|
4803
4969
|
// src/core/apply-plan.ts
|
|
4804
4970
|
function resolveWithinRoot(cwd, relativePath) {
|
|
4805
|
-
const root =
|
|
4806
|
-
const absolutePath =
|
|
4971
|
+
const root = resolve4(cwd);
|
|
4972
|
+
const absolutePath = resolve4(root, relativePath);
|
|
4807
4973
|
if (absolutePath !== root && !absolutePath.startsWith(root + sep)) {
|
|
4808
4974
|
throw new Error(`Refusing to write outside the module root: "${relativePath}".`);
|
|
4809
4975
|
}
|
|
4810
4976
|
return absolutePath;
|
|
4811
4977
|
}
|
|
4812
4978
|
function readIfExists(absolutePath) {
|
|
4813
|
-
return
|
|
4979
|
+
return existsSync19(absolutePath) ? readFileSync19(absolutePath, "utf8") : void 0;
|
|
4814
4980
|
}
|
|
4815
4981
|
function* leaves(value, prefix = []) {
|
|
4816
4982
|
for (const [key, keyValue] of Object.entries(value)) {
|
|
@@ -4928,8 +5094,8 @@ function applyPlan(cwd, plan2) {
|
|
|
4928
5094
|
}
|
|
4929
5095
|
|
|
4930
5096
|
// src/core/config/preset-evidence.ts
|
|
4931
|
-
import { existsSync as
|
|
4932
|
-
import { join as
|
|
5097
|
+
import { existsSync as existsSync20, readdirSync as readdirSync3, readFileSync as readFileSync20 } from "fs";
|
|
5098
|
+
import { join as join23 } from "path";
|
|
4933
5099
|
var PATH_SIGNALS = [
|
|
4934
5100
|
{
|
|
4935
5101
|
preset: "nest",
|
|
@@ -4943,11 +5109,11 @@ var DEPENDENCY_SIGNALS = [
|
|
|
4943
5109
|
{ preset: "nest", pattern: /^@nestjs\// }
|
|
4944
5110
|
];
|
|
4945
5111
|
function dependencyNames(cwd) {
|
|
4946
|
-
const path =
|
|
4947
|
-
if (!
|
|
5112
|
+
const path = join23(cwd, "package.json");
|
|
5113
|
+
if (!existsSync20(path)) return [];
|
|
4948
5114
|
try {
|
|
4949
5115
|
const manifest = parseJsonc(
|
|
4950
|
-
|
|
5116
|
+
readFileSync20(path, "utf8"),
|
|
4951
5117
|
path
|
|
4952
5118
|
);
|
|
4953
5119
|
return [
|
|
@@ -4970,7 +5136,7 @@ function declaresJsx(cwd) {
|
|
|
4970
5136
|
for (const name of entries) {
|
|
4971
5137
|
try {
|
|
4972
5138
|
const config = parseJsonc(
|
|
4973
|
-
|
|
5139
|
+
readFileSync20(join23(cwd, name), "utf8"),
|
|
4974
5140
|
name
|
|
4975
5141
|
);
|
|
4976
5142
|
if (config.compilerOptions?.jsx !== void 0) return true;
|
|
@@ -5120,6 +5286,7 @@ export {
|
|
|
5120
5286
|
WORKSPACE_ROOT_MARKER,
|
|
5121
5287
|
findWorkspaceRoot,
|
|
5122
5288
|
ensureWorkspacePrep,
|
|
5289
|
+
inspectWorkspacePrep,
|
|
5123
5290
|
palette,
|
|
5124
5291
|
resolveBin,
|
|
5125
5292
|
VERBS,
|
|
@@ -5130,4 +5297,4 @@ export {
|
|
|
5130
5297
|
detectFramework,
|
|
5131
5298
|
dispatch
|
|
5132
5299
|
};
|
|
5133
|
-
//# sourceMappingURL=chunk-
|
|
5300
|
+
//# sourceMappingURL=chunk-RGIEWANO.js.map
|
package/dist/index.js
CHANGED
package/oxlint/nest.json
CHANGED
package/oxlint/react-lib.json
CHANGED
package/oxlint/react.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hublo/sentinel",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "One CLI that guards code health across Hublo repos: shared lint/typescript/build/test presets, static & dynamic analysis, and architecture checks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|