fix(yaml/tag): prevent yaml role=tag from being stripped & generalize

data-config
This commit is contained in:
2026-09-03 10:39:05 +08:00
parent 5fc2bc5262
commit 80e2b5b209
3 changed files with 14 additions and 16 deletions
+4
View File
@@ -56,12 +56,16 @@ export function parseBlockAttrs(info: string): BlockAttrs {
* Defaults: * Defaults:
* - role is set, no explicit as → "none" (strip — it's metadata) * - role is set, no explicit as → "none" (strip — it's metadata)
* - role=spark-table → "md-table" (render as md-table directive) * - 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) * - no role, no as → "codeblock" (keep as visible code block)
*/ */
export function resolveBlockAs(role: string | undefined, as: string | undefined): string { export function resolveBlockAs(role: string | undefined, as: string | undefined): string {
if (as) return as; if (as) return as;
// spark-table blocks render as md-table by default // spark-table blocks render as md-table by default
if (role === "spark-table") return "md-table"; 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"; if (role) return "none";
return "codeblock"; return "codeblock";
} }
@@ -269,6 +269,10 @@ describe("resolveBlockAs", () => {
expect(resolveBlockAs("spark-table", undefined)).toBe("md-table"); expect(resolveBlockAs("spark-table", undefined)).toBe("md-table");
}); });
test("defaults to codeblock for tag role", () => {
expect(resolveBlockAs("tag", undefined)).toBe("codeblock");
});
test("defaults to codeblock when no role and no as", () => { test("defaults to codeblock when no role and no as", () => {
expect(resolveBlockAs(undefined, undefined)).toBe("codeblock"); expect(resolveBlockAs(undefined, undefined)).toBe("codeblock");
}); });
+6 -16
View File
@@ -38,21 +38,13 @@ export default function markedCodeBlockYamlTag(): MarkedExtension {
delete (rest as Record<string, unknown>).body; delete (rest as Record<string, unknown>).body;
} }
// A structured `data-config` object is serialized to JSON and // `data-*` props may hold structured YAML values, so they are
// passed as a single attribute (e.g. md-deck layers/templates). // serialized to JSON strings (e.g. md-deck layers/templates).
let configAttr = "";
if ("data-config" in rest) {
const rawConfig = rest["data-config"];
delete (rest as Record<string, unknown>)["data-config"];
if (rawConfig !== undefined && rawConfig !== null) {
const json = JSON.stringify(rawConfig).replace(/"/g, "&quot;");
configAttr = ` data-config="${json}"`;
}
}
const propsStr = Object.entries(rest) const propsStr = Object.entries(rest)
.map(([key, value]) => { .map(([key, value]) => {
const strValue = String(value); const strValue = key.startsWith("data-")
? JSON.stringify(value)
: String(value);
if (strValue.includes(" ") || strValue.includes('"')) { if (strValue.includes(" ") || strValue.includes('"')) {
return `${key}="${strValue.replace(/"/g, "&quot;")}"`; return `${key}="${strValue.replace(/"/g, "&quot;")}"`;
} }
@@ -65,15 +57,13 @@ export default function markedCodeBlockYamlTag(): MarkedExtension {
raw: match[0], raw: match[0],
tagName, tagName,
props: propsStr, props: propsStr,
config: configAttr,
content, content,
}; };
} }
}, },
renderer(token: any) { renderer(token: any) {
const propsAttr = token.props ? ` ${token.props}` : ""; const propsAttr = token.props ? ` ${token.props}` : "";
const configAttr = token.config || ""; return `<${token.tagName}${propsAttr}>${token.content || ""}</${token.tagName}>\n`;
return `<${token.tagName}${propsAttr}${configAttr}>${token.content || ""}</${token.tagName}>\n`;
}, },
}, },
], ],