@hublo/sentinel 1.2.0-alpha.26 → 1.2.0-alpha.28

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.
@@ -22,7 +22,7 @@ import {
22
22
  registerAdapters,
23
23
  resolve,
24
24
  resolveBin
25
- } from "../chunk-O7OXACCN.js";
25
+ } from "../chunk-LNSAUFIM.js";
26
26
 
27
27
  // bin/sentinel.ts
28
28
  import { program } from "commander";
@@ -241,9 +241,16 @@ function workspaceRootHops(cwd, target) {
241
241
  import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
242
242
  import { join as join5 } from "path";
243
243
 
244
- // src/roles/build/adoption-drift.ts
245
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
246
- import { join as join4 } from "path";
244
+ // src/core/config/module-type.ts
245
+ function esmReadiness(fileName, packageType) {
246
+ if (/\.(mts|mjs)$/.test(fileName)) return { kind: "ready" };
247
+ if (packageType === "module") return { kind: "ready" };
248
+ const rename = fileName.replace(/\.(ts|js)$/, ".mts");
249
+ return {
250
+ kind: "blocked",
251
+ why: `sentinel is ESM only, and this module ${packageType === void 0 ? 'declares no `"type"`' : `declares \`"type": "${packageType}"\``}, so Node loads ${fileName} as CommonJS and the import fails. Two remedies, and adoption takes neither by itself because both reach beyond this build: add \`"type": "module"\` to this module's package.json, which changes how EVERY \`.js\` here loads, or rename the config to \`${rename}\`, which settles it in the filename where nothing can undo it.`
252
+ };
253
+ }
247
254
 
248
255
  // src/core/config/manifest.ts
249
256
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
@@ -406,7 +413,9 @@ var SENTINEL_OWNED_BUILD_PACKAGES = OWNED_PACKAGES.map(
406
413
  (entry) => entry.package
407
414
  );
408
415
 
409
- // src/roles/build/adoption-drift.ts
416
+ // src/roles/build/project-json.ts
417
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
418
+ import { join as join4 } from "path";
410
419
  function projectTargets(cwd) {
411
420
  const path = join4(cwd, "project.json");
412
421
  if (!existsSync4(path)) return void 0;
@@ -416,6 +425,8 @@ function projectTargets(cwd) {
416
425
  return void 0;
417
426
  }
418
427
  }
428
+
429
+ // src/roles/build/adoption-drift.ts
419
430
  function buildAdoptionDrift(cwd) {
420
431
  const drift = [];
421
432
  const manifest = readProjectPackageJson(cwd);
@@ -457,6 +468,12 @@ function buildAdoptionDrift(cwd) {
457
468
  `still declares a \`${BUILD_SCRIPT_NAME}\` target in project.json beside the one in package.json, so the module has two and which runs depends on the caller`
458
469
  );
459
470
  }
471
+ const devScript = moduleScripts(cwd)[DEV_SCRIPT_NAME];
472
+ if (devScript?.includes(SENTINEL_DEV_COMMAND) === true && projectTargets(cwd)?.[DEV_SCRIPT_NAME] !== void 0) {
473
+ drift.push(
474
+ `runs its \`${DEV_SCRIPT_NAME}\` script through sentinel but still declares a \`${DEV_SCRIPT_NAME}\` target in project.json, so one dev server has two declarations and editing the target stops the script from being what runs`
475
+ );
476
+ }
460
477
  return drift;
461
478
  }
462
479
 
@@ -511,7 +528,10 @@ function readBuildAdoption(cwd) {
511
528
  } catch (error) {
512
529
  return NOT_ADOPTED(configFile, error instanceof Error ? error.message : String(error));
513
530
  }
514
- if (!importsPreset(source)) return NOT_ADOPTED(configFile);
531
+ if (!importsPreset(source)) {
532
+ const esm = esmReadiness(configFile, readProjectPackageJson(cwd).type);
533
+ return NOT_ADOPTED(configFile, esm.kind === "blocked" ? esm.why : null);
534
+ }
515
535
  const manifest = readProjectPackageJson(cwd);
516
536
  const declared = { ...manifest.dependencies, ...manifest.devDependencies };
