71 lines
2.5 KiB
TypeScript
71 lines
2.5 KiB
TypeScript
/**
|
|
* Shared block scanning utilities — safe for both CLI and browser.
|
|
*
|
|
* Parses fenced code blocks with attributes:
|
|
* ```lang id=xxx role=xxx as=xxx
|
|
*
|
|
* - `role` dispatches to content scanners (declare, spark-table)
|
|
* - `as` controls rendering (none, codeblock, md-table, md-card, md-dice)
|
|
* - `id` is used for cross-references (file paths)
|
|
*/
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Types
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface BlockAttrs {
|
|
lang: string;
|
|
id?: string;
|
|
role?: string;
|
|
as?: string;
|
|
/** Any other attributes not in the standard set */
|
|
extra: Record<string, string>;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Regex
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** Matches fenced code blocks with an info string (at least one attr). */
|
|
export const FENCED_BLOCK_RE = /^```(\w*)\s+(\S.*?)\s*\n([\s\S]*?)```\s*$/gm;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Attribute parsing
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** Parse key="value" and key=value pairs from an attribute string. */
|
|
export function parseBlockAttrs(info: string): BlockAttrs {
|
|
const attrs: Record<string, string> = {};
|
|
const re = /(\w+)\s*=\s*("[^"]*"|\S+)/g;
|
|
let m: RegExpExecArray | null;
|
|
while ((m = re.exec(info)) !== null) {
|
|
attrs[m[1]] = m[2].replace(/^"|"$/g, "");
|
|
}
|
|
|
|
const { lang, id, role, as, ...extra } = attrs;
|
|
return { lang: lang || "", id, role, as, extra };
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// as resolution
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Determine the effective `as` value.
|
|
*
|
|
* Defaults:
|
|
* - role is set, no explicit as → "none" (strip — it's metadata)
|
|
* - role=spark-table → "md-table" (render as md-table directive)
|
|
* - role=tag → "codeblock" (kept for the marked yaml-tag extension)
|
|
* - no role, no as → "codeblock" (keep as visible code block)
|
|
*/
|
|
export function resolveBlockAs(role: string | undefined, as: string | undefined): string {
|
|
if (as) return as;
|
|
// spark-table blocks render as md-table by default
|
|
if (role === "spark-table") return "md-table";
|
|
// yaml-defined tag blocks must survive stripping so the
|
|
// code-block-yaml-tag marked extension can render them
|
|
if (role === "tag") return "codeblock";
|
|
if (role) return "none";
|
|
return "codeblock";
|
|
} |