mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-12-17 22:40:13 +01:00
Add support for using custom rules.
This commit is contained in:
parent
4619a8c824
commit
f24f98e146
11 changed files with 497 additions and 71 deletions
|
|
@ -9,54 +9,58 @@ var rules = require("./rules");
|
|||
var shared = require("./shared");
|
||||
|
||||
// Class for results with toString for pretty display
|
||||
function Results() {}
|
||||
Results.prototype.toString = function resultsToString(useAlias) {
|
||||
var that = this;
|
||||
var ruleNameToRule = null;
|
||||
var results = [];
|
||||
Object.keys(that).forEach(function forFile(file) {
|
||||
var fileResults = that[file];
|
||||
if (Array.isArray(fileResults)) {
|
||||
fileResults.forEach(function forResult(result) {
|
||||
var ruleMoniker = result.ruleNames ?
|
||||
result.ruleNames.join("/") :
|
||||
(result.ruleName + "/" + result.ruleAlias);
|
||||
results.push(
|
||||
file + ": " +
|
||||
result.lineNumber + ": " +
|
||||
ruleMoniker + " " +
|
||||
result.ruleDescription +
|
||||
(result.errorDetail ?
|
||||
" [" + result.errorDetail + "]" :
|
||||
"") +
|
||||
(result.errorContext ?
|
||||
" [Context: \"" + result.errorContext + "\"]" :
|
||||
""));
|
||||
});
|
||||
} else {
|
||||
if (!ruleNameToRule) {
|
||||
ruleNameToRule = {};
|
||||
rules.forEach(function forRule(rule) {
|
||||
var ruleName = rule.names[0].toUpperCase();
|
||||
ruleNameToRule[ruleName] = rule;
|
||||
function newResults(ruleSet) {
|
||||
function Results() {}
|
||||
Results.prototype.toString = function resultsToString(useAlias) {
|
||||
var that = this;
|
||||
var ruleNameToRule = null;
|
||||
var results = [];
|
||||
Object.keys(that).forEach(function forFile(file) {
|
||||
var fileResults = that[file];
|
||||
if (Array.isArray(fileResults)) {
|
||||
fileResults.forEach(function forResult(result) {
|
||||
var ruleMoniker = result.ruleNames ?
|
||||
result.ruleNames.join("/") :
|
||||
(result.ruleName + "/" + result.ruleAlias);
|
||||
results.push(
|
||||
file + ": " +
|
||||
result.lineNumber + ": " +
|
||||
ruleMoniker + " " +
|
||||
result.ruleDescription +
|
||||
(result.errorDetail ?
|
||||
" [" + result.errorDetail + "]" :
|
||||
"") +
|
||||
(result.errorContext ?
|
||||
" [Context: \"" + result.errorContext + "\"]" :
|
||||
""));
|
||||
});
|
||||
} else {
|
||||
if (!ruleNameToRule) {
|
||||
ruleNameToRule = {};
|
||||
ruleSet.forEach(function forRule(rule) {
|
||||
var ruleName = rule.names[0].toUpperCase();
|
||||
ruleNameToRule[ruleName] = rule;
|
||||
});
|
||||
}
|
||||
Object.keys(fileResults).forEach(function forRule(ruleName) {
|
||||
var rule = ruleNameToRule[ruleName.toUpperCase()];
|
||||
var ruleResults = fileResults[ruleName];
|
||||
ruleResults.forEach(function forLine(lineNumber) {
|
||||
var nameIndex = Math.min(useAlias ? 1 : 0, rule.names.length - 1);
|
||||
var result =
|
||||
file + ": " +
|
||||
lineNumber + ": " +
|
||||
rule.names[nameIndex] + " " +
|
||||
rule.description;
|
||||
results.push(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
Object.keys(fileResults).forEach(function forRule(ruleName) {
|
||||
var rule = ruleNameToRule[ruleName];
|
||||
var ruleResults = fileResults[ruleName];
|
||||
ruleResults.forEach(function forLine(lineNumber) {
|
||||
var result =
|
||||
file + ": " +
|
||||
lineNumber + ": " +
|
||||
rule.names[useAlias ? 1 : 0] + " " +
|
||||
rule.description;
|
||||
results.push(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
return results.join("\n");
|
||||
};
|
||||
});
|
||||
return results.join("\n");
|
||||
};
|
||||
return new Results();
|
||||
}
|
||||
|
||||
// Remove front matter (if present at beginning of content)
|
||||
function removeFrontMatter(content, frontMatter) {
|
||||
|
|
@ -151,16 +155,16 @@ function mapAliasToRuleNames(ruleList) {
|
|||
return aliasToRuleNames;
|
||||
}
|
||||
|
||||
// Merge rules/tags and sanitize config
|
||||
function mergeRulesAndSanitize(config, aliasToRuleNames) {
|
||||
// Apply (and normalize) config
|
||||
function getEffectiveConfig(ruleSet, config, aliasToRuleNames) {
|
||||
var defaultKey = Object.keys(config).filter(function forKey(key) {
|
||||
return key.toUpperCase() === "DEFAULT";
|
||||
});
|
||||
var ruleDefault = (defaultKey.length === 0) || !!config[defaultKey[0]];
|
||||
var mergedRules = {};
|
||||
rules.forEach(function forRule(rule) {
|
||||
var effectiveConfig = {};
|
||||
ruleSet.forEach(function forRule(rule) {
|
||||
var ruleName = rule.names[0].toUpperCase();
|
||||
mergedRules[ruleName] = ruleDefault;
|
||||
effectiveConfig[ruleName] = ruleDefault;
|
||||
});
|
||||
Object.keys(config).forEach(function forKey(key) {
|
||||
var value = config[key];
|
||||
|
|
@ -173,21 +177,22 @@ function mergeRulesAndSanitize(config, aliasToRuleNames) {
|
|||
}
|
||||
var keyUpper = key.toUpperCase();
|
||||
(aliasToRuleNames[keyUpper] || []).forEach(function forRule(ruleName) {
|
||||
mergedRules[ruleName] = value;
|
||||
effectiveConfig[ruleName] = value;
|
||||
});
|
||||
});
|
||||
return mergedRules;
|
||||
return effectiveConfig;
|
||||
}
|
||||
|
||||
// Create mapping of enabled rules per line
|
||||
function getEnabledRulesPerLineNumber(
|
||||
lines, frontMatterLines, noInlineConfig, mergedRules, aliasToRuleNames) {
|
||||
ruleSet, lines, frontMatterLines, noInlineConfig,
|
||||
effectiveConfig, aliasToRuleNames) {
|
||||
var enabledRules = {};
|
||||
var allRuleNames = [];
|
||||
rules.forEach(function forRule(rule) {
|
||||
ruleSet.forEach(function forRule(rule) {
|
||||
var ruleName = rule.names[0].toUpperCase();
|
||||
allRuleNames.push(ruleName);
|
||||
enabledRules[ruleName] = !!mergedRules[ruleName];
|
||||
enabledRules[ruleName] = !!effectiveConfig[ruleName];
|
||||
});
|
||||
function forMatch(match) {
|
||||
var enabled = match[1].toUpperCase() === "EN";
|
||||
|
|
@ -229,7 +234,7 @@ function uniqueFilterForSortedErrors(value, index, array) {
|
|||
|
||||
// Lints a single string
|
||||
function lintContent(
|
||||
content, config, frontMatter, noInlineConfig, resultVersion) {
|
||||
ruleSet, content, config, frontMatter, noInlineConfig, resultVersion) {
|
||||
// Remove UTF-8 byte order marker (if present)
|
||||
if (content.charCodeAt(0) === 0xfeff) {
|
||||
content = content.slice(1);
|
||||
|
|
@ -244,10 +249,11 @@ function lintContent(
|
|||
var tokens = md.parse(content, {});
|
||||
var lines = content.split(shared.newLineRe);
|
||||
var tokenLists = annotateTokens(tokens, lines);
|
||||
var aliasToRuleNames = mapAliasToRuleNames(rules);
|
||||
var mergedRules = mergeRulesAndSanitize(config, aliasToRuleNames);
|
||||
var aliasToRuleNames = mapAliasToRuleNames(ruleSet);
|
||||
var effectiveConfig = getEffectiveConfig(ruleSet, config, aliasToRuleNames);
|
||||
var enabledRulesPerLineNumber = getEnabledRulesPerLineNumber(
|
||||
lines, frontMatterLines, noInlineConfig, mergedRules, aliasToRuleNames);
|
||||
ruleSet, lines, frontMatterLines, noInlineConfig,
|
||||
effectiveConfig, aliasToRuleNames);
|
||||
// Create parameters for rules
|
||||
var params = {
|
||||
"tokens": tokens,
|
||||
|
|
@ -257,10 +263,11 @@ function lintContent(
|
|||
};
|
||||
// Run each rule
|
||||
var result = (resultVersion === 0) ? {} : [];
|
||||
rules.forEach(function forRule(rule) {
|
||||
ruleSet.forEach(function forRule(rule) {
|
||||
// Configure rule
|
||||
var ruleName = rule.names[0].toUpperCase();
|
||||
params.config = mergedRules[ruleName];
|
||||
var ruleNameFriendly = rule.names[0];
|
||||
var ruleName = ruleNameFriendly.toUpperCase();
|
||||
params.config = effectiveConfig[ruleName];
|
||||
var errors = [];
|
||||
function onError(errorInfo) {
|
||||
errors.push({
|
||||
|
|
@ -286,8 +293,8 @@ function lintContent(
|
|||
var errorObject = {};
|
||||
errorObject.lineNumber = error.lineNumber;
|
||||
if (resultVersion === 1) {
|
||||
errorObject.ruleName = rule.names[0];
|
||||
errorObject.ruleAlias = rule.names[1];
|
||||
errorObject.ruleName = ruleNameFriendly;
|
||||
errorObject.ruleAlias = rule.names[1] || rule.names[0];
|
||||
} else {
|
||||
errorObject.ruleNames = rule.names;
|
||||
}
|
||||
|
|
@ -299,9 +306,9 @@ function lintContent(
|
|||
});
|
||||
if (filteredErrors.length) {
|
||||
if (resultVersion === 0) {
|
||||
result[ruleName] = filteredErrors;
|
||||
result[ruleNameFriendly] = filteredErrors;
|
||||
} else {
|
||||
result.push.apply(result, filteredErrors);
|
||||
Array.prototype.push.apply(result, filteredErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -311,6 +318,7 @@ function lintContent(
|
|||
|
||||
// Lints a single file
|
||||
function lintFile(
|
||||
ruleSet,
|
||||
file,
|
||||
config,
|
||||
frontMatter,
|
||||
|
|
@ -323,7 +331,7 @@ function lintFile(
|
|||
return callback(err);
|
||||
}
|
||||
var result = lintContent(
|
||||
content, config, frontMatter, noInlineConfig, resultVersion);
|
||||
ruleSet, content, config, frontMatter, noInlineConfig, resultVersion);
|
||||
callback(null, result);
|
||||
}
|
||||
// Make a/synchronous call to read file
|
||||
|
|
@ -338,6 +346,7 @@ function lintInput(options, synchronous, callback) {
|
|||
// Normalize inputs
|
||||
options = options || {};
|
||||
callback = callback || function noop() {};
|
||||
var ruleSet = rules.concat(options.customRules || []);
|
||||
var files = [];
|
||||
if (Array.isArray(options.files)) {
|
||||
files = options.files.slice();
|
||||
|
|
@ -351,12 +360,13 @@ function lintInput(options, synchronous, callback) {
|
|||
var noInlineConfig = !!options.noInlineConfig;
|
||||
var resultVersion = (options.resultVersion === undefined) ?
|
||||
2 : options.resultVersion;
|
||||
var results = new Results();
|
||||
var results = newResults(ruleSet);
|
||||
// Helper to lint the next file in the array
|
||||
function lintFilesArray() {
|
||||
var file = files.shift();
|
||||
if (file) {
|
||||
lintFile(
|
||||
ruleSet,
|
||||
file,
|
||||
config,
|
||||
frontMatter,
|
||||
|
|
@ -378,6 +388,7 @@ function lintInput(options, synchronous, callback) {
|
|||
// Lint strings
|
||||
Object.keys(strings).forEach(function forKey(key) {
|
||||
var result = lintContent(
|
||||
ruleSet,
|
||||
strings[key] || "",
|
||||
config,
|
||||
frontMatter,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue