The dialect's outline
syntax.md read as data: each construct by the id on the {#id} line above its heading, and
the Not supported rows by warning code. The grammar says which constructs exist and where they
belong; this reads what the page says about them, so the tests can hold the two together. Nothing
here depends on wording: headings and cells can be reworded as long as the ids and the codes
stay.
import syntax from "../../docs/syntax.md?raw";
import { type WarningCode } from "../../src/index";
export type Part = "Metadata" | "Block" | "Inline" | "Not supported";
/** A construct as `syntax.md` presents it. */
export interface Anchor {
id: string;
title: string;
/** The `##` section it sits under. */
part: string;
}
export interface Row {
code: WarningCode;
/** The Syntax cell. */
syntax: string;
instead: string;
/** Metadata, block or inline forms. */
group: string;
}
export const anchors: Anchor[] = [];
{
const lines = syntax.split("\n");
let fence = "";
let part = "";
let id: string | null = null;
for (let i = 0; i < lines.length; i++) {
const line = lines[i]!;
const ticks = /^(`{3,})/.exec(line)?.[1];
if (fence) {
if (ticks && ticks.length >= fence.length && line.trim() === ticks) fence = "";
continue;
}
if (ticks) fence = ticks;
else if (/^\{#[\w-]+\}$/.test(line)) id = line.slice(2, -1);
else if (/^#{2,3} /.test(line)) {
const title = line.replace(/^#+ /, "");
if (line.startsWith("## ")) part = title;
if (id) anchors.push({ id, title, part });
id = null;
}
}
}
export const anchor = (id: string): Anchor | undefined => anchors.find((a) => a.id === id);
// Between two known headings, since the samples in syntax.md contain `##` lines of their own.
const between = (from: string, to: string) =>
syntax.split(`\n## ${from}\n`)[1]!.split(`\n## ${to}\n`)[0]!;
export const rows: Row[] = [];
for (const block of between("Not supported", "Formatters").split(/^### /m).slice(1)) {
const group = block.slice(0, block.indexOf("\n"));
for (const line of block.split("\n")) {
if (!line.startsWith("| ") || line.startsWith("| Code") || line.startsWith("| ---")) continue;
const [code, syntax, instead] = line
.slice(2)
.split(/ +\| +/)
.map((c) => c.trim());
rows.push({
code: code!.slice(1, -1) as WarningCode,
syntax: syntax!,
instead: instead!,
group,
});
}
}
export const row = (code: string): Row | undefined => rows.find((r) => r.code === code);
/** Every warning code `syntax.md` names in backticks, in a table or a construct's prose. */
export const named = (code: string): boolean => syntax.includes(`\`${code}\``);
/** Where a section belongs: a construct's part, or Not supported for a row's code. */
export function part(section: string): Part | undefined {
const a = anchor(section);
if (a) return a.part as Part;
return row(section) ? "Not supported" : undefined;
}
/** What the site shows for a section: the construct's heading, or the row's code. */
export const title = (section: string): string => anchor(section)?.title ?? section;