517
537
  const ownDeclarations = SENTINEL_OWNED_BUILD_PACKAGES.filter((name) => name in declared).map(
@@ -886,6 +906,43 @@ function composeDevScript(existing, command) {
886
906
  return chunks.join(CHAIN);
887
907
  }
888
908
 
909
+ // src/roles/build/dev-target.ts
910
+ var DELEGATION_KEYS = /* @__PURE__ */ new Set(["executor", "options"]);
911
+ var DELEGATION_OPTION_KEYS = /* @__PURE__ */ new Set(["command", "cwd"]);
912
+ function delegatesToScript(target, scriptName) {
913
+ if (typeof target !== "object" || target === null || Array.isArray(target)) return false;
914
+ const entries = target;
915
+ if (!Object.keys(entries).every((key) => DELEGATION_KEYS.has(key))) return false;
916
+ if (entries["executor"] !== "nx:run-commands") return false;
917
+ const options = entries["options"];
918
+ if (typeof options !== "object" || options === null || Array.isArray(options)) return false;
919
+ const declared = options;
920
+ if (!Object.keys(declared).every((key) => DELEGATION_OPTION_KEYS.has(key))) return false;
921
+ const command = declared["command"];
922
+ if (typeof command !== "string") return false;
923
+ return new RegExp(String.raw`(^|\s)run\s+${scriptName}$`).test(command.trim());
924
+ }
925
+ function devScriptIsAdopted(devScript) {
926
+ return devScript !== void 0 && devScript.includes(SENTINEL_DEV_COMMAND);
927
+ }
928
+ function devTargetOperations(cwd, devScript) {
929
+ if (!devScriptIsAdopted(devScript)) return { operations: [] };
930
+ const target = projectTargets(cwd)?.[DEV_SCRIPT_NAME];
931
+ if (target === void 0) return { operations: [] };
932
+ if (!delegatesToScript(target, DEV_SCRIPT_NAME)) {
933
+ return {
934
+ operations: [],
935
+ note: `left the \`${DEV_SCRIPT_NAME}\` target in project.json alone: it does more than call \`pnpm run ${DEV_SCRIPT_NAME}\`, so removing it would drop behaviour this role never declared. Move what it does into the script, or keep it and accept the second home.`
936
+ };
937
+ }
938
+ return {
939
+ operations: [
940
+ { kind: "remove-json-keys", path: "project.json", keys: [["targets", DEV_SCRIPT_NAME]] }
941
+ ],
942
+ note: `removed the \`${DEV_SCRIPT_NAME}\` target from project.json: it only called \`pnpm run ${DEV_SCRIPT_NAME}\`, which nx infers from the script itself. host-admin has run without that target all along.`
943
+ };
944
+ }
945
+
889
946
  // src/roles/build/nest/adopt.ts
890
947
  import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
891
948
  import { join as join10 } from "path";
@@ -977,11 +1034,6 @@ function describeUnmet(preset, unmet) {
977
1034
  // src/core/config/rewrite-imports.ts
978
1035
  import { parseSync } from "oxc-parser";
979
1036
  var movedSpecifiers = (toolchain) => new Set(toolchain.owned.map((entry) => entry.specifier));
980
- function esmBlocker(fileName, packageType) {
981
- if (/\.(mts|mjs)$/.test(fileName)) return void 0;
982
- if (packageType === "module") return void 0;
983
- return `sentinel is ESM only, and this module does not declare \`"type": "module"\`, so Vite would load ${fileName} as CommonJS and fail to require it. Add \`"type": "module"\` to this module's package.json, or rename the config to \`${fileName.replace(/\.(ts|js)$/, ".m$1")}\`, then run this again.`;
984
- }
985
1037
  var defaultExportNames = (toolchain) => Object.fromEntries(
986
1038
  toolchain.owned.filter((entry) => entry.default !== void 0).map((entry) => [entry.specifier, entry.default])
987
1039
  );
@@ -1152,8 +1204,8 @@ function plan(context) {
1152
1204
  function planReactApp(context, configFile) {
1153
1205
  const notes = [];
1154
1206
  const operations = [];
1155
- const notEsm = esmBlocker(configFile, readProjectPackageJson(context.cwd).type);
1156
- if (notEsm !== void 0) return { operations: [], skipped: notEsm };
1207
+ const esm = esmReadiness(configFile, readProjectPackageJson(context.cwd).type);
1208
+ if (esm.kind === "blocked") return { operations: [], skipped: esm.why };
1157
1209
  const rewrite = rewriteBuildImports(readConfigSource(context.cwd, configFile), configFile);
1158
1210
  if (rewrite.kind === "blocked") {
1159
1211
  return {
@@ -1182,7 +1234,8 @@ function planReactApp(context, configFile) {
1182
1234
  `${configFile} now imports ${rewrite.moved.join(", ")} from \`${BUILD_PRESET_SPECIFIER}\`. Only the import lines changed: every attribute, alias and path is untouched, so this build produces what it produced before.`
1183
1235
  );
1184
1236
  }
1185
- operations.push(manifestOperation(context.cwd, buildScripts(context.cwd)));
1237
+ const scripts = buildScripts(context.cwd);
1238
+ operations.push(manifestOperation(context.cwd, scripts));
1186
1239
  operations.push(
1187
1240
  ...nxTargetOperations({
1188
1241
  cwd: context.cwd,
@@ -1192,6 +1245,9 @@ function planReactApp(context, configFile) {
1192
1245
  })
1193
1246
  })
1194
1247
  );
1248
+ const dev = devTargetOperations(context.cwd, scripts[DEV_SCRIPT_NAME]);
1249
+ operations.push(...dev.operations);
1250
+ if (dev.note !== void 0) notes.push(dev.note);
1195
1251
  const owned = ownedBuildDependencies(context.cwd);
1196
1252
  if (owned.length > 0) {
1197
1253
  operations.push({ kind: "remove-json-keys", path: "package.json", keys: owned });
@@ -6631,4 +6687,4 @@ export {
6631
6687
  detectFramework,
6632
6688
  dispatch
6633
6689
  };
6634
- //# sourceMappingURL=chunk-O7OXACCN.js.map
6690
+ //# sourceMappingURL=chunk-LNSAUFIM.js.map
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  registerAdapters,
8
8
  resolve,
9
9
  setDefaultRunner
10
- } from "./chunk-O7OXACCN.js";
10
+ } from "./chunk-LNSAUFIM.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.2.0-alpha.26",
3
+ "version": "1.2.0-alpha.28",
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",