@godxjp/markdown 31.19.0 → 31.20.0

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/README.md CHANGED
@@ -34,15 +34,16 @@ as `react-markdown` + `remark-gfm` + `rehype-sanitize`, which is what the apps r
34
34
 
35
35
  ## Host extension points
36
36
 
37
- | prop | use |
38
- | ----------------------------------- | -------------------------------------------------------------------------------------- |
39
- | `remarkPlugins` | Host markers (callouts, embeds). Their output is still sanitised. |
40
- | `schema` | `{ tagNames, attributes }` the markers need. Additive: it cannot widen the URL policy. |
41
- | `resolveUrl(url, key)` | Map a host scheme (`asset:…`) to a real URL before the sanitiser judges it. |
42
- | `headingId({ depth, text, index })` | Use server-assigned anchors instead of slugs. |
43
- | `components` | Element overrides; a host `pre` replaces the Mermaid default. |
44
- | `rehypePlugins` | Run after the sanitiser — presentation only. |
45
- | `mermaid={false}` | Keep ```mermaid fences as code. |
37
+ | prop | use |
38
+ | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
39
+ | `remarkPlugins` | Host markers (callouts, embeds). Their output is still sanitised. |
40
+ | `schema` | `{ tagNames, attributes }` the markers need. A rule for an attribute the base already constrains is merged into it (`code` `className` + `math-inline`). Cannot widen the URL policy. |
41
+ | `resolveUrl(url, key)` | Map a host scheme (`asset:…`) to a real URL before the sanitiser judges it. |
42
+ | `headingId({ depth, text, index })` | Use server-assigned anchors instead of slugs. |
43
+ | `components` | Element overrides. Mermaid fences are drawn before a host `pre` is asked, so a host code renderer keeps Mermaid. |
44
+ | `rehypePlugins` | Run after the sanitiser — presentation only. |
45
+ | `mermaid={false}` | Keep ```mermaid fences as code (or hand them to the host `pre`). |
46
+ | `allowedElements` / `unwrapDisallowed` | Narrow a body (an activity feed: `strong`, `em`, `del`, `code`). Only removes. |
46
47
 
47
48
  ## Stored versions
48
49
 
package/dist/index.d.ts CHANGED
@@ -49,7 +49,12 @@ type SchemaExtension = {
49
49
  /**
50
50
  * The schema plus a host's additions. ADDITIVE: an extension can allow a tag or an attribute, never
51
51
  * widen `protocols` or bring raw HTML back, so a consumer cannot loosen the URL policy by accident.
52
- * An attribute list for a tag the base already lists is appended to, not replaced.
52
+ *
53
+ * A rule for an attribute the base ALREADY constrains is MERGED into that rule (gh#1116): the
54
+ * sanitiser reads the first rule it finds for an attribute, so appending `["className",
55
+ * "math-inline"]` beside the default `["className", /^language-./]` was silently ignored and the
56
+ * formula class stripped. Merged, it is one rule allowing both. An attribute the base allows with
57
+ * no value constraint stays unconstrained.
53
58
  */
54
59
  declare function extendSchema(extension?: SchemaExtension): Options;
55
60
  /**
@@ -93,8 +98,22 @@ type MarkdownProps = {
93
98
  resolveUrl?: (url: string, key: "href" | "src" | string) => string | undefined;
94
99
  /** Heading anchors: GitHub slugs by default; a host with server-side anchors supplies its own. */
95
100
  headingId?: HeadingIdResolver;
96
- /** Render ```mermaid fences as gated diagrams (default true). `false` keeps them as code. */
101
+ /**
102
+ * Render ```mermaid fences as gated diagrams (default true). They are drawn BEFORE a host `pre`
103
+ * is consulted, so a host code renderer keeps Mermaid; `false` hands mermaid fences to the host
104
+ * `pre` (or plain code) as well (gh#1116).
105
+ */
97
106
  mermaid?: boolean;
107
+ /**
108
+ * NARROW the elements a body may produce — an activity feed that shows only inline formatting.
109
+ * react-markdown's own options, applied after the sanitiser; they can only remove, never allow
110
+ * what the schema strips (gh#1116).
111
+ */
112
+ allowedElements?: Options$1["allowedElements"];
113
+ disallowedElements?: Options$1["disallowedElements"];
114
+ allowElement?: Options$1["allowElement"];
115
+ /** With an element filter: keep a removed element's children (its text) instead of dropping it. */
116
+ unwrapDisallowed?: boolean;
98
117
  };
