Cache all top-level calls to filterByTypes (~7% runtime reduction).

This commit is contained in:
David Anson 2024-08-24 22:05:16 -07:00
parent 85e704f32a
commit dfcb4529f3
47 changed files with 427 additions and 481 deletions

File diff suppressed because it is too large Load diff

View file

@ -2,14 +2,69 @@
"use strict";
const helpers = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
/** @type {Map<string, object>} */
const map = new Map();
// eslint-disable-next-line no-undef-init
let params = undefined;
module.exports.set = (keyValuePairs) => {
for (const [ key, value ] of Object.entries(keyValuePairs)) {
map.set(key, value);
/**
* Initializes (resets) the cache.
*
* @param {import("./markdownlint").RuleParams} [p] Rule parameters object.
* @returns {void}
*/
function initialize(p) {
map.clear();
params = p;
}
/**
* Gets a cached object value - computes it and caches it.
*
* @param {string} name Cache object name.
* @param {Function} getValue Getter for object value.
* @returns {Object} Object value.
*/
function getCached(name, getValue) {
if (map.has(name)) {
return map.get(name);
}
};
module.exports.clear = () => map.clear();
const value = getValue();
map.set(name, value);
return value;
}
module.exports.referenceLinkImageData =
() => map.get("referenceLinkImageData");
/**
* Filters a list of Micromark tokens by type and caches the result.
*
* @param {import("./markdownlint").MicromarkTokenType[]} types Types to allow.
* @param {boolean} [htmlFlow] Whether to include htmlFlow content.
* @returns {import("./markdownlint").MicromarkToken[]} Filtered tokens.
*/
function filterByTypesCached(types, htmlFlow) {
return getCached(
types.join("|"),
() => filterByTypes(params.parsers.micromark.tokens, types, htmlFlow)
);
}
/**
* Gets a reference link and image data object.
*
* @returns {Object} Reference link and image data object.
*/
function getReferenceLinkImageData() {
return getCached(
getReferenceLinkImageData.name,
() => helpers.getReferenceLinkImageData(params.parsers.micromark.tokens)
);
}
module.exports = {
initialize,
filterByTypesCached,
getReferenceLinkImageData
};

View file

@ -604,14 +604,13 @@ function lintContent(
const parsersNone = Object.freeze({});
const paramsBase = {
name,
"parsers": parsersMarkdownIt,
"lines": Object.freeze(lines),
"frontMatterLines": Object.freeze(frontMatterLines)
};
const referenceLinkImageData =
helpers.getReferenceLinkImageData(micromarkTokens);
cache.set({
referenceLinkImageData
cache.initialize({
...paramsBase,
"parsers": parsersMicromark,
"config": null
});
// Function to run for each rule
let results = [];
@ -826,7 +825,7 @@ function lintContent(
} catch (error) {
callbackError(error);
} finally {
cache.clear();
cache.initialize();
}
}

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorDetailIf } = require("../helpers");
const { filterByTypes, getHeadingLevel } = require("../helpers/micromark.cjs");
const { getHeadingLevel } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -14,11 +15,7 @@ module.exports = {
"parser": "micromark",
"function": function MD001(params, onError) {
let prevLevel = Number.MAX_SAFE_INTEGER;
const headings = filterByTypes(
params.parsers.micromark.tokens,
[ "atxHeading", "setextHeading" ]
);
for (const heading of headings) {
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
const level = getHeadingLevel(heading);
if (level > prevLevel) {
addErrorDetailIf(

View file

@ -3,8 +3,8 @@
"use strict";
const { addErrorDetailIf } = require("../helpers");
const { filterByTypes, getHeadingLevel, getHeadingStyle } =
require("../helpers/micromark.cjs");
const { getHeadingLevel, getHeadingStyle } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -15,11 +15,7 @@ module.exports = {
"parser": "micromark",
"function": function MD003(params, onError) {
let style = String(params.config.style || "consistent");
const headings = filterByTypes(
params.parsers.micromark.tokens,
[ "atxHeading", "setextHeading" ]
);
for (const heading of headings) {
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
const styleForToken = getHeadingStyle(heading);
if (style === "consistent") {
style = styleForToken;

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorDetailIf } = require("../helpers");
const { filterByTypes, getDescendantsByType, getTokenParentOfType } = require("../helpers/micromark.cjs");
const { getDescendantsByType, getTokenParentOfType } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const markerToStyle = {
"-": "dash",
@ -39,11 +40,12 @@ module.exports = {
const style = String(params.config.style || "consistent");
let expectedStyle = validStyles.has(style) ? style : "dash";
const nestingStyles = [];
for (const listUnordered of filterByTypes(params.parsers.micromark.tokens, [ "listUnordered" ])) {
for (const listUnordered of filterByTypesCached([ "listUnordered" ])) {
let nesting = 0;
if (style === "sublist") {
/** @type {import("../helpers/micromark.cjs").Token | null} */
let parent = listUnordered;
// @ts-ignore
while ((parent = getTokenParentOfType(parent, [ "listOrdered", "listUnordered" ]))) {
nesting++;
}

View file

@ -3,7 +3,7 @@
"use strict";
const { addError, addErrorDetailIf } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -13,11 +13,7 @@ module.exports = {
"tags": [ "bullet", "ul", "indentation" ],
"parser": "micromark",
"function": function MD005(params, onError) {
const lists = filterByTypes(
params.parsers.micromark.tokens,
[ "listOrdered", "listUnordered" ]
);
for (const list of lists) {
for (const list of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
const expectedIndent = list.startColumn - 1;
let expectedEnd = 0;
let endMatching = false;

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorDetailIf } = require("../helpers");
const { filterByTypes, getTokenParentOfType } = require("../helpers/micromark.cjs");
const { getTokenParentOfType } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("markdownlint-micromark").TokenType[] */
@ -27,10 +28,7 @@ module.exports = {
const startIndent = Number(params.config.start_indent || indent);
const unorderedListNesting = new Map();
let lastBlockQuotePrefix = null;
const tokens = filterByTypes(
params.parsers.micromark.tokens,
unorderedListTypes
);
const tokens = filterByTypesCached(unorderedListTypes);
for (const token of tokens) {
const { endColumn, parent, startColumn, startLine, type } = token;
if (type === "blockQuotePrefix") {
@ -40,6 +38,7 @@ module.exports = {
/** @type {import("../helpers/micromark.cjs").Token | null} */
let current = token;
while (
// @ts-ignore
(current = getTokenParentOfType(current, unorderedParentTypes))
) {
if (current.type === "listUnordered") {

View file

@ -3,7 +3,8 @@
"use strict";
const { addError } = require("../helpers");
const { addRangeToSet, filterByTypes } = require("../helpers/micromark.cjs");
const { addRangeToSet } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -17,17 +18,16 @@ module.exports = {
brSpaces = Number((brSpaces === undefined) ? 2 : brSpaces);
const listItemEmptyLines = !!params.config.list_item_empty_lines;
const strict = !!params.config.strict;
const { tokens } = params.parsers.micromark;
const codeBlockLineNumbers = new Set();
for (const codeBlock of filterByTypes(tokens, [ "codeFenced" ])) {
for (const codeBlock of filterByTypesCached([ "codeFenced" ])) {
addRangeToSet(codeBlockLineNumbers, codeBlock.startLine + 1, codeBlock.endLine - 1);
}
for (const codeBlock of filterByTypes(tokens, [ "codeIndented" ])) {
for (const codeBlock of filterByTypesCached([ "codeIndented" ])) {
addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
}
const listItemLineNumbers = new Set();
if (listItemEmptyLines) {
for (const listBlock of filterByTypes(tokens, [ "listOrdered", "listUnordered" ])) {
for (const listBlock of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
addRangeToSet(listItemLineNumbers, listBlock.startLine, listBlock.endLine);
let trailingIndent = true;
for (let i = listBlock.children.length - 1; i >= 0; i--) {
@ -53,10 +53,10 @@ module.exports = {
const paragraphLineNumbers = new Set();
const codeInlineLineNumbers = new Set();
if (strict) {
for (const paragraph of filterByTypes(tokens, [ "paragraph" ])) {
for (const paragraph of filterByTypesCached([ "paragraph" ])) {
addRangeToSet(paragraphLineNumbers, paragraph.startLine, paragraph.endLine - 1);
}
for (const codeText of filterByTypes(tokens, [ "codeText" ])) {
for (const codeText of filterByTypesCached([ "codeText" ])) {
addRangeToSet(codeInlineLineNumbers, codeText.startLine, codeText.endLine - 1);
}
}

View file

@ -3,7 +3,8 @@
"use strict";
const { addError, withinAnyRange } = require("../helpers");
const { filterByTypes, getDescendantsByType, getExclusionsForToken } = require("../helpers/micromark.cjs");
const { getDescendantsByType, getExclusionsForToken } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const tabRe = /\t+/g;
@ -36,10 +37,7 @@ module.exports = {
} else {
exclusionTypes.push("codeFenced", "codeIndented", "codeText");
}
const codeTokens = filterByTypes(
params.parsers.micromark.tokens,
exclusionTypes
).filter((token) => {
const codeTokens = filterByTypesCached(exclusionTypes).filter((token) => {
if ((token.type === "codeFenced") && (ignoreCodeLanguages.size > 0)) {
const fenceInfos = getDescendantsByType(token, [ "codeFencedFence", "codeFencedFenceInfo" ]);
return fenceInfos.every((fenceInfo) => ignoreCodeLanguages.has(fenceInfo.text.toLowerCase()));

View file

@ -3,7 +3,8 @@
"use strict";
const { addError, withinAnyRange } = require("../helpers");
const { addRangeToSet, getExclusionsForToken, filterByTypes } = require("../helpers/micromark.cjs");
const { addRangeToSet, getExclusionsForToken } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const reversedLinkRe =
/(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)\](?!\()/g;
@ -16,13 +17,12 @@ module.exports = {
"tags": [ "links" ],
"parser": "micromark",
"function": function MD011(params, onError) {
const { tokens } = params.parsers.micromark;
const codeBlockLineNumbers = new Set();
for (const codeBlock of filterByTypes(tokens, [ "codeFenced", "codeIndented" ])) {
for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
}
const exclusions = [];
for (const codeText of filterByTypes(tokens, [ "codeText" ])) {
for (const codeText of filterByTypesCached([ "codeText" ])) {
exclusions.push(...getExclusionsForToken(params.lines, codeText));
}
for (const [ lineIndex, line ] of params.lines.entries()) {

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorDetailIf } = require("../helpers");
const { addRangeToSet, filterByTypes } = require("../helpers/micromark.cjs");
const { addRangeToSet } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -14,9 +15,9 @@ module.exports = {
"parser": "micromark",
"function": function MD012(params, onError) {
const maximum = Number(params.config.maximum || 1);
const { lines, parsers } = params;
const { lines } = params;
const codeBlockLineNumbers = new Set();
for (const codeBlock of filterByTypes(parsers.micromark.tokens, [ "codeFenced", "codeIndented" ])) {
for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
}
let count = 0;

View file

@ -3,8 +3,9 @@
"use strict";
const { addErrorDetailIf } = require("../helpers");
const { referenceLinkImageData } = require("./cache");
const { addRangeToSet, filterByTypes, getDescendantsByType } = require("../helpers/micromark.cjs");
const { getReferenceLinkImageData } = require("./cache");
const { addRangeToSet, getDescendantsByType } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const longLineRePrefix = "^.{";
const longLineRePostfixRelaxed = "}.*\\s.*$";
@ -40,25 +41,24 @@ module.exports = {
const includeTables = (tables === undefined) ? true : !!tables;
const headings = params.config.headings;
const includeHeadings = (headings === undefined) ? true : !!headings;
const { tokens } = params.parsers.micromark;
const headingLineNumbers = new Set();
for (const heading of filterByTypes(tokens, [ "atxHeading", "setextHeading" ])) {
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
addRangeToSet(headingLineNumbers, heading.startLine, heading.endLine);
}
const codeBlockLineNumbers = new Set();
for (const codeBlock of filterByTypes(tokens, [ "codeFenced", "codeIndented" ])) {
for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
}
const tableLineNumbers = new Set();
for (const table of filterByTypes(tokens, [ "table" ])) {
for (const table of filterByTypesCached([ "table" ])) {
addRangeToSet(tableLineNumbers, table.startLine, table.endLine);
}
const linkLineNumbers = new Set();
for (const link of filterByTypes(tokens, [ "autolink", "image", "link", "literalAutolink" ])) {
for (const link of filterByTypesCached([ "autolink", "image", "link", "literalAutolink" ])) {
addRangeToSet(linkLineNumbers, link.startLine, link.endLine);
}
const paragraphDataLineNumbers = new Set();
for (const paragraph of filterByTypes(tokens, [ "paragraph" ])) {
for (const paragraph of filterByTypesCached([ "paragraph" ])) {
for (const data of getDescendantsByType(paragraph, [ "data" ])) {
addRangeToSet(paragraphDataLineNumbers, data.startLine, data.endLine);
}
@ -69,7 +69,7 @@ module.exports = {
linkOnlyLineNumbers.add(lineNumber);
}
}
const definitionLineIndices = new Set(referenceLinkImageData().definitionLineIndices);
const definitionLineIndices = new Set(getReferenceLinkImageData().definitionLineIndices);
for (let lineIndex = 0; lineIndex < params.lines.length; lineIndex++) {
const line = params.lines[lineIndex];
const lineNumber = lineIndex + 1;

View file

@ -4,6 +4,7 @@
const { addErrorContext } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const dollarCommandRe = /^(\s*)(\$\s+)/;
@ -15,11 +16,7 @@ module.exports = {
"tags": [ "code" ],
"parser": "micromark",
"function": function MD014(params, onError) {
const codeBlocks = filterByTypes(
params.parsers.micromark.tokens,
[ "codeFenced", "codeIndented" ]
);
for (const codeBlock of codeBlocks) {
for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
const codeFlowValues = filterByTypes(
codeBlock.children,
[ "codeFlowValue" ]

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorContext } = require("../helpers");
const { addRangeToSet, filterByTypes } = require("../helpers/micromark.cjs");
const { addRangeToSet } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -13,9 +14,9 @@ module.exports = {
"tags": [ "headings", "atx", "spaces" ],
"parser": "micromark",
"function": function MD018(params, onError) {
const { lines, parsers } = params;
const { lines } = params;
const ignoreBlockLineNumbers = new Set();
for (const ignoreBlock of filterByTypes(parsers.micromark.tokens, [ "codeIndented", "codeFenced", "htmlFlow" ])) {
for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "htmlFlow" ])) {
addRangeToSet(ignoreBlockLineNumbers, ignoreBlock.startLine, ignoreBlock.endLine);
}
for (const [ lineIndex, line ] of lines.entries()) {

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorContext } = require("../helpers/helpers");
const { filterByTypes, getHeadingStyle } = require("../helpers/micromark.cjs");
const { getHeadingStyle } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
/**
* Validate heading sequence and whitespace length at start or end.
@ -55,10 +56,8 @@ module.exports = [
"tags": [ "headings", "atx", "spaces" ],
"parser": "micromark",
"function": function MD019(params, onError) {
const atxHeadings = filterByTypes(
params.parsers.micromark.tokens,
[ "atxHeading" ]
).filter((heading) => getHeadingStyle(heading) === "atx");
const atxHeadings = filterByTypesCached([ "atxHeading" ])
.filter((heading) => getHeadingStyle(heading) === "atx");
for (const atxHeading of atxHeadings) {
validateHeadingSpaces(onError, atxHeading, 1);
}
@ -70,10 +69,8 @@ module.exports = [
"tags": [ "headings", "atx_closed", "spaces" ],
"parser": "micromark",
"function": function MD021(params, onError) {
const atxClosedHeadings = filterByTypes(
params.parsers.micromark.tokens,
[ "atxHeading" ]
).filter((heading) => getHeadingStyle(heading) === "atx_closed");
const atxClosedHeadings = filterByTypesCached([ "atxHeading" ])
.filter((heading) => getHeadingStyle(heading) === "atx_closed");
for (const atxClosedHeading of atxClosedHeadings) {
validateHeadingSpaces(onError, atxClosedHeading, 1);
validateHeadingSpaces(onError, atxClosedHeading, -1);

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorContext } = require("../helpers");
const { addRangeToSet, filterByTypes } = require("../helpers/micromark.cjs");
const { addRangeToSet } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -13,9 +14,9 @@ module.exports = {
"tags": [ "headings", "atx_closed", "spaces" ],
"parser": "micromark",
"function": function MD020(params, onError) {
const { lines, parsers } = params;
const { lines } = params;
const ignoreBlockLineNumbers = new Set();
for (const ignoreBlock of filterByTypes(parsers.micromark.tokens, [ "codeIndented", "codeFenced", "htmlFlow" ])) {
for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "htmlFlow" ])) {
addRangeToSet(ignoreBlockLineNumbers, ignoreBlock.startLine, ignoreBlock.endLine);
}
for (const [ lineIndex, line ] of lines.entries()) {

View file

@ -2,9 +2,9 @@
"use strict";
const { addErrorDetailIf, blockquotePrefixRe, isBlankLine } =
require("../helpers");
const { filterByTypes, getHeadingLevel } = require("../helpers/micromark.cjs");
const { addErrorDetailIf, blockquotePrefixRe, isBlankLine } = require("../helpers");
const { getHeadingLevel } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const defaultLines = 1;
@ -41,12 +41,7 @@ module.exports = {
const getLinesAbove = getLinesFunction(params.config.lines_above);
const getLinesBelow = getLinesFunction(params.config.lines_below);
const { lines } = params;
const headings = filterByTypes(
// eslint-disable-next-line unicorn/consistent-destructuring
params.parsers.micromark.tokens,
[ "atxHeading", "setextHeading" ]
);
for (const heading of headings) {
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
const { startLine, endLine } = heading;
const line = lines[startLine - 1].trim();

View file

@ -3,7 +3,7 @@
"use strict";
const { addErrorContext } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -13,10 +13,7 @@ module.exports = {
"tags": [ "headings", "spaces" ],
"parser": "micromark",
"function": function MD023(params, onError) {
const headings = filterByTypes(
params.parsers.micromark.tokens,
[ "atxHeading", "linePrefix", "setextHeading" ]
);
const headings = filterByTypesCached([ "atxHeading", "linePrefix", "setextHeading" ]);
for (let i = 0; i < headings.length - 1; i++) {
if (
(headings[i].type === "linePrefix") &&

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorContext } = require("../helpers");
const { filterByTypes, getHeadingLevel, getHeadingText } = require("../helpers/micromark.cjs");
const { getHeadingLevel, getHeadingText } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -17,11 +18,7 @@ module.exports = {
const knownContents = [ null, [] ];
let lastLevel = 1;
let knownContent = knownContents[lastLevel];
const headings = filterByTypes(
params.parsers.micromark.tokens,
[ "atxHeading", "setextHeading" ]
);
for (const heading of headings) {
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
const headingText = getHeadingText(heading);
if (siblingsOnly) {
const newLevel = getHeadingLevel(heading);

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorContext, frontMatterHasTitle } = require("../helpers");
const { filterByTypes, getHeadingLevel, getHeadingText } = require("../helpers/micromark.cjs");
const { getHeadingLevel, getHeadingText } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -20,11 +21,7 @@ module.exports = {
params.config.front_matter_title
);
let hasTopLevelHeading = false;
const headings = filterByTypes(
params.parsers.micromark.tokens,
[ "atxHeading", "setextHeading" ]
);
for (const heading of headings) {
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
const headingLevel = getHeadingLevel(heading);
if (headingLevel === level) {
if (hasTopLevelHeading || foundFrontMatterTitle) {

View file

@ -4,7 +4,7 @@
const { addError, allPunctuationNoQuestion, endOfLineGemojiCodeRe,
endOfLineHtmlEntityRe, escapeForRegExp } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -20,10 +20,7 @@ module.exports = {
);
const trailingPunctuationRe =
new RegExp("\\s*[" + escapeForRegExp(punctuation) + "]+$");
const headings = filterByTypes(
params.parsers.micromark.tokens,
[ "atxHeadingText", "setextHeadingText" ]
);
const headings = filterByTypesCached([ "atxHeadingText", "setextHeadingText" ]);
for (const heading of headings) {
const { endColumn, endLine, text } = heading;
const match = trailingPunctuationRe.exec(text);

View file

@ -3,7 +3,7 @@
"use strict";
const { addErrorContext } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -13,9 +13,8 @@ module.exports = {
"tags": ["blockquote", "whitespace", "indentation"],
"parser": "micromark",
"function": function MD027(params, onError) {
const micromarkTokens = params.parsers.micromark.tokens;
for (const token of filterByTypes(micromarkTokens, [ "linePrefix" ])) {
const siblings = token.parent?.children || micromarkTokens;
for (const token of filterByTypesCached([ "linePrefix" ])) {
const siblings = token.parent?.children || params.parsers.micromark.tokens;
if (siblings[siblings.indexOf(token) - 1]?.type === "blockQuotePrefix") {
const { startColumn, startLine, text } = token;
const { length } = text;

View file

@ -3,7 +3,7 @@
"use strict";
const { addError } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const ignoreTypes = new Set([ "lineEnding", "listItemIndent", "linePrefix" ]);
@ -15,10 +15,9 @@ module.exports = {
"tags": [ "blockquote", "whitespace" ],
"parser": "micromark",
"function": function MD028(params, onError) {
const micromarkTokens = params.parsers.micromark.tokens;
for (const token of filterByTypes(micromarkTokens, [ "blockQuote" ])) {
for (const token of filterByTypesCached([ "blockQuote" ])) {
const errorLineNumbers = [];
const siblings = token.parent?.children || micromarkTokens;
const siblings = token.parent?.children || params.parsers.micromark.tokens;
for (let i = siblings.indexOf(token) + 1; i < siblings.length; i++) {
const sibling = siblings[i];
const { startLine, type } = sibling;

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorDetailIf } = require("../helpers");
const { filterByTypes, getDescendantsByType, getTokenTextByType } = require("../helpers/micromark.cjs");
const { getDescendantsByType, getTokenTextByType } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const listStyleExamples = {
"one": "1/1/1",
@ -30,7 +31,7 @@ module.exports = {
"parser": "micromark",
"function": function MD029(params, onError) {
const style = String(params.config.style || "one_or_ordered");
for (const listOrdered of filterByTypes(params.parsers.micromark.tokens, [ "listOrdered" ])) {
for (const listOrdered of filterByTypesCached([ "listOrdered" ])) {
const listItemPrefixes = getDescendantsByType(listOrdered, [ "listItemPrefix" ]);
let expected = 1;
let incrementing = false;

View file

@ -3,7 +3,7 @@
"use strict";
const { addErrorDetailIf } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -17,11 +17,7 @@ module.exports = {
const olSingle = Number(params.config.ol_single || 1);
const ulMulti = Number(params.config.ul_multi || 1);
const olMulti = Number(params.config.ol_multi || 1);
const lists = filterByTypes(
params.parsers.micromark.tokens,
[ "listOrdered", "listUnordered" ]
);
for (const list of lists) {
for (const list of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
const ordered = (list.type === "listOrdered");
const listItemPrefixes =
list.children.filter((token) => (token.type === "listItemPrefix"));

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorContext, isBlankLine } = require("../helpers");
const { filterByTypes, getTokenParentOfType } = require("../helpers/micromark.cjs");
const { getTokenParentOfType } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const codeFencePrefixRe = /^(.*?)[`~]/;
@ -47,8 +48,8 @@ module.exports = {
"function": function MD031(params, onError) {
const listItems = params.config.list_items;
const includeListItems = (listItems === undefined) ? true : !!listItems;
const { lines, parsers } = params;
for (const codeBlock of filterByTypes(parsers.micromark.tokens, [ "codeFenced" ])) {
const { lines } = params;
for (const codeBlock of filterByTypesCached([ "codeFenced" ])) {
if (includeListItems || !(getTokenParentOfType(codeBlock, [ "listOrdered", "listUnordered" ]))) {
if (!isBlankLine(lines[codeBlock.startLine - 2])) {
addError(onError, lines, codeBlock.startLine, true);

View file

@ -3,8 +3,8 @@
"use strict";
const { addError, nextLinesRe } = require("../helpers");
const { filterByTypes, getHtmlTagInfo } =
require("../helpers/micromark.cjs");
const { getHtmlTagInfo } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -17,7 +17,7 @@ module.exports = {
let allowedElements = params.config.allowed_elements;
allowedElements = Array.isArray(allowedElements) ? allowedElements : [];
allowedElements = allowedElements.map((element) => element.toLowerCase());
for (const token of filterByTypes(params.parsers.micromark.tokens, [ "htmlText" ], true)) {
for (const token of filterByTypesCached([ "htmlText" ], true)) {
const htmlTagInfo = getHtmlTagInfo(token);
if (
htmlTagInfo &&

View file

@ -3,8 +3,8 @@
"use strict";
const { addErrorContext } = require("../helpers");
const { filterByPredicate, filterByTypes, getHtmlTagInfo, inHtmlFlow, parse } =
require("../helpers/micromark.cjs");
const { filterByPredicate, getHtmlTagInfo, inHtmlFlow, parse } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -70,10 +70,7 @@ module.exports = {
}
)
);
const autoLinks = filterByTypes(
params.parsers.micromark.tokens,
[ "literalAutolink" ]
);
const autoLinks = filterByTypesCached([ "literalAutolink" ]);
if (autoLinks.length > 0) {
// Re-parse with correct link/image reference definition handling
const document = params.lines.join("\n");

View file

@ -3,7 +3,7 @@
"use strict";
const { addErrorDetailIf } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -14,10 +14,7 @@ module.exports = {
"parser": "micromark",
"function": function MD035(params, onError) {
let style = String(params.config.style || "consistent").trim();
const thematicBreaks = filterByTypes(
params.parsers.micromark.tokens,
[ "thematicBreak" ]
);
const thematicBreaks = filterByTypesCached([ "thematicBreak" ]);
for (const token of thematicBreaks) {
const { startLine, text } = token;
if (style === "consistent") {

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorContext, allPunctuation } = require("../helpers");
const { filterByTypes, matchAndGetTokensByType } = require("../helpers/micromark.cjs");
const { matchAndGetTokensByType } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
/** @typedef {import("../helpers/micromark.cjs").TokenType} TokenType */
/** @type {Map<TokenType, TokenType[]>} */
@ -24,7 +25,7 @@ module.exports = {
punctuation = String((punctuation === undefined) ? allPunctuation : punctuation);
const punctuationRe = new RegExp("[" + punctuation + "]$");
const paragraphTokens =
filterByTypes(params.parsers.micromark.tokens, [ "paragraph" ]).
filterByTypesCached([ "paragraph" ]).
filter((token) =>
(token.parent?.type === "content") && !token.parent?.parent && (token.children.length === 1)
);

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorContext } = require("../helpers");
const { filterByTypes, tokenIfType } = require("../helpers/micromark.cjs");
const { tokenIfType } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const leftSpaceRe = /^\s(?:[^`]|$)/;
const rightSpaceRe = /[^`]\s$/;
@ -26,10 +27,7 @@ module.exports = {
"tags": [ "whitespace", "code" ],
"parser": "micromark",
"function": function MD038(params, onError) {
const codeTexts = filterByTypes(
params.parsers.micromark.tokens,
[ "codeText" ]
);
const codeTexts = filterByTypesCached([ "codeText" ]);
for (const codeText of codeTexts) {
const { children } = codeText;
const first = 0;

View file

@ -4,7 +4,7 @@
const { addErrorContext } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { referenceLinkImageData } = require("./cache");
const { getReferenceLinkImageData, filterByTypesCached } = require("./cache");
/**
* Adds an error for a label space issue.
@ -56,11 +56,9 @@ module.exports = {
"tags": [ "whitespace", "links" ],
"parser": "micromark",
"function": function MD039(params, onError) {
const { definitions } = referenceLinkImageData();
const labels = filterByTypes(
params.parsers.micromark.tokens,
[ "label" ]
).filter((label) => label.parent?.type === "link");
const { definitions } = getReferenceLinkImageData();
const labels = filterByTypesCached([ "label" ]).
filter((label) => label.parent?.type === "link");
for (const label of labels) {
const labelTexts = filterByTypes(
label.children,

View file

@ -3,8 +3,8 @@
"use strict";
const { addError, addErrorContext } = require("../helpers");
const { filterByTypes, getTokenTextByType, tokenIfType } =
require("../helpers/micromark.cjs");
const { getTokenTextByType, tokenIfType } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -17,10 +17,7 @@ module.exports = {
let allowed = params.config.allowed_languages;
allowed = Array.isArray(allowed) ? allowed : [];
const languageOnly = !!params.config.language_only;
const fencedCodes = filterByTypes(
params.parsers.micromark.tokens,
[ "codeFenced" ]
);
const fencedCodes = filterByTypesCached([ "codeFenced" ]);
for (const fencedCode of fencedCodes) {
const openingFence = tokenIfType(fencedCode.children[0], "codeFencedFence");
if (openingFence) {

View file

@ -3,8 +3,8 @@
"use strict";
const { addErrorContext } = require("../helpers");
const { getDescendantsByType, filterByTypes } = require("../helpers/micromark.cjs");
const { referenceLinkImageData } = require("./cache");
const { getDescendantsByType } = require("../helpers/micromark.cjs");
const { getReferenceLinkImageData, filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -14,15 +14,12 @@ module.exports = {
"tags": [ "links" ],
"parser": "micromark",
"function": function MD042(params, onError) {
const { definitions } = referenceLinkImageData();
const { definitions } = getReferenceLinkImageData();
const isReferenceDefinitionHash = (token) => {
const definition = definitions.get(token.text.trim());
return (definition && (definition[1] === "#"));
};
const links = filterByTypes(
params.parsers.micromark.tokens,
[ "link" ]
);
const links = filterByTypesCached([ "link" ]);
for (const link of links) {
const labelText = getDescendantsByType(link, [ "label", "labelText" ]);
const reference = getDescendantsByType(link, [ "reference" ]);

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorContext, addErrorDetailIf } = require("../helpers");
const { filterByTypes, getHeadingLevel, getHeadingText } = require("../helpers/micromark.cjs");
const { getHeadingLevel, getHeadingText } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -25,11 +26,7 @@ module.exports = {
let anyHeadings = false;
const getExpected = () => requiredHeadings[i++] || "[None]";
const handleCase = (str) => (matchCase ? str : str.toLowerCase());
const headings = filterByTypes(
params.parsers.micromark.tokens,
[ "atxHeading", "setextHeading" ]
);
for (const heading of headings) {
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
if (!hasError) {
const headingText = getHeadingText(heading);
const headingLevel = getHeadingLevel(heading);

View file

@ -4,6 +4,7 @@
const { addError, getHtmlAttributeRe, nextLinesRe } = require("../helpers");
const { filterByTypes, getHtmlTagInfo } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const altRe = getHtmlAttributeRe("alt");
@ -15,13 +16,8 @@ module.exports = {
"tags": [ "accessibility", "images" ],
"parser": "micromark",
"function": function MD045(params, onError) {
const micromarkTokens = params.parsers.micromark.tokens;
// Process Markdown images
const images = filterByTypes(
micromarkTokens,
[ "image" ]
);
const images = filterByTypesCached([ "image" ]);
for (const image of images) {
const labelTexts = filterByTypes(image.children, [ "labelText" ]);
if (labelTexts.some((labelText) => labelText.text.length === 0)) {
@ -39,11 +35,7 @@ module.exports = {
}
// Process HTML images
const htmlTexts = filterByTypes(
micromarkTokens,
[ "htmlText" ],
true
);
const htmlTexts = filterByTypesCached([ "htmlText" ], true);
for (const htmlText of htmlTexts) {
const { startColumn, startLine, text } = htmlText;
const htmlTagInfo = getHtmlTagInfo(htmlText);

View file

@ -3,7 +3,7 @@
"use strict";
const { addErrorDetailIf } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const tokenTypeToStyle = {
"codeFenced": "fenced",
@ -19,11 +19,7 @@ module.exports = {
"parser": "micromark",
"function": function MD046(params, onError) {
let expectedStyle = String(params.config.style || "consistent");
const codeBlocksAndFences = filterByTypes(
params.parsers.micromark.tokens,
[ "codeFenced", "codeIndented" ]
);
for (const token of codeBlocksAndFences) {
for (const token of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
const { startLine, type } = token;
if (expectedStyle === "consistent") {
expectedStyle = tokenTypeToStyle[type];

View file

@ -3,7 +3,8 @@
"use strict";
const { addErrorDetailIf, fencedCodeBlockStyleFor } = require("../helpers");
const { filterByTypes, tokenIfType } = require("../helpers/micromark.cjs");
const { tokenIfType } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -15,10 +16,7 @@ module.exports = {
"function": function MD048(params, onError) {
const style = String(params.config.style || "consistent");
let expectedStyle = style;
const codeFenceds = filterByTypes(
params.parsers.micromark.tokens,
[ "codeFenced" ]
);
const codeFenceds = filterByTypesCached([ "codeFenced" ]);
for (const codeFenced of codeFenceds) {
const codeFencedFence = tokenIfType(codeFenced.children[0], "codeFencedFence");
if (codeFencedFence) {

View file

@ -2,10 +2,9 @@
"use strict";
const { addError, addErrorDetailIf, getHtmlAttributeRe } =
require("../helpers");
const { filterByPredicate, filterByTypes, getHtmlTagInfo } =
require("../helpers/micromark.cjs");
const { addError, addErrorDetailIf, getHtmlAttributeRe } = require("../helpers");
const { filterByPredicate, filterByTypes, getHtmlTagInfo } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// Regular expression for identifying HTML anchor names
const idRe = getHtmlAttributeRe("id");
@ -69,11 +68,10 @@ module.exports = {
"tags": [ "links" ],
"parser": "micromark",
"function": function MD051(params, onError) {
const micromarkTokens = params.parsers.micromark.tokens;
const fragments = new Map();
// Process headings
const headingTexts = filterByTypes(micromarkTokens, [ "atxHeadingText", "setextHeadingText" ]);
const headingTexts = filterByTypesCached([ "atxHeadingText", "setextHeadingText" ]);
for (const headingText of headingTexts) {
const fragment = convertHeadingToHTMLFragment(headingText);
if (fragment !== "#") {
@ -93,7 +91,7 @@ module.exports = {
}
// Process HTML anchors
for (const token of filterByTypes(micromarkTokens, [ "htmlText" ], true)) {
for (const token of filterByTypesCached([ "htmlText" ], true)) {
const htmlTagInfo = getHtmlTagInfo(token);
if (htmlTagInfo && !htmlTagInfo.close) {
const anchorMatch = idRe.exec(token.text) ||
@ -112,7 +110,7 @@ module.exports = {
[ "definition", "definitionDestinationString" ]
];
for (const [ parentType, definitionType ] of parentChilds) {
const links = filterByTypes(micromarkTokens, [ parentType ]);
const links = filterByTypesCached([ parentType ]);
for (const link of links) {
const definitions = filterByTypes(link.children, [ definitionType ]);
for (const definition of definitions) {

View file

@ -3,7 +3,7 @@
"use strict";
const { addError } = require("../helpers");
const { referenceLinkImageData } = require("./cache");
const { getReferenceLinkImageData } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -16,7 +16,7 @@ module.exports = {
"function": function MD052(params, onError) {
const { config, lines } = params;
const shortcutSyntax = config.shortcut_syntax || false;
const { definitions, references, shortcuts } = referenceLinkImageData();
const { definitions, references, shortcuts } = getReferenceLinkImageData();
const entries = shortcutSyntax ?
[ ...references.entries(), ...shortcuts.entries() ] :
references.entries();

View file

@ -4,7 +4,7 @@
const { addError, ellipsify, linkReferenceDefinitionRe } =
require("../helpers");
const { referenceLinkImageData } = require("./cache");
const { getReferenceLinkImageData } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -17,7 +17,7 @@ module.exports = {
const ignored = new Set(params.config.ignored_definitions || [ "//" ]);
const lines = params.lines;
const { references, shortcuts, definitions, duplicateDefinitions } =
referenceLinkImageData();
getReferenceLinkImageData();
const singleLineDefinition = (line) => (
line.replace(linkReferenceDefinitionRe, "").trim().length > 0
);

View file

@ -3,9 +3,8 @@
"use strict";
const { addErrorContext, nextLinesRe } = require("../helpers");
const { filterByTypes, filterByPredicate, getTokenTextByType } =
require("../helpers/micromark.cjs");
const { referenceLinkImageData } = require("./cache");
const { filterByPredicate, getTokenTextByType } = require("../helpers/micromark.cjs");
const { getReferenceLinkImageData, filterByTypesCached } = require("./cache");
const backslashEscapeRe = /\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g;
const removeBackslashEscapes = (text) => text.replace(backslashEscapeRe, "$1");
@ -40,11 +39,8 @@ module.exports = {
// Everything allowed, nothing to check
return;
}
const { definitions } = referenceLinkImageData();
const links = filterByTypes(
params.parsers.micromark.tokens,
[ "autolink", "image", "link" ]
);
const { definitions } = getReferenceLinkImageData();
const links = filterByTypesCached([ "autolink", "image", "link" ]);
for (const link of links) {
let label = null;
let destination = null;

View file

@ -4,6 +4,7 @@
const { addErrorDetailIf } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const whitespaceTypes = new Set([ "linePrefix", "whitespace" ]);
const ignoreWhitespace = (tokens) => tokens.filter(
@ -27,10 +28,7 @@ module.exports = {
((expectedStyle !== "no_leading_or_trailing") && (expectedStyle !== "trailing_only"));
let expectedTrailingPipe =
((expectedStyle !== "no_leading_or_trailing") && (expectedStyle !== "leading_only"));
const tables = filterByTypes(
params.parsers.micromark.tokens,
[ "table" ]
);
const tables = filterByTypesCached([ "table" ]);
for (const table of tables) {
const rows = filterByTypes(
table.children,

View file

@ -4,6 +4,7 @@
const { addErrorDetailIf } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
const makeRange = (start, end) => [ start, end - start + 1 ];
@ -15,10 +16,7 @@ module.exports = {
"tags": [ "table" ],
"parser": "micromark",
"function": function MD056(params, onError) {
const tables = filterByTypes(
params.parsers.micromark.tokens,
[ "table" ]
);
const tables = filterByTypesCached([ "table" ]);
for (const table of tables) {
const rows = filterByTypes(
table.children,

View file

@ -3,7 +3,7 @@
"use strict";
const { addErrorContextForLine, isBlankLine } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
@ -13,12 +13,9 @@ module.exports = {
"tags": [ "table" ],
"parser": "micromark",
"function": function MD058(params, onError) {
const { lines, parsers } = params;
const { lines } = params;
// For every table...
const tables = filterByTypes(
parsers.micromark.tokens,
[ "table" ]
);
const tables = filterByTypesCached([ "table" ]);
for (const table of tables) {
// Look for a blank line above the table
const firstIndex = table.startLine - 1;

View file

@ -921,8 +921,8 @@ test("getReferenceLinkImageData().shortcuts", (t) => {
"parser": "none",
"function":
() => {
const { referenceLinkImageData } = require("../lib/cache");
const { shortcuts } = referenceLinkImageData();
const { getReferenceLinkImageData } = require("../lib/cache");
const { shortcuts } = getReferenceLinkImageData();
t.is(shortcuts.size, 0, [ ...shortcuts.keys() ].join(", "));
}
}