@hublo/sentinel 1.4.0-alpha.17 → 1.4.0-alpha.19

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-I64KRL6O.js";
19
+ } from "../chunk-AQGNE3UO.js";
20
20
  import {
21
21
  WORKSPACE_ROOT_MARKER,
22
22
  ensureWorkspacePrep,
@@ -5404,7 +5404,7 @@ function registerLint() {
5404
5404
  import { spawnSync as spawnSync5 } from "child_process";
5405
5405
  import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync30 } from "fs";
5406
5406
  import { tmpdir as tmpdir2 } from "os";
5407
- import { isAbsolute as isAbsolute3, join as join37, resolve as resolve6 } from "path";
5407
+ import { isAbsolute as isAbsolute3, join as join38, resolve as resolve6 } from "path";
5408
5408
 
5409
5409
  // src/roles/test/presets/toolchain.json
5410
5410
  var toolchain_default2 = {
@@ -5481,9 +5481,9 @@ function testTarget(options) {
5481
5481
  }
5482
5482
 
5483
5483
  // src/roles/test/jest-migration.ts
5484
- import { existsSync as existsSync29, readFileSync as readFileSync28, readdirSync as readdirSync8 } from "fs";
5484
+ import { existsSync as existsSync30, readFileSync as readFileSync28, readdirSync as readdirSync8 } from "fs";
5485
5485
  import { createRequire as createRequire6 } from "module";
5486
- import { dirname as dirname7, join as join35, relative as relative7 } from "path";
5486
+ import { dirname as dirname8, join as join36, relative as relative7 } from "path";
5487
5487
 
5488
5488
  // src/roles/test/codemod/migrate-module.ts
5489
5489
  import { readdirSync as readdirSync6, readFileSync as readFileSync25, statSync as statSync6 } from "fs";
@@ -5738,7 +5738,7 @@ function rewriteAssertionReturnValue(source, program) {
5738
5738
  for (const reading of readings) {
5739
5739
  const from = reading.start;
5740
5740
  let to = reading.end;
5741
- while (to < source.length && /[\s;]/.test(source[to])) to += 1;
5741
+ while (to < source.length && /[ \t;]/.test(source[to])) to += 1;
5742
5742
  let head = from;
5743
5743
  while (head > 0 && /[ \t]/.test(source[head - 1])) head -= 1;
5744
5744
  if (head > 0 && source[head - 1] === "\n") head -= 1;
@@ -8262,12 +8262,59 @@ function runModes(cwd, fallback) {
8262
8262
  );
8263
8263
  }
8264
8264
 
8265
+ // src/roles/test/written-file-formatter.ts
8266
+ import { existsSync as existsSync29 } from "fs";
8267
+ import { dirname as dirname7, join as join35 } from "path";
8268
+ var PRETTIER_CONFIGS = [
8269
+ ".prettierrc",
8270
+ ".prettierrc.json",
8271
+ ".prettierrc.json5",
8272
+ ".prettierrc.yaml",
8273
+ ".prettierrc.yml",
8274
+ ".prettierrc.js",
8275
+ ".prettierrc.cjs",
8276
+ ".prettierrc.mjs",
8277
+ ".prettierrc.toml",
8278
+ "prettier.config.js",
8279
+ "prettier.config.cjs",
8280
+ "prettier.config.mjs"
8281
+ ];
8282
+ function prettierConfigFrom(cwd) {
8283
+ let dir = cwd;
8284
+ for (; ; ) {
8285
+ for (const name of PRETTIER_CONFIGS) {
8286
+ if (existsSync29(join35(dir, name))) return join35(dir, name);
8287
+ }
8288
+ const parent = dirname7(dir);
8289
+ if (parent === dir) return void 0;
8290
+ dir = parent;
8291
+ }
8292
+ }
8293
+ function writtenFileFormatter(cwd) {
8294
+ if (readFormatAdoption(cwd).configFile !== null) {
8295
+ const oxfmt = resolveOxfmt(cwd);
8296
+ if (oxfmt !== void 0) {
8297
+ return { bin: oxfmt, args: ["--write"], origin: "this module's own oxfmt config" };
8298
+ }
8299
+ }
8300
+ const config = prettierConfigFrom(cwd);
8301
+ const prettier = resolveBin(cwd, "prettier");
8302
+ if (config !== void 0 && prettier !== void 0) {
8303
+ return {
8304
+ bin: prettier,
8305
+ args: ["--write", "--log-level", "warn"],
8306
+ origin: `the workspace's Prettier`
8307
+ };
8308
+ }
8309
+ return void 0;
8310
+ }
8311
+
8265
8312
  // src/roles/test/jest-migration.ts
8266
8313
  var SPEC_TSCONFIG = "tsconfig.spec.json";
8267
8314
  var VITEST_GLOBAL_TYPES = "vitest/globals";
8268
8315
  function specTypesOperation(cwd) {
8269
- const path = join35(cwd, SPEC_TSCONFIG);
8270
- if (!existsSync29(path)) return void 0;
8316
+ const path = join36(cwd, SPEC_TSCONFIG);
8317
+ if (!existsSync30(path)) return void 0;
8271
8318
  let config;
8272
8319
  try {
8273
8320
  config = parseJsonc(
@@ -8291,9 +8338,9 @@ function specTypesOperation(cwd) {
8291
8338
  }
8292
8339
  function shippedTypesPath(cwd, declaration) {
8293
8340
  try {
8294
- const manifest = createRequire6(join35(cwd, "noop.cjs")).resolve("@hublo/sentinel/package.json");
8295
- const file = join35(dirname7(manifest), declaration);
8296
- if (existsSync29(file)) {
8341
+ const manifest = createRequire6(join36(cwd, "noop.cjs")).resolve("@hublo/sentinel/package.json");
8342
+ const file = join36(dirname8(manifest), declaration);
8343
+ if (existsSync30(file)) {
8297
8344
  const path = relative7(cwd, file);
8298
8345
  return path.startsWith(".") ? path : `./${path}`;
8299
8346
  }
@@ -8313,7 +8360,7 @@ function tsconfigFileListOperations(cwd) {
8313
8360
  let config;
8314
8361
  try {
8315
8362
  config = parseJsonc(
8316
- readFileSync28(join35(cwd, name), "utf8"),
8363
+ readFileSync28(join36(cwd, name), "utf8"),
8317
8364
  name
8318
8365
  );
8319
8366
  } catch {
@@ -8446,9 +8493,16 @@ function planJestMigration(context, adoption) {
8446
8493
  `${SPEC_TSCONFIG} now declares \`${VITEST_GLOBAL_TYPES}\` instead of \`jest\`. Without it the module keeps depending on @types/jest and \`jest.Mock\` still resolves, so the migration would look finished while the dependency it cuts is still declared.`
8447
8494
  );
8448
8495
  }
8449
- notes.push(
8450
- `the files this rewrote were run through this module's own formatter afterwards. Two things needed it: the Vitest type imports land beside the existing ones, and a rewritten call is often SHORTER than the one it replaced, so the line breaks around it are no longer the ones your config would pick. Neither is a defect, and both would have failed your format check.`
8451
- );
8496
+ const formatter = writtenFileFormatter(context.cwd);
8497
+ if (formatter !== void 0) {
8498
+ notes.push(
8499
+ `the files this rewrote were run through ${formatter.origin} afterwards. Two things needed it: the Vitest type imports land beside the existing ones, and a rewritten call is often SHORTER than the one it replaced, so the line breaks around it are no longer the ones your config would pick. Neither is a defect, and both would have failed your format check. What no formatter does is ORDER imports, so if your lint enforces \`import/order\`, run its fixer too.`
8500
+ );
8501
+ } else {
8502
+ notes.push(
8503
+ `no style is declared here, by this module or its workspace, so what was written keeps the shape it was written in, and the Vitest type imports sit beside the existing ones rather than where your \`import/order\` wants them. That rule is classified by a prefix list this package cannot read, so it is left to the tool that owns it: run your module's own lint fixer (\`nx run <module>:lint --fix\` here) and its formatter before committing.`
8504
+ );
8505
+ }
8452
8506
  notes.push(
8453
8507
  `then run this module's TYPECHECK, not only its tests. The migration rewrites types as well as calls, and a wrong type is invisible to a green suite: measured on a real module, \`--run --test\` passed while \`--run --typescript\` reported ten errors it had not had before.`
8454
8508
  );
@@ -8595,9 +8649,9 @@ function plan3(context) {
8595
8649
  // src/roles/test/reference.ts
8596
8650
  import { execFileSync } from "child_process";
8597
8651
  import { createHash } from "crypto";
8598
- import { existsSync as existsSync30, mkdirSync, mkdtempSync, readFileSync as readFileSync29, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
8652
+ import { existsSync as existsSync31, mkdirSync, mkdtempSync, readFileSync as readFileSync29, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
8599
8653
  import { tmpdir } from "os";
8600
- import { join as join36, resolve as resolve5 } from "path";
8654
+ import { join as join37, resolve as resolve5 } from "path";
8601
8655
 
8602
8656
  // src/roles/test/baseline.ts
8603
8657
  function readSnapshot(report) {
@@ -8841,22 +8895,22 @@ function describeComparison(comparison) {
8841
8895
  // src/roles/test/reference.ts
8842
8896
  function referencePath(cwd) {
8843
8897
  const key = createHash("sha256").update(resolve5(cwd)).digest("hex").slice(0, 16);
8844
- return join36(tmpdir(), "sentinel-test-reference", `${key}.json`);
8898
+ return join37(tmpdir(), "sentinel-test-reference", `${key}.json`);
8845
8899
  }
8846
8900
  function runJest(cwd, config) {
8847
8901
  const bin = resolveBin(cwd, "jest");
8848
8902
  if (bin === void 0) return void 0;
8849
8903
  const root = findWorkspaceRoot(cwd) ?? cwd;
8850
- const out = join36(mkdtempSync(join36(tmpdir(), "sentinel-reference-")), "jest.json");
8904
+ const out = join37(mkdtempSync(join37(tmpdir(), "sentinel-reference-")), "jest.json");
8851
8905
  try {
8852
- execFileSync(bin, ["-c", join36(cwd, config), "--json", `--outputFile=${out}`, "--silent"], {
8906
+ execFileSync(bin, ["-c", join37(cwd, config), "--json", `--outputFile=${out}`, "--silent"], {
8853
8907
  cwd: root,
8854
8908
  stdio: ["ignore", "ignore", "ignore"],
8855
8909
  maxBuffer: 1 << 28
8856
8910
  });
8857
8911
  } catch {
8858
8912
  }
8859
- if (!existsSync30(out)) return void 0;
8913
+ if (!existsSync31(out)) return void 0;
8860
8914
  try {
8861
8915
  return JSON.parse(readFileSync29(out, "utf8"));
8862
8916
  } catch {
@@ -8886,7 +8940,7 @@ function captureReference(cwd, config) {
8886
8940
  snapshot
8887
8941
  };
8888
8942
  const path = referencePath(cwd);
8889
- mkdirSync(join36(path, ".."), { recursive: true });
8943
+ mkdirSync(join37(path, ".."), { recursive: true });
8890
8944
  writeFileSync3(path, `${JSON.stringify(stored, void 0, 2)}
8891
8945
  `);
8892
8946
  return {
@@ -8895,7 +8949,7 @@ function captureReference(cwd, config) {
8895
8949
  };
8896
8950
  }
8897
8951
  function hasReference(cwd) {
8898
- return existsSync30(referencePath(cwd));
8952
+ return existsSync31(referencePath(cwd));
8899
8953
  }
8900
8954
  function compareAgainstReference(cwd, report) {
8901
8955
  const path = referencePath(cwd);
@@ -8923,6 +8977,7 @@ function resolveVitest(cwd) {
8923
8977
  return fromModule !== void 0 ? { bin: fromModule, origin: "module" } : { bin: void 0, origin: "module" };
8924
8978
  }
8925
8979
  var FORMATTABLE = /\.(m|c)?(j|t)sx?$|\.json$/;
8980
+ var FORMAT_BATCH = 25;
8926
8981
  var VitestAdapter = class extends BaseAdapter {
8927
8982
  target = "test";
8928
8983
  runner = "vitest";
@@ -8992,20 +9047,27 @@ var VitestAdapter = class extends BaseAdapter {
8992
9047
  async afterInit(ctx) {
8993
9048
  const written = (ctx.written ?? []).filter((file) => FORMATTABLE.test(file));
8994
9049
  if (written.length === 0) return Promise.resolve();
8995
- if (readFormatAdoption(ctx.cwd).configFile === null) {
9050
+ const formatter = writtenFileFormatter(ctx.cwd);
9051
+ if (formatter === void 0) {
8996
9052
  this.say(
8997
- `no formatter configured here, so the ${written.length} file(s) this wrote keep the shape they were written in. Run your own formatter over them before committing.`
9053
+ `no style is declared here, by this module or its workspace, so the ${written.length} file(s) this wrote keep the shape they were written in. Run your own formatter over them before committing.`
8998
9054
  );
8999
9055
  return Promise.resolve();
9000
9056
  }
9001
- const oxfmt = resolveOxfmt(ctx.cwd);
9002
- if (oxfmt === void 0) return Promise.resolve();
9003
- const pass = spawnSync5(oxfmt, ["--write", ...written], { cwd: ctx.cwd, encoding: "utf8" });
9057
+ let failed = 0;
9058
+ for (let at = 0; at < written.length; at += FORMAT_BATCH) {
9059
+ const batch = written.slice(at, at + FORMAT_BATCH);
9060
+ const pass = spawnSync5(formatter.bin, [...formatter.args, ...batch], {
9061
+ cwd: ctx.cwd,
9062
+ encoding: "utf8"
9063
+ });
9064
+ if (pass.status !== 0) failed += batch.length;
9065
+ }
9004
9066
  this.say(
9005
- pass.status === 0 ? `formatted the ${written.length} file(s) this wrote with the module's own oxfmt config, so its \`--run --format\` passes on files it did not write itself.` : (
9067
+ failed === 0 ? `formatted the ${written.length} file(s) this wrote with ${formatter.origin}, so a format check passes on files it did not write itself.` : (
9006
9068
  // Adoption SUCCEEDED; only the tidy-up did not. Failing here would revert a migration
9007
9069
  // over a formatting pass, which is the wrong trade.
9008
- `the module's formatter could not run over what this wrote (${pass.status ?? "no exit code"}). Adoption itself is applied; run \`sentinel --run --format --fix\` before committing.`
9070
+ `${formatter.origin} could not run over ${failed} of the ${written.length} file(s) this wrote. Adoption itself is applied; run your formatter before committing.`
9009
9071
  )
9010
9072
  );
9011
9073
  return Promise.resolve();
@@ -9060,7 +9122,7 @@ var VitestAdapter = class extends BaseAdapter {
9060
9122
  const { cwd: runFrom, options, paths } = fromWorkspaceRoot(ctx.cwd, typedOptions, typedPaths);
9061
9123
  const watching = options.includes("--watch") || options.includes("-w");
9062
9124
  const proving = !watching && hasReference(ctx.cwd);
9063
- const reportFile = proving ? join37(mkdtempSync2(join37(tmpdir2(), "sentinel-run-")), "vitest.json") : void 0;
9125
+ const reportFile = proving ? join38(mkdtempSync2(join38(tmpdir2(), "sentinel-run-")), "vitest.json") : void 0;
9064
9126
  const reporters = reportFile === void 0 ? [] : ["--reporter=default", "--reporter=json", `--outputFile=${reportFile}`];
9065
9127
  const argv = watching ? [...options, ...paths] : ["run", ...reporters, ...options, ...paths];
9066
9128
  const result = spawnSync5(bin, argv, { cwd: runFrom, stdio: "inherit" });
@@ -9125,9 +9187,9 @@ function registerTest() {
9125
9187
 
9126
9188
  // src/roles/typescript/adapters/tsc/tsc.adapter.ts
9127
9189
  import { spawnSync as spawnSync6 } from "child_process";
9128
- import { existsSync as existsSync31, readFileSync as readFileSync32 } from "fs";
9190
+ import { existsSync as existsSync32, readFileSync as readFileSync32 } from "fs";
9129
9191
  import { createRequire as createRequire7 } from "module";
9130
- import { join as join39 } from "path";
9192
+ import { join as join40 } from "path";
9131
9193
 
9132
9194
  // src/roles/typescript/presets/nest.json
9133
9195
  var nest_default2 = {
@@ -9309,7 +9371,7 @@ function readTsconfigAdoption(cwd) {
9309
9371
 
9310
9372
  // src/roles/typescript/adapters/tsc/plan.ts
9311
9373
  import { readFileSync as readFileSync31 } from "fs";
9312
- import { join as join38 } from "path";
9374
+ import { join as join39 } from "path";
9313
9375
 
9314
9376
  // src/roles/typescript/typecheck-script.ts
9315
9377
  var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
@@ -9402,7 +9464,7 @@ function planAdoption(context) {
9402
9464
  };
9403
9465
  }
9404
9466
  const existing = parseJsonc(
9405
- readFileSync31(join38(context.cwd, target.path), "utf8"),
9467
+ readFileSync31(join39(context.cwd, target.path), "utf8"),
9406
9468
  target.path
9407
9469
  );
9408
9470
  const extendsChain = composeExtends(existing.extends, preset);
@@ -9540,7 +9602,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9540
9602
  let chain;
9541
9603
  try {
9542
9604
  const parsed = parseJsonc(
9543
- readFileSync32(join39(cwd, target.path), "utf8"),
9605
+ readFileSync32(join40(cwd, target.path), "utf8"),
9544
9606
  target.path
9545
9607
  );
9546
9608
  chain = parsed.extends;
@@ -9553,7 +9615,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9553
9615
  );
9554
9616
  if (preset === void 0) return void 0;
9555
9617
  try {
9556
- createRequire7(join39(cwd, "noop.js")).resolve(preset);
9618
+ createRequire7(join40(cwd, "noop.js")).resolve(preset);
9557
9619
  return void 0;
9558
9620
  } catch {
9559
9621
  return preset;
@@ -9649,7 +9711,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9649
9711
  referencedProjects(cwd, config) {
9650
9712
  try {
9651
9713
  const parsed = parseJsonc(
9652
- readFileSync32(join39(cwd, config), "utf8"),
9714
+ readFileSync32(join40(cwd, config), "utf8"),
9653
9715
  config
9654
9716
  );
9655
9717
  const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path) => typeof path === "string" && path.length > 0);
@@ -9665,7 +9727,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9665
9727
  * check.
9666
9728
  */
9667
9729
  typecheckTarget(cwd) {
9668
- if (existsSync31(join39(cwd, "tsconfig.json"))) return "tsconfig.json";
9730
+ if (existsSync32(join40(cwd, "tsconfig.json"))) return "tsconfig.json";
9669
9731
  const target = resolveTsconfigTarget(cwd);
9670
9732
  return target.reason === "none" ? null : target.path;
9671
9733
  }
@@ -9870,7 +9932,7 @@ function replaceLines(current, replacements) {
9870
9932
  }
9871
9933
 
9872
9934
  // src/core/apply-plan.ts
9873
- import { existsSync as existsSync32, readFileSync as readFileSync33, renameSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
9935
+ import { existsSync as existsSync33, readFileSync as readFileSync33, renameSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
9874
9936
  import { resolve as resolve7, sep } from "path";
9875
9937
  import { applyEdits as applyEdits3, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
9876
9938
 
@@ -9889,7 +9951,7 @@ function resolveWithinRoot(cwd, relativePath) {
9889
9951
  return absolutePath;
9890
9952
  }
9891
9953
  function readIfExists(absolutePath) {
9892
- return existsSync32(absolutePath) ? readFileSync33(absolutePath, "utf8") : void 0;
9954
+ return existsSync33(absolutePath) ? readFileSync33(absolutePath, "utf8") : void 0;
9893
9955
  }
9894
9956
  function* leaves(value, prefix = []) {
9895
9957
  for (const [key, keyValue] of Object.entries(value)) {
@@ -10007,8 +10069,8 @@ function applyPlan(cwd, plan4) {
10007
10069
  }
10008
10070
 
10009
10071
  // src/core/config/preset-evidence.ts
10010
- import { existsSync as existsSync33, readdirSync as readdirSync9, readFileSync as readFileSync34 } from "fs";
10011
- import { join as join40 } from "path";
10072
+ import { existsSync as existsSync34, readdirSync as readdirSync9, readFileSync as readFileSync34 } from "fs";
10073
+ import { join as join41 } from "path";
10012
10074
  var PATH_SIGNALS = [
10013
10075
  {
10014
10076
  preset: "nest",
@@ -10042,12 +10104,12 @@ function sourceImports(cwd, pattern) {
10042
10104
  }
10043
10105
  for (const entry of entries) {
10044
10106
  if (entry.isDirectory()) {
10045
- if (!NOT_SCANNED.has(entry.name)) stack.push(join40(dir, entry.name));
10107
+ if (!NOT_SCANNED.has(entry.name)) stack.push(join41(dir, entry.name));
10046
10108
  continue;
10047
10109
  }
10048
10110
  if (!SOURCE_FILE2.test(entry.name)) continue;
10049
10111
  try {
10050
- if (pattern.test(readFileSync34(join40(dir, entry.name), "utf8"))) return true;
10112
+ if (pattern.test(readFileSync34(join41(dir, entry.name), "utf8"))) return true;
10051
10113
  } catch {
10052
10114
  continue;
10053
10115
  }
@@ -10056,8 +10118,8 @@ function sourceImports(cwd, pattern) {
10056
10118
  return false;
10057
10119
  }
10058
10120
  function dependencyNames(cwd) {
10059
- const path = join40(cwd, "package.json");
10060
- if (!existsSync33(path)) return [];
10121
+ const path = join41(cwd, "package.json");
10122
+ if (!existsSync34(path)) return [];
10061
10123
  try {
10062
10124
  const manifest = parseJsonc(
10063
10125
  readFileSync34(path, "utf8"),
@@ -10083,7 +10145,7 @@ function declaresJsx(cwd) {
10083
10145
  for (const name of entries) {
10084
10146
  try {
10085
10147
  const config = parseJsonc(
10086
- readFileSync34(join40(cwd, name), "utf8"),
10148
+ readFileSync34(join41(cwd, name), "utf8"),
10087
10149
  name
10088
10150
  );
10089
10151
  if (config.compilerOptions?.jsx !== void 0) return true;
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  registerAdapters,
8
8
  resolve,
9
9
  setDefaultRunner
10
- } from "./chunk-I64KRL6O.js";
10
+ } from "./chunk-AQGNE3UO.js";
11
11
  import "./chunk-WLFE5RUU.js";
12
12
  export {
13
13
  BaseAdapter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hublo/sentinel",
3
- "version": "1.4.0-alpha.17",
3
+ "version": "1.4.0-alpha.19",
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",