@hublo/sentinel 1.2.0-alpha.10 → 1.2.0-alpha.12

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-7WB7MQHD.js";
25
+ } from "../chunk-I4ATYZKK.js";
26
26
 
27
27
  // bin/sentinel.ts
28
28
  import { program } from "commander";
@@ -114,7 +114,7 @@ function resolveContext(cwd2, opts2) {
114
114
  );
115
115
  }
116
116
 
117
- // src/roles/build/scope.ts
117
+ // src/roles/build/declined.ts
118
118
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
119
119
  import { join as join3 } from "path";
120
120
  var DEPRECATED_NX_BUILDER = "@nx/webpack:webpack";
@@ -128,7 +128,7 @@ function buildsWithDeprecatedExecutor(cwd2) {
128
128
  return false;
129
129
  }
130
130
  }
131
- function explainBuildScope(cwd2) {
131
+ function explainDecline(cwd2) {
132
132
  if (buildConfigFile(cwd2) !== void 0) {
133
133
  return ` This module HAS a Vite config, so detection is the likely problem: the build toolchain is declared at the workspace root, which makes a front app detect as "node". Pass --preset to say what it is.`;
134
134
  }
@@ -176,7 +176,7 @@ async function runInit(ctx) {
176
176
  } catch (error) {
177
177
  if (error instanceof PresetUnsupportedError) {
178
178
  if (ctx.targetsExplicit) {
179
- const hint = type === "build" ? explainBuildScope(module.root) : ` Pass --preset if it was detected wrongly (hoisted dependencies make detection fall back to "node").`;
179
+ const hint = type === "build" ? explainDecline(module.root) : ` Pass --preset if it was detected wrongly (hoisted dependencies make detection fall back to "node").`;
180
180
  process.stderr.write(
181
181
  `
182
182
  sentinel (${type}): this module's preset is "${error.preset}", and --${type} has no adapter for it.${hint}
@@ -504,7 +504,7 @@ function composeDevScript(existing, command) {
504
504
  if (at === -1) return existing;
505
505
  const rest = (chunks[at] ?? "").replace(/^.*?(^|\s|\/)vite(\s|$)/, "");
506
506
  const options = appOptions(rest);
507
- chunks[at] = options === "" ? command : `${command} -- ${options}`;
507
+ chunks[at] = options === "" ? `${command} --` : `${command} -- ${options}`;
508
508
  return chunks.join(CHAIN);
509
509
  }
510
510
 
@@ -656,6 +656,7 @@ var REACT_PLUGIN_SPECIFIERS = ["@vitejs/plugin-react", "@tanstack/react-start"];
656
656
  function declaredBuildPreset(cwd) {
657
657
  const source = readBuildConfigSource(cwd);
658
658
  if (source === void 0) {
659
+ if (buildConfigFile(cwd) !== void 0) return void 0;
659
660
  return webpackBuildTarget(cwd) === void 0 ? void 0 : "nest";
660
661
  }
661
662
  if (importsSpecifier(source, BUILD_PRESET_SPECIFIERS.nest)) return "nest";
@@ -857,9 +858,9 @@ function buildScripts(cwd) {
857
858
  const own2 = moduleName(cwd);
858
859
  scripts[DEV_SCRIPT_NAME] = composeDevScript(
859
860
  existingDev,
860
- // Trailing `--` for the same reason as the build script: whatever the caller appends
861
- // reaches Vite, not sentinel's parser. It goes after `--module`, which is sentinel's own.
862
- `${selfCommand(cwd, "--run --dev") ?? SENTINEL_DEV_COMMAND} --module ${own2} --`
861
+ // No trailing `--` here: `composeDevScript` owns the separator, and adding one on both
862
+ // sides produced `-- --`, handing Vite an argument nobody wrote.
863
+ `${selfCommand(cwd, "--run --dev") ?? SENTINEL_DEV_COMMAND} --module ${own2}`
863
864
  );
864
865
  }
865
866
  return scripts;
@@ -873,75 +874,59 @@ function plan(context) {
873
874
  if (unmet.length > 0) {
874
875
  return { operations: [], skipped: describeUnmet(context.preset, unmet) };
875
876
  }
876
- if (declaredBuildPreset(context.cwd) === void 0 && buildConfigFile(context.cwd) !== void 0) {
877
+ const declaredPreset2 = declaredBuildPreset(context.cwd);
878
+ const configFile = buildConfigFile(context.cwd);
879
+ if (declaredPreset2 === void 0 && configFile !== void 0) {
877
880
  return {
878
881
  operations: [],
879
- skipped: `this module has a Vite config, but it does not build a React app (no @vitejs/plugin-react or @tanstack/react-start in it). The build role ships a React preset only.`
882
+ skipped: `this module has a Vite config, but it builds neither a React app (no @vitejs/plugin-react or @tanstack/react-start) nor a Nest service (no ${BUILD_PRESET_SPECIFIERS.nest}). Those are the two families this role serves.`
880
883
  };
881
884
  }
882
- const notes = [];
883
- const operations = [];
884
- const configFile = buildConfigFile(context.cwd);
885
885
  const webpackTarget = webpackBuildTarget(context.cwd);
886
- const isNest = declaredBuildPreset(context.cwd) === "nest" || webpackTarget !== void 0;
887
- if (isNest) {
888
- if (webpackTarget === void 0) {
889
- return {
890
- operations: [],
891
- skipped: `already building through sentinel: ${configFile ?? NEST_CONFIG_FILE} imports the nest preset and the nx target runs it. Nothing to change.`
892
- };
893
- }
894
- const refusal = refusalFor(context.cwd, webpackTarget);
895
- if (refusal !== void 0) return { operations: [], blocked: refusal };
896
- const dropped = Object.keys(webpackTarget.configurations ?? {});
897
- return {
898
- operations: nestAdoptionOperations(context.cwd, webpackTarget),
899
- notes: [
900
- `wrote ${NEST_CONFIG_FILE} and pointed the nx build target at sentinel. The target stays \`nx:run-commands\` with \`forwardAllArgs: false\`, so the image's \`--generatePackageJson\` still works and the Dockerfile needs no change.`,
901
- ...dropped.length > 0 ? [
902
- `dropped the \`${dropped.join("`, `")}\` configuration${dropped.length > 1 ? "s" : ""} from the build target: its fileReplacements point at files that do not exist, so it replaced nothing. A live one would have blocked this instead.`
903
- ] : [],
904
- `verify with \`nx run ${webpackTarget.project}:generate-swagger-file\` and \`git diff --exit-code\`: it runs the built bundle, so it proves the service boots AND that its published contract did not move.`
905
- ]
906
- };
886
+ if (declaredBuildPreset(context.cwd) === "nest" || webpackTarget !== void 0) {
887
+ return planNestService(context, configFile, webpackTarget);
907
888
  }
908
889
  if (configFile === void 0) {
909
890
  return {
910
891
  operations: [],
911
892
  skipped: `no Vite config in this module, so there is nothing to point at sentinel. This role moves an existing config's imports; it does not introduce a bundler. If this module builds another way (36 services here build through \`@nx/webpack\`), the build role does not apply to it.`
912
893
  };
913
- } else {
914
- const notEsm = esmBlocker(configFile, readProjectPackageJson(context.cwd).type);
915
- if (notEsm !== void 0) return { operations: [], skipped: notEsm };
916
- const rewrite = rewriteBuildImports(readConfigSource(context.cwd, configFile), configFile);
917
- if (rewrite.kind === "blocked") {
918
- return {
919
- operations: [],
920
- skipped: `${configFile} could not be pointed at sentinel, so nothing was written: ${rewrite.why}. Nothing here is broken; this needs a look before the role applies.`
921
- };
922
- }
923
- if (rewrite.kind === "rewritten") {
924
- operations.push({ kind: "write", path: configFile, contents: rewrite.source });
925
- }
926
- for (const name of COMPANION_CONFIG_FILES) {
927
- const source = readConfigSource(context.cwd, name);
928
- if (source === "") continue;
929
- const companion = rewriteBuildImports(source, name);
930
- if (companion.kind === "rewritten") {
931
- operations.push({ kind: "write", path: name, contents: companion.source });
932
- notes.push(`${name} now imports ${companion.moved.join(", ")} from sentinel too.`);
933
- } else if (companion.kind === "blocked") {
934
- notes.push(
935
- `${name} imports the toolchain and could NOT be pointed at sentinel: ${companion.why}. It will stop resolving when the workspace root drops these packages, and it will fail your TESTS rather than your build.`
936
- );
937
- }
938
- }
939
- if (rewrite.kind === "rewritten") {
894
+ }
895
+ return planReactApp(context, configFile);
896
+ }
897
+ function planReactApp(context, configFile) {
898
+ const notes = [];
899
+ const operations = [];
900
+ const notEsm = esmBlocker(configFile, readProjectPackageJson(context.cwd).type);
901
+ if (notEsm !== void 0) return { operations: [], skipped: notEsm };
902
+ const rewrite = rewriteBuildImports(readConfigSource(context.cwd, configFile), configFile);
903
+ if (rewrite.kind === "blocked") {
904
+ return {
905
+ operations: [],
906
+ skipped: `${configFile} could not be pointed at sentinel, so nothing was written: ${rewrite.why}. Nothing here is broken; this needs a look before the role applies.`
907
+ };
908
+ }
909
+ if (rewrite.kind === "rewritten") {
910
+ operations.push({ kind: "write", path: configFile, contents: rewrite.source });
911
+ }
912
+ for (const name of COMPANION_CONFIG_FILES) {
913
+ const source = readConfigSource(context.cwd, name);
914
+ if (source === "") continue;
915
+ const companion = rewriteBuildImports(source, name);
916
+ if (companion.kind === "rewritten") {
917
+ operations.push({ kind: "write", path: name, contents: companion.source });
918
+ notes.push(`${name} now imports ${companion.moved.join(", ")} from sentinel too.`);
919
+ } else if (companion.kind === "blocked") {
940
920
  notes.push(
941
- `${configFile} now imports ${rewrite.moved.join(", ")} from \`${BUILD_PRESET_SPECIFIER}\`. Only the import lines changed: every attribute, alias and path is untouched, so this build produces what it produced before.`
921
+ `${name} imports the toolchain and could NOT be pointed at sentinel: ${companion.why}. It will stop resolving when the workspace root drops these packages, and it will fail your TESTS rather than your build.`
942
922
  );
943
923
  }
944
924
  }
925
+ if (rewrite.kind === "rewritten") {
926
+ notes.push(
927
+ `${configFile} now imports ${rewrite.moved.join(", ")} from \`${BUILD_PRESET_SPECIFIER}\`. Only the import lines changed: every attribute, alias and path is untouched, so this build produces what it produced before.`
928
+ );
929
+ }
945
930
  operations.push(manifestOperation(context.cwd, buildScripts(context.cwd)));
946
931
  operations.push(...nxTargetOperations({ cwd: context.cwd, targets: buildTarget() }));
947
932
  const owned = ownedBuildDependencies(context.cwd);
@@ -953,6 +938,27 @@ function plan(context) {
953
938
  }
954
939
  return { operations, notes };
955
940
  }
941
+ function planNestService(context, configFile, webpackTarget) {
942
+ if (webpackTarget === void 0) {
943
+ return {
944
+ operations: [],
945
+ skipped: `already building through sentinel: ${configFile ?? NEST_CONFIG_FILE} imports the nest preset and the nx target runs it. Nothing to change.`
946
+ };
947
+ }
948
+ const refusal = refusalFor(context.cwd, webpackTarget);
949
+ if (refusal !== void 0) return { operations: [], blocked: refusal };
950
+ const dropped = Object.keys(webpackTarget.configurations ?? {});
951
+ return {
952
+ operations: nestAdoptionOperations(context.cwd, webpackTarget),
953
+ notes: [
954
+ `wrote ${NEST_CONFIG_FILE} and pointed the nx build target at sentinel. The target stays \`nx:run-commands\` with \`forwardAllArgs: false\`, so the image's \`--generatePackageJson\` still works and the Dockerfile needs no change.`,
955
+ ...dropped.length > 0 ? [
956
+ `dropped the \`${dropped.join("`, `")}\` configuration${dropped.length > 1 ? "s" : ""} from the build target: its fileReplacements point at files that do not exist, so it replaced nothing. A live one would have blocked this instead.`
957
+ ] : [],
958
+ `verify with \`nx run ${webpackTarget.project}:generate-swagger-file\` and \`git diff --exit-code\`: it runs the built bundle, so it proves the service boots AND that its published contract did not move.`
959
+ ]
960
+ };
961
+ }
956
962
  function readConfigSource(cwd, configFile) {
957
963
  if (!existsSync8(join8(cwd, configFile))) return "";
958
964
  try {
@@ -6427,4 +6433,4 @@ export {
6427
6433
  detectFramework,
6428
6434
  dispatch
6429
6435
  };
6430
- //# sourceMappingURL=chunk-7WB7MQHD.js.map
6436
+ //# sourceMappingURL=chunk-I4ATYZKK.js.map
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  registerAdapters,
8
8
  resolve,
9
9
  setDefaultRunner
10
- } from "./chunk-7WB7MQHD.js";
10
+ } from "./chunk-I4ATYZKK.js";
11
11
  export {
12
12
  BaseAdapter,
13
13
  all,
@@ -93,8 +93,9 @@ var DEFAULT_PROVIDED_BY_IMAGE = ["@prisma"];
93
93
  var externalPackages = (bundle) => {
94
94
  const emitted = new Set(Object.keys(bundle));
95
95
  const packages = /* @__PURE__ */ new Set();
96
- for (const chunk of Object.values(bundle)) {
97
- for (const imported of chunk.imports ?? []) {
96
+ for (const emittedFile of Object.values(bundle)) {
97
+ if (emittedFile.type !== "chunk") continue;
98
+ for (const imported of emittedFile.imports) {
98
99
  if (emitted.has(imported)) continue;
99
100
  if (imported.startsWith(".") || imported.startsWith("/")) continue;
100
101
  if (isBuiltin(imported)) continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hublo/sentinel",
3
- "version": "1.2.0-alpha.10",
3
+ "version": "1.2.0-alpha.12",
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",