UNPKG

myst-to-jats

Version:
94 lines (93 loc) 3.17 kB
import { liftChildren, NotebookCell } from 'myst-common'; import { remove } from 'unist-util-remove'; import { selectAll } from 'unist-util-select'; export function sectionAttrsFromBlock(node) { const output = {}; if (node.kind) { const blockType = node.kind; if (Object.values(NotebookCell).includes(blockType)) { output['sec-type'] = blockType; } } if (node.identifier) output.id = node.identifier; return output; } function blockIsNotebookCode(node) { // Markdown blocks will be divided to sections later by headings. return sectionAttrsFromBlock(node)['sec-type'] === NotebookCell.code; } function blockIsNotebookFigure(node) { return !!node.data?.['fig-cap']; } function headingsToSections(tree) { const stack = []; const children = []; function push(child) { const top = stack[stack.length - 1]; if (top) { top.children.push(child); } else { children.push(child); } } function newSection(heading) { const { enumerator, enumerated, ...filtered } = heading; const next = { ...filtered, type: 'section', children: [] }; while (stack[stack.length - 1] && stack[stack.length - 1].depth >= heading.depth) stack.pop(); push(next); stack.push(next); return { enumerator, enumerated }; } tree.children?.forEach((child) => { if (child.type === 'heading') { const { enumerator, enumerated } = newSection(child); push({ type: 'heading', enumerator, enumerated, children: child.children }); } else { push(child); } }); tree.children = children; } /** * This transform does the following: * - For sub-articles: * - Blocks are converted to sections so notebook cell divisions are maintained. * - Within each block, headers are converted to sections. * - For main articles: * - Notebook code cell blocks (with meta.type of "notebook-code") are deleted. * - Remaining blocks are removed, lifting children up a level * - Top-level heading nodes are then used to break the tree into section nodes, * with heading and subsequent nodes as children */ export function sectionTransform(tree, opts) { if (opts?.isSubArticle) { selectAll('block', tree).forEach((node) => { node.type = 'section'; node.depth = 0; headingsToSections(node); }); return; } selectAll('block', tree).forEach((node) => { if (blockIsNotebookFigure(node)) { node.type = 'section'; } else if (blockIsNotebookCode(node)) { node.type = '__delete__'; } }); const removed = remove(tree, '__delete__'); if (removed === null) { // remove is unhappy if all children are removed - this forces it through tree.children = []; } liftChildren(tree, 'block'); // this looses part information. TODO: milestones headingsToSections(tree); } export const sectionPlugin = (opts) => (tree) => { sectionTransform(tree, opts); };