myst-to-jats
Version:
Export from MyST Markdown to JATS
104 lines (103 loc) • 3.99 kB
JavaScript
import { selectAll } from 'unist-util-select';
const CONTAINER_KINDS = ['figure', 'table', 'code', 'quote'];
function updateInventory(node, key, idPrefix, inventory) {
const keyInv = inventory[key] ?? { count: 0, lookup: {} };
keyInv.count += 1;
const newId = typeof idPrefix === 'function' ? idPrefix(keyInv.count) : `${idPrefix}-${keyInv.count}`;
if (node.identifier) {
keyInv.lookup[node.identifier] = newId;
}
node.identifier = newId;
inventory[key] = keyInv;
}
/**
* Traverse mdast tree, find reference targets, and replace them with simple identifiers
*
* This builds a map of old identifiers to new identifiers, subsequently used
* for updating references. Modified nodes include sections, containers,
* footnotes, inlineExpressions, math, and citations.
*/
export function referenceTargetTransform(mdast, inventory, citations) {
const sections = selectAll('section', mdast);
sections.forEach((sec) => {
updateInventory(sec, 'section', 'sec', inventory);
});
const expressions = selectAll('inlineExpression', mdast);
expressions.forEach((expr) => {
updateInventory(expr, 'expression', 'expr', inventory);
});
const equations = selectAll('math', mdast);
equations.forEach((eq) => {
updateInventory(eq, 'equation', 'eq', inventory);
});
const footnotes = selectAll('footnoteDefinition', mdast);
footnotes.forEach((fn) => {
updateInventory(fn, 'footnote', 'fn', inventory);
});
const proofs = selectAll('proof', mdast);
proofs.forEach((fn) => {
updateInventory(fn, 'proof', 'stm', inventory);
});
const containers = selectAll('container', mdast);
containers.forEach((container) => {
if (!container.kind || !CONTAINER_KINDS.includes(container.kind)) {
return;
}
updateInventory(container, container.kind, container.kind, inventory);
});
if (!citations)
return;
const citationIds = Object.keys(citations);
citationIds.forEach((citationId) => {
inventory.cite ??= { count: 0, lookup: {} };
if (!inventory.cite.lookup[citationId]) {
inventory.cite.count += 1;
const newId = `ref-${inventory.cite.count}`;
inventory.cite.lookup[citationId] = newId;
citations[newId] = citations[citationId];
}
delete citations[citationId];
});
}
/**
* Use reference lookup from referenceTargetTransform to update cross references
*/
export function referenceResolutionTransform(mdast, inventory) {
const xrefs = selectAll('crossReference', mdast);
const lookup = {
...inventory.section?.lookup,
...inventory.expression?.lookup,
...inventory.equation?.lookup,
...inventory.figure?.lookup,
...inventory.table?.lookup,
...inventory.code?.lookup,
...inventory.quote?.lookup,
...inventory.proof?.lookup,
};
xrefs.forEach((xref) => {
if (xref.identifier && lookup[xref.identifier]) {
xref.identifier = lookup[xref.identifier];
}
});
const supplements = selectAll('supplementaryMaterial', mdast);
supplements.forEach((supp) => {
if (supp.figIdentifier && lookup[supp.figIdentifier]) {
supp.figIdentifier = lookup[supp.figIdentifier];
}
if (supp.embedIdentifier && lookup[supp.embedIdentifier]) {
supp.embedIdentifier = lookup[supp.embedIdentifier];
}
});
const footnotes = selectAll('footnoteReference', mdast);
footnotes.forEach((fn) => {
if (fn.identifier && inventory.footnote?.lookup[fn.identifier]) {
fn.identifier = inventory.footnote.lookup[fn.identifier];
}
});
const citations = selectAll('cite', mdast);
citations.forEach((cite) => {
if (cite.label && inventory.cite?.lookup[cite.label]) {
cite.label = inventory.cite.lookup[cite.label];
}
});
}