mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-12-17 06:20:12 +01:00
117 lines
2.9 KiB
JavaScript
117 lines
2.9 KiB
JavaScript
// @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
|
|
};
|