mystjs
Version:
Markdown parser for MyST markdown in JavaScript
204 lines • 8.05 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveReferences = exports.enumerateTargets = exports.State = exports.formatHeadingEnumerator = exports.incrementHeadingCounts = exports.ReferenceKind = exports.TargetKind = void 0;
const unist_util_visit_1 = require("unist-util-visit");
const unist_util_select_1 = require("unist-util-select");
const mdast_util_find_and_replace_1 = require("mdast-util-find-and-replace");
const utils_1 = require("./utils");
var TargetKind;
(function (TargetKind) {
TargetKind["heading"] = "heading";
TargetKind["math"] = "math";
TargetKind["figure"] = "figure";
TargetKind["table"] = "table";
TargetKind["code"] = "code";
})(TargetKind = exports.TargetKind || (exports.TargetKind = {}));
var ReferenceKind;
(function (ReferenceKind) {
ReferenceKind["ref"] = "ref";
ReferenceKind["numref"] = "numref";
ReferenceKind["eq"] = "eq";
})(ReferenceKind = exports.ReferenceKind || (exports.ReferenceKind = {}));
/**
* See https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-numref
*/
function fillReferenceEnumerators(node, enumerator) {
const num = String(enumerator);
(0, mdast_util_find_and_replace_1.findAndReplace)(node, { '%s': num, '{number}': num });
}
function copyNode(node) {
return JSON.parse(JSON.stringify(node));
}
function kindFromNode(node) {
return node.type === 'container' ? node.kind : node.type;
}
/**
* Increment heading counts based on depth to increment
*
* depth is the depth to increment
* counts is a list of 6 counts, corresponding to 6 heading depths
*
* When a certain depth is incremented, shallower depths are left the same
* and deeper depths are reset to zero. Null counts anywhere are ignored.
*/
function incrementHeadingCounts(depth, counts) {
const incrementIndex = depth - 1;
return counts.map((count, index) => {
if (count === null || index < incrementIndex)
return count;
if (index === incrementIndex)
return count + 1;
return 0;
});
}
exports.incrementHeadingCounts = incrementHeadingCounts;
/**
* Return dot-delimited header numbering based on heading counts
*
* counts is a list of 6 counts, corresponding to 6 heading depths
*
* Leading zeros are kept, trailing zeros are removed, nulls are ignored.
*/
function formatHeadingEnumerator(counts) {
counts = counts.filter((d) => d !== null);
while (counts && counts[counts.length - 1] === 0) {
counts.pop();
}
return counts.join('.');
}
exports.formatHeadingEnumerator = formatHeadingEnumerator;
class State {
constructor(targetCounts, targets) {
this.targetCounts = targetCounts || {};
this.targets = targets || {};
}
addTarget(node) {
const kind = kindFromNode(node);
if (kind && kind in TargetKind) {
let enumerator = null;
if (node.enumerated !== false) {
enumerator = this.incrementCount(node, kind);
node.enumerator = enumerator;
}
if (node.identifier) {
this.targets[node.identifier] = {
node: copyNode(node),
kind: kind,
};
}
}
}
initializeNumberedHeadingDepths(tree) {
const headings = (0, unist_util_select_1.selectAll)('heading', tree).filter((node) => node.enumerated !== false);
const headingDepths = new Set(headings.map((node) => node.depth));
this.targetCounts.heading = [1, 2, 3, 4, 5, 6].map((depth) => headingDepths.has(depth) ? 0 : null);
}
incrementCount(node, kind) {
if (kind === TargetKind.heading) {
// Ideally initializeNumberedHeadingDepths is called before incrementing
// heading count to do a better job initializng headers based on tree
if (!this.targetCounts.heading)
this.targetCounts.heading = [0, 0, 0, 0, 0, 0];
this.targetCounts.heading = incrementHeadingCounts(node.depth, this.targetCounts.heading);
return formatHeadingEnumerator(this.targetCounts.heading);
}
if (kind in this.targetCounts) {
this.targetCounts[kind] += 1;
}
else {
this.targetCounts[kind] = 1;
}
return String(this.targetCounts[kind]);
}
getTarget(identifier) {
if (!identifier)
return undefined;
return this.targets[identifier];
}
resolveReferenceContent(node) {
var _a;
const target = this.getTarget(node.identifier);
if (!target) {
return;
}
const kinds = {
ref: {
eq: node.kind === ReferenceKind.eq,
ref: node.kind === ReferenceKind.ref,
numref: node.kind === ReferenceKind.numref,
},
target: {
math: target.kind === TargetKind.math,
figure: target.kind === TargetKind.figure,
table: target.kind === TargetKind.table,
heading: target.kind === TargetKind.heading,
},
};
const noNodeChildren = !((_a = node.children) === null || _a === void 0 ? void 0 : _a.length);
if (kinds.ref.eq && kinds.target.math && target.node.enumerator) {
if (noNodeChildren) {
(0, utils_1.setTextAsChild)(node, `(${target.node.enumerator})`);
}
node.resolved = true;
}
else if (kinds.ref.ref && kinds.target.heading) {
if (noNodeChildren) {
node.children = copyNode(target.node).children;
}
node.resolved = true;
}
else if (kinds.ref.ref && (kinds.target.figure || kinds.target.table)) {
if (noNodeChildren) {
const caption = (0, unist_util_select_1.select)('caption > paragraph', target.node);
node.children = copyNode(caption).children;
}
node.resolved = true;
}
else if (kinds.ref.numref && kinds.target.figure && target.node.enumerator) {
if (noNodeChildren) {
(0, utils_1.setTextAsChild)(node, 'Figure %s');
}
fillReferenceEnumerators(node, target.node.enumerator);
node.resolved = true;
}
else if (kinds.ref.numref && kinds.target.table && target.node.enumerator) {
if (noNodeChildren) {
(0, utils_1.setTextAsChild)(node, 'Table %s');
}
fillReferenceEnumerators(node, target.node.enumerator);
node.resolved = true;
}
}
}
exports.State = State;
const enumerateTargets = (state, tree, opts) => {
state.initializeNumberedHeadingDepths(tree);
if (!opts.disableContainerEnumeration) {
(0, unist_util_visit_1.visit)(tree, 'container', (node) => state.addTarget(node));
}
if (!opts.disableEquationEnumeration) {
(0, unist_util_visit_1.visit)(tree, 'math', (node) => state.addTarget(node));
}
if (!opts.disableHeadingEnumeration) {
(0, unist_util_visit_1.visit)(tree, 'heading', (node) => state.addTarget(node));
}
return tree;
};
exports.enumerateTargets = enumerateTargets;
const resolveReferences = (state, tree) => {
(0, unist_util_select_1.selectAll)('link', tree).forEach((node) => {
const reference = (0, utils_1.normalizeLabel)(node.url);
if (reference && reference.identifier in state.targets) {
node.type = 'crossReference';
node.kind = state.targets[reference.identifier].kind === TargetKind.math ? 'eq' : 'ref';
node.identifier = reference.identifier;
node.label = reference.label;
delete node.url;
}
});
(0, unist_util_visit_1.visit)(tree, 'crossReference', (node) => {
state.resolveReferenceContent(node);
});
};
exports.resolveReferences = resolveReferences;
//# sourceMappingURL=state.js.map
;