markdownlint/helpers/micromark.cjs

118 lines
2.9 KiB
JavaScript
Raw Normal View History

// @ts-check
"use strict";
/* eslint-disable n/no-unpublished-require */
// @ts-ignore
const { parse, postprocess, preprocess } = require("../micromark/micromark.cjs");
/**
* Markdown token.
*
* @typedef {Object} Token
* @property {string} type Token type.
* @property {number} startLine Start line (1-based).
* @property {number} startColumn Start column (1-based).
* @property {number} endLine End line (1-based).
* @property {number} endColumn End column (1-based).
* @property {string} text Token text.
* @property {Token[]} tokens Child tokens.
*/
/**
* Parses a Markdown document and returns (frozen) tokens.
*
* @param {string} markdown Markdown document.
* @param {Object} [options] Options for micromark.
* @returns {Token[]} Micromark tokens (frozen).
*/
function micromarkParse(markdown, options) {
// Use micromark to parse document into Events
const encoding = undefined;
const eol = true;
const chunks = preprocess()(markdown, encoding, eol);
const parseContext = parse(options).document().write(chunks);
const events = postprocess(parseContext);
// Create Token objects
const document = [];
let current = {
"tokens": document
};
const history = [ current ];
for (const event of events) {
const [ kind, token, context ] = event;
const { type, start, end } = token;
const { "column": startColumn, "line": startLine } = start;
const { "column": endColumn, "line": endLine } = end;
let text = null;
try {
text = context.sliceSerialize(token);
} catch {
// https://github.com/micromark/micromark/issues/131
}
if (kind === "enter") {
const previous = current;
history.push(previous);
current = {
type,
startLine,
startColumn,
endLine,
endColumn,
text,
"tokens": []
};
previous.tokens.push(current);
} else if (kind === "exit") {
Object.freeze(current.tokens);
Object.freeze(current);
// @ts-ignore
current = history.pop();
}
}
// Return document
Object.freeze(document);
return document;
}
/**
* Filter a list of Micromark tokens by predicate.
*
* @param {Token[]} tokens Micromark tokens.
* @param {Function} predicate Filter predicate.
* @returns {Token[]} Filtered tokens.
*/
function filterByPredicate(tokens, predicate) {
const result = [];
const pending = [ ...tokens ];
let token = null;
while ((token = pending.shift())) {
if (predicate(token)) {
result.push(token);
}
pending.unshift(...token.tokens);
}
return result;
}
/**
* Filter a list of Micromark tokens by type.
*
* @param {Token[]} tokens Micromark tokens.
* @param {string[]} types Types to allow.
* @returns {Token[]} Filtered tokens.
*/
function filterByTypes(tokens, ...types) {
return filterByPredicate(tokens, (token) => types.includes(token.type));
}
module.exports = {
filterByPredicate,
filterByTypes,
"parse": micromarkParse
};