UNPKG

@mui/internal-docs-infra

Version:

MUI Infra - internal documentation creation tools.

82 lines (81 loc) 3.02 kB
import { hasClassName, isFrameSpan } from "../parseSource/isFrameSpan.mjs"; /** * Strip all non-structural `<span>` elements from a HAST tree while preserving * semantic structure and text content. Produces a "links-only" version of the * tree suitable as a lightweight server-rendered fallback for deferred highlighting. * * - All `<span>` elements except frame and collapse spans: removed, children promoted * - Frame `<span>` elements (`frame`): preserved with their data attributes * (except `data-lined`, which is redundant once line spans are gone) * - Collapse `<span>` elements (`collapse`): preserved with their `data-lines` * attribute so CSS can size the placeholder, keeping the fallback render's * height in sync with the fully-highlighted render * - `<a>` elements: preserved, children recursively processed * - text nodes: preserved, adjacent text nodes merged * - other elements (pre, code, etc.): preserved, children recursively processed * * Does not mutate the input tree. */ export function stripHighlightingSpans(root) { return { ...root, children: processChildren(root.children) }; } function isCollapseSpan(element) { return hasClassName(element, 'collapse'); } function processChildren(children) { const flat = children.flatMap(node => { if (node.type !== 'element') { return [node]; } const element = node; if (element.tagName === 'span' && !isFrameSpan(element) && !isCollapseSpan(element)) { // Unwrap highlighting spans: replace with recursively-processed children return processChildren(element.children); } if (isCollapseSpan(element)) { // Collapse placeholders carry one empty `<span/>` per collapsed // line as a structural payload (consumer CSS sizes them by // intrinsic layout). The children are inert: no recursion needed, // and reusing the same element reference keeps downstream JSX // caches (WeakMap-keyed) stable across re-renders. return [element]; } // Keep semantic spans, links, and other elements — process their children const processed = { ...element, children: processChildren(element.children) }; // Strip data-lined from frame spans since line spans are removed in the // fallback HAST. if (isFrameSpan(element) && processed.properties) { const { dataLined, ...rest } = processed.properties; if (dataLined !== undefined) { processed.properties = rest; } } return [processed]; }); return mergeAdjacentText(flat); } function mergeAdjacentText(nodes) { const result = []; for (const node of nodes) { const prev = result[result.length - 1]; if (node.type === 'text' && prev?.type === 'text') { // Replace the previous text node with a merged one (no mutation) result[result.length - 1] = { type: 'text', value: prev.value + node.value }; } else { result.push(node); } } return result; }