UNPKG

mystjs

Version:
159 lines 6.05 kB
import { unified } from 'unified'; import rehypeParse from 'rehype-parse'; import rehypeRemark from 'rehype-remark'; import { all } from 'hast-util-to-mdast'; import { visit } from 'unist-util-visit'; import { select, selectAll } from 'unist-util-select'; import { findAfter } from 'unist-util-find-after'; import { remove } from 'unist-util-remove'; import { map } from 'unist-util-map'; import { AdmonitionKind } from './types'; import { admonitionKindToTitle, normalizeLabel } from './utils'; import { enumerateTargets, resolveReferences } from './state'; const defaultOptions = { addAdmonitionHeaders: true, addContainerCaptionNumbers: true, disableHeadingEnumeration: false, disableContainerEnumeration: false, disableEquationEnumeration: false, }; const defaultHtmlToMdastOptions = { keepBreaks: true, htmlHandlers: { table(h, node) { return h(node, 'table', all(h, node)); }, th(h, node) { const result = h(node, 'tableCell', all(h, node)); result.header = true; return result; }, _brKeep(h, node) { return h(node, '_break'); }, }, }; // Visit all admonitions and add headers if necessary export function addAdmonitionHeaders(tree) { visit(tree, 'admonition', (node) => { var _a; if (!node.kind || node.kind === AdmonitionKind.admonition) return; node.children = [ { type: 'admonitionTitle', children: [{ type: 'text', value: admonitionKindToTitle(node.kind) }], }, ...((_a = node.children) !== null && _a !== void 0 ? _a : []), ]; }); } // Visit all containers and add captions export function addContainerCaptionNumbers(tree, state) { selectAll('container', tree) .filter((container) => container.enumerator !== false) .forEach((container) => { var _a, _b; const enumerator = (_a = state.getTarget(container.identifier)) === null || _a === void 0 ? void 0 : _a.node.enumerator; const para = select('caption > paragraph', container); if (enumerator && para) { para.children = [ { type: 'captionNumber', kind: container.kind, value: enumerator }, ...((_b = para === null || para === void 0 ? void 0 : para.children) !== null && _b !== void 0 ? _b : []), ]; } }); } /** @deprecated use myst-common */ export function liftChildren(tree, nodeType) { map(tree, (node) => { var _a, _b; const children = (_b = (_a = node.children) === null || _a === void 0 ? void 0 : _a.map((child) => { if (child.type === nodeType && child.children) return child.children; return child; })) === null || _b === void 0 ? void 0 : _b.flat(); if (children !== undefined) node.children = children; return node; }); } /** * Propagate target identifier/value to subsequent node * * Note: While this propagation happens regardless of the * subsequent node type, references are only resolved to * the TargetKind nodes enumerated in state.ts. For example: * * (paragraph-target)= * Just a normal paragraph * * will add identifier/label to paragraph node, but the node * will still not be targetable. */ export function propagateTargets(tree) { visit(tree, 'mystTarget', (node, index) => { const nextNode = findAfter(tree, index); const normalized = normalizeLabel(node.label); if (nextNode && normalized) { nextNode.identifier = normalized.identifier; nextNode.label = normalized.label; } }); remove(tree, 'mystTarget'); } /** * Ensure caption content is nested in a paragraph. * * This function is idempotent! */ export function ensureCaptionIsParagraph(tree) { visit(tree, 'caption', (node) => { if (node.children && node.children[0].type !== 'paragraph') { node.children = [{ type: 'paragraph', children: node.children }]; } }); } export function convertHtmlToMdast(tree, opts) { const handlers = Object.assign(Object.assign({}, defaultHtmlToMdastOptions.htmlHandlers), opts === null || opts === void 0 ? void 0 : opts.htmlHandlers); const otherOptions = Object.assign(Object.assign({}, defaultHtmlToMdastOptions), opts); const htmlNodes = selectAll('html', tree); htmlNodes.forEach((node) => { const hast = unified() .use(rehypeParse, { fragment: true }) .parse(node.value); // hast-util-to-mdast removes breaks if they are the first/last children // and nests standalone breaks in paragraphs. // However, since HTML nodes may just be fragments in the middle of markdown text, // there is an option to `keepBreaks` which will simply convert `<br />` // tags to `break` nodes, without the special hast-util-to-mdast behavior. if (otherOptions.keepBreaks) { selectAll('[tagName=br]', hast).forEach((n) => { n.tagName = '_brKeep'; }); } const mdast = unified().use(rehypeRemark, { handlers }).runSync(hast); node.type = 'htmlParsed'; node.children = mdast.children; visit(node, (n) => delete n.position); }); liftChildren(tree, 'htmlParsed'); selectAll('_break', tree).forEach((n) => { n.type = 'break'; }); return tree; } export const transform = (state, o) => (tree) => { const opts = Object.assign(Object.assign({}, defaultOptions), o); ensureCaptionIsParagraph(tree); propagateTargets(tree); enumerateTargets(state, tree, opts); resolveReferences(state, tree); liftChildren(tree, 'mystDirective'); liftChildren(tree, 'mystRole'); if (opts.addAdmonitionHeaders) addAdmonitionHeaders(tree); if (opts.addContainerCaptionNumbers) addContainerCaptionNumbers(tree, state); }; //# sourceMappingURL=transforms.js.map