@dogsbay/format-astro 0.2.0-beta.137 → 0.2.0-beta.138

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.
Files changed (2) hide show
  1. package/dist/serialize.js +37 -3
  2. package/package.json +7 -7
package/dist/serialize.js CHANGED
@@ -1069,7 +1069,11 @@ function genericContainerToAstro(node, ctx) {
1069
1069
  const title = typeof node.meta?.title === "string" ? `<p class="font-semibold">${escapeTemplate(node.meta.title)}</p>\n` : "";
1070
1070
  const classAttr = mergeClassAttr("", node.props?.class);
1071
1071
  const passthrough = renderPassthroughAttrs(node.props, ["class"]);
1072
- return `<div data-directive="${escapeAttr(name)}"${classAttr}${passthrough}>\n${title}${childrenToAstro(node, ctx)}\n</div>`;
1072
+ // Nested props (a `---` block on an unknown directive) reach the page as one JSON attribute,
1073
+ // so a site's own component or script can read them; scalar props pass through as before.
1074
+ const nested = Object.fromEntries(Object.entries(node.props ?? {}).filter(([, v]) => v !== null && typeof v === "object"));
1075
+ const dataProps = Object.keys(nested).length ? ` data-props="${escapeAttr(JSON.stringify(nested))}"` : "";
1076
+ return `<div data-directive="${escapeAttr(name)}"${classAttr}${passthrough}${dataProps}>\n${title}${childrenToAstro(node, ctx)}\n</div>`;
1073
1077
  }
1074
1078
  function detailsToAstro(node, ctx) {
1075
1079
  ctx.imports.add("details");
@@ -1191,12 +1195,42 @@ const TITLE_TAG = /^<\/?(?:code|kbd|b|strong|i|em|s|del|mark|sub|sup|br)\s*\/?>$
1191
1195
  * and a title with `<img onerror=…>` ran a script in the summary. render-html reads titles the same way.
1192
1196
  */
1193
1197
  function titleInline(title) {
1194
- return captionInline(title).map((n) => (n.type === "html-inline" && !TITLE_TAG.test(n.html.trim()) ? { type: "text", text: n.html } : n));
1198
+ const nodes = captionInline(title);
1199
+ const kept = balancedTitleTags(nodes);
1200
+ return nodes.map((n, i) => (n.type === "html-inline" && !kept.has(i) ? { type: "text", text: n.html } : n));
1201
+ }
1202
+ /**
1203
+ * The indexes of the formatting tags a title keeps: a bare `TITLE_TAG` that is one half of a
1204
+ * matched pair (or `<br>`). An unclosed `<em>` was kept, and the browser carried it past the
1205
+ * title's element into the rest of the page (render-html has the same rule).
1206
+ */
1207
+ function balancedTitleTags(nodes) {
1208
+ const kept = new Set();
1209
+ const open = [];
1210
+ nodes.forEach((n, i) => {
1211
+ if (n.type !== "html-inline")
1212
+ return;
1213
+ const html = n.html.trim();
1214
+ if (!TITLE_TAG.test(html))
1215
+ return;
1216
+ const name = /^<\/?\s*(\w+)/.exec(html)[1].toLowerCase();
1217
+ if (name === "br")
1218
+ kept.add(i);
1219
+ else if (!html.startsWith("</"))
1220
+ open.push({ name, at: i });
1221
+ else if (open.length && open[open.length - 1].name === name) {
1222
+ kept.add(open.pop().at);
1223
+ kept.add(i);
1224
+ }
1225
+ });
1226
+ return kept;
1195
1227
  }
1196
1228
  /** A plain caption's text (escapes undone by the parse). */
1197
1229
  const captionText = (nodes) => nodes.map((n) => (n.type === "text" ? n.text : "")).join("");
1198
1230
  function captionToAstro(caption, ctx) {
1199
- const nodes = captionInline(caption);
1231
+ // Raw HTML other than balanced formatting tags is text, as in a title (an imported repo's caption
1232
+ // is someone else's; render-html draws captions the same way).
1233
+ const nodes = titleInline(caption);
1200
1234
  return isPlainCaption(nodes)
1201
1235
  ? escapeTemplate(captionText(nodes))
1202
1236
  : renderInline(nodes, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dogsbay/format-astro",
3
- "version": "0.2.0-beta.137",
3
+ "version": "0.2.0-beta.138",
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/render-html": "0.2.0-beta.137",
36
- "@dogsbay/format-dogsbay-md": "0.2.0-beta.137",
37
- "@dogsbay/site-files": "0.2.0-beta.137",
38
- "@dogsbay/serialize-core": "0.2.0-beta.137",
39
- "@dogsbay/types": "0.2.0-beta.137",
40
- "@dogsbay/ui": "0.2.0-beta.137"
35
+ "@dogsbay/format-dogsbay-md": "0.2.0-beta.138",
36
+ "@dogsbay/render-html": "0.2.0-beta.138",
37
+ "@dogsbay/serialize-core": "0.2.0-beta.138",
38
+ "@dogsbay/types": "0.2.0-beta.138",
39
+ "@dogsbay/site-files": "0.2.0-beta.138",
40
+ "@dogsbay/ui": "0.2.0-beta.138"
41
41
  },
42
42
  "repository": {
43
43
  "type": "git",