fix(mdx-loader): refactor and fix heading to toc html value serialization (#11004)

* refactor with iso behavior

* Add unit tests

* change behavior for <img> tags
This commit is contained in:
Sébastien Lorber 2025-03-18 17:52:26 +01:00 committed by GitHub
parent 1d4d17da18
commit e88f1aaf96
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 207 additions and 76 deletions

View file

@ -5,14 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/
import escapeHtml from 'escape-html';
import type {Parent, Node} from 'unist';
import type {PhrasingContent, Heading} from 'mdast';
import type {
MdxJsxAttribute,
MdxJsxAttributeValueExpression,
MdxJsxTextElement,
} from 'mdast-util-mdx';
import type {Node} from 'unist';
import type {MdxJsxAttributeValueExpression} from 'mdast-util-mdx';
/**
* Util to transform one node type to another node type
@ -35,70 +29,6 @@ export function transformNode<NewNode extends Node>(
return node as NewNode;
}
export function stringifyContent(
node: Parent,
toString: (param: unknown) => string, // TODO weird but works
): string {
return (node.children as PhrasingContent[])
.map((item) => toValue(item, toString))
.join('');
}
// TODO This is really a workaround, and not super reliable
// For now we only support serializing tagName, className and content
// Can we implement the TOC with real JSX nodes instead of html strings later?
function mdxJsxTextElementToHtml(
element: MdxJsxTextElement,
toString: (param: unknown) => string, // TODO weird but works
): string {
const tag = element.name;
const attributes = element.attributes.filter(
(child): child is MdxJsxAttribute => child.type === 'mdxJsxAttribute',
);
const classAttribute =
attributes.find((attr) => attr.name === 'className') ??
attributes.find((attr) => attr.name === 'class');
const classAttributeString = classAttribute
? `class="${escapeHtml(String(classAttribute.value))}"`
: ``;
const allAttributes = classAttributeString ? ` ${classAttributeString}` : '';
const content = stringifyContent(element, toString);
return `<${tag}${allAttributes}>${content}</${tag}>`;
}
export function toValue(
node: PhrasingContent | Heading | MdxJsxTextElement,
toString: (param: unknown) => string, // TODO weird but works
): string {
switch (node.type) {
case 'mdxJsxTextElement': {
return mdxJsxTextElementToHtml(node as MdxJsxTextElement, toString);
}
case 'text':
return escapeHtml(node.value);
case 'heading':
return stringifyContent(node, toString);
case 'inlineCode':
return `<code>${escapeHtml(node.value)}</code>`;
case 'emphasis':
return `<em>${stringifyContent(node, toString)}</em>`;
case 'strong':
return `<strong>${stringifyContent(node, toString)}</strong>`;
case 'delete':
return `<del>${stringifyContent(node, toString)}</del>`;
case 'link':
return stringifyContent(node, toString);
default:
return toString(node);
}
}
export function assetRequireAttributeValue(
requireString: string,
hash: string,