@hublo/sentinel 1.1.3 → 1.1.5

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.
@@ -16,7 +16,7 @@ import {
16
16
  registerAdapters,
17
17
  resolve,
18
18
  resolveBin
19
- } from "../chunk-TLNA2U3B.js";
19
+ } from "../chunk-RLVLDC5H.js";
20
20
 
21
21
  // bin/sentinel.ts
22
22
  import { program } from "commander";
@@ -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 readFileSync8 } from "fs";
144
- import { join as join11 } from "path";
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 join2 } from "path";
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(join2(dir, entry.name));
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 join4 } from "path";
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(join4(cwd, "project.json"))) {
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 readFileSync3, writeFileSync } from "fs";
298
- import { dirname as dirname2, join as join5, relative } from "path";
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(join5(dir, WORKSPACE_ROOT_MARKER))) return dir;
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 = join5(root, "package.json");
371
+ const pkgPath = join6(root, "package.json");
321
372
  if (!existsSync5(pkgPath)) return void 0;
322
- const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
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 = join5(root, WORKSPACE_YAML);
386
+ const yamlPath = join6(root, WORKSPACE_YAML);
336
387
  if (!existsSync5(yamlPath)) return void 0;
337
388
  const own = readOwnPackage().name;
338
- const lines = readFileSync3(yamlPath, "utf8").split("\n");
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 = join5(root, PRETTIER_IGNORE);
374
- const hasPrettier = existsSync5(ignorePath) || ROOT_PRETTIER_CONFIGS.some((name) => existsSync5(join5(root, name)));
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) ? readFileSync3(ignorePath, "utf8") : "";
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;
@@ -411,12 +462,12 @@ function ensureWorkspacePrep(opts) {
411
462
  // src/shared/resolve-bin.ts
412
463
  import { existsSync as existsSync6 } from "fs";
413
464
  import { createRequire } from "module";
414
- import { delimiter, dirname as dirname3, join as join6 } from "path";
465
+ import { delimiter, dirname as dirname3, join as join7 } from "path";
415
466
  var require2 = createRequire(import.meta.url);
416
467
  function resolveBin(fromDir, name) {
417
468
  let dir = fromDir;
418
469
  for (; ; ) {
419
- const candidate = join6(dir, "node_modules", ".bin", name);
470
+ const candidate = join7(dir, "node_modules", ".bin", name);
420
471
  if (existsSync6(candidate)) return candidate;
421
472
  const parent = dirname3(dir);
422
473
  if (parent === dir) return void 0;
@@ -429,7 +480,7 @@ function binFromOwnInstall(packageName, binName) {
429
480
  const bin = require2(manifest).bin;
430
481
  const relative3 = typeof bin === "string" ? bin : bin?.[binName];
431
482
  if (!relative3) return void 0;
432
- const executable = join6(dirname3(manifest), relative3);
483
+ const executable = join7(dirname3(manifest), relative3);
433
484
  return existsSync6(executable) ? executable : void 0;
434
485
  } catch {
435
486
  return void 0;
@@ -556,8 +607,8 @@ function writesWhenRewritten(name, command) {
556
607
  var CHECK_SCRIPT_NAMES = /* @__PURE__ */ new Set(["lint", "format", "check", "test", "typecheck", "verify"]);
557
608
 
558
609
  // src/roles/format/inherited-ignores.ts
559
- import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
560
- import { join as join7 } from "path";
610
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
611
+ import { join as join8 } from "path";
561
612
  var ROOT_IGNORE_FILE = ".prettierignore";
562
613
  function isPattern(line) {
563
614
  const trimmed = line.trim();
@@ -572,11 +623,11 @@ function toModulePattern(pattern) {
572
623
  }
573
624
  function inheritedIgnorePatterns(workspaceRoot) {
574
625
  if (!workspaceRoot) return [];
575
- const path = join7(workspaceRoot, ROOT_IGNORE_FILE);
626
+ const path = join8(workspaceRoot, ROOT_IGNORE_FILE);
576
627
  if (!existsSync7(path)) return [];
577
628
  let contents;
578
629
  try {
579
- contents = readFileSync4(path, "utf8");
630
+ contents = readFileSync5(path, "utf8");
580
631
  } catch {
581
632
  return [];
582
633
  }
@@ -663,25 +714,13 @@ function formatPresetFor(preset) {
663
714
  }
664
715
 
665
716
  // src/roles/format/prettier-config.ts
666
- import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
667
- import { join as join9 } from "path";
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
- }
717
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
718
+ import { join as join10 } from "path";
680
719
 
681
720
  // src/roles/format/resolve-oxfmt.ts
682
- import { readFileSync as readFileSync5 } from "fs";
721
+ import { readFileSync as readFileSync6 } from "fs";
683
722
  import { createRequire as createRequire2 } from "module";
684
- import { dirname as dirname4, join as join8 } from "path";
723
+ import { dirname as dirname4, join as join9 } from "path";
685
724
  function resolveOxfmt(cwd) {
686
725
  return resolveBin(cwd, "oxfmt") ?? binFromOwnInstall("oxfmt", "oxfmt");
687
726
  }
@@ -710,8 +749,8 @@ function configSchema() {
710
749
  function readConfigSchema() {
711
750
  try {
712
751
  const manifest = createRequire2(import.meta.url).resolve("oxfmt/package.json");
713
- const schemaPath = join8(dirname4(manifest), "configuration_schema.json");
714
- return JSON.parse(readFileSync5(schemaPath, "utf8"));
752
+ const schemaPath = join9(dirname4(manifest), "configuration_schema.json");
753
+ return JSON.parse(readFileSync6(schemaPath, "utf8"));
715
754
  } catch {
716
755
  return void 0;
717
756
  }
@@ -742,14 +781,14 @@ function toOxfmtOverrides(value) {
742
781
  return { overrides, unresolved };
743
782
  }
744
783
  function readPrettierSettings(cwd) {
745
- const file = PRETTIER_CONFIG_FILES.find((name) => existsSync8(join9(cwd, name)));
784
+ const file = PRETTIER_CONFIG_FILES.find((name) => existsSync8(join10(cwd, name)));
746
785
  if (!file) return { options: {}, unresolved: [] };
747
786
  if (/\.(js|cjs|mjs)$/.test(file)) {
748
787
  return { options: {}, file, unresolved: [`${file} is JavaScript, so it was not executed`] };
749
788
  }
750
789
  let parsed;
751
790
  try {
752
- parsed = parseJsonc(readFileSync6(join9(cwd, file), "utf8"), file);
791
+ parsed = parseJsonc(readFileSync7(join10(cwd, file), "utf8"), file);
753
792
  } catch {
754
793
  return { options: {}, file, unresolved: [`${file} could not be parsed`] };
755
794
  }
@@ -779,8 +818,8 @@ function readPrettierSettings(cwd) {
779
818
  }
780
819
 
781
820
  // src/roles/format/read-adoption.ts
782
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
783
- import { join as join10 } from "path";
821
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
822
+ import { join as join11 } from "path";
784
823
  var NOT_ADOPTED = (configFile, unreadable = null) => ({
785
824
  configFile,
786
825
  preset: null,
@@ -796,11 +835,11 @@ function sameValue(a, b) {
796
835
  return JSON.stringify(a) === JSON.stringify(b);
797
836
  }
798
837
  function readFormatAdoption(cwd) {
799
- const path = join10(cwd, FORMAT_CONFIG_FILE);
838
+ const path = join11(cwd, FORMAT_CONFIG_FILE);
800
839
  if (!existsSync9(path)) return NOT_ADOPTED(null);
801
840
  let parsed;
802
841
  try {
803
- parsed = parseJsonc(readFileSync7(path, "utf8"), FORMAT_CONFIG_FILE);
842
+ parsed = parseJsonc(readFileSync8(path, "utf8"), FORMAT_CONFIG_FILE);
804
843
  } catch (error) {
805
844
  const reason = error instanceof Error ? error.message : String(error);
806
845
  return NOT_ADOPTED(FORMAT_CONFIG_FILE, `${FORMAT_CONFIG_FILE} could not be parsed (${reason})`);
@@ -907,7 +946,7 @@ var OxfmtAdapter = class extends BaseAdapter {
907
946
  manifestOperation(context.cwd, this.formatScripts(context.cwd))
908
947
  ];
909
948
  const prettierConfigs = PRETTIER_CONFIG_FILES.filter(
910
- (name) => existsSync10(join11(context.cwd, name))
949
+ (name) => existsSync10(join12(context.cwd, name))
911
950
  );
912
951
  for (const name of prettierConfigs) operations.push({ kind: "delete", path: name });
913
952
  const removableDeps = this.modulePrettierDependencies(context.cwd);
@@ -1143,7 +1182,7 @@ var OxfmtAdapter = class extends BaseAdapter {
1143
1182
  modulePrettierDependencies(cwd) {
1144
1183
  const isPrettierPackage = (name) => name === "prettier" || name.startsWith("prettier-plugin-") || name.startsWith("@prettier/");
1145
1184
  try {
1146
- const manifest = JSON.parse(readFileSync8(join11(cwd, "package.json"), "utf8"));
1185
+ const manifest = JSON.parse(readFileSync9(join12(cwd, "package.json"), "utf8"));
1147
1186
  return Object.keys(manifest.devDependencies ?? {}).filter(isPrettierPackage).map((name) => ["devDependencies", name]);
1148
1187
  } catch {
1149
1188
  return [];
@@ -1166,7 +1205,9 @@ var OxfmtAdapter = class extends BaseAdapter {
1166
1205
  const ownCommand = selfCommand(cwd, "--run --format");
1167
1206
  const compose = (existing, keepFix) => composeFormatScript(existing, { keepFix, command: ownCommand });
1168
1207
  const rewritten = {
1169
- [FORMAT_SCRIPT_NAME]: compose(scripts[FORMAT_SCRIPT_NAME], false),
1208
+ // `existingCommand` for the role's own script: the command can live in an
1209
+ // `nx:run-commands` target that adoption deletes, taking anything else it ran with it.
1210
+ [FORMAT_SCRIPT_NAME]: compose(existingCommand(cwd, FORMAT_SCRIPT_NAME), false),
1170
1211
  [`${FORMAT_SCRIPT_NAME}:fix`]: compose(scripts[`${FORMAT_SCRIPT_NAME}:fix`], true)
1171
1212
  };
1172
1213
  for (const [name, command] of Object.entries(scripts)) {
@@ -1223,8 +1264,8 @@ function registerFormat() {
1223
1264
 
1224
1265
  // src/roles/lint/adapters/oxlint/oxlint.adapter.ts
1225
1266
  import { spawnSync as spawnSync2 } from "child_process";
1226
- import { existsSync as existsSync16, readFileSync as readFileSync14, rmSync, writeFileSync as writeFileSync2 } from "fs";
1227
- import { join as join18 } from "path";
1267
+ import { existsSync as existsSync16, readFileSync as readFileSync15, rmSync, writeFileSync as writeFileSync2 } from "fs";
1268
+ import { join as join19 } from "path";
1228
1269
 
1229
1270
  // src/core/config/deferred-rules.ts
1230
1271
  function deferredRuleNames(rules) {
@@ -3120,8 +3161,8 @@ function errorCountsByConfigRule(stdout) {
3120
3161
  }
3121
3162
 
3122
3163
  // src/core/config/read-adoption.ts
3123
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
3124
- import { join as join13 } from "path";
3164
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
3165
+ import { join as join14 } from "path";
3125
3166
 
3126
3167
  // src/core/config/owned-keys.ts
3127
3168
  function presetOwnedKeys(config, permitted, presetSets) {
@@ -3136,12 +3177,12 @@ function localOnlyKeys(config, permitted, presetSets) {
3136
3177
  }
3137
3178
 
3138
3179
  // src/core/config/resolve-config-target.ts
3139
- import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
3140
- import { join as join12 } from "path";
3180
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
3181
+ import { join as join13 } from "path";
3141
3182
  function readExtends(absolutePath) {
3142
3183
  let parsed;
3143
3184
  try {
3144
- parsed = parseJsonc(readFileSync9(absolutePath, "utf8"), absolutePath);
3185
+ parsed = parseJsonc(readFileSync10(absolutePath, "utf8"), absolutePath);
3145
3186
  } catch {
3146
3187
  return [];
3147
3188
  }
@@ -3155,7 +3196,7 @@ function resolveConfigTarget(moduleDir, { candidates, markers, fallback }) {
3155
3196
  let existing;
3156
3197
  let existingExtendsSomething = false;
3157
3198
  for (const candidate of candidates) {
3158
- const absolutePath = join12(moduleDir, candidate);
3199
+ const absolutePath = join13(moduleDir, candidate);
3159
3200
  if (!existsSync11(absolutePath)) continue;
3160
3201
  const chain = readExtends(absolutePath);
3161
3202
  if (existing === void 0) {
@@ -3189,12 +3230,12 @@ var NOT_ADOPTED2 = (configFile, unreadable = null) => ({
3189
3230
  });
3190
3231
  function readAdoption(cwd, options) {
3191
3232
  const target = resolveConfigTarget(cwd, options);
3192
- if (target.reason === "none" || !existsSync12(join13(cwd, target.path))) {
3233
+ if (target.reason === "none" || !existsSync12(join14(cwd, target.path))) {
3193
3234
  return NOT_ADOPTED2(target.reason === "none" ? null : target.path);
3194
3235
  }
3195
3236
  let parsed;
3196
3237
  try {
3197
- parsed = parseJsonc(readFileSync10(join13(cwd, target.path), "utf8"), target.path);
3238
+ parsed = parseJsonc(readFileSync11(join14(cwd, target.path), "utf8"), target.path);
3198
3239
  } catch (error) {
3199
3240
  const reason = error instanceof Error ? error.message : String(error);
3200
3241
  return NOT_ADOPTED2(target.path, `${target.path} could not be parsed (${reason})`);
@@ -3230,7 +3271,7 @@ function readLintAdoption(cwd) {
3230
3271
  // src/roles/lint/resolve-oxlint.ts
3231
3272
  import { existsSync as existsSync13 } from "fs";
3232
3273
  import { createRequire as createRequire3 } from "module";
3233
- import { delimiter as delimiter2, dirname as dirname5, join as join14 } from "path";
3274
+ import { delimiter as delimiter2, dirname as dirname5, join as join15 } from "path";
3234
3275
  import { fileURLToPath as fileURLToPath2 } from "url";
3235
3276
  var PACKAGE_OF = {
3236
3277
  oxlint: "oxlint",
@@ -3255,14 +3296,14 @@ function tsgolintShim(cwd) {
3255
3296
  for (const owner of ["oxlint-tsgolint", "oxlint"]) {
3256
3297
  try {
3257
3298
  const packageDir = dirname5(require3.resolve(`${owner}/package.json`));
3258
- candidates.push(join14(packageDir, "node_modules", ".bin", "tsgolint"));
3259
- candidates.push(join14(packageDir, "..", ".bin", "tsgolint"));
3299
+ candidates.push(join15(packageDir, "node_modules", ".bin", "tsgolint"));
3300
+ candidates.push(join15(packageDir, "..", ".bin", "tsgolint"));
3260
3301
  } catch {
3261
3302
  }
3262
3303
  }
3263
3304
  try {
3264
3305
  const ownRoot = dirname5(require3.resolve("@hublo/sentinel/package.json"));
3265
- candidates.push(join14(ownRoot, "node_modules", ".bin", "tsgolint"));
3306
+ candidates.push(join15(ownRoot, "node_modules", ".bin", "tsgolint"));
3266
3307
  } catch {
3267
3308
  candidates.push(resolveBin(dirname5(fileURLToPath2(import.meta.url)), "tsgolint") ?? "");
3268
3309
  }
@@ -3273,12 +3314,12 @@ function oxlintSearchPath(cwd) {
3273
3314
  }
3274
3315
 
3275
3316
  // src/roles/lint/adapters/oxlint/plan.ts
3276
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
3277
- import { join as join17 } from "path";
3317
+ import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
3318
+ import { join as join18 } from "path";
3278
3319
 
3279
3320
  // src/roles/lint/eslint-ignores.ts
3280
- import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
3281
- import { join as join15 } from "path";
3321
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
3322
+ import { join as join16 } from "path";
3282
3323
  var IGNORE_BLOCKS = [/\bignores\s*:\s*\[([^\]]*)\]/g, /\bglobalIgnores\s*\(\s*\[([^\]]*)\]/g];
3283
3324
  var STRING_LITERAL = /['"`]([^'"`]+)['"`]/g;
3284
3325
  var OPAQUE_SOURCE = /\b(includeIgnoreFile)\s*\([^)]*\)/g;
@@ -3291,11 +3332,11 @@ function readRootEslintIgnores(root) {
3291
3332
  return { patterns: patterns.filter((pattern) => pattern.startsWith("**/")), unresolved };
3292
3333
  }
3293
3334
  function readEslintIgnores(cwd) {
3294
- const config = ESLINT_CONFIG_FILES.map((name) => join15(cwd, name)).find((path) => existsSync14(path));
3335
+ const config = ESLINT_CONFIG_FILES.map((name) => join16(cwd, name)).find((path) => existsSync14(path));
3295
3336
  if (!config) return { patterns: [], unresolved: [] };
3296
3337
  let source;
3297
3338
  try {
3298
- source = readFileSync11(config, "utf8");
3339
+ source = readFileSync12(config, "utf8");
3299
3340
  } catch {
3300
3341
  return { patterns: [], unresolved: [] };
3301
3342
  }
@@ -3349,8 +3390,8 @@ function lintPresetFor(preset) {
3349
3390
  }
3350
3391
 
3351
3392
  // src/roles/lint/rename-suppressions.ts
3352
- import { readdirSync as readdirSync2, readFileSync as readFileSync12, statSync } from "fs";
3353
- import { join as join16, relative as relative2 } from "path";
3393
+ import { readdirSync as readdirSync2, readFileSync as readFileSync13, statSync } from "fs";
3394
+ import { join as join17, relative as relative2 } from "path";
3354
3395
  var SOURCE_EXTENSIONS = [
3355
3396
  ".ts",
3356
3397
  ".tsx",
@@ -3397,7 +3438,7 @@ function* sourceFiles(dir) {
3397
3438
  return;
3398
3439
  }
3399
3440
  for (const entry of entries) {
3400
- const full = join16(dir, entry);
3441
+ const full = join17(dir, entry);
3401
3442
  let isDirectory;
3402
3443
  try {
3403
3444
  isDirectory = statSync(full).isDirectory();
@@ -3417,7 +3458,7 @@ function findSuppressionRenames(cwd, renames) {
3417
3458
  for (const file of sourceFiles(cwd)) {
3418
3459
  let content;
3419
3460
  try {
3420
- content = readFileSync12(file, "utf8");
3461
+ content = readFileSync13(file, "utf8");
3421
3462
  } catch {
3422
3463
  continue;
3423
3464
  }
@@ -3535,7 +3576,7 @@ function plan(context) {
3535
3576
  keys: removableDeps
3536
3577
  });
3537
3578
  }
3538
- const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) => existsSync15(join17(context.cwd, name)));
3579
+ const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) => existsSync15(join18(context.cwd, name)));
3539
3580
  for (const name of eslintConfigs) operations.push({ kind: "delete", path: name });
3540
3581
  operations.push(
3541
3582
  ...nxTargetOperations({
@@ -3611,7 +3652,10 @@ function plan(context) {
3611
3652
  function lintScripts(cwd) {
3612
3653
  const scripts = moduleScripts(cwd);
3613
3654
  const rewritten = {
3614
- [LINT_SCRIPT_NAME]: composeLintScript(scripts[LINT_SCRIPT_NAME], {
3655
+ // `existingCommand`, not `scripts[...]`: the command can live in an `nx:run-commands`
3656
+ // target instead of a script, and adoption deletes that target. Composing against only
3657
+ // the script threw away whatever else the target ran.
3658
+ [LINT_SCRIPT_NAME]: composeLintScript(existingCommand(cwd, LINT_SCRIPT_NAME), {
3615
3659
  command: selfCommand(cwd, "--run --lint")
3616
3660
  })
3617
3661
  };
@@ -3630,7 +3674,7 @@ function lintScripts(cwd) {
3630
3674
  function committedIgnorePatterns(cwd) {
3631
3675
  try {
3632
3676
  const parsed = parseJsonc(
3633
- readFileSync13(join17(cwd, LINT_CONFIG_FILE), "utf8"),
3677
+ readFileSync14(join18(cwd, LINT_CONFIG_FILE), "utf8"),
3634
3678
  LINT_CONFIG_FILE
3635
3679
  );
3636
3680
  return Array.isArray(parsed.ignorePatterns) ? parsed.ignorePatterns.filter((entry) => typeof entry === "string") : [];
@@ -3642,7 +3686,7 @@ function moduleEslintDependencies(cwd) {
3642
3686
  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
3687
  let manifest;
3644
3688
  try {
3645
- manifest = JSON.parse(readFileSync13(join17(cwd, "package.json"), "utf8"));
3689
+ manifest = JSON.parse(readFileSync14(join18(cwd, "package.json"), "utf8"));
3646
3690
  } catch {
3647
3691
  return [];
3648
3692
  }
@@ -3761,20 +3805,20 @@ var OxlintAdapter = class extends BaseAdapter {
3761
3805
  * build the developer can see, rather than a silent half-adoption they cannot.
3762
3806
  */
3763
3807
  writeModuleBaseline(ctx, oxlint, env) {
3764
- const configPath = join18(ctx.cwd, LINT_CONFIG_FILE);
3765
- const baselinePath = join18(ctx.cwd, LINT_BASELINE_FILE);
3808
+ const configPath = join19(ctx.cwd, LINT_CONFIG_FILE);
3809
+ const baselinePath = join19(ctx.cwd, LINT_BASELINE_FILE);
3766
3810
  if (!existsSync16(configPath)) return;
3767
3811
  let config;
3768
3812
  try {
3769
3813
  config = parseJsonc(
3770
- readFileSync14(configPath, "utf8"),
3814
+ readFileSync15(configPath, "utf8"),
3771
3815
  LINT_CONFIG_FILE
3772
3816
  );
3773
3817
  } catch {
3774
3818
  return;
3775
3819
  }
3776
3820
  const current = Array.isArray(config.extends) ? config.extends : [];
3777
- const measurePath = join18(ctx.cwd, LINT_MEASURE_FILE);
3821
+ const measurePath = join19(ctx.cwd, LINT_MEASURE_FILE);
3778
3822
  let measured;
3779
3823
  try {
3780
3824
  writeFileSync2(
@@ -3822,7 +3866,7 @@ var OxlintAdapter = class extends BaseAdapter {
3822
3866
  }
3823
3867
  }
3824
3868
  async run(ctx) {
3825
- if (!existsSync16(join18(ctx.cwd, LINT_CONFIG_FILE))) {
3869
+ if (!existsSync16(join19(ctx.cwd, LINT_CONFIG_FILE))) {
3826
3870
  process.stderr.write(
3827
3871
  `sentinel lint(oxlint): no ${LINT_CONFIG_FILE} in this module; run \`sentinel --init --lint\` to adopt.
3828
3872
  `
@@ -3910,20 +3954,52 @@ var OxlintAdapter = class extends BaseAdapter {
3910
3954
  };
3911
3955
  }
3912
3956
  /** How many rules the extended preset enforces, read from the preset on disk. */
3957
+ /**
3958
+ * How many rules this module actually enforces.
3959
+ *
3960
+ * Counted from the PRESET it extends, not from its own file: the stub is one line by
3961
+ * design, so counting there would always answer zero and read as "this module lints
3962
+ * nothing".
3963
+ *
3964
+ * Two things this got wrong, both worth stating because both produced the same dangerous
3965
+ * output — a module reporting `rules=0` while `conformant=true`, which the migration page
3966
+ * counts as adopted and checking an empty set:
3967
+ *
3968
+ * - it used `JSON.parse`. The chain is JSONC: the per-module baseline carries a comment
3969
+ * per held rule explaining why. One comment anywhere in the chain threw, the catch
3970
+ * returned 0, and host-admin reported 188 rules as 0 the moment it gained a baseline.
3971
+ * - one unreadable entry zeroed the WHOLE count. An entry that cannot be read is a gap in
3972
+ * the count, not proof the module enforces nothing, and the difference matters most
3973
+ * exactly when something is broken.
3974
+ *
3975
+ * Counted as a SET of rule names, so the baseline re-stating a preset rule at `warn` is
3976
+ * the same rule, not a second one.
3977
+ */
3913
3978
  presetRuleCount(cwd) {
3914
3979
  const target = this.unresolvedPreset(cwd);
3915
3980
  if (target) return 0;
3916
- let total = 0;
3981
+ let stub;
3917
3982
  try {
3918
- const stub = JSON.parse(readFileSync14(join18(cwd, LINT_CONFIG_FILE), "utf8"));
3919
- for (const entry of stub.extends ?? []) {
3920
- const preset = JSON.parse(readFileSync14(join18(cwd, entry), "utf8"));
3921
- total += Object.keys(preset.rules ?? {}).length;
3922
- }
3983
+ stub = parseJsonc(
3984
+ readFileSync15(join19(cwd, LINT_CONFIG_FILE), "utf8"),
3985
+ LINT_CONFIG_FILE
3986
+ );
3923
3987
  } catch {
3924
3988
  return 0;
3925
3989
  }
3926
- return total;
3990
+ const names = /* @__PURE__ */ new Set();
3991
+ for (const entry of stub.extends ?? []) {
3992
+ try {
3993
+ const preset = parseJsonc(
3994
+ readFileSync15(join19(cwd, entry), "utf8"),
3995
+ entry
3996
+ );
3997
+ for (const rule of Object.keys(preset.rules ?? {})) names.add(rule);
3998
+ } catch {
3999
+ continue;
4000
+ }
4001
+ }
4002
+ return names.size;
3927
4003
  }
3928
4004
  /** Lint health for `--report`. Deepened in a later step; today it is the run outcome. */
3929
4005
  async report(ctx) {
@@ -3984,12 +4060,15 @@ var OxlintAdapter = class extends BaseAdapter {
3984
4060
  unresolvedPreset(cwd) {
3985
4061
  let parsed;
3986
4062
  try {
3987
- parsed = JSON.parse(readFileSync14(join18(cwd, LINT_CONFIG_FILE), "utf8"));
4063
+ parsed = parseJsonc(
4064
+ readFileSync15(join19(cwd, LINT_CONFIG_FILE), "utf8"),
4065
+ LINT_CONFIG_FILE
4066
+ );
3988
4067
  } catch {
3989
4068
  return void 0;
3990
4069
  }
3991
4070
  const targets = Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : typeof parsed.extends === "string" ? [parsed.extends] : [];
3992
- return targets.find((target) => !existsSync16(join18(cwd, target)));
4071
+ return targets.find((target) => !existsSync16(join19(cwd, target)));
3993
4072
  }
3994
4073
  /** Announce what is not enforced, so reduced coverage is never silent. */
3995
4074
  announceDisabled(preset) {
@@ -4021,9 +4100,9 @@ function registerLint() {
4021
4100
 
4022
4101
  // src/roles/typescript/adapters/tsc/tsc.adapter.ts
4023
4102
  import { spawnSync as spawnSync3 } from "child_process";
4024
- import { existsSync as existsSync17, readFileSync as readFileSync16 } from "fs";
4103
+ import { existsSync as existsSync17, readFileSync as readFileSync17 } from "fs";
4025
4104
  import { createRequire as createRequire4 } from "module";
4026
- import { join as join20 } from "path";
4105
+ import { join as join21 } from "path";
4027
4106
 
4028
4107
  // src/roles/typescript/presets/base.json
4029
4108
  var base_default3 = {
@@ -4204,8 +4283,8 @@ function readTsconfigAdoption(cwd) {
4204
4283
  }
4205
4284
 
4206
4285
  // src/roles/typescript/adapters/tsc/plan.ts
4207
- import { readFileSync as readFileSync15 } from "fs";
4208
- import { join as join19 } from "path";
4286
+ import { readFileSync as readFileSync16 } from "fs";
4287
+ import { join as join20 } from "path";
4209
4288
 
4210
4289
  // src/roles/typescript/typecheck-script.ts
4211
4290
  var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
@@ -4230,7 +4309,10 @@ var INSTALL_NOTE = "run `pnpm install` to fetch @hublo/sentinel (added to the mo
4230
4309
  var TYPECHECK_TARGET = { cache: true, inputs: ["default", "{projectRoot}/tsconfig.json"] };
4231
4310
  function typecheckScripts(cwd) {
4232
4311
  return {
4233
- [TYPECHECK_SCRIPT_NAME]: composeTypecheckScript(moduleScripts(cwd)[TYPECHECK_SCRIPT_NAME])
4312
+ // Reads the nx target too, not only the npm script: five modules in this repo keep a
4313
+ // real prerequisite (the brand-token generator) in an `nx:run-commands` typecheck
4314
+ // target, and adoption used to delete it along with the target.
4315
+ [TYPECHECK_SCRIPT_NAME]: composeTypecheckScript(existingCommand(cwd, TYPECHECK_SCRIPT_NAME))
4234
4316
  };
4235
4317
  }
4236
4318
  function composeExtends(current, preset) {
@@ -4290,7 +4372,7 @@ function planAdoption(context) {
4290
4372
  };
4291
4373
  }
4292
4374
  const existing = parseJsonc(
4293
- readFileSync15(join19(context.cwd, target.path), "utf8"),
4375
+ readFileSync16(join20(context.cwd, target.path), "utf8"),
4294
4376
  target.path
4295
4377
  );
4296
4378
  const extendsChain = composeExtends(existing.extends, preset);
@@ -4428,7 +4510,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
4428
4510
  let chain;
4429
4511
  try {
4430
4512
  const parsed = parseJsonc(
4431
- readFileSync16(join20(cwd, target.path), "utf8"),
4513
+ readFileSync17(join21(cwd, target.path), "utf8"),
4432
4514
  target.path
4433
4515
  );
4434
4516
  chain = parsed.extends;
@@ -4441,7 +4523,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
4441
4523
  );
4442
4524
  if (preset === void 0) return void 0;
4443
4525
  try {
4444
- createRequire4(join20(cwd, "noop.js")).resolve(preset);
4526
+ createRequire4(join21(cwd, "noop.js")).resolve(preset);
4445
4527
  return void 0;
4446
4528
  } catch {
4447
4529
  return preset;
@@ -4537,7 +4619,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
4537
4619
  referencedProjects(cwd, config) {
4538
4620
  try {
4539
4621
  const parsed = parseJsonc(
4540
- readFileSync16(join20(cwd, config), "utf8"),
4622
+ readFileSync17(join21(cwd, config), "utf8"),
4541
4623
  config
4542
4624
  );
4543
4625
  const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path) => typeof path === "string" && path.length > 0);
@@ -4553,7 +4635,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
4553
4635
  * check.
4554
4636
  */
4555
4637
  typecheckTarget(cwd) {
4556
- if (existsSync17(join20(cwd, "tsconfig.json"))) return "tsconfig.json";
4638
+ if (existsSync17(join21(cwd, "tsconfig.json"))) return "tsconfig.json";
4557
4639
  const target = resolveTsconfigTarget(cwd);
4558
4640
  return target.reason === "none" ? null : target.path;
4559
4641
  }
@@ -4756,7 +4838,7 @@ function replaceLines(current, replacements) {
4756
4838
  }
4757
4839
 
4758
4840
  // src/core/apply-plan.ts
4759
- import { existsSync as existsSync18, readFileSync as readFileSync17, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
4841
+ import { existsSync as existsSync18, readFileSync as readFileSync18, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
4760
4842
  import { resolve as resolve3, sep } from "path";
4761
4843
  import { applyEdits, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
4762
4844
 
@@ -4775,7 +4857,7 @@ function resolveWithinRoot(cwd, relativePath) {
4775
4857
  return absolutePath;
4776
4858
  }
4777
4859
  function readIfExists(absolutePath) {
4778
- return existsSync18(absolutePath) ? readFileSync17(absolutePath, "utf8") : void 0;
4860
+ return existsSync18(absolutePath) ? readFileSync18(absolutePath, "utf8") : void 0;
4779
4861
  }
4780
4862
  function* leaves(value, prefix = []) {
4781
4863
  for (const [key, keyValue] of Object.entries(value)) {
@@ -4893,8 +4975,8 @@ function applyPlan(cwd, plan2) {
4893
4975
  }
4894
4976
 
4895
4977
  // src/core/config/preset-evidence.ts
4896
- import { existsSync as existsSync19, readdirSync as readdirSync3, readFileSync as readFileSync18 } from "fs";
4897
- import { join as join21 } from "path";
4978
+ import { existsSync as existsSync19, readdirSync as readdirSync3, readFileSync as readFileSync19 } from "fs";
4979
+ import { join as join22 } from "path";
4898
4980
  var PATH_SIGNALS = [
4899
4981
  {
4900
4982
  preset: "nest",
@@ -4908,11 +4990,11 @@ var DEPENDENCY_SIGNALS = [
4908
4990
  { preset: "nest", pattern: /^@nestjs\// }
4909
4991
  ];
4910
4992
  function dependencyNames(cwd) {
4911
- const path = join21(cwd, "package.json");
4993
+ const path = join22(cwd, "package.json");
4912
4994
  if (!existsSync19(path)) return [];
4913
4995
  try {
4914
4996
  const manifest = parseJsonc(
4915
- readFileSync18(path, "utf8"),
4997
+ readFileSync19(path, "utf8"),
4916
4998
  path
4917
4999
  );
4918
5000
  return [
@@ -4935,7 +5017,7 @@ function declaresJsx(cwd) {
4935
5017
  for (const name of entries) {
4936
5018
  try {
4937
5019
  const config = parseJsonc(
4938
- readFileSync18(join21(cwd, name), "utf8"),
5020
+ readFileSync19(join22(cwd, name), "utf8"),
4939
5021
  name
4940
5022
  );
4941
5023
  if (config.compilerOptions?.jsx !== void 0) return true;
@@ -5095,4 +5177,4 @@ export {
5095
5177
  detectFramework,
5096
5178
  dispatch
5097
5179
  };
5098
- //# sourceMappingURL=chunk-TLNA2U3B.js.map
5180
+ //# sourceMappingURL=chunk-RLVLDC5H.js.map
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  registerAdapters,
8
8
  resolve,
9
9
  setDefaultRunner
10
- } from "./chunk-TLNA2U3B.js";
10
+ } from "./chunk-RLVLDC5H.js";
11
11
  export {
12
12
  BaseAdapter,
13
13
  all,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hublo/sentinel",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
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",