@hublo/sentinel 1.4.0-alpha.18 → 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-ICGZ3HCR.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";
@@ -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,13 +8493,14 @@ 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
- if (readFormatAdoption(context.cwd).configFile !== null) {
8496
+ const formatter = writtenFileFormatter(context.cwd);
8497
+ if (formatter !== void 0) {
8450
8498
  notes.push(
8451
- `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.`
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.`
8452
8500
  );
8453
8501
  } else {
8454
8502
  notes.push(
8455
- `this module has no formatter config, so what was written keeps the shape it was written in, and the Vitest type imports sit beside the existing ones rather than in the position your \`import/order\` wants. 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.`
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.`
8456
8504
  );
8457
8505
  }
8458
8506
  notes.push(
@@ -8601,9 +8649,9 @@ function plan3(context) {
8601
8649
  // src/roles/test/reference.ts
8602
8650
  import { execFileSync } from "child_process";
8603
8651
  import { createHash } from "crypto";
8604
- 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";
8605
8653
  import { tmpdir } from "os";
8606
- import { join as join36, resolve as resolve5 } from "path";
8654
+ import { join as join37, resolve as resolve5 } from "path";
8607
8655
 
8608
8656
  // src/roles/test/baseline.ts
8609
8657
  function readSnapshot(report) {
@@ -8847,22 +8895,22 @@ function describeComparison(comparison) {
8847
8895
  // src/roles/test/reference.ts
8848
8896
  function referencePath(cwd) {
8849
8897
  const key = createHash("sha256").update(resolve5(cwd)).digest("hex").slice(0, 16);
8850
- return join36(tmpdir(), "sentinel-test-reference", `${key}.json`);
8898
+ return join37(tmpdir(), "sentinel-test-reference", `${key}.json`);
8851
8899
  }
8852
8900
  function runJest(cwd, config) {
8853
8901
  const bin = resolveBin(cwd, "jest");
8854
8902
  if (bin === void 0) return void 0;
8855
8903
  const root = findWorkspaceRoot(cwd) ?? cwd;
8856
- const out = join36(mkdtempSync(join36(tmpdir(), "sentinel-reference-")), "jest.json");
8904
+ const out = join37(mkdtempSync(join37(tmpdir(), "sentinel-reference-")), "jest.json");
8857
8905
  try {
8858
- execFileSync(bin, ["-c", join36(cwd, config), "--json", `--outputFile=${out}`, "--silent"], {
8906
+ execFileSync(bin, ["-c", join37(cwd, config), "--json", `--outputFile=${out}`, "--silent"], {
8859
8907
  cwd: root,
8860
8908
  stdio: ["ignore", "ignore", "ignore"],
8861
8909
  maxBuffer: 1 << 28
8862
8910
  });
8863
8911
  } catch {
8864
8912
  }
8865
- if (!existsSync30(out)) return void 0;
8913
+ if (!existsSync31(out)) return void 0;
8866
8914
  try {
8867
8915
  return JSON.parse(readFileSync29(out, "utf8"));
8868
8916
  } catch {
@@ -8892,7 +8940,7 @@ function captureReference(cwd, config) {
8892
8940
  snapshot
8893
8941
  };
8894
8942
  const path = referencePath(cwd);
8895
- mkdirSync(join36(path, ".."), { recursive: true });
8943
+ mkdirSync(join37(path, ".."), { recursive: true });
8896
8944
  writeFileSync3(path, `${JSON.stringify(stored, void 0, 2)}
8897
8945
  `);
8898
8946
  return {
@@ -8901,7 +8949,7 @@ function captureReference(cwd, config) {
8901
8949
  };
8902
8950
  }
8903
8951
  function hasReference(cwd) {
8904
- return existsSync30(referencePath(cwd));
8952
+ return existsSync31(referencePath(cwd));
8905
8953
  }
8906
8954
  function compareAgainstReference(cwd, report) {
8907
8955
  const path = referencePath(cwd);
@@ -8929,6 +8977,7 @@ function resolveVitest(cwd) {
8929
8977
  return fromModule !== void 0 ? { bin: fromModule, origin: "module" } : { bin: void 0, origin: "module" };
8930
8978
  }
8931
8979
  var FORMATTABLE = /\.(m|c)?(j|t)sx?$|\.json$/;
8980
+ var FORMAT_BATCH = 25;
8932
8981
  var VitestAdapter = class extends BaseAdapter {
8933
8982
  target = "test";
8934
8983
  runner = "vitest";
@@ -8998,20 +9047,27 @@ var VitestAdapter = class extends BaseAdapter {
8998
9047
  async afterInit(ctx) {
8999
9048
  const written = (ctx.written ?? []).filter((file) => FORMATTABLE.test(file));
9000
9049
  if (written.length === 0) return Promise.resolve();
9001
- if (readFormatAdoption(ctx.cwd).configFile === null) {
9050
+ const formatter = writtenFileFormatter(ctx.cwd);
9051
+ if (formatter === void 0) {
9002
9052
  this.say(
9003
- `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.`
9004
9054
  );
9005
9055
  return Promise.resolve();
9006
9056
  }
9007
- const oxfmt = resolveOxfmt(ctx.cwd);
9008
- if (oxfmt === void 0) return Promise.resolve();
9009
- 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
+ }
9010
9066
  this.say(
9011
- 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.` : (
9012
9068
  // Adoption SUCCEEDED; only the tidy-up did not. Failing here would revert a migration
9013
9069
  // over a formatting pass, which is the wrong trade.
9014
- `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.`
9015
9071
  )
9016
9072
  );
9017
9073
  return Promise.resolve();
@@ -9066,7 +9122,7 @@ var VitestAdapter = class extends BaseAdapter {
9066
9122
  const { cwd: runFrom, options, paths } = fromWorkspaceRoot(ctx.cwd, typedOptions, typedPaths);
9067
9123
  const watching = options.includes("--watch") || options.includes("-w");
9068
9124
  const proving = !watching && hasReference(ctx.cwd);
9069
- 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;
9070
9126
  const reporters = reportFile === void 0 ? [] : ["--reporter=default", "--reporter=json", `--outputFile=${reportFile}`];
9071
9127
  const argv = watching ? [...options, ...paths] : ["run", ...reporters, ...options, ...paths];
9072
9128
  const result = spawnSync5(bin, argv, { cwd: runFrom, stdio: "inherit" });
@@ -9131,9 +9187,9 @@ function registerTest() {
9131
9187
 
9132
9188
  // src/roles/typescript/adapters/tsc/tsc.adapter.ts
9133
9189
  import { spawnSync as spawnSync6 } from "child_process";
9134
- import { existsSync as existsSync31, readFileSync as readFileSync32 } from "fs";
9190
+ import { existsSync as existsSync32, readFileSync as readFileSync32 } from "fs";
9135
9191
  import { createRequire as createRequire7 } from "module";
9136
- import { join as join39 } from "path";
9192
+ import { join as join40 } from "path";
9137
9193
 
9138
9194
  // src/roles/typescript/presets/nest.json
9139
9195
  var nest_default2 = {
@@ -9315,7 +9371,7 @@ function readTsconfigAdoption(cwd) {
9315
9371
 
9316
9372
  // src/roles/typescript/adapters/tsc/plan.ts
9317
9373
  import { readFileSync as readFileSync31 } from "fs";
9318
- import { join as join38 } from "path";
9374
+ import { join as join39 } from "path";
9319
9375
 
9320
9376
  // src/roles/typescript/typecheck-script.ts
9321
9377
  var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
@@ -9408,7 +9464,7 @@ function planAdoption(context) {
9408
9464
  };
9409
9465
  }
9410
9466
  const existing = parseJsonc(
9411
- readFileSync31(join38(context.cwd, target.path), "utf8"),
9467
+ readFileSync31(join39(context.cwd, target.path), "utf8"),
9412
9468
  target.path
9413
9469
  );
9414
9470
  const extendsChain = composeExtends(existing.extends, preset);
@@ -9546,7 +9602,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9546
9602
  let chain;
9547
9603
  try {
9548
9604
  const parsed = parseJsonc(
9549
- readFileSync32(join39(cwd, target.path), "utf8"),
9605
+ readFileSync32(join40(cwd, target.path), "utf8"),
9550
9606
  target.path
9551
9607
  );
9552
9608
  chain = parsed.extends;
@@ -9559,7 +9615,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9559
9615
  );
9560
9616
  if (preset === void 0) return void 0;
9561
9617
  try {
9562
- createRequire7(join39(cwd, "noop.js")).resolve(preset);
9618
+ createRequire7(join40(cwd, "noop.js")).resolve(preset);
9563
9619
  return void 0;
9564
9620
  } catch {
9565
9621
  return preset;
@@ -9655,7 +9711,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9655
9711
  referencedProjects(cwd, config) {
9656
9712
  try {
9657
9713
  const parsed = parseJsonc(
9658
- readFileSync32(join39(cwd, config), "utf8"),
9714
+ readFileSync32(join40(cwd, config), "utf8"),
9659
9715
  config
9660
9716
  );
9661
9717
  const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path) => typeof path === "string" && path.length > 0);
@@ -9671,7 +9727,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
9671
9727
  * check.
9672
9728
  */
9673
9729
  typecheckTarget(cwd) {
9674
- if (existsSync31(join39(cwd, "tsconfig.json"))) return "tsconfig.json";
9730
+ if (existsSync32(join40(cwd, "tsconfig.json"))) return "tsconfig.json";
9675
9731
  const target = resolveTsconfigTarget(cwd);
9676
9732
  return target.reason === "none" ? null : target.path;
9677
9733
  }
@@ -9876,7 +9932,7 @@ function replaceLines(current, replacements) {
9876
9932
  }
9877
9933
 
9878
9934
  // src/core/apply-plan.ts
9879
- 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";
9880
9936
  import { resolve as resolve7, sep } from "path";
9881
9937
  import { applyEdits as applyEdits3, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
9882
9938
 
@@ -9895,7 +9951,7 @@ function resolveWithinRoot(cwd, relativePath) {
9895
9951
  return absolutePath;
9896
9952
  }
9897
9953
  function readIfExists(absolutePath) {
9898
- return existsSync32(absolutePath) ? readFileSync33(absolutePath, "utf8") : void 0;
9954
+ return existsSync33(absolutePath) ? readFileSync33(absolutePath, "utf8") : void 0;
9899
9955
  }
9900
9956
  function* leaves(value, prefix = []) {
9901
9957
  for (const [key, keyValue] of Object.entries(value)) {
@@ -10013,8 +10069,8 @@ function applyPlan(cwd, plan4) {
10013
10069
  }
10014
10070
 
10015
10071
  // src/core/config/preset-evidence.ts
10016
- import { existsSync as existsSync33, readdirSync as readdirSync9, readFileSync as readFileSync34 } from "fs";
10017
- 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";
10018
10074
  var PATH_SIGNALS = [
10019
10075
  {
10020
10076
  preset: "nest",
@@ -10048,12 +10104,12 @@ function sourceImports(cwd, pattern) {
10048
10104
  }
10049
10105
  for (const entry of entries) {
10050
10106
  if (entry.isDirectory()) {
10051
- 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));
10052
10108
  continue;
10053
10109
  }
10054
10110
  if (!SOURCE_FILE2.test(entry.name)) continue;
10055
10111
  try {
10056
- if (pattern.test(readFileSync34(join40(dir, entry.name), "utf8"))) return true;
10112
+ if (pattern.test(readFileSync34(join41(dir, entry.name), "utf8"))) return true;
10057
10113
  } catch {
10058
10114
  continue;
10059
10115
  }
@@ -10062,8 +10118,8 @@ function sourceImports(cwd, pattern) {
10062
10118
  return false;
10063
10119
  }
10064
10120
  function dependencyNames(cwd) {
10065
- const path = join40(cwd, "package.json");
10066
- if (!existsSync33(path)) return [];
10121
+ const path = join41(cwd, "package.json");
10122
+ if (!existsSync34(path)) return [];
10067
10123
  try {
10068
10124
  const manifest = parseJsonc(
10069
10125
  readFileSync34(path, "utf8"),
@@ -10089,7 +10145,7 @@ function declaresJsx(cwd) {
10089
10145
  for (const name of entries) {
10090
10146
  try {
10091
10147
  const config = parseJsonc(
10092
- readFileSync34(join40(cwd, name), "utf8"),
10148
+ readFileSync34(join41(cwd, name), "utf8"),
10093
10149
  name
10094
10150
  );
10095
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-ICGZ3HCR.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.18",
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",