@reblu/site-blocks 0.4.0 → 0.4.1

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/index.d.cts CHANGED
@@ -51,9 +51,13 @@ declare function buildShareUrls(url: string, title: string): ShareUrls;
51
51
  * `dangerouslySetInnerHTML` consumers listed above. `MarkdownRenderer` re-parses that
52
52
  * string through `html-react-parser` into a token-styled component map
53
53
  * (`markdown-renderer.tsx` `buildOptions`), and that map rebuilds most elements from
54
- * scratch — so on the blog-body path author attributes are dropped downstream of this
55
- * module regardless of what the schema allows. Widening the renderer's attribute
56
- * forwarding is a separate change; do not read these comments as a claim about it.
54
+ * scratch. It forwards only the subset of author attributes that carries MEANING
55
+ * (`id`, `lang`, `dir`, `title`, `hidden`, `role`, `aria-*`, `data-*`, and the list
56
+ * numbering attributes) — see `PRESERVED_GLOBAL_ATTRIBUTES` there for the line and
57
+ * why it sits where it does. `class` and `style` in particular are guaranteed by THIS
58
+ * module and still dropped by that one, because the map owns presentation (REB-666).
59
+ * Nothing the renderer forwards widens this schema: it can only pass through what
60
+ * `sanitizeHtml` already allowed.
57
61
  *
58
62
  * REB-606: this used to run on `isomorphic-dompurify`, which pulls `jsdom` into the
59
63
  * import graph. `jsdom`'s transitive `html-encoding-sniffer` → `@exodus/bytes` chain
package/dist/index.d.ts CHANGED
@@ -51,9 +51,13 @@ declare function buildShareUrls(url: string, title: string): ShareUrls;
51
51
  * `dangerouslySetInnerHTML` consumers listed above. `MarkdownRenderer` re-parses that
52
52
  * string through `html-react-parser` into a token-styled component map
53
53
  * (`markdown-renderer.tsx` `buildOptions`), and that map rebuilds most elements from
54
- * scratch — so on the blog-body path author attributes are dropped downstream of this
55
- * module regardless of what the schema allows. Widening the renderer's attribute
56
- * forwarding is a separate change; do not read these comments as a claim about it.
54
+ * scratch. It forwards only the subset of author attributes that carries MEANING
55
+ * (`id`, `lang`, `dir`, `title`, `hidden`, `role`, `aria-*`, `data-*`, and the list
56
+ * numbering attributes) — see `PRESERVED_GLOBAL_ATTRIBUTES` there for the line and
57
+ * why it sits where it does. `class` and `style` in particular are guaranteed by THIS
58
+ * module and still dropped by that one, because the map owns presentation (REB-666).
59
+ * Nothing the renderer forwards widens this schema: it can only pass through what
60
+ * `sanitizeHtml` already allowed.
57
61
  *
58
62
  * REB-606: this used to run on `isomorphic-dompurify`, which pulls `jsdom` into the
59
63
  * import graph. `jsdom`'s transitive `html-encoding-sniffer` → `@exodus/bytes` chain
