Move markdown-it token manipulation code into a separate file which is only require()-ed if/when needed.

This commit is contained in:
David Anson 2024-09-14 17:33:46 -07:00
parent a65e05bff2
commit 98ff66209d
6 changed files with 384 additions and 344 deletions

View file

@ -197,87 +197,6 @@ function removeFrontMatter(content, frontMatter) {
};
}
/**
* Freeze all freeze-able members of a token and its children.
*
* @param {MarkdownItToken} token A markdown-it token.
* @returns {void}
*/
function freezeToken(token) {
if (token.attrs) {
for (const attr of token.attrs) {
Object.freeze(attr);
}
Object.freeze(token.attrs);
}
if (token.children) {
for (const child of token.children) {
freezeToken(child);
}
Object.freeze(token.children);
}
if (token.map) {
Object.freeze(token.map);
}
Object.freeze(token);
}
/**
* Annotate tokens with line/lineNumber and freeze them.
*
* @param {import("markdown-it").Token[]} tokens Array of markdown-it tokens.
* @param {string[]} lines Lines of Markdown content.
* @returns {void}
*/
function annotateAndFreezeTokens(tokens, lines) {
let trMap = null;
// eslint-disable-next-line jsdoc/valid-types
/** @type MarkdownItToken[] */
// @ts-ignore
const markdownItTokens = tokens;
for (const token of markdownItTokens) {
// Provide missing maps for table content
if (token.type === "tr_open") {
trMap = token.map;
} else if (token.type === "tr_close") {
trMap = null;
}
if (!token.map && trMap) {
token.map = [ ...trMap ];
}
// Update token metadata
if (token.map) {
token.line = lines[token.map[0]];
token.lineNumber = token.map[0] + 1;
// Trim bottom of token to exclude whitespace lines
while (token.map[1] && !((lines[token.map[1] - 1] || "").trim())) {
token.map[1]--;
}
}
// Annotate children with lineNumber
if (token.children) {
const codeSpanExtraLines = [];
if (token.children.some((child) => child.type === "code_inline")) {
helpers.forEachInlineCodeSpan(token.content, (code) => {
codeSpanExtraLines.push(code.split(helpers.newLineRe).length - 1);
});
}
let lineNumber = token.lineNumber;
for (const child of token.children) {
child.lineNumber = lineNumber;
child.line = lines[lineNumber - 1];
if ((child.type === "softbreak") || (child.type === "hardbreak")) {
lineNumber++;
} else if (child.type === "code_inline") {
lineNumber += codeSpanExtraLines.shift();
}
}
}
freezeToken(token);
}
Object.freeze(tokens);
}
/**
* Map rule names/tags to canonical rule name.
*
@ -533,7 +452,7 @@ function getEnabledRulesPerLineNumber(
* names.
* @param {string} name Identifier for the content.
* @param {string} content Markdown content.
* @param {GetMarkdownIt} getMarkdownIt Getter for instance of markdown-it.
* @param {Plugin[]} markdownItPlugins Additional plugins.
* @param {Configuration} config Configuration object.
* @param {ConfigurationParser[] | null} configParsers Configuration parsers.
* @param {RegExp | null} frontMatter Regular expression for front matter.
@ -548,7 +467,7 @@ function lintContent(
aliasToRuleNames,
name,
content,
getMarkdownIt,
markdownItPlugins,
config,
configParsers,
frontMatter,
@ -577,13 +496,15 @@ function lintContent(
(rule) => (rule.parser === "markdownit") || (rule.parser === undefined)
);
// Parse content into parser tokens
const markdownitTokens = needMarkdownItTokens ? getMarkdownIt().parse(content, {}) : [];
const micromarkTokens = micromark.parse(content);
// Hide the content of HTML comments from rules
const preClearedContent = content;
content = helpers.clearHtmlCommentText(content);
// Parse content into lines and update markdown-it tokens
// Parse content into lines and get markdown-it tokens
const lines = content.split(helpers.newLineRe);
annotateAndFreezeTokens(markdownitTokens, lines);
const markdownitTokens = needMarkdownItTokens ?
require("./markdownit.cjs").getMarkdownItTokens(markdownItPlugins, preClearedContent, lines) :
[];
// Create (frozen) parameters for rules
/** @type {MarkdownParsers} */
// @ts-ignore
@ -836,7 +757,7 @@ function lintContent(
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
* names.
* @param {string} file Path of file to lint.
* @param {GetMarkdownIt} getMarkdownIt Getter for instance of markdown-it.
* @param {Plugin[]} markdownItPlugins Additional plugins.
* @param {Configuration} config Configuration object.
* @param {ConfigurationParser[] | null} configParsers Configuration parsers.
* @param {RegExp | null} frontMatter Regular expression for front matter.
@ -852,7 +773,7 @@ function lintFile(
ruleList,
aliasToRuleNames,
file,
getMarkdownIt,
markdownItPlugins,
config,
configParsers,
frontMatter,
@ -872,7 +793,7 @@ function lintFile(
aliasToRuleNames,
file,
content,
getMarkdownIt,
markdownItPlugins,
config,
configParsers,
frontMatter,
@ -939,16 +860,7 @@ function lintInput(options, synchronous, callback) {
const resultVersion = (options.resultVersion === undefined) ?
3 :
options.resultVersion;
const getMarkdownIt = () => {
const markdownit = require("markdown-it");
const md = markdownit({ "html": true });
const markdownItPlugins = options.markdownItPlugins || [];
for (const plugin of markdownItPlugins) {
// @ts-ignore
md.use(...plugin);
}
return md;
};
const markdownItPlugins = options.markdownItPlugins || [];
const fs = options.fs || require("node:fs");
const aliasToRuleNames = mapAliasToRuleNames(ruleList);
const results = newResults(ruleList);
@ -980,7 +892,7 @@ function lintInput(options, synchronous, callback) {
ruleList,
aliasToRuleNames,
currentItem,
getMarkdownIt,
markdownItPlugins,
config,
configParsers,
frontMatter,
@ -999,7 +911,7 @@ function lintInput(options, synchronous, callback) {
aliasToRuleNames,
currentItem,
strings[currentItem] || "",
getMarkdownIt,
markdownItPlugins,
config,
configParsers,
frontMatter,
@ -1314,13 +1226,6 @@ module.exports = markdownlint;
// Type declarations
/**
* Function to get an instance of the markdown-it parser.
*
* @callback GetMarkdownIt
* @returns {import("markdown-it")}
*/
/**
* Function to implement rule logic.
*