@dogsbay/format-astro 0.2.0-beta.127 → 0.2.0-beta.128
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/serialize.js +44 -9
- package/package.json +7 -7
package/dist/serialize.js
CHANGED
|
@@ -176,6 +176,7 @@ export function escapeTemplate(s) {
|
|
|
176
176
|
export function treeToAstro(nodes, options) {
|
|
177
177
|
const ctx = {
|
|
178
178
|
imports: new Set(),
|
|
179
|
+
diagramIds: new Map(),
|
|
179
180
|
indent: 0,
|
|
180
181
|
scripts: new Set(),
|
|
181
182
|
mode: options?.mode ?? "template",
|
|
@@ -495,6 +496,9 @@ function templateInlineEmitters() {
|
|
|
495
496
|
strike: (s) => `<s>${s}</s>`,
|
|
496
497
|
}),
|
|
497
498
|
link: (node, ctx) => {
|
|
499
|
+
// Inside a whole-card link, a link is its text (wholeCardLink)
|
|
500
|
+
if (cardLinkDepth > 0)
|
|
501
|
+
return ctx.recurse(node.children);
|
|
498
502
|
// A link node an importer built directly never passed markdown-it's
|
|
499
503
|
// scheme check: a `javascript:` href keeps its text and loses the link.
|
|
500
504
|
if (!isSafeHref(node.href)) {
|
|
@@ -565,6 +569,8 @@ function htmlModeInlineEmitters() {
|
|
|
565
569
|
// EXPRESSION, inside a string that becomes a `set:html` value. Astro never
|
|
566
570
|
// parses it as markup, so the reader saw the braces and quotes.
|
|
567
571
|
link: (node, ctx) => {
|
|
572
|
+
if (cardLinkDepth > 0)
|
|
573
|
+
return ctx.recurse(node.children);
|
|
568
574
|
if (!isSafeHref(node.href)) {
|
|
569
575
|
ctx.report("link", `unsafe link target dropped (${node.href.slice(0, 40)})`);
|
|
570
576
|
return ctx.recurse(node.children);
|
|
@@ -1141,8 +1147,18 @@ function diagramToAstro(node, ctx) {
|
|
|
1141
1147
|
ctx.imports.add("diagram");
|
|
1142
1148
|
const lang = node.props?.lang || "mermaid";
|
|
1143
1149
|
const code = node.props?.code ?? "";
|
|
1144
|
-
|
|
1150
|
+
let svg = node.props?.svg ?? "";
|
|
1145
1151
|
const title = node.props?.title;
|
|
1152
|
+
// A Pele SVG's ids (`<prefix>-title`, `<prefix>-desc`, for aria-labelledby) are unique within
|
|
1153
|
+
// its source page. A book view or an include can show the same diagram twice on one page, so
|
|
1154
|
+
// the later copies are renamed; the prefix appears nowhere else in the SVG.
|
|
1155
|
+
const prefix = /^<svg[^>]*\bclass="pele\b/.test(svg) ? /\sid="(dg-[0-9a-f]{12}(?:-\d+)?)-/.exec(svg)?.[1] : undefined;
|
|
1156
|
+
if (prefix) {
|
|
1157
|
+
const n = (ctx.diagramIds.get(prefix) ?? 0) + 1;
|
|
1158
|
+
ctx.diagramIds.set(prefix, n);
|
|
1159
|
+
if (n > 1)
|
|
1160
|
+
svg = svg.split(prefix).join(`${prefix}-p${n}`);
|
|
1161
|
+
}
|
|
1146
1162
|
const attrs = [`lang="${escapeAttr(lang)}"`, `code={${escapeExpr(code)}}`, `svg={${escapeExpr(svg)}}`];
|
|
1147
1163
|
if (title)
|
|
1148
1164
|
attrs.push(componentAttr("title", title).trimStart());
|
|
@@ -1364,6 +1380,25 @@ function endpointToAstro(node, ctx) {
|
|
|
1364
1380
|
` <ApiCodePanel slot="code">${codeBody}</ApiCodePanel>\n` +
|
|
1365
1381
|
`</ApiLayout>`);
|
|
1366
1382
|
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Whole-card links being emitted. Inside one, a link in the card's body is its text: a browser
|
|
1385
|
+
* ends the outer <a> at an inner one, so the rest of the card fell out of the link (and lost
|
|
1386
|
+
* not-prose). The serializer is synchronous, so a depth count is enough.
|
|
1387
|
+
*/
|
|
1388
|
+
let cardLinkDepth = 0;
|
|
1389
|
+
function wholeCardLink(yes, fn) {
|
|
1390
|
+
if (!yes)
|
|
1391
|
+
return fn();
|
|
1392
|
+
cardLinkDepth++;
|
|
1393
|
+
try {
|
|
1394
|
+
return fn();
|
|
1395
|
+
}
|
|
1396
|
+
finally {
|
|
1397
|
+
cardLinkDepth--;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
/** A card's title: not-prose, or docs.css's h3 rule (1.25rem, 2rem above) applies inside the card. */
|
|
1401
|
+
const CARD_TITLE_CLASS = "not-prose m-0 border-0 p-0 text-base font-semibold";
|
|
1367
1402
|
function cardsToAstro(node, ctx) {
|
|
1368
1403
|
ctx.imports.add("grid");
|
|
1369
1404
|
ctx.imports.add("card");
|
|
@@ -1382,9 +1417,9 @@ function cardsToAstro(node, ctx) {
|
|
|
1382
1417
|
const icon = card.props?.icon;
|
|
1383
1418
|
// Description sources: inline content, children, or props.description.
|
|
1384
1419
|
// Child blocks carry their own <p>, so they get a <div>: <p><p>…</p></p> is invalid HTML.
|
|
1385
|
-
const blocks = childrenToAstro(card, ctx);
|
|
1420
|
+
const blocks = wholeCardLink(!!href, () => childrenToAstro(card, ctx));
|
|
1386
1421
|
const inner = blocks
|
|
1387
|
-
|| (card.inline ? inlineToTemplate(card.inline) : "")
|
|
1422
|
+
|| (card.inline ? wholeCardLink(!!href, () => inlineToTemplate(card.inline)) : "")
|
|
1388
1423
|
|| (description ? escapeTemplate(description) : "");
|
|
1389
1424
|
const descTag = blocks ? "div" : "p";
|
|
1390
1425
|
// Icon renders inline before the title — picks up Lucide
|
|
@@ -1396,7 +1431,7 @@ function cardsToAstro(node, ctx) {
|
|
|
1396
1431
|
if (icon) {
|
|
1397
1432
|
iconHtml = cardIconHtml(icon, title, ctx);
|
|
1398
1433
|
}
|
|
1399
|
-
const titleHtml = title ? `<h3 class="
|
|
1434
|
+
const titleHtml = title ? `<h3 class="${CARD_TITLE_CLASS}">${escapeTemplate(title)}</h3>` : "";
|
|
1400
1435
|
// A description and a body are both shown (the description used to be dropped when a body existed).
|
|
1401
1436
|
const lead = blocks && description
|
|
1402
1437
|
? `<p class="text-sm text-muted-foreground mt-1 [text-decoration:none]">${escapeTemplate(description)}</p>`
|
|
@@ -1415,7 +1450,7 @@ function cardsToAstro(node, ctx) {
|
|
|
1415
1450
|
"title", "href", "description", "icon", "variant", "class", "tone",
|
|
1416
1451
|
]);
|
|
1417
1452
|
if (href) {
|
|
1418
|
-
return `<Card${cardClassAttr}${cardPass}>\n<CardContent class="p-0">\n<a href="${escapeAttr(href)}" class="[text-decoration:none] text-foreground">\n${iconHtml}\n${titleHtml}\n${desc}\n</a>\n</CardContent>\n</Card>`;
|
|
1453
|
+
return `<Card${cardClassAttr}${cardPass}>\n<CardContent class="p-0">\n<a href="${escapeAttr(href)}" class="not-prose [text-decoration:none] text-foreground">\n${iconHtml}\n${titleHtml}\n${desc}\n</a>\n</CardContent>\n</Card>`;
|
|
1419
1454
|
}
|
|
1420
1455
|
return `<Card${cardClassAttr}${cardPass}>\n<CardContent class="p-0">\n${iconHtml}\n${titleHtml}\n${desc}\n</CardContent>\n</Card>`;
|
|
1421
1456
|
}).join("\n");
|
|
@@ -1448,9 +1483,9 @@ function standaloneCardToAstro(node, ctx) {
|
|
|
1448
1483
|
const description = node.props?.description;
|
|
1449
1484
|
const icon = node.props?.icon;
|
|
1450
1485
|
// Child blocks carry their own <p>, so they get a <div>: <p><p>…</p></p> is invalid HTML.
|
|
1451
|
-
const blocks = childrenToAstro(node, ctx);
|
|
1486
|
+
const blocks = wholeCardLink(!!href, () => childrenToAstro(node, ctx));
|
|
1452
1487
|
const inner = blocks
|
|
1453
|
-
|| (node.inline ? inlineToTemplate(node.inline) : "")
|
|
1488
|
+
|| (node.inline ? wholeCardLink(!!href, () => inlineToTemplate(node.inline)) : "")
|
|
1454
1489
|
|| (description ? escapeTemplate(description) : "");
|
|
1455
1490
|
const descTag = blocks ? "div" : "p";
|
|
1456
1491
|
// Icon renders inline before the title (same shape as the
|
|
@@ -1461,7 +1496,7 @@ function standaloneCardToAstro(node, ctx) {
|
|
|
1461
1496
|
iconHtml = `<Icon name="${escapeAttr(icon)}" class="size-5 mb-2 text-muted-foreground" />`;
|
|
1462
1497
|
}
|
|
1463
1498
|
const titleHtml = title
|
|
1464
|
-
? `<h3 class="
|
|
1499
|
+
? `<h3 class="${CARD_TITLE_CLASS}">${escapeTemplate(title)}</h3>`
|
|
1465
1500
|
: "";
|
|
1466
1501
|
// A description and a body are both shown (the description used to be dropped when a body existed).
|
|
1467
1502
|
const lead = blocks && description
|
|
@@ -1480,7 +1515,7 @@ function standaloneCardToAstro(node, ctx) {
|
|
|
1480
1515
|
"title", "href", "description", "icon", "variant", "class", "tone",
|
|
1481
1516
|
]);
|
|
1482
1517
|
if (href) {
|
|
1483
|
-
return `<Card${classAttr}${passthrough}>\n<CardContent class="p-0">\n<a href="${escapeAttr(href)}" class="[text-decoration:none] text-foreground">\n${iconHtml}\n${titleHtml}\n${desc}\n</a>\n</CardContent>\n</Card>`;
|
|
1518
|
+
return `<Card${classAttr}${passthrough}>\n<CardContent class="p-0">\n<a href="${escapeAttr(href)}" class="not-prose [text-decoration:none] text-foreground">\n${iconHtml}\n${titleHtml}\n${desc}\n</a>\n</CardContent>\n</Card>`;
|
|
1484
1519
|
}
|
|
1485
1520
|
return `<Card${classAttr}${passthrough}>\n<CardContent class="p-0">\n${iconHtml}\n${titleHtml}\n${desc}\n</CardContent>\n</Card>`;
|
|
1486
1521
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dogsbay/format-astro",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.128",
|
|
4
4
|
"description": "Astro format plugin for Dogsbay — export TreeNode[] to Astro component markup",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"markdown-it": "^14.3.0",
|
|
35
|
-
"@dogsbay/format-dogsbay-md": "0.2.0-beta.
|
|
36
|
-
"@dogsbay/
|
|
37
|
-
"@dogsbay/
|
|
38
|
-
"@dogsbay/
|
|
39
|
-
"@dogsbay/types": "0.2.0-beta.
|
|
40
|
-
"@dogsbay/ui": "0.2.0-beta.
|
|
35
|
+
"@dogsbay/format-dogsbay-md": "0.2.0-beta.128",
|
|
36
|
+
"@dogsbay/render-html": "0.2.0-beta.128",
|
|
37
|
+
"@dogsbay/serialize-core": "0.2.0-beta.128",
|
|
38
|
+
"@dogsbay/site-files": "0.2.0-beta.128",
|
|
39
|
+
"@dogsbay/types": "0.2.0-beta.128",
|
|
40
|
+
"@dogsbay/ui": "0.2.0-beta.128"
|
|
41
41
|
},
|
|
42
42
|
"repository": {
|
|
43
43
|
"type": "git",
|