What's the problem?
The react→html attribute-name table is missing strokeLinecap and strokeLinejoin, while the rest of the SVG presentation-attribute family converts (strokeWidth, strokeOpacity, fillOpacity, fillRule, clipPath, fontSize, textAnchor, vectorEffect, paintOrder all map correctly). The two missing names pass through camelCase — invalid in HTML output, so browsers ignore them.
The gap shows up on every path that consults the table:
Repro (node repro.mjs):
import { markdownToHtml, defineHastPlugin } from 'satteri';
const plugin = defineHastPlugin({
name: 'repro',
element: {
filter: ['p'],
visit(node, ctx) {
ctx.appendChild(node, {
type: 'element',
tagName: 'svg',
properties: { strokeWidth: '1.2', strokeLinecap: 'round', strokeLinejoin: 'miter' },
children: [],
});
},
},
});
const { html } = await markdownToHtml('hello', { hastPlugins: [plugin] });
console.log(html);
Output (satteri 0.9.5 — identical on 0.9.1):
<p>hello<svg stroke-width="1.2" strokeLinecap="round" strokeLinejoin="miter"></svg></p>
Amusingly, strokeLinecap ↔ stroke-linecap is the example pair in the elementAttributeNameCase doc comment in compile.d.ts:
/**
* Casing for HTML/SVG attribute names on plain (rehype-produced) elements.
*
* - `"react"` (default): `className`, `htmlFor`, `strokeLinecap`, `xmlLang`.
* - `"html"`: `class`, `for`, `stroke-linecap`, `xml:lang`.
* ...
*/
Workaround: write these names kebab-case in plugin properties — unknown names pass through verbatim on every path.
Environment Information
- satteri npm v0.9.5 (reproduced identically on v0.9.1), prebuilt binary
@bruits/satteri-darwin-arm64
- macOS 26.6 (arm64)
- Rust: n/a (prebuilt NAPI binary)
- Node.js v24.18.0
What's the expected result?
<p>hello<svg stroke-width="1.2" stroke-linecap="round" stroke-linejoin="miter"></svg></p>
strokeLinecap and strokeLinejoin convert like the rest of the stroke/fill family. Worth sweeping the table against React's known SVG attribute list while in there, in case other names are missing.
What's the problem?
The react→html attribute-name table is missing
strokeLinecapandstrokeLinejoin, while the rest of the SVG presentation-attribute family converts (strokeWidth,strokeOpacity,fillOpacity,fillRule,clipPath,fontSize,textAnchor,vectorEffect,paintOrderall map correctly). The two missing names pass through camelCase — invalid in HTML output, so browsers ignore them.The gap shows up on every path that consults the table:
markdownToHtmlserializer (repro below), andelementAttributeNameCase: "html", for nested children of anappendChildsubtree (root-node/setPropertyproperties bypass the table entirely — that's the separateelementAttributeNameCase: "html"is not applied toappendChildroot properties orsetPropertynames (nested children convert) #192).Repro (
node repro.mjs):Output (satteri 0.9.5 — identical on 0.9.1):
Amusingly,
strokeLinecap↔stroke-linecapis the example pair in theelementAttributeNameCasedoc comment incompile.d.ts:Workaround: write these names kebab-case in plugin
properties— unknown names pass through verbatim on every path.Environment Information
@bruits/satteri-darwin-arm64What's the expected result?
strokeLinecapandstrokeLinejoinconvert like the rest of the stroke/fill family. Worth sweeping the table against React's known SVG attribute list while in there, in case other names are missing.