|
5 | 5 | * LICENSE file in the root directory of this source tree. |
6 | 6 | */ |
7 | 7 |
|
8 | | -import {toValue} from '../utils'; |
9 | | -import type {Node} from 'unist'; |
10 | | -import type {MdxjsEsm} from 'mdast-util-mdx'; |
| 8 | +import escapeHtml from 'escape-html'; |
| 9 | +import type {Node, Parent} from 'unist'; |
| 10 | +import type { |
| 11 | + MdxjsEsm, |
| 12 | + MdxJsxAttribute, |
| 13 | + MdxJsxTextElement, |
| 14 | +} from 'mdast-util-mdx'; |
11 | 15 | import type {TOCHeading, TOCItem, TOCItems, TOCSlice} from './types'; |
12 | 16 | import type { |
13 | 17 | Program, |
14 | 18 | SpreadElement, |
15 | 19 | ImportDeclaration, |
16 | 20 | ImportSpecifier, |
17 | 21 | } from 'estree'; |
| 22 | +import type {Heading, PhrasingContent} from 'mdast'; |
18 | 23 |
|
19 | 24 | export function getImportDeclarations(program: Program): ImportDeclaration[] { |
20 | 25 | return program.body.filter( |
@@ -118,7 +123,7 @@ export async function createTOCExportNodeAST({ |
118 | 123 | const {toString} = await import('mdast-util-to-string'); |
119 | 124 | const {valueToEstree} = await import('estree-util-value-to-estree'); |
120 | 125 | const value: TOCItem = { |
121 | | - value: toValue(heading, toString), |
| 126 | + value: toHeadingHTMLValue(heading, toString), |
122 | 127 | id: heading.data!.id!, |
123 | 128 | level: heading.depth, |
124 | 129 | }; |
@@ -172,3 +177,67 @@ export async function createTOCExportNodeAST({ |
172 | 177 | }, |
173 | 178 | }; |
174 | 179 | } |
| 180 | + |
| 181 | +function stringifyChildren( |
| 182 | + node: Parent, |
| 183 | + toString: (param: unknown) => string, // TODO weird but works |
| 184 | +): string { |
| 185 | + return (node.children as PhrasingContent[]) |
| 186 | + .map((item) => toHeadingHTMLValue(item, toString)) |
| 187 | + .join(''); |
| 188 | +} |
| 189 | + |
| 190 | +// TODO This is really a workaround, and not super reliable |
| 191 | +// For now we only support serializing tagName, className and content |
| 192 | +// Can we implement the TOC with real JSX nodes instead of html strings later? |
| 193 | +function mdxJsxTextElementToHtml( |
| 194 | + element: MdxJsxTextElement, |
| 195 | + toString: (param: unknown) => string, // TODO weird but works |
| 196 | +): string { |
| 197 | + const tag = element.name; |
| 198 | + |
| 199 | + const attributes = element.attributes.filter( |
| 200 | + (child): child is MdxJsxAttribute => child.type === 'mdxJsxAttribute', |
| 201 | + ); |
| 202 | + |
| 203 | + const classAttribute = |
| 204 | + attributes.find((attr) => attr.name === 'className') ?? |
| 205 | + attributes.find((attr) => attr.name === 'class'); |
| 206 | + |
| 207 | + const classAttributeString = classAttribute |
| 208 | + ? `class="${escapeHtml(String(classAttribute.value))}"` |
| 209 | + : ``; |
| 210 | + |
| 211 | + const allAttributes = classAttributeString ? ` ${classAttributeString}` : ''; |
| 212 | + |
| 213 | + const content = stringifyChildren(element, toString); |
| 214 | + |
| 215 | + return `<${tag}${allAttributes}>${content}</${tag}>`; |
| 216 | +} |
| 217 | + |
| 218 | +export function toHeadingHTMLValue( |
| 219 | + node: PhrasingContent | Heading | MdxJsxTextElement, |
| 220 | + toString: (param: unknown) => string, // TODO weird but works |
| 221 | +): string { |
| 222 | + switch (node.type) { |
| 223 | + case 'mdxJsxTextElement': { |
| 224 | + return mdxJsxTextElementToHtml(node as MdxJsxTextElement, toString); |
| 225 | + } |
| 226 | + case 'text': |
| 227 | + return escapeHtml(node.value); |
| 228 | + case 'heading': |
| 229 | + return stringifyChildren(node, toString); |
| 230 | + case 'inlineCode': |
| 231 | + return `<code>${escapeHtml(node.value)}</code>`; |
| 232 | + case 'emphasis': |
| 233 | + return `<em>${stringifyChildren(node, toString)}</em>`; |
| 234 | + case 'strong': |
| 235 | + return `<strong>${stringifyChildren(node, toString)}</strong>`; |
| 236 | + case 'delete': |
| 237 | + return `<del>${stringifyChildren(node, toString)}</del>`; |
| 238 | + case 'link': |
| 239 | + return stringifyChildren(node, toString); |
| 240 | + default: |
| 241 | + return toString(node); |
| 242 | + } |
| 243 | +} |
0 commit comments