feat(cli): Complete the check rule catalog
Add D002 (malformed attribute blocks), R004 (skipped declare rows), and V001-V005 (CSV data files). CSV checks run once per referenced file and also cover inline spark-table bodies. Generate docs/reference.md from the known.ts catalog via npm run gen:reference, and add drift guards asserting the catalog matches the customElement call sites and the committed reference.
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
* roles.
|
||||
*
|
||||
* This is the single source of truth for validation. `docs/reference.md` is
|
||||
* intended to be generated from it (see docs/check-plan.md §7.11); a drift
|
||||
* test asserts this list matches the `customElement(...)` call sites.
|
||||
* generated from it (see docs/check-plan.md §7.11); a drift test asserts this
|
||||
* list matches the `customElement(...)` call sites.
|
||||
*/
|
||||
|
||||
/** What a directive's body refers to, used for reference checks. */
|
||||
@@ -19,6 +19,8 @@ export interface ElementSpec {
|
||||
attrs: string[];
|
||||
summary: string;
|
||||
refKind: RefKind;
|
||||
/** Canonical usage examples, rendered into the reference. */
|
||||
examples: string[];
|
||||
}
|
||||
|
||||
/** The registered `md-*` custom elements (mirrors `customElement(...)` calls). */
|
||||
@@ -29,6 +31,11 @@ export const KNOWN_ELEMENTS: readonly ElementSpec[] = [
|
||||
attrs: ["key"],
|
||||
summary: "Clickable dice roller; `key` stores the result in the URL.",
|
||||
refKind: "none",
|
||||
examples: [
|
||||
':md-dice[1d20+5]{key="attack"}',
|
||||
":md-dice[2d6+3]",
|
||||
":md-dice[3d6kh1]",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "md-table",
|
||||
@@ -36,6 +43,11 @@ export const KNOWN_ELEMENTS: readonly ElementSpec[] = [
|
||||
attrs: ["roll", "remix"],
|
||||
summary: "Tab-separated view of a CSV with optional group tabs.",
|
||||
refKind: "csv",
|
||||
examples: [
|
||||
":md-table[./npcs.csv]",
|
||||
":md-table[./encounters.csv]{roll=true}",
|
||||
":md-table[./quests.csv]{roll=true remix=true}",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "md-deck",
|
||||
@@ -43,6 +55,9 @@ export const KNOWN_ELEMENTS: readonly ElementSpec[] = [
|
||||
attrs: ["size", "grid", "layers"],
|
||||
summary: "Card layout from a CSV (legacy; prefer `yaml role=tag`).",
|
||||
refKind: "csv",
|
||||
examples: [
|
||||
':md-deck[./cards.csv]{size="63x88" grid="5x5" layers="title:1,1-5,1f8 body:1,5-5,8f3"}',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "md-link",
|
||||
@@ -50,6 +65,7 @@ export const KNOWN_ELEMENTS: readonly ElementSpec[] = [
|
||||
attrs: [],
|
||||
summary: "Clickable link that expands the target article inline.",
|
||||
refKind: "md",
|
||||
examples: [":md-link[./rules.md]", ":md-link[./rules.md#combat]"],
|
||||
},
|
||||
{
|
||||
name: "md-embed",
|
||||
@@ -57,6 +73,7 @@ export const KNOWN_ELEMENTS: readonly ElementSpec[] = [
|
||||
attrs: ["headingBase"],
|
||||
summary: "Renders the target article or section inline.",
|
||||
refKind: "md",
|
||||
examples: [":md-embed[./rules.md#combat]{headingBase=1}"],
|
||||
},
|
||||
{
|
||||
name: "md-bg",
|
||||
@@ -64,6 +81,7 @@ export const KNOWN_ELEMENTS: readonly ElementSpec[] = [
|
||||
attrs: ["fit"],
|
||||
summary: "Sets the surrounding article card background.",
|
||||
refKind: "image",
|
||||
examples: [':md-bg[./images/dungeon-bg.jpg]{fit="cover"}', ":md-bg[#2a1a3a]"],
|
||||
},
|
||||
{
|
||||
name: "md-border",
|
||||
@@ -71,6 +89,10 @@ export const KNOWN_ELEMENTS: readonly ElementSpec[] = [
|
||||
attrs: ["width", "slice"],
|
||||
summary: "Applies a border to the article card.",
|
||||
refKind: "image",
|
||||
examples: [
|
||||
":md-border[crimson]{.b}",
|
||||
':md-border[./frame.png]{width="2mm" slice="10" .all.round}',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "md-font",
|
||||
@@ -78,6 +100,9 @@ export const KNOWN_ELEMENTS: readonly ElementSpec[] = [
|
||||
attrs: ["source", "weight", "color"],
|
||||
summary: "Sets the article font.",
|
||||
refKind: "none",
|
||||
examples: [
|
||||
':md-font[Noto Sans SC]{source="google" weight="700" color="#1a1a2e"}',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "md-pins",
|
||||
@@ -85,6 +110,10 @@ export const KNOWN_ELEMENTS: readonly ElementSpec[] = [
|
||||
attrs: ["pins", "fixed", "labelStart"],
|
||||
summary: "Numbered markers on an image.",
|
||||
refKind: "image",
|
||||
examples: [
|
||||
':md-pins[./images/battle-map.png]{pins="A:25,50 B:75,30 C:50,80" fixed}',
|
||||
':md-pins[./images/dungeon.png]{labelStart="1"}',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "md-commander",
|
||||
@@ -92,6 +121,7 @@ export const KNOWN_ELEMENTS: readonly ElementSpec[] = [
|
||||
attrs: ["placeholder", "class", "height"],
|
||||
summary: "Interactive command history / tracker panel.",
|
||||
refKind: "csv",
|
||||
examples: [':md-commander[./commands.csv]{placeholder="输入命令…" height="400px"}'],
|
||||
},
|
||||
{
|
||||
name: "md-yarn-spinner",
|
||||
@@ -99,6 +129,7 @@ export const KNOWN_ELEMENTS: readonly ElementSpec[] = [
|
||||
attrs: ["start"],
|
||||
summary: "Runs a Yarn Spinner dialogue file.",
|
||||
refKind: "yarn",
|
||||
examples: [':md-yarn-spinner[./story.yarn]{start="start"}'],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -122,15 +153,46 @@ export const COLOR_BODY_ELEMENTS: ReadonlySet<string> = new Set([
|
||||
|
||||
export interface RoleSpec {
|
||||
role: string;
|
||||
/** Example info string, e.g. "csv role=spark-table". */
|
||||
info: string;
|
||||
summary: string;
|
||||
/** Canonical fenced-block examples, rendered into the reference. */
|
||||
examples: string[];
|
||||
}
|
||||
|
||||
/** Code-block `role=` values the scanner understands. */
|
||||
export const ROLE_SPECS: readonly RoleSpec[] = [
|
||||
{ role: "spark-table", summary: "CSV (or markdown pipe table) rendered as `:md-table`." },
|
||||
{ role: "declare", summary: "Reactive variable / tag-modifier definitions." },
|
||||
{ role: "file", summary: "Stored as inline text content and stripped." },
|
||||
{ role: "tag", summary: "YAML rendered as a custom element." },
|
||||
{
|
||||
role: "spark-table",
|
||||
info: "csv role=spark-table",
|
||||
summary: "CSV (or markdown pipe table) rendered as `:md-table`.",
|
||||
examples: [
|
||||
"```csv role=spark-table\nd6,结果\n1,遭遇强盗\n2,平安无事\n```",
|
||||
"```markdown role=spark-table\n| d6 | 结果 |\n|----|------|\n| 1 | 遭遇强盗 |\n| 2 | 平安无事 |\n```",
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "declare",
|
||||
info: "csv role=declare",
|
||||
summary: "Reactive variable / tag-modifier definitions.",
|
||||
examples: [
|
||||
"```csv role=declare\ntag,threshold,key,expr\n,,$hp,$con*5+$mod_hp\n#warrior,2,$mod_hp,20\n```",
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "file",
|
||||
info: "<lang> role=file",
|
||||
summary: "Stored as inline text content and stripped.",
|
||||
examples: ["```text role=file\nStored as inline content.\n```"],
|
||||
},
|
||||
{
|
||||
role: "tag",
|
||||
info: "yaml role=tag",
|
||||
summary: "YAML rendered as a custom element.",
|
||||
examples: [
|
||||
"```yaml role=tag\ntag: md-deck\nbody: ./cards.csv\nsize: 63x88\ngrid: 5x5\n```",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const KNOWN_ROLES: ReadonlySet<string> = new Set(
|
||||
|
||||
Reference in New Issue
Block a user