Add support for disabling/enabling rules within Markdown content (fixes #5).

This commit is contained in:
David Anson 2015-09-26 16:55:33 -07:00
parent 071bba88fc
commit 31141cc3ed
10 changed files with 192 additions and 42 deletions

View file

@ -6,9 +6,11 @@ var rules = require("./rules");
var shared = require("./shared");
// Mappings from rule to description and tag to rules
var allRuleNames = [];
var ruleToDescription = {};
var tagUpperToRules = {};
rules.forEach(function forRule(rule) {
allRuleNames.push(rule.name);
ruleToDescription[rule.name] = rule.desc;
// The following is useful for updating README.md
// console.log("* **" + rule.name + "** - " + rule.desc);
@ -55,11 +57,11 @@ function uniqueFilterForSorted(value, index, array) {
}
// Lints a single string
function lintContent(content, config, frontMatter) {
function lintContent(content, config, frontMatter) { // eslint-disable-line
// Remove front matter (if present at beginning of content)
var frontMatterLines = 0;
if (frontMatter) {
var frontMatterMatch = frontMatter.exec(content);
var frontMatterMatch = content.match(frontMatter);
if (frontMatterMatch && !frontMatterMatch.index) {
var contentMatched = frontMatterMatch[0];
content = content.slice(contentMatched.length);
@ -93,12 +95,6 @@ function lintContent(content, config, frontMatter) {
}
tokenLists[token.type].push(token);
});
// Create parameters for rules
var params = {
"tokens": tokens,
"tokenLists": tokenLists,
"lines": lines
};
// Merge rules/tags and sanitize config
var defaultKey = Object.keys(config).filter(function forKey(key) {
return key.toUpperCase() === "DEFAULT";
@ -126,22 +122,64 @@ function lintContent(content, config, frontMatter) {
});
}
});
// Run each enabled rule
// Create mapping of enabled rules per line
var enabledRules = {};
rules.forEach(function forRule(rule) {
enabledRules[rule.name] = !!mergedRules[rule.name];
});
function forMatch(match) {
var enabled = match[1].toUpperCase() === "EN";
var items = match[2] ?
match[2].trim().toUpperCase().split(/\s+/) :
allRuleNames;
items.forEach(function forItem(nameUpper) {
if (ruleToDescription[nameUpper]) {
enabledRules[nameUpper] = enabled;
} else if (tagUpperToRules[nameUpper]) {
tagUpperToRules[nameUpper].forEach(function forRule(ruleName) {
enabledRules[ruleName] = enabled;
});
}
});
}
var enabledRulesPerLineNumber = [ null ];
lines.forEach(function forLine(line) {
var match = shared.inlineCommentRe.exec(line);
if (match) {
enabledRules = shared.clone(enabledRules);
while (match) {
forMatch(match);
match = shared.inlineCommentRe.exec(line);
}
}
enabledRulesPerLineNumber.push(enabledRules);
});
// Create parameters for rules
var params = {
"tokens": tokens,
"tokenLists": tokenLists,
"lines": lines
};
// Run each rule
var result = {};
rules.forEach(function forRule(rule) {
if (mergedRules[rule.name]) {
// Configure rule
params.options = mergedRules[rule.name];
var errors = [];
rule.func(params, errors);
// Record any errors
if (errors.length) {
errors.sort(numberComparison);
result[rule.name] = errors
.filter(uniqueFilterForSorted)
.map(function adjustLineNumbers(error) {
return error + frontMatterLines;
});
// Configure rule
params.options = mergedRules[rule.name];
var errors = [];
rule.func(params, errors);
// Record any errors (significant performance benefit from length check)
if (errors.length) {
errors.sort(numberComparison);
var filteredErrors = errors
.filter(uniqueFilterForSorted)
.filter(function removeDisabledRules(lineNumber) {
return enabledRulesPerLineNumber[lineNumber][rule.name];
})
.map(function adjustLineNumbers(error) {
return error + frontMatterLines;
});
if (filteredErrors.length) {
result[rule.name] = filteredErrors;
}
}
});