mystjs
Version:
Markdown parser for MyST markdown in JavaScript
172 lines • 7.29 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.transform = exports.convertHtmlToMdast = exports.ensureCaptionIsParagraph = exports.propagateTargets = exports.liftChildren = exports.addContainerCaptionNumbers = exports.addAdmonitionHeaders = void 0;
const unified_1 = require("unified");
const rehype_parse_1 = __importDefault(require("rehype-parse"));
const rehype_remark_1 = __importDefault(require("rehype-remark"));
const hast_util_to_mdast_1 = require("hast-util-to-mdast");
const unist_util_visit_1 = require("unist-util-visit");
const unist_util_select_1 = require("unist-util-select");
const unist_util_find_after_1 = require("unist-util-find-after");
const unist_util_remove_1 = require("unist-util-remove");
const unist_util_map_1 = require("unist-util-map");
const types_1 = require("./types");
const utils_1 = require("./utils");
const state_1 = require("./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', (0, hast_util_to_mdast_1.all)(h, node));
},
th(h, node) {
const result = h(node, 'tableCell', (0, hast_util_to_mdast_1.all)(h, node));
result.header = true;
return result;
},
_brKeep(h, node) {
return h(node, '_break');
},
},
};
// Visit all admonitions and add headers if necessary
function addAdmonitionHeaders(tree) {
(0, unist_util_visit_1.visit)(tree, 'admonition', (node) => {
var _a;
if (!node.kind || node.kind === types_1.AdmonitionKind.admonition)
return;
node.children = [
{
type: 'admonitionTitle',
children: [{ type: 'text', value: (0, utils_1.admonitionKindToTitle)(node.kind) }],
},
...((_a = node.children) !== null && _a !== void 0 ? _a : []),
];
});
}
exports.addAdmonitionHeaders = addAdmonitionHeaders;
// Visit all containers and add captions
function addContainerCaptionNumbers(tree, state) {
(0, unist_util_select_1.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 = (0, unist_util_select_1.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 : []),
];
}
});
}
exports.addContainerCaptionNumbers = addContainerCaptionNumbers;
/** @deprecated use myst-common */
function liftChildren(tree, nodeType) {
(0, unist_util_map_1.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;
});
}
exports.liftChildren = liftChildren;
/**
* 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.
*/
function propagateTargets(tree) {
(0, unist_util_visit_1.visit)(tree, 'mystTarget', (node, index) => {
const nextNode = (0, unist_util_find_after_1.findAfter)(tree, index);
const normalized = (0, utils_1.normalizeLabel)(node.label);
if (nextNode && normalized) {
nextNode.identifier = normalized.identifier;
nextNode.label = normalized.label;
}
});
(0, unist_util_remove_1.remove)(tree, 'mystTarget');
}
exports.propagateTargets = propagateTargets;
/**
* Ensure caption content is nested in a paragraph.
*
* This function is idempotent!
*/
function ensureCaptionIsParagraph(tree) {
(0, unist_util_visit_1.visit)(tree, 'caption', (node) => {
if (node.children && node.children[0].type !== 'paragraph') {
node.children = [{ type: 'paragraph', children: node.children }];
}
});
}
exports.ensureCaptionIsParagraph = ensureCaptionIsParagraph;
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 = (0, unist_util_select_1.selectAll)('html', tree);
htmlNodes.forEach((node) => {
const hast = (0, unified_1.unified)()
.use(rehype_parse_1.default, { 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) {
(0, unist_util_select_1.selectAll)('[tagName=br]', hast).forEach((n) => {
n.tagName = '_brKeep';
});
}
const mdast = (0, unified_1.unified)().use(rehype_remark_1.default, { handlers }).runSync(hast);
node.type = 'htmlParsed';
node.children = mdast.children;
(0, unist_util_visit_1.visit)(node, (n) => delete n.position);
});
liftChildren(tree, 'htmlParsed');
(0, unist_util_select_1.selectAll)('_break', tree).forEach((n) => {
n.type = 'break';
});
return tree;
}
exports.convertHtmlToMdast = convertHtmlToMdast;
const transform = (state, o) => (tree) => {
const opts = Object.assign(Object.assign({}, defaultOptions), o);
ensureCaptionIsParagraph(tree);
propagateTargets(tree);
(0, state_1.enumerateTargets)(state, tree, opts);
(0, state_1.resolveReferences)(state, tree);
liftChildren(tree, 'mystDirective');
liftChildren(tree, 'mystRole');
if (opts.addAdmonitionHeaders)
addAdmonitionHeaders(tree);
if (opts.addContainerCaptionNumbers)
addContainerCaptionNumbers(tree, state);
};
exports.transform = transform;
//# sourceMappingURL=transforms.js.map
;