mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-12-19 07:20:13 +01:00
Add "enabled" and "severity"/"warning" to rule in Configuration object, add corresponding documentation and tests, update demo web app (fixes #254)”.
Some checks failed
Checkers / linkcheck (push) Has been cancelled
Checkers / spellcheck (push) Has been cancelled
CI / build (20, macos-latest) (push) Has been cancelled
CI / build (20, ubuntu-latest) (push) Has been cancelled
CI / build (20, windows-latest) (push) Has been cancelled
CI / build (22, macos-latest) (push) Has been cancelled
CI / build (22, ubuntu-latest) (push) Has been cancelled
CI / build (22, windows-latest) (push) Has been cancelled
CI / build (24, macos-latest) (push) Has been cancelled
CI / build (24, ubuntu-latest) (push) Has been cancelled
CI / build (24, windows-latest) (push) Has been cancelled
CI / pnpm (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
TestRepos / build (latest, ubuntu-latest) (push) Has been cancelled
UpdateTestRepos / update (push) Has been cancelled
Some checks failed
Checkers / linkcheck (push) Has been cancelled
Checkers / spellcheck (push) Has been cancelled
CI / build (20, macos-latest) (push) Has been cancelled
CI / build (20, ubuntu-latest) (push) Has been cancelled
CI / build (20, windows-latest) (push) Has been cancelled
CI / build (22, macos-latest) (push) Has been cancelled
CI / build (22, ubuntu-latest) (push) Has been cancelled
CI / build (22, windows-latest) (push) Has been cancelled
CI / build (24, macos-latest) (push) Has been cancelled
CI / build (24, ubuntu-latest) (push) Has been cancelled
CI / build (24, windows-latest) (push) Has been cancelled
CI / pnpm (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
TestRepos / build (latest, ubuntu-latest) (push) Has been cancelled
UpdateTestRepos / update (push) Has been cancelled
This commit is contained in:
parent
87ddc2e022
commit
0f5a8c701e
20 changed files with 3792 additions and 867 deletions
|
|
@ -236,44 +236,87 @@ function mapAliasToRuleNames(ruleList) {
|
|||
return aliasToRuleNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result object for getEffectiveConfig.
|
||||
*
|
||||
* @typedef {Object} GetEffectiveConfigResult
|
||||
* @property {Configuration} effectiveConfig Effective configuration.
|
||||
* @property {Map<string, boolean>} rulesEnabled Rules enabled.
|
||||
* @property {Map<string, "error" | "warning">} rulesSeverity Rules severity.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Apply (and normalize) configuration object.
|
||||
*
|
||||
* @param {Rule[]} ruleList List of rules.
|
||||
* @param {Configuration} config Configuration object.
|
||||
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
|
||||
* names.
|
||||
* @returns {Configuration} Effective configuration.
|
||||
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule names.
|
||||
* @returns {GetEffectiveConfigResult} Effective configuration and rule severities.
|
||||
*/
|
||||
function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
||||
const defaultKey = Object.keys(config).filter(
|
||||
(key) => key.toUpperCase() === "DEFAULT"
|
||||
);
|
||||
const ruleDefault = (defaultKey.length === 0) || !!config[defaultKey[0]];
|
||||
let ruleDefaultEnable = true;
|
||||
/** @type {"error" | "warning"} */
|
||||
let ruleDefaultSeverity = "error";
|
||||
Object.entries(config).every(([ key, value ]) => {
|
||||
if (key.toUpperCase() === "DEFAULT") {
|
||||
ruleDefaultEnable = !!value;
|
||||
if (value === "warning") {
|
||||
ruleDefaultSeverity = "warning";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
/** @type {Configuration} */
|
||||
const effectiveConfig = {};
|
||||
for (const rule of ruleList) {
|
||||
const ruleName = rule.names[0].toUpperCase();
|
||||
effectiveConfig[ruleName] = ruleDefault;
|
||||
/** @type {Map<string, boolean>} */
|
||||
const rulesEnabled = new Map();
|
||||
/** @type {Map<string, "error" | "warning">} */
|
||||
const rulesSeverity = new Map();
|
||||
const emptyObject = Object.freeze({});
|
||||
for (const ruleName of ruleList.map((rule) => rule.names[0].toUpperCase())) {
|
||||
effectiveConfig[ruleName] = emptyObject;
|
||||
rulesEnabled.set(ruleName, ruleDefaultEnable);
|
||||
rulesSeverity.set(ruleName, ruleDefaultSeverity);
|
||||
}
|
||||
// for (const ruleName of deprecatedRuleNames) {
|
||||
// effectiveConfig[ruleName] = false;
|
||||
// }
|
||||
for (const key of Object.keys(config)) {
|
||||
let value = config[key];
|
||||
if (value) {
|
||||
value = (value instanceof Object) ?
|
||||
Object.fromEntries(Object.entries(value).filter(([ k ]) => k !== "severity")) :
|
||||
{};
|
||||
} else {
|
||||
value = false;
|
||||
}
|
||||
for (const [ key, value ] of Object.entries(config)) {
|
||||
const keyUpper = key.toUpperCase();
|
||||
/** @type {boolean} */
|
||||
let enabled = false;
|
||||
/** @type {"error" | "warning"} */
|
||||
let severity = "error";
|
||||
let effectiveValue = {};
|
||||
if (value) {
|
||||
if (value instanceof Object) {
|
||||
/** @type {{ enabled?: boolean, severity?: "error" | "warning" }} */
|
||||
const valueObject = value;
|
||||
enabled = (valueObject.enabled === undefined) ? true : !!valueObject.enabled;
|
||||
severity = (valueObject.severity === "warning") ? "warning" : "error";
|
||||
effectiveValue = Object.fromEntries(
|
||||
Object.entries(value).filter(
|
||||
([ k ]) => (k !== "enabled") && (k !== "severity")
|
||||
)
|
||||
);
|
||||
} else {
|
||||
enabled = true;
|
||||
severity = (value === "warning") ? "warning" : "error";
|
||||
}
|
||||
}
|
||||
for (const ruleName of (aliasToRuleNames[keyUpper] || [])) {
|
||||
effectiveConfig[ruleName] = value;
|
||||
Object.freeze(effectiveValue);
|
||||
effectiveConfig[ruleName] = effectiveValue;
|
||||
rulesEnabled.set(ruleName, enabled);
|
||||
rulesSeverity.set(ruleName, severity);
|
||||
}
|
||||
}
|
||||
return effectiveConfig;
|
||||
return {
|
||||
effectiveConfig,
|
||||
rulesEnabled,
|
||||
rulesSeverity
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -281,8 +324,9 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|||
*
|
||||
* @typedef {Object} EnabledRulesPerLineNumberResult
|
||||
* @property {Configuration} effectiveConfig Effective configuration.
|
||||
* @property {any[]} enabledRulesPerLineNumber Enabled rules per line number.
|
||||
* @property {Map<string, boolean>[]} enabledRulesPerLineNumber Enabled rules per line number.
|
||||
* @property {Rule[]} enabledRuleList Enabled rule list.
|
||||
* @property {Map<string, "error" | "warning">} rulesSeverity Rules severity.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -294,8 +338,7 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|||
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
||||
* @param {Configuration} config Configuration object.
|
||||
* @param {ConfigurationParser[] | undefined} configParsers Configuration parsers.
|
||||
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
|
||||
* names.
|
||||
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule names.
|
||||
* @returns {EnabledRulesPerLineNumberResult} Effective configuration and enabled rules per line number.
|
||||
*/
|
||||
function getEnabledRulesPerLineNumber(
|
||||
|
|
@ -307,9 +350,10 @@ function getEnabledRulesPerLineNumber(
|
|||
configParsers,
|
||||
aliasToRuleNames) {
|
||||
// Shared variables
|
||||
let enabledRules = {};
|
||||
let capturedRules = {};
|
||||
const allRuleNames = [];
|
||||
/** @type {Map<string, boolean>} */
|
||||
let enabledRules = new Map();
|
||||
/** @type {Map<string, boolean>} */
|
||||
let capturedRules = enabledRules;
|
||||
const enabledRulesPerLineNumber = new Array(1 + frontMatterLines.length);
|
||||
// Helper functions
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
|
|
@ -349,13 +393,14 @@ function getEnabledRulesPerLineNumber(
|
|||
}
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
function applyEnableDisable(action, parameter, state) {
|
||||
state = { ...state };
|
||||
state = new Map(state);
|
||||
const enabled = (action.startsWith("ENABLE"));
|
||||
const trimmed = parameter && parameter.trim();
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
const items = trimmed ? trimmed.toUpperCase().split(/\s+/) : allRuleNames;
|
||||
for (const nameUpper of items) {
|
||||
for (const ruleName of (aliasToRuleNames[nameUpper] || [])) {
|
||||
state[ruleName] = enabled;
|
||||
state.set(ruleName, enabled);
|
||||
}
|
||||
}
|
||||
return state;
|
||||
|
|
@ -397,29 +442,24 @@ function getEnabledRulesPerLineNumber(
|
|||
}
|
||||
// Handle inline comments
|
||||
handleInlineConfig([ lines.join("\n") ], configureFile);
|
||||
const effectiveConfig = getEffectiveConfig(
|
||||
ruleList, config, aliasToRuleNames);
|
||||
for (const rule of ruleList) {
|
||||
const ruleName = rule.names[0].toUpperCase();
|
||||
allRuleNames.push(ruleName);
|
||||
enabledRules[ruleName] = !!effectiveConfig[ruleName];
|
||||
}
|
||||
const { effectiveConfig, rulesEnabled, rulesSeverity } = getEffectiveConfig(ruleList, config, aliasToRuleNames);
|
||||
const allRuleNames = [ ...rulesEnabled.keys() ];
|
||||
enabledRules = new Map(rulesEnabled);
|
||||
capturedRules = enabledRules;
|
||||
handleInlineConfig(lines, enableDisableFile);
|
||||
handleInlineConfig(lines, captureRestoreEnableDisable, updateLineState);
|
||||
handleInlineConfig(lines, disableLineNextLine);
|
||||
// Create the list of rules that are used at least once
|
||||
const enabledRuleList = [];
|
||||
for (const [ index, ruleName ] of allRuleNames.entries()) {
|
||||
if (enabledRulesPerLineNumber.some((enabledRulesForLine) => enabledRulesForLine[ruleName])) {
|
||||
enabledRuleList.push(ruleList[index]);
|
||||
}
|
||||
}
|
||||
const enabledRuleList = ruleList.filter((rule) => {
|
||||
const ruleName = rule.names[0].toUpperCase();
|
||||
return enabledRulesPerLineNumber.some((enabledRulesForLine) => enabledRulesForLine.get(ruleName));
|
||||
});
|
||||
// Return results
|
||||
return {
|
||||
effectiveConfig,
|
||||
enabledRulesPerLineNumber,
|
||||
enabledRuleList
|
||||
enabledRuleList,
|
||||
rulesSeverity
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -464,7 +504,7 @@ function lintContent(
|
|||
const { frontMatterLines } = removeFrontMatterResult;
|
||||
content = removeFrontMatterResult.content;
|
||||
// Get enabled rules per line (with HTML comments present)
|
||||
const { effectiveConfig, enabledRulesPerLineNumber, enabledRuleList } =
|
||||
const { effectiveConfig, enabledRulesPerLineNumber, enabledRuleList, rulesSeverity } =
|
||||
getEnabledRulesPerLineNumber(
|
||||
ruleList,
|
||||
content.split(helpers.newLineRe),
|
||||
|
|
@ -541,12 +581,12 @@ function lintContent(
|
|||
} else if (rule.parser === "micromark") {
|
||||
parsers = parsersMicromark;
|
||||
}
|
||||
const params = {
|
||||
const params = Object.freeze({
|
||||
...paramsBase,
|
||||
...tokens,
|
||||
parsers,
|
||||
"config": (typeof effectiveConfig[ruleName] === "object") ? { ...effectiveConfig[ruleName] } : effectiveConfig[ruleName]
|
||||
};
|
||||
"config": effectiveConfig[ruleName]
|
||||
});
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
function throwError(property) {
|
||||
throw new Error(
|
||||
|
|
@ -561,7 +601,7 @@ function lintContent(
|
|||
throwError("lineNumber");
|
||||
}
|
||||
const lineNumber = errorInfo.lineNumber + frontMatterLines.length;
|
||||
if (!enabledRulesPerLineNumber[lineNumber][ruleName]) {
|
||||
if (!enabledRulesPerLineNumber[lineNumber].get(ruleName)) {
|
||||
return;
|
||||
}
|
||||
if (errorInfo.detail &&
|
||||
|
|
@ -638,7 +678,8 @@ function lintContent(
|
|||
"errorContext": errorInfo.context?.replace(helpers.newLineRe, " ") || null,
|
||||
"errorRange": errorInfo.range ? [ ...errorInfo.range ] : null,
|
||||
"fixInfo": fixInfo ? cleanFixInfo : null,
|
||||
"severity": "error"
|
||||
// @ts-ignore
|
||||
"severity": rulesSeverity.get(ruleName)
|
||||
});
|
||||
}
|
||||
// Call (possibly external) rule function to report errors
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue