Drop the mermaid import from the markdown module. The extension only emits <pre class="mermaid">, so initialize mermaid in the app entry instead. Use explicit .js specifiers plus a bundler extensionAlias so both the NodeNext CLI build and the web build resolve the sources.
88 lines
3.0 KiB
TypeScript
88 lines
3.0 KiB
TypeScript
import type { MarkedExtension } from "marked";
|
|
import yaml from "js-yaml";
|
|
import { parseBlockAttrs } from "./block-attrs.js";
|
|
|
|
/**
|
|
* YAML-defined tag blocks:
|
|
*
|
|
* ```yaml role=tag tag=md-deck id=my-deck
|
|
* body: ./cards.csv
|
|
* data-config: ...
|
|
* ```
|
|
*
|
|
* `role=tag` on the info string marks the block (and lets the content
|
|
* scanner keep it intact). `tag=` / `id=` / any other `key=value` fence
|
|
* attributes override the same keys in the YAML body, so simple cases
|
|
* stay on one line.
|
|
*/
|
|
export default function markedCodeBlockYamlTag(): MarkedExtension {
|
|
return {
|
|
extensions: [
|
|
{
|
|
name: "code-block-yaml-tag",
|
|
level: "block",
|
|
start(src: string) {
|
|
return src.match(/^```yaml\s+role=tag(?:\s|\n)/m)?.index;
|
|
},
|
|
tokenizer(src: string) {
|
|
const rule = /^```yaml\s+role=tag([^\n]*)\n([\s\S]*?)\n```/;
|
|
const match = rule.exec(src);
|
|
if (match) {
|
|
const yamlContent = match[2]?.trim() || "";
|
|
let yamlProps: Record<string, unknown> = {};
|
|
try {
|
|
yamlProps =
|
|
(yaml.load(yamlContent) as Record<string, unknown>) || {};
|
|
} catch (e) {
|
|
console.error("YAML Parse Error in code-block-yaml-tag:", e);
|
|
yamlProps = { error: "Invalid YAML content" };
|
|
}
|
|
|
|
// Fence attributes override YAML body values.
|
|
const attrs = parseBlockAttrs(match[1] || "");
|
|
const fenceProps: Record<string, unknown> = {
|
|
...(attrs.id ? { id: attrs.id } : {}),
|
|
...attrs.extra,
|
|
};
|
|
const props: Record<string, unknown> = { ...yamlProps, ...fenceProps };
|
|
|
|
const tagName = (props.tag as string) || "tag-unknown";
|
|
const { tag, ...rest } = props;
|
|
|
|
let content = "";
|
|
if ("body" in rest) {
|
|
content = String(rest.body || "");
|
|
delete (rest as Record<string, unknown>).body;
|
|
}
|
|
|
|
// `data-*` props may hold structured YAML values, so they are
|
|
// serialized to JSON strings (e.g. md-deck layers/templates).
|
|
const propsStr = Object.entries(rest)
|
|
.map(([key, value]) => {
|
|
const strValue = key.startsWith("data-")
|
|
? JSON.stringify(value)
|
|
: String(value);
|
|
if (strValue.includes(" ") || strValue.includes('"')) {
|
|
return `${key}="${strValue.replace(/"/g, """)}"`;
|
|
}
|
|
return `${key}="${strValue}"`;
|
|
})
|
|
.join(" ");
|
|
|
|
return {
|
|
type: "code-block-yaml-tag",
|
|
raw: match[0],
|
|
tagName,
|
|
props: propsStr,
|
|
content,
|
|
};
|
|
}
|
|
},
|
|
renderer(token: any) {
|
|
const propsAttr = token.props ? ` ${token.props}` : "";
|
|
return `<${token.tagName}${propsAttr}>${token.content || ""}</${token.tagName}>\n`;
|
|
},
|
|
},
|
|
],
|
|
};
|
|
} |