myst-parser
Version:
Markdown parser for MyST markdown in JavaScript
112 lines (111 loc) • 4.39 kB
JavaScript
import MarkdownIt from 'markdown-it';
import { defaultDirectives } from 'myst-directives';
import { defaultRoles } from 'myst-roles';
import { VFile } from 'vfile';
import { tlds } from './tlds.js';
import { EXCLUDE_TLDS, MARKDOWN_IT_CONFIG } from './config.js';
import { tokensToMyst } from './tokensToMyst.js';
import { mathPlugin, convertFrontMatter, frontMatterPlugin, colonFencePlugin, mystBlockPlugin, footnotePlugin, mystPlugin, deflistPlugin, tasklistPlugin, citationsPlugin, } from './plugins.js';
import { applyDirectives } from './directives.js';
import { applyRoles } from './roles.js';
import { visit } from 'unist-util-visit';
export const defaultOptions = {
markdownit: {
html: true,
},
extensions: {
smartquotes: true,
colonFences: true,
frontmatter: true,
math: true,
footnotes: true,
citations: true,
deflist: true,
tasklist: true,
tables: true,
blocks: true,
strikethrough: false,
},
mdast: {},
directives: defaultDirectives,
roles: defaultRoles,
};
function parseOptions(opts) {
var _a, _b, _c;
const parsedOpts = {
vfile: (_a = opts === null || opts === void 0 ? void 0 : opts.vfile) !== null && _a !== void 0 ? _a : new VFile(),
mdast: { ...defaultOptions.mdast, ...opts === null || opts === void 0 ? void 0 : opts.mdast },
markdownit: { ...defaultOptions.markdownit, ...opts === null || opts === void 0 ? void 0 : opts.markdownit },
extensions: { ...defaultOptions.extensions, ...opts === null || opts === void 0 ? void 0 : opts.extensions },
directives: [...defaultOptions.directives, ...((_b = opts === null || opts === void 0 ? void 0 : opts.directives) !== null && _b !== void 0 ? _b : [])],
roles: [...defaultOptions.roles, ...((_c = opts === null || opts === void 0 ? void 0 : opts.roles) !== null && _c !== void 0 ? _c : [])],
};
return parsedOpts;
}
export function createTokenizer(opts) {
const parsedOpts = parseOptions(opts);
const { extensions, markdownit } = parsedOpts;
const tokenizer = MarkdownIt({
...MARKDOWN_IT_CONFIG,
options: {
...MARKDOWN_IT_CONFIG.options,
typographer: extensions.smartquotes ? true : false,
},
}, markdownit);
if (markdownit.linkify) {
tokenizer.linkify.tlds(tlds.filter((tld) => !EXCLUDE_TLDS.includes(tld)));
}
if (extensions.strikethrough)
tokenizer.enable('strikethrough');
if (extensions.smartquotes)
tokenizer.enable('smartquotes');
if (extensions.tables)
tokenizer.enable('table');
if (extensions.colonFences)
tokenizer.use(colonFencePlugin);
if (extensions.frontmatter)
tokenizer.use(frontMatterPlugin, () => ({})).use(convertFrontMatter);
if (extensions.blocks)
tokenizer.use(mystBlockPlugin);
if (extensions.footnotes)
tokenizer.use(footnotePlugin).disable('footnote_inline'); // not yet implemented in myst-parser
if (extensions.citations)
tokenizer.use(citationsPlugin);
tokenizer.use(mystPlugin);
if (extensions.math)
tokenizer.use(mathPlugin, extensions.math);
if (extensions.deflist)
tokenizer.use(deflistPlugin);
if (extensions.tasklist)
tokenizer.use(tasklistPlugin);
return tokenizer;
}
export function mystParse(content, opts) {
const { vfile } = opts || {};
const parsedOpts = parseOptions(opts);
const tokenizer = createTokenizer(parsedOpts);
const tree = tokensToMyst(content, tokenizer.parse(content, { vfile }), parsedOpts.mdast);
applyDirectives(tree, parsedOpts.directives, parsedOpts.vfile, {
parseMyst: (source, offset = 0) => {
const mdast = mystParse(source, opts);
// Fix-up the node's (global) position offsets
visit(mdast, (node) => {
if (node.position) {
node.position.start.line += offset;
node.position.end.line += offset;
}
});
return mdast;
},
});
applyRoles(tree, parsedOpts.roles, parsedOpts.vfile);
return tree;
}
/**
* MyST Parser as a Unified Plugin
*/
export const mystParser = function mystParser(opts) {
this.Parser = (content) => {
return mystParse(content, opts);
};
};