99
118
  /**
100
119
  * THE ONE RENDERER (gh#1108). GFM — tables, task lists, fenced code, autolinks, footnotes,
@@ -106,7 +125,7 @@ type MarkdownProps = {
106
125
  * Renders bare elements; the host wraps it (`<Prose>` from `@godxjp/ui/data-display`), so this
107
126
  * package carries no UI-kit dependency.
108
127
  */
109
- declare function Markdown({ children, remarkPlugins, rehypePlugins, schema, components, resolveUrl, headingId, mermaid, }: MarkdownProps): React.JSX.Element;
128
+ declare function Markdown({ children, remarkPlugins, rehypePlugins, schema, components, resolveUrl, headingId, mermaid, allowedElements, disallowedElements, allowElement, unwrapDisallowed, }: MarkdownProps): React.JSX.Element;
110
129
 
111
130
  type MermaidSvgCheck = {
112
131
  ok: true;
package/dist/index.js CHANGED
@@ -178,10 +178,23 @@ var markdownSchema = {
178
178
  h6: [...defaultSchema.attributes?.h6 ?? [], "id"]
179
179
  }
180
180
  };
181
+ var ruleName = (rule) => Array.isArray(rule) ? rule[0] : rule;
181
182
  function extendSchema(extension = {}) {
182
183
  const attributes = { ...markdownSchema.attributes };
183
184
  for (const [tag, list] of Object.entries(extension.attributes ?? {})) {
184
- attributes[tag] = [...attributes[tag] ?? [], ...list];
185
+ const merged = [...attributes[tag] ?? []];
186
+ for (const rule of list) {
187
+ const name = ruleName(rule);
188
+ const at = merged.findIndex((existing2) => ruleName(existing2) === name);
189
+ if (at === -1) {
190
+ merged.push(rule);
191
+ continue;
192
+ }
193
+ const existing = merged[at];
194
+ if (!Array.isArray(existing)) continue;
195
+ merged[at] = Array.isArray(rule) ? [name, ...existing.slice(1), ...rule.slice(1)] : rule;
196
+ }
197
+ attributes[tag] = merged;
185
198
  }
186
199
  return {
187
200
  ...markdownSchema,
@@ -238,19 +251,25 @@ function Markdown({
238
251
  components,
239
252
  resolveUrl,
240
253
  headingId,
241
- mermaid = true
254
+ mermaid = true,
255
+ allowedElements,
256
+ disallowedElements,
257
+ allowElement,
258
+ unwrapDisallowed
242
259
  }) {
243
260
  const sanitizeSchema = React2.useMemo(() => extendSchema(schema), [schema]);
244
261
  const merged = React2.useMemo(() => {
245
- const base = {};
246
- if (mermaid) {
247
- base.pre = ({ node, children: inner, ...props }) => {
248
- const fence = fenceOf(node);
249
- if (fence?.language === "mermaid") return /* @__PURE__ */ jsx2(MermaidDiagram, { source: fence.text });
250
- return /* @__PURE__ */ jsx2("pre", { ...props, children: inner });
251
- };
252
- }
253
- return { ...base, ...components };
262
+ if (!mermaid) return { ...components };
263
+ const HostPre = components?.pre;
264
+ const pre = (props) => {
265
+ const fence = fenceOf(props.node);
266
+ if (fence?.language === "mermaid") return /* @__PURE__ */ jsx2(MermaidDiagram, { source: fence.text });
267
+ if (HostPre)
268
+ return typeof HostPre === "string" ? React2.createElement(HostPre, props) : /* @__PURE__ */ jsx2(HostPre, { ...props });
269
+ const { node: _node, children: inner, ...rest } = props;
270
+ return /* @__PURE__ */ jsx2("pre", { ...rest, children: inner });
271
+ };
272
+ return { ...components, pre };
254
273
  }, [components, mermaid]);
255
274
  return /* @__PURE__ */ jsx2(
256
275
  ReactMarkdown,
@@ -264,6 +283,10 @@ function Markdown({
264
283
  ],
265
284
  urlTransform: safeUrl,
266
285
  components: merged,
286
+ allowedElements,
287
+ disallowedElements,
288
+ allowElement,
289
+ unwrapDisallowed,
267
290
  children
268
291
  }
269
292
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@godxjp/markdown",
3
- "version": "31.19.0",
3
+ "version": "31.20.0",
4
4
  "description": "The one Markdown renderer for GoDX apps: GFM + heading anchors, one sanitiser schema, a shared fixture corpus, and Mermaid drawn only after its SVG passes a fail-closed gate.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",