forked from withastro/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rehype-optimize-static.ts
79 lines (74 loc) · 2.74 KB
/
rehype-optimize-static.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import type { Root } from 'hast';
import { toHtml } from 'hast-util-to-html';
import type { Transformer } from 'unified';
import { walk } from 'unist-util-walker';
// accessing untyped hast and mdx types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Node = any;
const headingRe = /h([0-6])/;
/**
* For MDX only, collapse static subtrees of the hast into `set:html`. Subtrees
* do not include any MDX elements or headings (for `rehypeHeadingIds` to work).
* This optimization reduces the JS output as more content are represented as a
* string instead, which also reduces the AST size that Rollup holds in memory.
*/
export function rehypeOptimizeStatic(): Transformer<Root, Root> {
return (tree) => {
// All possible elements that could be the root of a subtree
const allPossibleElements = new Set<Node>();
// The current collapsible element stack while traversing the tree
const elementStack: Node[] = [];
walk(tree, {
enter(node) {
// @ts-expect-error test tagName naively
const isHeading = node.tagName && headingRe.test(node.tagName);
// For nodes that can't be optimized, eliminate all elements in the
// `elementStack` from the `allPossibleElements` set.
if (node.type.startsWith('mdx') || isHeading) {
for (const el of elementStack) {
allPossibleElements.delete(el);
}
}
// If is heading node, skip it and its children. This prevents the content
// from being optimized, as the content is used to generate the heading text.
if (isHeading) {
this.skip();
return;
}
// For possible subtree root nodes, record them
if (node.type === 'element' || node.type === 'mdxJsxFlowElement') {
elementStack.push(node);
allPossibleElements.add(node);
}
},
leave(node, parent) {
// Similar as above, but pop the `elementStack`
if (node.type === 'element' || node.type === 'mdxJsxFlowElement') {
elementStack.pop();
// Many possible elements could be part of a subtree, in order to find
// the root, we check the parent of the element we're popping. If the
// parent exists in `allPossibleElements`, then we're definitely not
// the root, so remove ourselves. This will work retroactively as we
// climb back up the tree.
if (allPossibleElements.has(parent)) {
allPossibleElements.delete(node);
}
}
},
});
// For all possible subtree roots, collapse them into `set:html` and
// strip of their children
for (const el of allPossibleElements) {
if (el.type === 'mdxJsxFlowElement') {
el.attributes.push({
type: 'mdxJsxAttribute',
name: 'set:html',
value: toHtml(el.children),
});
} else {
el.properties['set:html'] = toHtml(el.children);
}
el.children = [];
}
};
}