mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-09-22 05:40:48 +02: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
|
@ -63,7 +63,7 @@
|
||||||
"max-len": "error",
|
"max-len": "error",
|
||||||
"max-lines": "off",
|
"max-lines": "off",
|
||||||
"max-nested-callbacks": "error",
|
"max-nested-callbacks": "error",
|
||||||
"max-params": ["error", 7],
|
"max-params": ["error", 8],
|
||||||
"max-statements": ["error", 20],
|
"max-statements": ["error", 20],
|
||||||
"max-statements-per-line": "error",
|
"max-statements-per-line": "error",
|
||||||
"multiline-comment-style": ["error", "separate-lines"],
|
"multiline-comment-style": ["error", "separate-lines"],
|
||||||
|
|
23
README.md
23
README.md
|
@ -184,6 +184,26 @@ Type: `Object`
|
||||||
|
|
||||||
Configures the function.
|
Configures the function.
|
||||||
|
|
||||||
|
##### options.customRules
|
||||||
|
|
||||||
|
Type: `Array` of `Object`
|
||||||
|
|
||||||
|
List of custom rules to include with the default rule set for linting.
|
||||||
|
|
||||||
|
Each array element should define a rule. Rules are typically exported by another
|
||||||
|
package, but can be defined inline.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var extraRules = require("extraRules");
|
||||||
|
var options = {
|
||||||
|
"customRules": [ extraRules.one, extraRules.two ]
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
See [CustomRules.md](doc/CustomRules.md) for details about authoring custom rules.
|
||||||
|
|
||||||
##### options.files
|
##### options.files
|
||||||
|
|
||||||
Type: `Array` of `String`
|
Type: `Array` of `String`
|
||||||
|
@ -316,7 +336,8 @@ The default value:
|
||||||
/^(---|\+\+\+)$[^]*?^\1$(\r\n|\r|\n)/m
|
/^(---|\+\+\+)$[^]*?^\1$(\r\n|\r|\n)/m
|
||||||
```
|
```
|
||||||
|
|
||||||
Ignores [YAML](https://en.wikipedia.org/wiki/YAML) and [TOML](https://en.wikipedia.org/wiki/TOML) such as:
|
Ignores [YAML](https://en.wikipedia.org/wiki/YAML) and
|
||||||
|
[TOML](https://en.wikipedia.org/wiki/TOML) such as:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
---
|
---
|
||||||
|
|
1
doc/CustomRules.md
Normal file
1
doc/CustomRules.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# TBD
|
|
@ -9,54 +9,58 @@ var rules = require("./rules");
|
||||||
var shared = require("./shared");
|
var shared = require("./shared");
|
||||||
|
|
||||||
// Class for results with toString for pretty display
|
// Class for results with toString for pretty display
|
||||||
function Results() {}
|
function newResults(ruleSet) {
|
||||||
Results.prototype.toString = function resultsToString(useAlias) {
|
function Results() {}
|
||||||
var that = this;
|
Results.prototype.toString = function resultsToString(useAlias) {
|
||||||
var ruleNameToRule = null;
|
var that = this;
|
||||||
var results = [];
|
var ruleNameToRule = null;
|
||||||
Object.keys(that).forEach(function forFile(file) {
|
var results = [];
|
||||||
var fileResults = that[file];
|
Object.keys(that).forEach(function forFile(file) {
|
||||||
if (Array.isArray(fileResults)) {
|
var fileResults = that[file];
|
||||||
fileResults.forEach(function forResult(result) {
|
if (Array.isArray(fileResults)) {
|
||||||
var ruleMoniker = result.ruleNames ?
|
fileResults.forEach(function forResult(result) {
|
||||||
result.ruleNames.join("/") :
|
var ruleMoniker = result.ruleNames ?
|
||||||
(result.ruleName + "/" + result.ruleAlias);
|
result.ruleNames.join("/") :
|
||||||
results.push(
|
(result.ruleName + "/" + result.ruleAlias);
|
||||||
file + ": " +
|
results.push(
|
||||||
result.lineNumber + ": " +
|
file + ": " +
|
||||||
ruleMoniker + " " +
|
result.lineNumber + ": " +
|
||||||
result.ruleDescription +
|
ruleMoniker + " " +
|
||||||
(result.errorDetail ?
|
result.ruleDescription +
|
||||||
" [" + result.errorDetail + "]" :
|
(result.errorDetail ?
|
||||||
"") +
|
" [" + result.errorDetail + "]" :
|
||||||
(result.errorContext ?
|
"") +
|
||||||
" [Context: \"" + result.errorContext + "\"]" :
|
(result.errorContext ?
|
||||||
""));
|
" [Context: \"" + result.errorContext + "\"]" :
|
||||||
});
|
""));
|
||||||
} else {
|
});
|
||||||
if (!ruleNameToRule) {
|
} else {
|
||||||
ruleNameToRule = {};
|
if (!ruleNameToRule) {
|
||||||
rules.forEach(function forRule(rule) {
|
ruleNameToRule = {};
|
||||||
var ruleName = rule.names[0].toUpperCase();
|
ruleSet.forEach(function forRule(rule) {
|
||||||
ruleNameToRule[ruleName] = 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];
|
return results.join("\n");
|
||||||
var ruleResults = fileResults[ruleName];
|
};
|
||||||
ruleResults.forEach(function forLine(lineNumber) {
|
return new Results();
|
||||||
var result =
|
}
|
||||||
file + ": " +
|
|
||||||
lineNumber + ": " +
|
|
||||||
rule.names[useAlias ? 1 : 0] + " " +
|
|
||||||
rule.description;
|
|
||||||
results.push(result);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return results.join("\n");
|
|
||||||
};
|
|
||||||
|
|
||||||
// Remove front matter (if present at beginning of content)
|
// Remove front matter (if present at beginning of content)
|
||||||
function removeFrontMatter(content, frontMatter) {
|
function removeFrontMatter(content, frontMatter) {
|
||||||
|
@ -151,16 +155,16 @@ function mapAliasToRuleNames(ruleList) {
|
||||||
return aliasToRuleNames;
|
return aliasToRuleNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge rules/tags and sanitize config
|
// Apply (and normalize) config
|
||||||
function mergeRulesAndSanitize(config, aliasToRuleNames) {
|
function getEffectiveConfig(ruleSet, config, aliasToRuleNames) {
|
||||||
var defaultKey = Object.keys(config).filter(function forKey(key) {
|
var defaultKey = Object.keys(config).filter(function forKey(key) {
|
||||||
return key.toUpperCase() === "DEFAULT";
|
return key.toUpperCase() === "DEFAULT";
|
||||||
});
|
});
|
||||||
var ruleDefault = (defaultKey.length === 0) || !!config[defaultKey[0]];
|
var ruleDefault = (defaultKey.length === 0) || !!config[defaultKey[0]];
|
||||||
var mergedRules = {};
|
var effectiveConfig = {};
|
||||||
rules.forEach(function forRule(rule) {
|
ruleSet.forEach(function forRule(rule) {
|
||||||
var ruleName = rule.names[0].toUpperCase();
|
var ruleName = rule.names[0].toUpperCase();
|
||||||
mergedRules[ruleName] = ruleDefault;
|
effectiveConfig[ruleName] = ruleDefault;
|
||||||
});
|
});
|
||||||
Object.keys(config).forEach(function forKey(key) {
|
Object.keys(config).forEach(function forKey(key) {
|
||||||
var value = config[key];
|
var value = config[key];
|
||||||
|
@ -173,21 +177,22 @@ function mergeRulesAndSanitize(config, aliasToRuleNames) {
|
||||||
}
|
}
|
||||||
var keyUpper = key.toUpperCase();
|
var keyUpper = key.toUpperCase();
|
||||||
(aliasToRuleNames[keyUpper] || []).forEach(function forRule(ruleName) {
|
(aliasToRuleNames[keyUpper] || []).forEach(function forRule(ruleName) {
|
||||||
mergedRules[ruleName] = value;
|
effectiveConfig[ruleName] = value;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return mergedRules;
|
return effectiveConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create mapping of enabled rules per line
|
// Create mapping of enabled rules per line
|
||||||
function getEnabledRulesPerLineNumber(
|
function getEnabledRulesPerLineNumber(
|
||||||
lines, frontMatterLines, noInlineConfig, mergedRules, aliasToRuleNames) {
|
ruleSet, lines, frontMatterLines, noInlineConfig,
|
||||||
|
effectiveConfig, aliasToRuleNames) {
|
||||||
var enabledRules = {};
|
var enabledRules = {};
|
||||||
var allRuleNames = [];
|
var allRuleNames = [];
|
||||||
rules.forEach(function forRule(rule) {
|
ruleSet.forEach(function forRule(rule) {
|
||||||
var ruleName = rule.names[0].toUpperCase();
|
var ruleName = rule.names[0].toUpperCase();
|
||||||
allRuleNames.push(ruleName);
|
allRuleNames.push(ruleName);
|
||||||
enabledRules[ruleName] = !!mergedRules[ruleName];
|
enabledRules[ruleName] = !!effectiveConfig[ruleName];
|
||||||
});
|
});
|
||||||
function forMatch(match) {
|
function forMatch(match) {
|
||||||
var enabled = match[1].toUpperCase() === "EN";
|
var enabled = match[1].toUpperCase() === "EN";
|
||||||
|
@ -229,7 +234,7 @@ function uniqueFilterForSortedErrors(value, index, array) {
|
||||||
|
|
||||||
// Lints a single string
|
// Lints a single string
|
||||||
function lintContent(
|
function lintContent(
|
||||||
content, config, frontMatter, noInlineConfig, resultVersion) {
|
ruleSet, content, config, frontMatter, noInlineConfig, resultVersion) {
|
||||||
// Remove UTF-8 byte order marker (if present)
|
// Remove UTF-8 byte order marker (if present)
|
||||||
if (content.charCodeAt(0) === 0xfeff) {
|
if (content.charCodeAt(0) === 0xfeff) {
|
||||||
content = content.slice(1);
|
content = content.slice(1);
|
||||||
|
@ -244,10 +249,11 @@ function lintContent(
|
||||||
var tokens = md.parse(content, {});
|
var tokens = md.parse(content, {});
|
||||||
var lines = content.split(shared.newLineRe);
|
var lines = content.split(shared.newLineRe);
|
||||||
var tokenLists = annotateTokens(tokens, lines);
|
var tokenLists = annotateTokens(tokens, lines);
|
||||||
var aliasToRuleNames = mapAliasToRuleNames(rules);
|
var aliasToRuleNames = mapAliasToRuleNames(ruleSet);
|
||||||
var mergedRules = mergeRulesAndSanitize(config, aliasToRuleNames);
|
var effectiveConfig = getEffectiveConfig(ruleSet, config, aliasToRuleNames);
|
||||||
var enabledRulesPerLineNumber = getEnabledRulesPerLineNumber(
|
var enabledRulesPerLineNumber = getEnabledRulesPerLineNumber(
|
||||||
lines, frontMatterLines, noInlineConfig, mergedRules, aliasToRuleNames);
|
ruleSet, lines, frontMatterLines, noInlineConfig,
|
||||||
|
effectiveConfig, aliasToRuleNames);
|
||||||
// Create parameters for rules
|
// Create parameters for rules
|
||||||
var params = {
|
var params = {
|
||||||
"tokens": tokens,
|
"tokens": tokens,
|
||||||
|
@ -257,10 +263,11 @@ function lintContent(
|
||||||
};
|
};
|
||||||
// Run each rule
|
// Run each rule
|
||||||
var result = (resultVersion === 0) ? {} : [];
|
var result = (resultVersion === 0) ? {} : [];
|
||||||
rules.forEach(function forRule(rule) {
|
ruleSet.forEach(function forRule(rule) {
|
||||||
// Configure rule
|
// Configure rule
|
||||||
var ruleName = rule.names[0].toUpperCase();
|
var ruleNameFriendly = rule.names[0];
|
||||||
params.config = mergedRules[ruleName];
|
var ruleName = ruleNameFriendly.toUpperCase();
|
||||||
|
params.config = effectiveConfig[ruleName];
|
||||||
var errors = [];
|
var errors = [];
|
||||||
function onError(errorInfo) {
|
function onError(errorInfo) {
|
||||||
errors.push({
|
errors.push({
|
||||||
|
@ -286,8 +293,8 @@ function lintContent(
|
||||||
var errorObject = {};
|
var errorObject = {};
|
||||||
errorObject.lineNumber = error.lineNumber;
|
errorObject.lineNumber = error.lineNumber;
|
||||||
if (resultVersion === 1) {
|
if (resultVersion === 1) {
|
||||||
errorObject.ruleName = rule.names[0];
|
errorObject.ruleName = ruleNameFriendly;
|
||||||
errorObject.ruleAlias = rule.names[1];
|
errorObject.ruleAlias = rule.names[1] || rule.names[0];
|
||||||
} else {
|
} else {
|
||||||
errorObject.ruleNames = rule.names;
|
errorObject.ruleNames = rule.names;
|
||||||
}
|
}
|
||||||
|
@ -299,9 +306,9 @@ function lintContent(
|
||||||
});
|
});
|
||||||
if (filteredErrors.length) {
|
if (filteredErrors.length) {
|
||||||
if (resultVersion === 0) {
|
if (resultVersion === 0) {
|
||||||
result[ruleName] = filteredErrors;
|
result[ruleNameFriendly] = filteredErrors;
|
||||||
} else {
|
} else {
|
||||||
result.push.apply(result, filteredErrors);
|
Array.prototype.push.apply(result, filteredErrors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,6 +318,7 @@ function lintContent(
|
||||||
|
|
||||||
// Lints a single file
|
// Lints a single file
|
||||||
function lintFile(
|
function lintFile(
|
||||||
|
ruleSet,
|
||||||
file,
|
file,
|
||||||
config,
|
config,
|
||||||
frontMatter,
|
frontMatter,
|
||||||
|
@ -323,7 +331,7 @@ function lintFile(
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
var result = lintContent(
|
var result = lintContent(
|
||||||
content, config, frontMatter, noInlineConfig, resultVersion);
|
ruleSet, content, config, frontMatter, noInlineConfig, resultVersion);
|
||||||
callback(null, result);
|
callback(null, result);
|
||||||
}
|
}
|
||||||
// Make a/synchronous call to read file
|
// Make a/synchronous call to read file
|
||||||
|
@ -338,6 +346,7 @@ function lintInput(options, synchronous, callback) {
|
||||||
// Normalize inputs
|
// Normalize inputs
|
||||||
options = options || {};
|
options = options || {};
|
||||||
callback = callback || function noop() {};
|
callback = callback || function noop() {};
|
||||||
|
var ruleSet = rules.concat(options.customRules || []);
|
||||||
var files = [];
|
var files = [];
|
||||||
if (Array.isArray(options.files)) {
|
if (Array.isArray(options.files)) {
|
||||||
files = options.files.slice();
|
files = options.files.slice();
|
||||||
|
@ -351,12 +360,13 @@ function lintInput(options, synchronous, callback) {
|
||||||
var noInlineConfig = !!options.noInlineConfig;
|
var noInlineConfig = !!options.noInlineConfig;
|
||||||
var resultVersion = (options.resultVersion === undefined) ?
|
var resultVersion = (options.resultVersion === undefined) ?
|
||||||
2 : options.resultVersion;
|
2 : options.resultVersion;
|
||||||
var results = new Results();
|
var results = newResults(ruleSet);
|
||||||
// Helper to lint the next file in the array
|
// Helper to lint the next file in the array
|
||||||
function lintFilesArray() {
|
function lintFilesArray() {
|
||||||
var file = files.shift();
|
var file = files.shift();
|
||||||
if (file) {
|
if (file) {
|
||||||
lintFile(
|
lintFile(
|
||||||
|
ruleSet,
|
||||||
file,
|
file,
|
||||||
config,
|
config,
|
||||||
frontMatter,
|
frontMatter,
|
||||||
|
@ -378,6 +388,7 @@ function lintInput(options, synchronous, callback) {
|
||||||
// Lint strings
|
// Lint strings
|
||||||
Object.keys(strings).forEach(function forKey(key) {
|
Object.keys(strings).forEach(function forKey(key) {
|
||||||
var result = lintContent(
|
var result = lintContent(
|
||||||
|
ruleSet,
|
||||||
strings[key] || "",
|
strings[key] || "",
|
||||||
config,
|
config,
|
||||||
frontMatter,
|
frontMatter,
|
||||||
|
|
12
test/custom-rules.md
Normal file
12
test/custom-rules.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Heading
|
||||||
|
|
||||||
|
Sample text.
|
||||||
|
<!-- markdownlint-disable letters-e-x -->
|
||||||
|
Sample text.
|
||||||
|
<!-- markdownlint-enable LETTERS-E-X -->
|
||||||
|
Sample text.
|
||||||
|
<!-- markdownlint-disable TeSt -->
|
||||||
|
Sample text.
|
||||||
|
<!-- markdownlint-enable TEST -->
|
||||||
|
|
||||||
|
> Blockquote
|
|
@ -8,6 +8,7 @@ var tv4 = require("tv4");
|
||||||
var markdownlint = require("../lib/markdownlint");
|
var markdownlint = require("../lib/markdownlint");
|
||||||
var shared = require("../lib/shared");
|
var shared = require("../lib/shared");
|
||||||
var rules = require("../lib/rules");
|
var rules = require("../lib/rules");
|
||||||
|
var customRules = require("./rules");
|
||||||
var defaultConfig = require("./markdownlint-test-default-config.json");
|
var defaultConfig = require("./markdownlint-test-default-config.json");
|
||||||
var configSchema = require("../schema/markdownlint-config-schema.json");
|
var configSchema = require("../schema/markdownlint-config-schema.json");
|
||||||
|
|
||||||
|
@ -890,6 +891,7 @@ module.exports.readmeHeaders = function readmeHeaders(test) {
|
||||||
"## API",
|
"## API",
|
||||||
"### Linting",
|
"### Linting",
|
||||||
"#### options",
|
"#### options",
|
||||||
|
"##### options.customRules",
|
||||||
"##### options.files",
|
"##### options.files",
|
||||||
"##### options.strings",
|
"##### options.strings",
|
||||||
"##### options.config",
|
"##### options.config",
|
||||||
|
@ -1546,3 +1548,278 @@ module.exports.configBadChildJsonSync = function configBadChildJsonSync(test) {
|
||||||
}, "Did not get exception for bad child JSON.");
|
}, "Did not get exception for bad child JSON.");
|
||||||
test.done();
|
test.done();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.customRulesV0 = function customRulesV0(test) {
|
||||||
|
test.expect(4);
|
||||||
|
var customRulesMd = "./test/custom-rules.md";
|
||||||
|
var options = {
|
||||||
|
"customRules": customRules.all,
|
||||||
|
"files": [ customRulesMd ],
|
||||||
|
"resultVersion": 0
|
||||||
|
};
|
||||||
|
markdownlint(options, function callback(err, actualResult) {
|
||||||
|
test.ifError(err);
|
||||||
|
var expectedResult = {};
|
||||||
|
expectedResult[customRulesMd] = {
|
||||||
|
"blockquote": [ 12 ],
|
||||||
|
"every-n-lines": [ 2, 4, 6, 10, 12 ],
|
||||||
|
"letters-E-X": [ 3, 7 ]
|
||||||
|
};
|
||||||
|
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
||||||
|
var actualMessage = actualResult.toString();
|
||||||
|
var expectedMessage =
|
||||||
|
"./test/custom-rules.md: 12: blockquote" +
|
||||||
|
" Rule that reports an error for blockquotes\n" +
|
||||||
|
"./test/custom-rules.md: 2: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines\n" +
|
||||||
|
"./test/custom-rules.md: 4: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines\n" +
|
||||||
|
"./test/custom-rules.md: 6: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines\n" +
|
||||||
|
"./test/custom-rules.md: 10: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines\n" +
|
||||||
|
"./test/custom-rules.md: 12: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines\n" +
|
||||||
|
"./test/custom-rules.md: 3: letters-E-X" +
|
||||||
|
" Rule that reports an error for lines with the letters 'EX'\n" +
|
||||||
|
"./test/custom-rules.md: 7: letters-E-X" +
|
||||||
|
" Rule that reports an error for lines with the letters 'EX'";
|
||||||
|
test.equal(actualMessage, expectedMessage, "Incorrect message (name).");
|
||||||
|
actualMessage = actualResult.toString(true);
|
||||||
|
expectedMessage =
|
||||||
|
"./test/custom-rules.md: 12: blockquote" +
|
||||||
|
" Rule that reports an error for blockquotes\n" +
|
||||||
|
"./test/custom-rules.md: 2: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines\n" +
|
||||||
|
"./test/custom-rules.md: 4: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines\n" +
|
||||||
|
"./test/custom-rules.md: 6: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines\n" +
|
||||||
|
"./test/custom-rules.md: 10: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines\n" +
|
||||||
|
"./test/custom-rules.md: 12: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines\n" +
|
||||||
|
"./test/custom-rules.md: 3: letter-E-letter-X" +
|
||||||
|
" Rule that reports an error for lines with the letters 'EX'\n" +
|
||||||
|
"./test/custom-rules.md: 7: letter-E-letter-X" +
|
||||||
|
" Rule that reports an error for lines with the letters 'EX'";
|
||||||
|
test.equal(actualMessage, expectedMessage, "Incorrect message (alias).");
|
||||||
|
test.done();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.customRulesV1 = function customRulesV1(test) {
|
||||||
|
test.expect(3);
|
||||||
|
var customRulesMd = "./test/custom-rules.md";
|
||||||
|
var options = {
|
||||||
|
"customRules": customRules.all,
|
||||||
|
"files": [ customRulesMd ],
|
||||||
|
"resultVersion": 1
|
||||||
|
};
|
||||||
|
markdownlint(options, function callback(err, actualResult) {
|
||||||
|
test.ifError(err);
|
||||||
|
var expectedResult = {};
|
||||||
|
expectedResult[customRulesMd] = [
|
||||||
|
{ "lineNumber": 12,
|
||||||
|
"ruleName": "blockquote",
|
||||||
|
"ruleAlias": "blockquote",
|
||||||
|
"ruleDescription": "Rule that reports an error for blockquotes",
|
||||||
|
"errorDetail": "Blockquote spans 1 line(s).",
|
||||||
|
"errorContext": "> Block",
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 2,
|
||||||
|
"ruleName": "every-n-lines",
|
||||||
|
"ruleAlias": "every-n-lines",
|
||||||
|
"ruleDescription": "Rule that reports an error every N lines",
|
||||||
|
"errorDetail": "Line number 2",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 4,
|
||||||
|
"ruleName": "every-n-lines",
|
||||||
|
"ruleAlias": "every-n-lines",
|
||||||
|
"ruleDescription": "Rule that reports an error every N lines",
|
||||||
|
"errorDetail": "Line number 4",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 6,
|
||||||
|
"ruleName": "every-n-lines",
|
||||||
|
"ruleAlias": "every-n-lines",
|
||||||
|
"ruleDescription": "Rule that reports an error every N lines",
|
||||||
|
"errorDetail": "Line number 6",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 10,
|
||||||
|
"ruleName": "every-n-lines",
|
||||||
|
"ruleAlias": "every-n-lines",
|
||||||
|
"ruleDescription": "Rule that reports an error every N lines",
|
||||||
|
"errorDetail": "Line number 10",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 12,
|
||||||
|
"ruleName": "every-n-lines",
|
||||||
|
"ruleAlias": "every-n-lines",
|
||||||
|
"ruleDescription": "Rule that reports an error every N lines",
|
||||||
|
"errorDetail": "Line number 12",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 3,
|
||||||
|
"ruleName": "letters-E-X",
|
||||||
|
"ruleAlias": "letter-E-letter-X",
|
||||||
|
"ruleDescription":
|
||||||
|
"Rule that reports an error for lines with the letters 'EX'",
|
||||||
|
"errorDetail": null,
|
||||||
|
"errorContext": "text",
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 7,
|
||||||
|
"ruleName": "letters-E-X",
|
||||||
|
"ruleAlias": "letter-E-letter-X",
|
||||||
|
"ruleDescription":
|
||||||
|
"Rule that reports an error for lines with the letters 'EX'",
|
||||||
|
"errorDetail": null,
|
||||||
|
"errorContext": "text",
|
||||||
|
"errorRange": null }
|
||||||
|
];
|
||||||
|
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
||||||
|
var actualMessage = actualResult.toString();
|
||||||
|
var expectedMessage =
|
||||||
|
"./test/custom-rules.md: 12: blockquote/blockquote" +
|
||||||
|
" Rule that reports an error for blockquotes" +
|
||||||
|
" [Blockquote spans 1 line(s).] [Context: \"> Block\"]\n" +
|
||||||
|
"./test/custom-rules.md: 2: every-n-lines/every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines [Line number 2]\n" +
|
||||||
|
"./test/custom-rules.md: 4: every-n-lines/every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines [Line number 4]\n" +
|
||||||
|
"./test/custom-rules.md: 6: every-n-lines/every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines [Line number 6]\n" +
|
||||||
|
"./test/custom-rules.md: 10: every-n-lines/every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines [Line number 10]\n" +
|
||||||
|
"./test/custom-rules.md: 12: every-n-lines/every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines [Line number 12]\n" +
|
||||||
|
"./test/custom-rules.md: 3: letters-E-X/letter-E-letter-X" +
|
||||||
|
" Rule that reports an error for lines with the letters 'EX'" +
|
||||||
|
" [Context: \"text\"]\n" +
|
||||||
|
"./test/custom-rules.md: 7: letters-E-X/letter-E-letter-X" +
|
||||||
|
" Rule that reports an error for lines with the letters 'EX'" +
|
||||||
|
" [Context: \"text\"]";
|
||||||
|
test.equal(actualMessage, expectedMessage, "Incorrect message.");
|
||||||
|
test.done();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.customRulesV2 = function customRulesV2(test) {
|
||||||
|
test.expect(3);
|
||||||
|
var customRulesMd = "./test/custom-rules.md";
|
||||||
|
var options = {
|
||||||
|
"customRules": customRules.all,
|
||||||
|
"files": [ customRulesMd ],
|
||||||
|
"resultVersion": 2
|
||||||
|
};
|
||||||
|
markdownlint(options, function callback(err, actualResult) {
|
||||||
|
test.ifError(err);
|
||||||
|
var expectedResult = {};
|
||||||
|
expectedResult[customRulesMd] = [
|
||||||
|
{ "lineNumber": 12,
|
||||||
|
"ruleNames": [ "blockquote" ],
|
||||||
|
"ruleDescription": "Rule that reports an error for blockquotes",
|
||||||
|
"errorDetail": "Blockquote spans 1 line(s).",
|
||||||
|
"errorContext": "> Block",
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 2,
|
||||||
|
"ruleNames": [ "every-n-lines" ],
|
||||||
|
"ruleDescription": "Rule that reports an error every N lines",
|
||||||
|
"errorDetail": "Line number 2",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 4,
|
||||||
|
"ruleNames": [ "every-n-lines" ],
|
||||||
|
"ruleDescription": "Rule that reports an error every N lines",
|
||||||
|
"errorDetail": "Line number 4",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 6,
|
||||||
|
"ruleNames": [ "every-n-lines" ],
|
||||||
|
"ruleDescription": "Rule that reports an error every N lines",
|
||||||
|
"errorDetail": "Line number 6",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 10,
|
||||||
|
"ruleNames": [ "every-n-lines" ],
|
||||||
|
"ruleDescription": "Rule that reports an error every N lines",
|
||||||
|
"errorDetail": "Line number 10",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 12,
|
||||||
|
"ruleNames": [ "every-n-lines" ],
|
||||||
|
"ruleDescription": "Rule that reports an error every N lines",
|
||||||
|
"errorDetail": "Line number 12",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 3,
|
||||||
|
"ruleNames": [ "letters-E-X", "letter-E-letter-X", "contains-ex" ],
|
||||||
|
"ruleDescription":
|
||||||
|
"Rule that reports an error for lines with the letters 'EX'",
|
||||||
|
"errorDetail": null,
|
||||||
|
"errorContext": "text",
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 7,
|
||||||
|
"ruleNames": [ "letters-E-X", "letter-E-letter-X", "contains-ex" ],
|
||||||
|
"ruleDescription":
|
||||||
|
"Rule that reports an error for lines with the letters 'EX'",
|
||||||
|
"errorDetail": null,
|
||||||
|
"errorContext": "text",
|
||||||
|
"errorRange": null }
|
||||||
|
];
|
||||||
|
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
||||||
|
var actualMessage = actualResult.toString();
|
||||||
|
var expectedMessage =
|
||||||
|
"./test/custom-rules.md: 12: blockquote" +
|
||||||
|
" Rule that reports an error for blockquotes" +
|
||||||
|
" [Blockquote spans 1 line(s).] [Context: \"> Block\"]\n" +
|
||||||
|
"./test/custom-rules.md: 2: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines [Line number 2]\n" +
|
||||||
|
"./test/custom-rules.md: 4: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines [Line number 4]\n" +
|
||||||
|
"./test/custom-rules.md: 6: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines [Line number 6]\n" +
|
||||||
|
"./test/custom-rules.md: 10: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines [Line number 10]\n" +
|
||||||
|
"./test/custom-rules.md: 12: every-n-lines" +
|
||||||
|
" Rule that reports an error every N lines [Line number 12]\n" +
|
||||||
|
"./test/custom-rules.md: 3: letters-E-X/letter-E-letter-X/contains-ex" +
|
||||||
|
" Rule that reports an error for lines with the letters 'EX'" +
|
||||||
|
" [Context: \"text\"]\n" +
|
||||||
|
"./test/custom-rules.md: 7: letters-E-X/letter-E-letter-X/contains-ex" +
|
||||||
|
" Rule that reports an error for lines with the letters 'EX'" +
|
||||||
|
" [Context: \"text\"]";
|
||||||
|
test.equal(actualMessage, expectedMessage, "Incorrect message.");
|
||||||
|
test.done();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.customRulesConfig = function customRulesConfig(test) {
|
||||||
|
test.expect(2);
|
||||||
|
var customRulesMd = "./test/custom-rules.md";
|
||||||
|
var options = {
|
||||||
|
"customRules": customRules.all,
|
||||||
|
"files": [ customRulesMd ],
|
||||||
|
"config": {
|
||||||
|
"blockquote": true,
|
||||||
|
"every-n-lines": {
|
||||||
|
"n": 3
|
||||||
|
},
|
||||||
|
"letters-e-x": false
|
||||||
|
},
|
||||||
|
"resultVersion": 0
|
||||||
|
};
|
||||||
|
markdownlint(options, function callback(err, actualResult) {
|
||||||
|
test.ifError(err);
|
||||||
|
var expectedResult = {};
|
||||||
|
expectedResult[customRulesMd] = {
|
||||||
|
"blockquote": [ 12 ],
|
||||||
|
"every-n-lines": [ 3, 6, 12 ],
|
||||||
|
"letters-E-X": [ 7 ]
|
||||||
|
};
|
||||||
|
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
||||||
|
test.done();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
22
test/rules/blockquote.js
Normal file
22
test/rules/blockquote.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
"names": [ "blockquote" ],
|
||||||
|
"description": "Rule that reports an error for blockquotes",
|
||||||
|
"tags": [ "test" ],
|
||||||
|
"function": function rule(params, onError) {
|
||||||
|
params.tokens.filter(function filterToken(token) {
|
||||||
|
return token.type === "blockquote_open";
|
||||||
|
}).forEach(function forToken(blockquote) {
|
||||||
|
var lines = blockquote.map[1] - blockquote.map[0];
|
||||||
|
onError({
|
||||||
|
"lineNumber": blockquote.lineNumber,
|
||||||
|
"detail": "Blockquote spans " + lines + " line(s).",
|
||||||
|
"context": blockquote.line.substr(0, 7),
|
||||||
|
"range": null
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
23
test/rules/every-n-lines.js
Normal file
23
test/rules/every-n-lines.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
"names": [ "every-n-lines" ],
|
||||||
|
"description": "Rule that reports an error every N lines",
|
||||||
|
"tags": [ "test" ],
|
||||||
|
"function": function rule(params, onError) {
|
||||||
|
var n = params.config.n || 2;
|
||||||
|
params.lines.forEach(function forLine(line, lineIndex) {
|
||||||
|
var lineNumber = lineIndex + 1;
|
||||||
|
if ((lineNumber % n) === 0) {
|
||||||
|
onError({
|
||||||
|
"lineNumber": lineNumber,
|
||||||
|
"detail": "Line number " + lineNumber,
|
||||||
|
"context": null,
|
||||||
|
"range": null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
28
test/rules/letters-E-X.js
Normal file
28
test/rules/letters-E-X.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
"names": [ "letters-E-X", "letter-E-letter-X", "contains-ex" ],
|
||||||
|
"description": "Rule that reports an error for lines with the letters 'EX'",
|
||||||
|
"tags": [ "test" ],
|
||||||
|
"function": function rule(params, onError) {
|
||||||
|
params.tokens.filter(function filterToken(token) {
|
||||||
|
return token.type === "inline";
|
||||||
|
}).forEach(function forToken(inline) {
|
||||||
|
inline.children.filter(function filterChild(child) {
|
||||||
|
return child.type === "text";
|
||||||
|
}).forEach(function forChild(text) {
|
||||||
|
var index = text.content.toLowerCase().indexOf("ex");
|
||||||
|
if (index !== -1) {
|
||||||
|
onError({
|
||||||
|
"lineNumber": text.lineNumber,
|
||||||
|
"detail": null,
|
||||||
|
"context": text.content.substr(index - 1, 4),
|
||||||
|
"range": null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
13
test/rules/package.json
Normal file
13
test/rules/package.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "markdownlint-rules-test",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Package of markdownlint custom rules used for testing",
|
||||||
|
"main": "rules.js",
|
||||||
|
"author": "David Anson (https://dlaa.me/)",
|
||||||
|
"homepage": "https://github.com/DavidAnson/markdownlint",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"markdownlint-rules"
|
||||||
|
],
|
||||||
|
"private": true
|
||||||
|
}
|
18
test/rules/rules.js
Normal file
18
test/rules/rules.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var blockquote = require("./blockquote");
|
||||||
|
module.exports.blockquote = blockquote;
|
||||||
|
|
||||||
|
var everyNLines = require("./every-n-lines");
|
||||||
|
module.exports.everyNLines = everyNLines;
|
||||||
|
|
||||||
|
var lettersEX = require("./letters-E-X");
|
||||||
|
module.exports.lettersEX = lettersEX;
|
||||||
|
|
||||||
|
module.exports.all = [
|
||||||
|
blockquote,
|
||||||
|
everyNLines,
|
||||||
|
lettersEX
|
||||||
|
];
|
Loading…
Add table
Add a link
Reference in a new issue