Skip to content

Commit 989660d

Browse files
committed
refactor(layering): keep the rule catalog free of a cutover-policy helper
The catalog walked the AST with visitAst from record-runtime-policy-ast.ts, which #1745 renames to cutover-policy-ast.ts -- verified by trial merge: the import fails to resolve there. A guard-integrity check that breaks when a neighbouring policy is renamed is the fragility this rule exists to remove, and a rule catalog has no business depending on a cutover-specific module, so it collects string literals locally instead.
1 parent b457bbb commit 989660d

1 file changed

Lines changed: 25 additions & 8 deletions

File tree

scripts/layering/rule-catalog.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,29 @@
1010
import fs from 'node:fs';
1111
import path from 'node:path';
1212
import { parseSync } from 'oxc-parser';
13-
import { visitAst } from './record-runtime-policy-ast.ts';
1413

1514
/** A rule identifier exactly as the report emits it: a number and one kebab-case name. */
1615
const RULE_IDENTIFIER = /^R(\d+) ([a-z0-9]+(?:-[a-z0-9]+)*)$/;
1716

17+
/**
18+
* Local, so the integrity check does not fail with a neighbouring policy's helper: the shared
19+
* walker lives in a cutover-specific module that #1745 renames, and a gate that breaks when a
20+
* neighbour is renamed is the fragility this rule exists to remove.
21+
*/
22+
function stringLiterals(node: unknown, into: string[]): string[] {
23+
if (node === null || typeof node !== 'object') return into;
24+
if (Array.isArray(node)) {
25+
for (const child of node) stringLiterals(child, into);
26+
return into;
27+
}
28+
const record = node as Record<string, unknown>;
29+
if (record['type'] === 'Literal' && typeof record['value'] === 'string') {
30+
into.push(record['value']);
31+
}
32+
for (const child of Object.values(record)) stringLiterals(child, into);
33+
return into;
34+
}
35+
1836
export type RuleDeclaration = Readonly<{ number: number; name: string; file: string }>;
1937
export type RuleNamespaceCollision = Readonly<{ number: number; names: readonly string[] }>;
2038
export type LayeringPolicySource = Readonly<{ path: string; source: string }>;
@@ -32,15 +50,14 @@ export function ruleDeclarations(
3250
const seen = new Set<string>();
3351
for (const file of sources) {
3452
const parsed = parseSync(file.path, file.source);
35-
visitAst(parsed.program.body, (node) => {
36-
if (node['type'] !== 'Literal' || typeof node['value'] !== 'string') return;
37-
const match = RULE_IDENTIFIER.exec(node['value']);
38-
if (!match) return;
39-
const key = `${file.path} ${node['value']}`;
40-
if (seen.has(key)) return;
53+
for (const value of stringLiterals(parsed.program.body, [])) {
54+
const match = RULE_IDENTIFIER.exec(value);
55+
if (!match) continue;
56+
const key = `${file.path} ${value}`;
57+
if (seen.has(key)) continue;
4158
seen.add(key);
4259
declarations.push({ number: Number(match[1]), name: String(match[2]), file: file.path });
43-
});
60+
}
4461
}
4562
return declarations;
4663
}

0 commit comments

Comments
 (0)