package/dist/index.js CHANGED
@@ -13033,6 +13033,41 @@ var HEADING_CLASSES = {
13033
13033
  function hasClass(el, name) {
13034
13034
  return typeof el.attribs.class === "string" && el.attribs.class.split(/\s+/).includes(name);
13035
13035
  }
13036
+ var PRESERVED_GLOBAL_ATTRIBUTES = [
13037
+ // Anchor targets. In-document links ("see clause 4" → `#clause-4`) resolve to
13038
+ // nothing without them.
13039
+ "id",
13040
+ // Language and direction of a passage — a bilingual legal page is the normal case
13041
+ // here, and screen readers switch voice on it.
13042
+ "lang",
13043
+ "dir",
13044
+ // Advisory text the author wrote; it is content, not styling.
13045
+ "title",
13046
+ // Explicit "do not publish this". Without it a block authored as `<p hidden>`
13047
+ // becomes VISIBLE — publishing what the author marked as not-for-publication.
13048
+ "hidden",
13049
+ // Accessible semantics. `../lib/markdown` restores these through the sanitiser
13050
+ // precisely so they are not lost (REB-286's axe gate); dropping them one layer
13051
+ // later would undo that.
13052
+ "role"
13053
+ ];
13054
+ var PRESERVED_ATTRIBUTE_PREFIXES = ["aria-", "data-"];
13055
+ var PRESERVED_TAG_ATTRIBUTES = {
13056
+ ol: ["start", "type"],
13057
+ li: ["value", "type"]
13058
+ };
13059
+ var BOOLEAN_ATTRIBUTES = /* @__PURE__ */ new Set(["hidden"]);
13060
+ function authorAttributes(el) {
13061
+ const tagSpecific = PRESERVED_TAG_ATTRIBUTES[el.name] ?? [];
13062
+ const preserved = {};
13063
+ for (const [rawName, value] of Object.entries(el.attribs)) {
13064
+ const name = rawName.toLowerCase();
13065
+ const keep = PRESERVED_GLOBAL_ATTRIBUTES.includes(name) || tagSpecific.includes(name) || PRESERVED_ATTRIBUTE_PREFIXES.some((prefix) => name.startsWith(prefix));
13066
+ if (!keep) continue;
13067
+ preserved[name] = BOOLEAN_ATTRIBUTES.has(name) ? true : value;
13068
+ }
13069
+ return preserved;
13070
+ }
13036
13071
  function isExternalHref(href) {
13037
13072
  return !!href && /^(https?:)?\/\//.test(href);
13038
13073
  }
@@ -13042,29 +13077,46 @@ function buildOptions(headingOffset) {
13042
13077
  if (!(node instanceof Element)) return void 0;
13043
13078
  const children2 = () => domToReact(node.children, options);
13044
13079
  const { name, attribs } = node;
13080
+ const author = authorAttributes(node);
13045
13081
  const headingMatch = /^h([1-6])$/.exec(name);
13046
13082
  if (headingMatch) {
13047
13083
  const rendered = Math.min(6, Number(headingMatch[1]) + headingOffset);
13048
13084
  const Tag = `h${rendered}`;
13049
- return /* @__PURE__ */ jsx(Tag, { className: HEADING_CLASSES[rendered], children: children2() });
13085
+ return /* @__PURE__ */ jsx(Tag, { ...author, className: HEADING_CLASSES[rendered], children: children2() });
13050
13086
  }
13051
13087
  switch (name) {
13052
13088
  case "p":
13053
- return /* @__PURE__ */ jsx("p", { className: "my-4 leading-relaxed text-foreground/90", children: children2() });
13089
+ return /* @__PURE__ */ jsx("p", { ...author, className: "my-4 leading-relaxed text-foreground/90", children: children2() });
13054
13090
  case "ul":
13055
- return /* @__PURE__ */ jsx("ul", { className: hasClass(node, "contains-task-list") ? "list-none pl-0 my-4 space-y-1" : "list-disc ml-6 my-4", children: children2() });
13091
+ return /* @__PURE__ */ jsx("ul", { ...author, className: hasClass(node, "contains-task-list") ? "list-none pl-0 my-4 space-y-1" : "list-disc ml-6 my-4", children: children2() });
13056
13092
  case "ol":
13057
- return /* @__PURE__ */ jsx("ol", { className: "list-decimal ml-6 my-4", children: children2() });
13093
+ return /* @__PURE__ */ jsx("ol", { ...author, className: "list-decimal ml-6 my-4", children: children2() });
13058
13094
  case "li":
13059
- return /* @__PURE__ */ jsx("li", { className: hasClass(node, "task-list-item") ? "flex items-start gap-2 my-1" : "my-1", children: children2() });
13095
+ return /* @__PURE__ */ jsx("li", { ...author, className: hasClass(node, "task-list-item") ? "flex items-start gap-2 my-1" : "my-1", children: children2() });
13060
13096
  case "blockquote":
13061
- return /* @__PURE__ */ jsx("blockquote", { className: "border-l-4 border-primary/50 pl-6 italic my-6 text-muted-foreground", children: children2() });
13097
+ return /* @__PURE__ */ jsx(
13098
+ "blockquote",
13099
+ {
13100
+ ...author,
13101
+ className: "border-l-4 border-primary/50 pl-6 italic my-6 text-muted-foreground",
13102
+ children: children2()
13103
+ }
13104
+ );
13062
13105
  case "pre":
13063
- return /* @__PURE__ */ jsx("pre", { className: "rounded-lg border border-input bg-muted p-4 my-6 overflow-x-auto font-mono text-sm", children: children2() });
13106
+ return /* @__PURE__ */ jsx(
13107
+ "pre",
13108
+ {
13109
+ ...author,
13110
+ className: "rounded-lg border border-input bg-muted p-4 my-6 overflow-x-auto font-mono text-sm",
13111
+ children: children2()
13112
+ }
13113
+ );
13064
13114
  case "code": {
13065
13115
  const isBlock = node.parent instanceof Element && node.parent.name === "pre";
13066
- if (isBlock) return /* @__PURE__ */ jsx("code", { className: "block", children: children2() });
13067
- return /* @__PURE__ */ jsx("code", { className: "rounded bg-muted px-1.5 py-0.5 font-mono text-sm", children: children2() });
13116
+ if (isBlock) {
13117
+ return /* @__PURE__ */ jsx("code", { ...author, className: "block", children: children2() });
13118
+ }
13119
+ return /* @__PURE__ */ jsx("code", { ...author, className: "rounded bg-muted px-1.5 py-0.5 font-mono text-sm", children: children2() });
13068
13120
  }
13069
13121
  case "a": {
13070
13122
  const href = attribs.href;
@@ -13072,6 +13124,7 @@ function buildOptions(headingOffset) {
13072
13124
  return /* @__PURE__ */ jsx(
13073
13125
  "a",
13074
13126
  {
13127
+ ...author,
13075
13128
  href,
13076
13129
  className: "text-primary underline underline-offset-4 hover:text-primary/80",
13077
13130
  ...external ? { target: "_blank", rel: "noopener noreferrer" } : {},
@@ -13088,6 +13141,7 @@ function buildOptions(headingOffset) {
13088
13141
  /* @__PURE__ */ jsx(
13089
13142
  "img",
13090
13143
  {
13144
+ ...author,
13091
13145
  src: attribs.src,
13092
13146
  alt: attribs.alt ?? "",
13093
13147
  loading: "lazy",
@@ -13099,6 +13153,7 @@ function buildOptions(headingOffset) {
13099
13153
  return /* @__PURE__ */ jsx("div", { className: "relative my-6 aspect-video w-full overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx(
13100
13154
  "iframe",
13101
13155
  {
13156
+ ...author,
13102
13157
  src: attribs.src,
13103
13158
  title: attribs.title ?? "Embedded video",
13104
13159
  allow: attribs.allow ?? "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
@@ -13107,13 +13162,13 @@ function buildOptions(headingOffset) {
13107
13162
  }
13108
13163
  ) });
13109
13164
  case "hr":
13110
- return /* @__PURE__ */ jsx("hr", { className: "my-8 border-muted-foreground/30" });
13165
+ return /* @__PURE__ */ jsx("hr", { ...author, className: "my-8 border-muted-foreground/30" });
13111
13166
  case "strong":
13112
- return /* @__PURE__ */ jsx("strong", { className: "font-bold text-foreground", children: children2() });
13167
+ return /* @__PURE__ */ jsx("strong", { ...author, className: "font-bold text-foreground", children: children2() });
13113
13168
  case "em":
13114
- return /* @__PURE__ */ jsx("em", { className: "italic", children: children2() });
13169
+ return /* @__PURE__ */ jsx("em", { ...author, className: "italic", children: children2() });
13115
13170
  case "del":
13116
- return /* @__PURE__ */ jsx("del", { className: "line-through", children: children2() });
13171
+ return /* @__PURE__ */ jsx("del", { ...author, className: "line-through", children: children2() });
13117
13172
  default:
13118
13173
  return void 0;
13119
13174
  }