mirror of
https://github.com/CharlesWiltgen/Axiom.git
synced 2026-09-20 19:58:20 +08:00
c689651ff4
The 12i check matched a single wrong-separator char then whitespace, so a double hyphen ` -- ` escaped — the second hyphen sat where \s was expected. Same blind spot one level below the single-hyphen fix the check's comment brags about. Quantify the class (WRONG_SEP+) so runs of 2+ are caught; add double/triple-hyphen fixtures, keep the -5/-v regression guards. Closes Axiom-b6p.
89 lines
3.7 KiB
TypeScript
89 lines
3.7 KiB
TypeScript
/**
|
||
* Docs dash-convention detection (pre-deploy check 12i).
|
||
*
|
||
* Enforces `.claude/rules/documentation-style.md` §Dashes: a *list-led inline-heading
|
||
* separator* — a `**bold**` / `[link](url)` / `` `code` `` head at the start of a bullet
|
||
* or numbered list item, immediately followed by the separator — must use a spaced
|
||
* EN-dash " – " (U+2013). Running prose keeps the spaced em-dash, so the anchored
|
||
* pattern only ever looks at the separator position and never touches prose.
|
||
*
|
||
* Extracted from pre-deploy.ts so the pattern is unit-testable. It previously lived
|
||
* inline and untested, and flagged ONLY the em-dash — so `- **Label** - desc` (ASCII
|
||
* hyphen) passed silently and six violations shipped on a docs page, past the very gate
|
||
* meant to catch them. See docs-dashes.test.ts.
|
||
*
|
||
* Scope: `docs/` only. For-LLM skill files are exempt by design (not human reading
|
||
* material) — they carry ~1,271 such separators and scanning them is meaningless.
|
||
*/
|
||
|
||
/**
|
||
* Every WRONG separator, enumerated.
|
||
*
|
||
* U+2013 EN DASH is the ONLY legal one here, so enumerate the whole complement rather
|
||
* than adding wrong dashes one at a time — that incremental habit is what let the ASCII
|
||
* hyphen through. Covers: ASCII hyphen, U+2010 HYPHEN, U+2011 NON-BREAKING HYPHEN,
|
||
* U+2012 FIGURE DASH, U+2014 EM DASH, U+2015 HORIZONTAL BAR, U+2212 MINUS SIGN.
|
||
*
|
||
* The hyphen is ESCAPED deliberately. A trailing `-` in a character class is literal, so
|
||
* an unescaped `[—-]` happens to work — but appending the Unicode minus (the obvious next
|
||
* edit) gives `[—-−]`, a silent RANGE U+2014–U+2212 matching ~510 code points. Escaping
|
||
* makes the class order- and insertion-proof.
|
||
*/
|
||
const WRONG_SEP = "[\\-\\u2010\\u2011\\u2012\\u2014\\u2015\\u2212]";
|
||
|
||
/**
|
||
* The trailing `\s` is LOAD-BEARING: it is what excludes negative numbers and CLI flags
|
||
* (`- **Delta** -5 degrees`, `- **Usage** -v for verbose`). Do not "tidy" it away, or
|
||
* every such line starts blocking commits.
|
||
*
|
||
* Matches inline links in the `[text](url)` form only — not reference links
|
||
* (`[text][ref]`). That is exhaustive for VitePress docs; widen the alternation if
|
||
* reference-link heads ever appear.
|
||
*/
|
||
export const dashSepPattern = new RegExp(
|
||
"^\\s*(?:[-*]|\\d+\\.)\\s+(?:\\*\\*[^*]+\\*\\*|\\[[^\\]]+\\]\\([^)]+\\)|`[^`]+`)\\s+" +
|
||
// `+` matches a RUN of wrong separators, so ` -- ` / ` --- ` are caught, not just a
|
||
// single wrong char — the single-char form let ` -- ` through the very check hardened
|
||
// for the single hyphen. The trailing `\s` still excludes `-5` / `-v`. (Axiom-b6p)
|
||
WRONG_SEP + "+\\s",
|
||
);
|
||
|
||
/** True when a single line is a list-led inline-heading separator using the wrong dash. */
|
||
export function isDashViolation(line: string): boolean {
|
||
return dashSepPattern.test(line);
|
||
}
|
||
|
||
/**
|
||
* Scan one file's contents, returning the 1-indexed line numbers that violate the rule.
|
||
* Skips fenced code blocks and YAML frontmatter — a bullet inside a ```markdown example
|
||
* is illustrative, not prose, and must not be flagged.
|
||
*/
|
||
export function findDashViolations(contents: string): number[] {
|
||
const lines = contents.split("\n");
|
||
const violations: number[] = [];
|
||
let inFence = false;
|
||
let inFrontmatter = false;
|
||
|
||
for (let i = 0; i < lines.length; i++) {
|
||
const line = lines[i];
|
||
|
||
if (i === 0 && line.trim() === "---") {
|
||
inFrontmatter = true;
|
||
continue;
|
||
}
|
||
if (inFrontmatter) {
|
||
if (line.trim() === "---") inFrontmatter = false;
|
||
continue;
|
||
}
|
||
const trimmed = line.trim();
|
||
if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) {
|
||
inFence = !inFence;
|
||
continue;
|
||
}
|
||
if (inFence) continue;
|
||
|
||
if (isDashViolation(line)) violations.push(i + 1);
|
||
}
|
||
return violations;
|
||
}
|