@rtorcato/repo-tooling 3.8.2 → 3.8.3

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.
@@ -24,13 +24,17 @@ export async function checkNestedLanguages(dir, rootLanguage) {
24
24
  hint: 'Run doctor with `--directory <path>` to audit a nested project on its own',
25
25
  };
26
26
  }
27
+ /** Applies a `FileCheck` matcher of either form to a file's contents. */
28
+ export function matches(matcher, contents) {
29
+ return typeof matcher === 'function' ? matcher(contents) : matcher.test(contents);
30
+ }
27
31
  export async function checkFile(dir, spec) {
28
32
  for (const candidate of spec.candidates) {
29
33
  const filepath = path.join(dir, candidate);
30
34
  if (!(await fs.pathExists(filepath)))
31
35
  continue;
32
36
  const contents = await fs.readFile(filepath, 'utf-8');
33
- if (spec.matcher.test(contents)) {
37
+ if (matches(spec.matcher, contents)) {
34
38
  return {
35
39
  check: spec.check,
36
40
  status: 'ok',
@@ -42,6 +42,53 @@ export function evaluateNodeVersion(version) {
42
42
  detail: display,
43
43
  };
44
44
  }
45
+ /**
46
+ * JSONC → object, or null when it genuinely won't parse. `biome.jsonc` is a
47
+ * declared candidate and the format allows comments and trailing commas, so
48
+ * bare `JSON.parse` would reject configs Biome itself accepts.
49
+ *
50
+ * The first alternative consumes whole string literals, so a `//` or `/*`
51
+ * inside one (a `$schema` URL, most obviously) is never mistaken for a comment.
52
+ */
53
+ function parseJsonc(text) {
54
+ const withoutComments = text.replace(/("(?:\\.|[^"\\])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\//g, (_, str) => str ?? '');
55
+ try {
56
+ return JSON.parse(withoutComments.replace(/,(\s*[}\]])/g, '$1'));
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ const BIOME_PRESET_REF = /@rtorcato\/(?:js|repo)-tooling\/biome/;
63
+ /**
64
+ * Both shapes of a `biome.json` this package produces (#378): the thin
65
+ * `extends` pointer `fix biome` writes, and the whole preset inlined, which is
66
+ * what `copy biome` drops — it carries no `extends` at all, so matching only
67
+ * the first left every `copy biome` repo permanently drifted with no way out.
68
+ *
69
+ * Read structurally, not as text. Accepting the inline form on the presence of
70
+ * a `$schema` URL and a `preset` key alone would pass a config that keeps both
71
+ * markers and turns the linter off — the very drift this check exists to catch
72
+ * — and the markers could even sit in a `biome.jsonc` comment. So the config is
73
+ * parsed, `linter.enabled: false` disqualifies either shape, and anything
74
+ * unparseable counts as drift rather than being waved through.
75
+ */
76
+ function matchesBiomeConfig(contents) {
77
+ const config = parseJsonc(contents);
78
+ if (!config)
79
+ return false;
80
+ const linter = config.linter;
81
+ if (linter?.enabled === false)
82
+ return false;
83
+ if (BIOME_PRESET_REF.test(JSON.stringify(config.extends ?? '')))
84
+ return true;
85
+ // The inlined preset: biome's own `$schema` plus the `linter.rules.preset`
86
+ // key the shipped `tooling/biome/biome.json` sets.
87
+ const schema = config.$schema;
88
+ return (typeof schema === 'string' &&
89
+ schema.includes('biomejs.dev/schemas/') &&
90
+ linter?.rules?.preset !== undefined);
91
+ }
45
92
  export const FILE_CHECKS = [
46
93
  {
47
94
  check: 'TypeScript',
@@ -53,11 +100,9 @@ export const FILE_CHECKS = [
53
100
  {
54
101
  check: 'Biome',
55
102
  candidates: ['biome.json', 'biome.jsonc'],
56
- expected: `extends "${PACKAGE}/biome"`,
57
- matcher: /@rtorcato\/(?:js|repo)-tooling\/biome/,
103
+ expected: `extends "${PACKAGE}/biome" or inlines the preset, with the linter on`,
104
+ matcher: matchesBiomeConfig,
58
105
  optional: true,
59
- // `fix biome`, not `copy biome`: copy drops the whole preset inline, which
60
- // carries no `extends` for the matcher above to find (#365).
61
106
  hint: 'Run `npx @rtorcato/repo-tooling fix biome` to scaffold',
62
107
  },
63
108
  {
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import path from 'node:path';
20
20
  import fs from 'fs-extra';
21
- import { checkFile } from '../../base/checks.js';
21
+ import { checkFile, matches } from '../../base/checks.js';
22
22
  import { PYTHON_GITIGNORE_SENTINELS } from './gitignore.js';
23
23
  import { PYTHON_HOOKS_DIR } from './git-hooks.js';
24
24
  const PYTHON_TOOL_CHECKS = [
@@ -68,7 +68,7 @@ async function checkPythonTool(dir, spec) {
68
68
  const filepath = path.join(dir, candidate);
69
69
  if (!(await fs.pathExists(filepath)))
70
70
  continue;
71
- if (spec.dedicated.matcher.test(await fs.readFile(filepath, 'utf-8'))) {
71
+ if (matches(spec.dedicated.matcher, await fs.readFile(filepath, 'utf-8'))) {
72
72
  return {
73
73
  check: spec.dedicated.check,
74
74
  status: 'ok',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rtorcato/repo-tooling",
3
- "version": "3.8.2",
3
+ "version": "3.8.3",
4
4
  "description": "One CLI to scaffold, audit and fix your repo's whole toolchain — linting, tests, commits, releases & CI.",
5
5
  "type": "module",
6
6
  "keywords": [