Add Rule.parser property for custom rules to specify which Markdown parser output they use, honor it strictly, and add full type support.

This commit is contained in:
David Anson 2024-03-09 16:17:50 -08:00
parent d2acc168d2
commit d5994ae4de
68 changed files with 635 additions and 115 deletions

View file

@ -33,17 +33,16 @@ function validateRuleList(ruleList, synchronous) {
for (const [ index, rule ] of ruleList.entries()) {
const customIndex = index - rules.length;
// eslint-disable-next-line no-inner-declarations, jsdoc/require-jsdoc
function newError(property) {
function newError(property, value) {
return new Error(
"Property '" + property + "' of custom rule at index " +
customIndex + " is incorrect.");
`Property '${property}' of custom rule at index ${customIndex} is incorrect: '${value}'.`);
}
for (const property of [ "names", "tags" ]) {
const value = rule[property];
if (!result &&
(!value || !Array.isArray(value) || (value.length === 0) ||
!value.every(helpers.isString) || value.some(helpers.isEmptyString))) {
result = newError(property);
result = newError(property, value);
}
}
for (const propertyInfo of [
@ -53,22 +52,31 @@ function validateRuleList(ruleList, synchronous) {
const property = propertyInfo[0];
const value = rule[property];
if (!result && (!value || (typeof value !== propertyInfo[1]))) {
result = newError(property);
result = newError(property, value);
}
}
if (
!result &&
(rule.parser !== undefined) &&
(rule.parser !== "markdownit") &&
!((customIndex < 0) && (rule.parser === "micromark")) &&
(rule.parser !== "none")
) {
result = newError("parser", rule.parser);
}
if (
!result &&
rule.information &&
!helpers.isUrl(rule.information)
) {
result = newError("information");
result = newError("information", rule.information);
}
if (
!result &&
(rule.asynchronous !== undefined) &&
(typeof rule.asynchronous !== "boolean")
) {
result = newError("asynchronous");
result = newError("asynchronous", rule.asynchronous);
}
if (!result && rule.asynchronous && synchronous) {
result = new Error(
@ -563,18 +571,26 @@ function lintContent(
const lines = content.split(helpers.newLineRe);
annotateAndFreezeTokens(markdownitTokens, lines);
// Create (frozen) parameters for rules
const parsers = Object.freeze({
/** @type {MarkdownParsers} */
// @ts-ignore
const parsersMarkdownIt = Object.freeze({
"markdownit": Object.freeze({
"tokens": markdownitTokens
}),
})
});
/** @type {MarkdownParsers} */
// @ts-ignore
const parsersMicromark = Object.freeze({
"micromark": Object.freeze({
"tokens": micromarkTokens
})
});
/** @type {MarkdownParsers} */
// @ts-ignore
const parsersNone = Object.freeze({});
const paramsBase = {
name,
parsers,
"tokens": markdownitTokens,
"parsers": parsersMarkdownIt,
"lines": Object.freeze(lines),
"frontMatterLines": Object.freeze(frontMatterLines)
};
@ -583,9 +599,9 @@ function lintContent(
const codeBlockAndSpanRanges =
helpers.codeBlockAndSpanRanges(paramsBase, lineMetadata);
const flattenedLists =
helpers.flattenLists(paramsBase.parsers.markdownit.tokens);
helpers.flattenLists(markdownitTokens);
const referenceLinkImageData =
helpers.getReferenceLinkImageData(paramsBase);
helpers.getReferenceLinkImageData(micromarkTokens);
cache.set({
codeBlockAndSpanRanges,
flattenedLists,
@ -594,12 +610,27 @@ function lintContent(
});
// Function to run for each rule
let results = [];
// eslint-disable-next-line jsdoc/require-jsdoc
function forRule(rule) {
/**
* @param {Rule} rule Rule.
* @returns {Promise<void> | null} Promise.
*/
const forRule = (rule) => {
// Configure rule
const ruleName = rule.names[0].toUpperCase();
const tokens = {};
let parsers = parsersNone;
if (rule.parser === undefined) {
tokens.tokens = markdownitTokens;
parsers = parsersMarkdownIt;
} else if (rule.parser === "markdownit") {
parsers = parsersMarkdownIt;
} else if (rule.parser === "micromark") {
parsers = parsersMicromark;
}
const params = {
...paramsBase,
...tokens,
parsers,
"config": effectiveConfig[ruleName]
};
// eslint-disable-next-line jsdoc/require-jsdoc
@ -875,6 +906,7 @@ function lintInput(options, synchronous, callback) {
"description": rule.description,
"information": helpers.cloneIfUrl(rule.information),
"tags": helpers.cloneIfArray(rule.tags),
"parser": rule.parser,
"asynchronous": rule.asynchronous,
"function": rule.function
}));
@ -1281,22 +1313,27 @@ module.exports = markdownlint;
* @returns {void}
*/
/* eslint-disable jsdoc/valid-types */
/**
* Rule parameters.
*
* @typedef {Object} RuleParams
* @property {string} name File/string name.
* @property {MarkdownParsers} parsers Markdown parser data.
* @property {string[]} lines File/string lines.
* @property {string[]} frontMatterLines Front matter lines.
* @property {readonly string[]} lines File/string lines.
* @property {readonly string[]} frontMatterLines Front matter lines.
* @property {RuleConfiguration} config Rule configuration.
*/
/* eslint-enable jsdoc/valid-types */
/**
* Markdown parser data.
*
* @typedef {Object} MarkdownParsers
* @property {ParserMarkdownIt} markdownit Markdown parser data from markdown-it.
* @property {ParserMarkdownIt} markdownit Markdown parser data from markdown-it (only present when Rule.parser is "markdownit").
* @property {ParserMicromark} micromark Markdown parser data from micromark (only present when Rule.parser is "micromark").
*/
/**
@ -1306,6 +1343,13 @@ module.exports = markdownlint;
* @property {MarkdownItToken[]} tokens Token objects from markdown-it.
*/
/**
* Markdown parser data from micromark.
*
* @typedef {Object} ParserMicromark
* @property {MicromarkToken[]} tokens Token objects from micromark.
*/
/**
* markdown-it token.
*
@ -1327,6 +1371,22 @@ module.exports = markdownlint;
* @property {string} line Line content.
*/
/** @typedef {import("markdownlint-micromark").TokenType} MicromarkTokenType */
/**
* micromark token.
*
* @typedef {Object} MicromarkToken
* @property {MicromarkTokenType} 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 {MicromarkToken[]} children Child tokens.
* @property {MicromarkToken | null} parent Parent token.
*/
/**
* Error-reporting callback.
*
@ -1365,6 +1425,7 @@ module.exports = markdownlint;
* @property {string} description Rule description.
* @property {URL} [information] Link to more information.
* @property {string[]} tags Rule tag(s).
* @property {"markdownit" | "micromark" | "none"} parser Parser used.
* @property {boolean} [asynchronous] True if asynchronous.
* @property {RuleFunction} function Rule implementation.
*/