UNPKG

remark-rehype-wrap

Version:

Wrap `remark` or `rehype` subtrees in a new container node

120 lines (111 loc) 3.07 kB
import { selectAll, matches } from 'unist-util-select' function takeProps(nodes, selector, newNode) { let newNodeProps = { ...newNode.properties } if (selector) { for (let child of nodes) { if (matches(selector, child)) { newNodeProps = { ...newNodeProps, ...child.properties } child.properties = {} break } } } return newNodeProps } function wrap(tree, opts) { let { start, end, startNodes, startExcludedNodes, endNodes, endExcludedNodes, newNode, props, transform = (n) => n, } = opts let inside = false let current = [] let newChildren = [] for (let node of tree.children ?? []) { if (!inside && startNodes.includes(node) && !startExcludedNodes.includes(node)) { if (start.inclusive) { current.push(node) } else { newChildren.push(node) } inside = true startNodes.splice(startNodes.indexOf(node), 1) } else if (inside && endNodes.includes(node) && !endExcludedNodes.includes(node)) { if (end.inclusive) { current.push(node) } newChildren.push( transform({ ...newNode, properties: takeProps(current, props, newNode), children: current, }) ) if (!end.inclusive && startNodes.includes(node) && !startExcludedNodes.includes(node)) { if (start.inclusive) { current = [node] } else { current = [] newChildren.push(node) } startNodes.splice(startNodes.indexOf(node), 1) } else { if (!end.inclusive) { newChildren.push(wrap(node, opts)) } current = [] inside = false } endNodes.splice(endNodes.indexOf(node), 1) } else if (inside) { current.push(node) } else { newChildren.push(wrap(node, opts)) } } if (current.length) { newChildren.push( transform({ ...newNode, properties: takeProps(current, props, newNode), children: current, }) ) } tree.children = newChildren return tree } export function remarkRehypeWrap(...rules) { return (tree) => { for (let rule of rules) { if (!rule.end) { rule.end = rule.start } let start = typeof rule.start === 'string' ? { selector: rule.start } : { ...rule.start } start.inclusive = start.inclusive ?? true let end = typeof rule.end === 'string' ? { selector: rule.end } : { ...rule.end } end.inclusive = end.inclusive ?? false let startNodes = selectAll(start.selector, tree) let startExcludedNodes = start.exclude ? selectAll(start.exclude, tree) : [] let endNodes = selectAll(end.selector, tree) let endExcludedNodes = end.exclude ? selectAll(end.exclude, tree) : [] wrap(tree, { start, end, startNodes, startExcludedNodes, endNodes, endExcludedNodes, newNode: rule.node, props: rule.props, transform: rule.transform, }) } } }