docs/solutions/logic-errors/2026-04-03-gfm-extension-fallbacks-must-preserve-user-visible-syntax.md
Two GFM seams were lying in different ways:
[url](url)[^id] marker entirelyBoth failures broke the same rule: if Plate cannot preserve the exact feature model yet, it still has to preserve the syntax users actually typed.
link node, which let
remark-stringify choose bracket-link output.footnoteReference without a deserializer, so the marker vanished.When a link node is just a plain URL with identical text and href, serialize it as raw markdown text instead of a normal link node, unless the caller explicitly forces resource links:
const isBareAutolinkLiteral =
children.length === 1 &&
children[0]?.type === 'text' &&
children[0].value === node.url &&
options.remarkStringifyOptions?.resourceLink !== true &&
BARE_AUTOLINK_PROTOCOL_REGEX.test(node.url ?? '');
if (isBareAutolinkLiteral) {
return {
type: 'html',
value: node.url,
};
}
That preserves:
https://platejs.org
instead of degrading to:
[https://platejs.org](https://platejs.org)
But when the caller sets remarkStringifyOptions.resourceLink = true, return
the normal mdast link node and let remark-stringify emit:
[https://platejs.org](https://platejs.org)
Until Plate has a first-class footnote model:
[^id] references into literal text nodesThat keeps the visible syntax alive:
[^1]
[^1]: Footnote text
instead of silently dropping the reference marker.
There are two levels of correctness:
If full support is not there yet, fallback still needs to preserve what the user sees and typed. Losing the marker or rewriting a URL into a different link form is data drift, not a harmless implementation detail.
Serializer fallbacks also have to stay below caller-controlled stringify
options. If an option like resourceLink exists specifically to force a
markdown wire shape, the Plate shortcut should yield the normal mdast node and
let remark-stringify honor that setting.
remarkStringifyOptions already exposes the caller's requested wire shape.remarkStringifyOptions overrides for the same surfaceThese checks passed:
bun test packages/markdown/src/lib/gfmSurface.spec.ts packages/markdown/src/lib/commonmarkSurface.spec.ts packages/markdown/src/lib/defaultRules.spec.ts apps/www/src/__tests__/package-integration/markdown-rich/defaultRule.spec.ts packages/link/src/lib/withLink.spec.tsx
pnpm lint:fix
Additional resource-link regression:
pnpm exec bun test packages/markdown/src/lib/gfmSurface.spec.ts
pnpm turbo build --filter=./packages/markdown
pnpm build
pnpm turbo typecheck --filter=./packages/markdown
pnpm lint:fix