Add rule aliases, support throughout (fixes #1).

This commit is contained in:
David Anson 2016-01-12 21:29:17 -08:00
parent b7342485d9
commit 9546cc520e
9 changed files with 283 additions and 101 deletions

114
README.md
View file

@ -38,45 +38,45 @@ cases come directly from that project.
[`markdownlint` demo](http://dlaa.me/markdownlint/), an interactive, in-browser [`markdownlint` demo](http://dlaa.me/markdownlint/), an interactive, in-browser
playground for learning and exploring. playground for learning and exploring.
## Rules ## Rules / Aliases
* **MD001** - Header levels should only increment by one level at a time * **MD001** *header-increment* - Header levels should only increment by one level at a time
* **MD002** - First header should be a h1 header * **MD002** *first-header-h1* - First header should be a h1 header
* **MD003** - Header style * **MD003** *header-style* - Header style
* **MD004** - Unordered list style * **MD004** *ul-style* - Unordered list style
* **MD005** - Inconsistent indentation for list items at the same level * **MD005** *list-indent* - Inconsistent indentation for list items at the same level
* **MD006** - Consider starting bulleted lists at the beginning of the line * **MD006** *ul-start-left* - Consider starting bulleted lists at the beginning of the line
* **MD007** - Unordered list indentation * **MD007** *ul-indent* - Unordered list indentation
* **MD009** - Trailing spaces * **MD009** *no-trailing-spaces* - Trailing spaces
* **MD010** - Hard tabs * **MD010** *no-hard-tabs* - Hard tabs
* **MD011** - Reversed link syntax * **MD011** *no-reversed-links* - Reversed link syntax
* **MD012** - Multiple consecutive blank lines * **MD012** *no-multiple-blanks* - Multiple consecutive blank lines
* **MD013** - Line length * **MD013** *line-length* - Line length
* **MD014** - Dollar signs used before commands without showing output * **MD014** *commands-show-output* - Dollar signs used before commands without showing output
* **MD018** - No space after hash on atx style header * **MD018** *no-missing-space-atx* - No space after hash on atx style header
* **MD019** - Multiple spaces after hash on atx style header * **MD019** *no-multiple-space-atx* - Multiple spaces after hash on atx style header
* **MD020** - No space inside hashes on closed atx style header * **MD020** *no-missing-space-closed-atx* - No space inside hashes on closed atx style header
* **MD021** - Multiple spaces inside hashes on closed atx style header * **MD021** *no-multiple-space-closed-atx* - Multiple spaces inside hashes on closed atx style header
* **MD022** - Headers should be surrounded by blank lines * **MD022** *blanks-around-headers* - Headers should be surrounded by blank lines
* **MD023** - Headers must start at the beginning of the line * **MD023** *header-start-left* - Headers must start at the beginning of the line
* **MD024** - Multiple headers with the same content * **MD024** *no-duplicate-header* - Multiple headers with the same content
* **MD025** - Multiple top level headers in the same document * **MD025** *single-h1* - Multiple top level headers in the same document
* **MD026** - Trailing punctuation in header * **MD026** *no-trailing-punctuation* - Trailing punctuation in header
* **MD027** - Multiple spaces after blockquote symbol * **MD027** *no-multiple-space-blockquote* - Multiple spaces after blockquote symbol
* **MD028** - Blank line inside blockquote * **MD028** *no-blanks-blockquote* - Blank line inside blockquote
* **MD029** - Ordered list item prefix * **MD029** *ol-prefix* - Ordered list item prefix
* **MD030** - Spaces after list markers * **MD030** *list-marker-space* - Spaces after list markers
* **MD031** - Fenced code blocks should be surrounded by blank lines * **MD031** *blanks-around-fences* - Fenced code blocks should be surrounded by blank lines
* **MD032** - Lists should be surrounded by blank lines * **MD032** *blanks-around-lists* - Lists should be surrounded by blank lines
* **MD033** - Inline HTML * **MD033** *no-inline-html* - Inline HTML
* **MD034** - Bare URL used * **MD034** *no-bare-urls* - Bare URL used
* **MD035** - Horizontal rule style * **MD035** *hr-style* - Horizontal rule style
* **MD036** - Emphasis used instead of a header * **MD036** *no-emphasis-as-header* - Emphasis used instead of a header
* **MD037** - Spaces inside emphasis markers * **MD037** *no-space-in-emphasis* - Spaces inside emphasis markers
* **MD038** - Spaces inside code span elements * **MD038** *no-space-in-code* - Spaces inside code span elements
* **MD039** - Spaces inside link text * **MD039** *no-space-in-links* - Spaces inside link text
* **MD040** - Fenced code blocks should have a language specified * **MD040** *fenced-code-language* - Fenced code blocks should have a language specified
* **MD041** - First line in file should be a top level header * **MD041** *first-line-h1* - First line in file should be a top level header
See [Rules.md](doc/Rules.md) for more details. See [Rules.md](doc/Rules.md) for more details.
@ -229,7 +229,7 @@ Type: `Object` mapping `String` to `Boolean | Object`
Configures the rules to use. Configures the rules to use.
Object keys are rule names and values are the rule's configuration. Object keys are rule names or aliases and values are the rule's configuration.
The value `false` disables a rule, `true` enables its default configuration, The value `false` disables a rule, `true` enables its default configuration,
and passing an object customizes its settings. Setting the special `default` and passing an object customizes its settings. Setting the special `default`
rule to `true` or `false` includes/excludes all rules by default. Enabling or rule to `true` or `false` includes/excludes all rules by default. Enabling or
@ -237,7 +237,7 @@ disabling a tag name (ex: `whitespace`) affects all rules having that tag.
The `default` rule is applied first, then keys are processed in order from top The `default` rule is applied first, then keys are processed in order from top
to bottom with later values overriding earlier ones. Keys (including rule names, to bottom with later values overriding earlier ones. Keys (including rule names,
tags, and `default`) are not case-sensitive. aliases, tags, and `default`) are not case-sensitive.
Example: Example:
@ -246,7 +246,7 @@ Example:
"default": true, "default": true,
"MD003": { "style": "atx_closed" }, "MD003": { "style": "atx_closed" },
"MD007": { "indent": 4 }, "MD007": { "indent": 4 },
"MD009": false, "no-hard-tabs": false,
"whitespace": false "whitespace": false
} }
``` ```
@ -276,7 +276,8 @@ Standard completion callback.
Type: `Object` Type: `Object`
Call `result.toString()` for convenience or see below for an example of the Call `result.toString()` for convenience or see below for an example of the
structure of the `result` object. structure of the `result` object. Passing the value `true` to `toString()`
uses rule aliases (ex: `no-hard-tabs`) instead of names (ex: `MD010`).
## Usage ## Usage
@ -300,14 +301,7 @@ markdownlint(options, function callback(err, result) {
}); });
``` ```
Or invoke `markdownlint.sync` for a synchronous call: Output:
```js
var result = markdownlint.sync(options);
console.log(result.toString());
```
Output of both calls:
```text ```text
bad.string: 3: MD010 Hard tabs bad.string: 3: MD010 Hard tabs
@ -318,6 +312,24 @@ bad.md: 1: MD018 No space after hash on atx style header
bad.md: 3: MD018 No space after hash on atx style header bad.md: 3: MD018 No space after hash on atx style header
``` ```
Or invoke `markdownlint.sync` for a synchronous call:
```js
var result = markdownlint.sync(options);
console.log(result.toString(true));
```
Output:
```text
bad.string: 3: no-hard-tabs Hard tabs
bad.string: 1: no-missing-space-atx No space after hash on atx style header
bad.string: 3: no-missing-space-atx No space after hash on atx style header
bad.md: 3: no-hard-tabs Hard tabs
bad.md: 1: no-missing-space-atx No space after hash on atx style header
bad.md: 3: no-missing-space-atx No space after hash on atx style header
```
To examine the `result` object directly: To examine the `result` object directly:
```js ```js
@ -420,7 +432,7 @@ bad.md: 3: MD018 No space after hash on atx style header
## Browser ## Browser
`markdownlint` works in the browser. `markdownlint` also works in the browser.
Generate normal and minified scripts with: Generate normal and minified scripts with:

View file

@ -8,6 +8,8 @@ versions of the examples.
Tags: headers Tags: headers
Aliases: header-increment
This rule is triggered when you skip header levels in a markdown document, for This rule is triggered when you skip header levels in a markdown document, for
example: example:
@ -37,6 +39,8 @@ level at a time:
Tags: headers Tags: headers
Aliases: first-header-h1
This rule is triggered when the first header in the document isn't a h1 header: This rule is triggered when the first header in the document isn't a h1 header:
## This isn't a H1 header ## This isn't a H1 header
@ -53,6 +57,8 @@ The first header in the document should be a h1 header:
Tags: headers Tags: headers
Aliases: header-style
Parameters: style ("consistent", "atx", "atx_closed", "setext", "setext_with_atx"; default "consistent") Parameters: style ("consistent", "atx", "atx_closed", "setext", "setext_with_atx"; default "consistent")
This rule is triggered when different header styles (atx, setext, and 'closed' This rule is triggered when different header styles (atx, setext, and 'closed'
@ -90,6 +96,8 @@ consistent within the document.
Tags: bullet, ul Tags: bullet, ul
Aliases: ul-style
Parameters: style ("consistent", "asterisk", "plus", "dash"; default "consistent") Parameters: style ("consistent", "asterisk", "plus", "dash"; default "consistent")
This rule is triggered when the symbols used in the document for unordered This rule is triggered when the symbols used in the document for unordered
@ -114,6 +122,8 @@ document.
Tags: bullet, ul, indentation Tags: bullet, ul, indentation
Aliases: list-indent
This rule is triggered when list items are parsed as being at the same level, This rule is triggered when list items are parsed as being at the same level,
but don't have the same indentation: but don't have the same indentation:
@ -134,6 +144,8 @@ for the list to fix it:
Tags: bullet, ul, indentation Tags: bullet, ul, indentation
Aliases: ul-start-left
This rule is triggered when top level lists don't start at the beginning of a This rule is triggered when top level lists don't start at the beginning of a
line: line:
@ -160,6 +172,8 @@ characters if you use 4 space tabs, or 1 character if you use 2 space tabs).
Tags: bullet, ul, indentation Tags: bullet, ul, indentation
Aliases: ul-indent
Parameters: indent (number; default 2) Parameters: indent (number; default 2)
This rule is triggered when list items are not indented by the configured This rule is triggered when list items are not indented by the configured
@ -193,6 +207,8 @@ for a description of the problem.
Tags: whitespace Tags: whitespace
Aliases: no-trailing-spaces
Parameters: br_spaces (number; default: 0) Parameters: br_spaces (number; default: 0)
This rule is triggered on any lines that end with whitespace. To fix this, This rule is triggered on any lines that end with whitespace. To fix this,
@ -211,6 +227,8 @@ set to the default of 0.
Tags: whitespace, hard_tab Tags: whitespace, hard_tab
Aliases: no-hard-tabs
This rule is triggered by any lines that contain hard tab characters instead This rule is triggered by any lines that contain hard tab characters instead
of using spaces for indentation. To fix this, replace any hard tab characters of using spaces for indentation. To fix this, replace any hard tab characters
with spaces instead. with spaces instead.
@ -231,6 +249,8 @@ Corrected example:
Tags: links Tags: links
Aliases: no-reversed-links
This rule is triggered when text that appears to be a link is encountered, but This rule is triggered when text that appears to be a link is encountered, but
where the syntax appears to have been reversed (the `[]` and `()` are where the syntax appears to have been reversed (the `[]` and `()` are
reversed): reversed):
@ -245,6 +265,8 @@ To fix this, swap the `[]` and `()` around:
Tags: whitespace, blank_lines Tags: whitespace, blank_lines
Aliases: no-multiple-blanks
This rule is triggered when there are multiple consecutive blank lines in the This rule is triggered when there are multiple consecutive blank lines in the
document: document:
@ -266,6 +288,8 @@ lines inside code blocks.
Tags: line_length Tags: line_length
Aliases: line-length
Parameters: line_length, code_blocks, tables (number; default 80, boolean; default true) Parameters: line_length, code_blocks, tables (number; default 80, boolean; default true)
This rule is triggered when there are lines that are longer than the This rule is triggered when there are lines that are longer than the
@ -287,6 +311,8 @@ rules. Still, some languages do not lend themselves to short lines.
Tags: code Tags: code
Aliases: commands-show-output
This rule is triggered when there are code blocks showing shell commands to be This rule is triggered when there are code blocks showing shell commands to be
typed, and the shell commands are preceded by dollar signs ($): typed, and the shell commands are preceded by dollar signs ($):
@ -320,6 +346,8 @@ for more information.
Tags: headers, atx, spaces Tags: headers, atx, spaces
Aliases: no-missing-space-atx
This rule is triggered when spaces are missing after the hash characters This rule is triggered when spaces are missing after the hash characters
in an atx style header: in an atx style header:
@ -338,6 +366,8 @@ space:
Tags: headers, atx, spaces Tags: headers, atx, spaces
Aliases: no-multiple-space-atx
This rule is triggered when more than one space is used to separate the This rule is triggered when more than one space is used to separate the
header text from the hash characters in an atx style header: header text from the hash characters in an atx style header:
@ -356,6 +386,8 @@ space:
Tags: headers, atx_closed, spaces Tags: headers, atx_closed, spaces
Aliases: no-missing-space-closed-atx
This rule is triggered when spaces are missing inside the hash characters This rule is triggered when spaces are missing inside the hash characters
in a closed atx style header: in a closed atx style header:
@ -376,6 +408,8 @@ Note: this rule will fire if either side of the header is missing spaces.
Tags: headers, atx_closed, spaces Tags: headers, atx_closed, spaces
Aliases: no-multiple-space-closed-atx
This rule is triggered when more than one space is used to separate the This rule is triggered when more than one space is used to separate the
header text from the hash characters in a closed atx style header: header text from the hash characters in a closed atx style header:
@ -397,6 +431,8 @@ spaces.
Tags: headers, blank_lines Tags: headers, blank_lines
Aliases: blanks-around-headers
This rule is triggered when headers (any style) are either not preceded or not This rule is triggered when headers (any style) are either not preceded or not
followed by a blank line: followed by a blank line:
@ -425,6 +461,8 @@ regular text.
Tags: headers, spaces Tags: headers, spaces
Aliases: header-start-left
This rule is triggered when a header is indented by one or more spaces: This rule is triggered when a header is indented by one or more spaces:
Some text Some text
@ -444,6 +482,8 @@ parsed as headers, and will instead appear as regular text.
Tags: headers Tags: headers
Aliases: no-duplicate-header
This rule is triggered if there are multiple headers in the document that have This rule is triggered if there are multiple headers in the document that have
the same text: the same text:
@ -465,6 +505,8 @@ this.
Tags: headers Tags: headers
Aliases: single-h1
This rule is triggered when a top level header is in use (the first line of This rule is triggered when a top level header is in use (the first line of
the file is a h1 header), and more than one h1 header is in use in the the file is a h1 header), and more than one h1 header is in use in the
document: document:
@ -492,6 +534,8 @@ should be contained within this header.
Tags: headers Tags: headers
Aliases: no-trailing-punctuation
Parameters: punctuation (string; default ".,;:!?") Parameters: punctuation (string; default ".,;:!?")
This rule is triggered on any header that has a punctuation character as the This rule is triggered on any header that has a punctuation character as the
@ -512,6 +556,8 @@ in an FAQ.
Tags: blockquote, whitespace, indentation Tags: blockquote, whitespace, indentation
Aliases: no-multiple-space-blockquote
This rule is triggered when blockquotes have more than one space after the This rule is triggered when blockquotes have more than one space after the
blockquote (`>`) symbol: blockquote (`>`) symbol:
@ -527,6 +573,8 @@ To fix, remove any extraneous space:
Tags: blockquote, whitespace Tags: blockquote, whitespace
Aliases: no-blanks-blockquote
This rule is triggered when two blockquote blocks are separated by nothing This rule is triggered when two blockquote blocks are separated by nothing
except for a blank line: except for a blank line:
@ -560,6 +608,8 @@ separate blockquotes.
Tags: ol Tags: ol
Aliases: ol-prefix
Parameters: style ("one", "ordered"; default "one") Parameters: style ("one", "ordered"; default "one")
This rule is triggered on ordered lists that do not either start with '1.' or This rule is triggered on ordered lists that do not either start with '1.' or
@ -582,6 +632,8 @@ Example valid list if the style is configured as 'ordered':
Tags: ol, ul, whitespace Tags: ol, ul, whitespace
Aliases: list-marker-space
Parameters: ul_single, ol_single, ul_multi, ol_multi (number, default 1) Parameters: ul_single, ol_single, ul_multi, ol_multi (number, default 1)
This rule checks for the number of spaces between a list marker (e.g. '`-`', This rule checks for the number of spaces between a list marker (e.g. '`-`',
@ -641,6 +693,8 @@ for your selected document style.
Tags: code, blank_lines Tags: code, blank_lines
Aliases: blanks-around-fences
This rule is triggered when fenced code blocks are either not preceded or not This rule is triggered when fenced code blocks are either not preceded or not
followed by a blank line: followed by a blank line:
@ -676,6 +730,8 @@ not parse fenced code blocks that don't have blank lines before and after them.
Tags: bullet, ul, ol, blank_lines Tags: bullet, ul, ol, blank_lines
Aliases: blanks-around-lists
This rule is triggered when lists (of any kind) are either not preceded or not This rule is triggered when lists (of any kind) are either not preceded or not
followed by a blank line: followed by a blank line:
@ -716,6 +772,8 @@ items with hanging indents are okay:
Tags: html Tags: html
Aliases: no-inline-html
Parameters: allowed_elements (array of string; default empty) Parameters: allowed_elements (array of string; default empty)
This rule is triggered whenever raw HTML is used in a markdown document: This rule is triggered whenever raw HTML is used in a markdown document:
@ -736,6 +794,8 @@ Note: To allow specific HTML elements, use the 'allowed_elements' parameter.
Tags: links, url Tags: links, url
Aliases: no-bare-urls
This rule is triggered whenever a URL is given that isn't surrounded by angle This rule is triggered whenever a URL is given that isn't surrounded by angle
brackets: brackets:
@ -758,6 +818,8 @@ converted:
Tags: hr Tags: hr
Aliases: hr-style
Parameters: style ("consistent", "---", "***", or other string specifying the Parameters: style ("consistent", "---", "***", or other string specifying the
horizontal rule; default "consistent") horizontal rule; default "consistent")
@ -792,6 +854,8 @@ is allowed.
Tags: headers, emphasis Tags: headers, emphasis
Aliases: no-emphasis-as-header
This check looks for instances where emphasized (i.e. bold or italic) text is This check looks for instances where emphasized (i.e. bold or italic) text is
used to separate sections, where a header should be used instead: used to separate sections, where a header should be used instead:
@ -821,6 +885,8 @@ It won't fire on emphasis used within regular text.
Tags: whitespace, emphasis Tags: whitespace, emphasis
Aliases: no-space-in-emphasis
This rule is triggered when emphasis markers (bold, italic) are used, but they This rule is triggered when emphasis markers (bold, italic) are used, but they
have spaces between the markers and the text: have spaces between the markers and the text:
@ -851,6 +917,8 @@ intended by the author.
Tags: whitespace, code Tags: whitespace, code
Aliases: no-space-in-code
This rule is triggered on code span elements that have spaces right inside the This rule is triggered on code span elements that have spaces right inside the
backticks: backticks:
@ -868,6 +936,8 @@ To fix this, remove the spaces inside the codespan markers:
Tags: whitespace, links Tags: whitespace, links
Aliases: no-space-in-links
This rule is triggered on links that have spaces surrounding the link text: This rule is triggered on links that have spaces surrounding the link text:
[ a link ](http://www.example.com/) [ a link ](http://www.example.com/)
@ -880,6 +950,8 @@ To fix this, remove the spaces surrounding the link text:
Tags: code, language Tags: code, language
Aliases: fenced-code-language
This rule is triggered when fenced code blocks are used, but a language isn't This rule is triggered when fenced code blocks are used, but a language isn't
specified: specified:
@ -899,6 +971,8 @@ To fix this, add a language specifier to the code block:
Tags: headers Tags: headers
Aliases: first-line-h1
This rule is triggered when the first line in the file isn't a top level (h1) This rule is triggered when the first line in the file isn't a top level (h1)
header: header:

View file

@ -24,6 +24,6 @@ markdownlint(options, function callback(err, result) {
} }
}); });
// Make a synchronous call // Make a synchronous call, passing true to toString()
var result = markdownlint.sync(options); var result = markdownlint.sync(options);
console.log(result.toString()); console.log(result.toString(true));

View file

@ -7,38 +7,46 @@ var shared = require("./shared");
// Mappings from rule to description and tag to rules // Mappings from rule to description and tag to rules
var allRuleNames = []; var allRuleNames = [];
var ruleToDescription = {}; var ruleNameToRule = {};
var tagUpperToRules = {}; var idUpperToRuleNames = {};
rules.forEach(function forRule(rule) { rules.forEach(function forRule(rule) {
allRuleNames.push(rule.name); allRuleNames.push(rule.name);
ruleToDescription[rule.name] = rule.desc; ruleNameToRule[rule.name] = rule;
// The following is useful for updating README.md // The following is useful for updating README.md
// console.log("* **" + rule.name + "** - " + rule.desc); // console.log("* **" + rule.name + "** *" +
// rule.aliases.join(", ") + "* - " + rule.desc);
rule.tags.forEach(function forTag(tag) { rule.tags.forEach(function forTag(tag) {
var tagUpper = tag.toUpperCase(); var tagUpper = tag.toUpperCase();
var tags = tagUpperToRules[tagUpper] || []; var ruleNames = idUpperToRuleNames[tagUpper] || [];
tags.push(rule.name); ruleNames.push(rule.name);
tagUpperToRules[tagUpper] = tags; idUpperToRuleNames[tagUpper] = ruleNames;
});
rule.aliases.forEach(function forAlias(alias) {
var aliasUpper = alias.toUpperCase();
idUpperToRuleNames[aliasUpper] = [ rule.name ];
}); });
}); });
// The following is useful for updating README.md // The following is useful for updating README.md
// Object.keys(tagToRules).sort().forEach(function forTag(tag) { // Object.keys(idUpperToRuleNames).sort().forEach(function forTag(tag) {
// console.log("* **" + tag + "** - " + tagToRules[tag].join(", ")); // console.log("* **" + tag + "** - " + idUpperToRuleNames[tag].join(", "));
// }); // });
// Class for results with toString for pretty display // Class for results with toString for pretty display
function Results() { } function Results() { }
Results.prototype.toString = function resultsToString() { Results.prototype.toString = function resultsToString(useAlias) {
var that = this; var that = this;
var results = []; var results = [];
Object.keys(that).forEach(function forFile(file) { Object.keys(that).forEach(function forFile(file) {
var fileResults = that[file]; var fileResults = that[file];
Object.keys(fileResults).forEach(function forRule(rule) { Object.keys(fileResults).forEach(function forRule(ruleName) {
var ruleResults = fileResults[rule]; var rule = ruleNameToRule[ruleName];
var ruleResults = fileResults[ruleName];
ruleResults.forEach(function forLine(lineNumber) { ruleResults.forEach(function forLine(lineNumber) {
var result = var result =
file + ": " + lineNumber + ": " + file + ": " +
rule + " " + ruleToDescription[rule]; lineNumber + ": " +
(useAlias ? rule.aliases[0] : rule.name) + " " +
rule.desc;
results.push(result); results.push(result);
}); });
}); });
@ -114,10 +122,10 @@ function lintContent(content, config, frontMatter) { // eslint-disable-line
value = false; value = false;
} }
var keyUpper = key.toUpperCase(); var keyUpper = key.toUpperCase();
if (ruleToDescription[keyUpper]) { if (ruleNameToRule[keyUpper]) {
mergedRules[keyUpper] = value; mergedRules[keyUpper] = value;
} else if (tagUpperToRules[keyUpper]) { } else if (idUpperToRuleNames[keyUpper]) {
tagUpperToRules[keyUpper].forEach(function forRule(ruleName) { idUpperToRuleNames[keyUpper].forEach(function forRule(ruleName) {
mergedRules[ruleName] = value; mergedRules[ruleName] = value;
}); });
} }
@ -133,10 +141,10 @@ function lintContent(content, config, frontMatter) { // eslint-disable-line
match[2].trim().toUpperCase().split(/\s+/) : match[2].trim().toUpperCase().split(/\s+/) :
allRuleNames; allRuleNames;
items.forEach(function forItem(nameUpper) { items.forEach(function forItem(nameUpper) {
if (ruleToDescription[nameUpper]) { if (ruleNameToRule[nameUpper]) {
enabledRules[nameUpper] = enabled; enabledRules[nameUpper] = enabled;
} else if (tagUpperToRules[nameUpper]) { } else if (idUpperToRuleNames[nameUpper]) {
tagUpperToRules[nameUpper].forEach(function forRule(ruleName) { idUpperToRuleNames[nameUpper].forEach(function forRule(ruleName) {
enabledRules[ruleName] = enabled; enabledRules[ruleName] = enabled;
}); });
} }

View file

@ -153,6 +153,7 @@ module.exports = [
"name": "MD001", "name": "MD001",
"desc": "Header levels should only increment by one level at a time", "desc": "Header levels should only increment by one level at a time",
"tags": [ "headers" ], "tags": [ "headers" ],
"aliases": [ "header-increment" ],
"func": function MD001(params, errors) { "func": function MD001(params, errors) {
var prevLevel = 0; var prevLevel = 0;
filterTokens(params, "heading_open", function forToken(token) { filterTokens(params, "heading_open", function forToken(token) {
@ -169,6 +170,7 @@ module.exports = [
"name": "MD002", "name": "MD002",
"desc": "First header should be a h1 header", "desc": "First header should be a h1 header",
"tags": [ "headers" ], "tags": [ "headers" ],
"aliases": [ "first-header-h1" ],
"func": function MD002(params, errors) { "func": function MD002(params, errors) {
params.tokens.every(function forToken(token) { params.tokens.every(function forToken(token) {
if (token.type === "heading_open") { if (token.type === "heading_open") {
@ -186,6 +188,7 @@ module.exports = [
"name": "MD003", "name": "MD003",
"desc": "Header style", "desc": "Header style",
"tags": [ "headers" ], "tags": [ "headers" ],
"aliases": [ "header-style" ],
"func": function MD003(params, errors) { "func": function MD003(params, errors) {
var style = params.options.style || "consistent"; var style = params.options.style || "consistent";
filterTokens(params, "heading_open", function forToken(token) { filterTokens(params, "heading_open", function forToken(token) {
@ -207,6 +210,7 @@ module.exports = [
"name": "MD004", "name": "MD004",
"desc": "Unordered list style", "desc": "Unordered list style",
"tags": [ "bullet", "ul" ], "tags": [ "bullet", "ul" ],
"aliases": [ "ul-style" ],
"func": function MD004(params, errors) { "func": function MD004(params, errors) {
var style = params.options.style || "consistent"; var style = params.options.style || "consistent";
flattenLists(params).forEach(function forList(list) { flattenLists(params).forEach(function forList(list) {
@ -228,6 +232,7 @@ module.exports = [
"name": "MD005", "name": "MD005",
"desc": "Inconsistent indentation for list items at the same level", "desc": "Inconsistent indentation for list items at the same level",
"tags": [ "bullet", "ul", "indentation" ], "tags": [ "bullet", "ul", "indentation" ],
"aliases": [ "list-indent" ],
"func": function MD005(params, errors) { "func": function MD005(params, errors) {
flattenLists(params).forEach(function forList(list) { flattenLists(params).forEach(function forList(list) {
var indent = indentFor(list.items[0]); var indent = indentFor(list.items[0]);
@ -244,6 +249,7 @@ module.exports = [
"name": "MD006", "name": "MD006",
"desc": "Consider starting bulleted lists at the beginning of the line", "desc": "Consider starting bulleted lists at the beginning of the line",
"tags": [ "bullet", "ul", "indentation" ], "tags": [ "bullet", "ul", "indentation" ],
"aliases": [ "ul-start-left" ],
"func": function MD006(params, errors) { "func": function MD006(params, errors) {
flattenLists(params).forEach(function forList(list) { flattenLists(params).forEach(function forList(list) {
if (!list.ordered && !list.nesting && indentFor(list.open)) { if (!list.ordered && !list.nesting && indentFor(list.open)) {
@ -257,6 +263,7 @@ module.exports = [
"name": "MD007", "name": "MD007",
"desc": "Unordered list indentation", "desc": "Unordered list indentation",
"tags": [ "bullet", "ul", "indentation" ], "tags": [ "bullet", "ul", "indentation" ],
"aliases": [ "ul-indent" ],
"func": function MD007(params, errors) { "func": function MD007(params, errors) {
var optionsIndent = params.options.indent || 2; var optionsIndent = params.options.indent || 2;
var prevIndent = 0; var prevIndent = 0;
@ -277,6 +284,7 @@ module.exports = [
"name": "MD009", "name": "MD009",
"desc": "Trailing spaces", "desc": "Trailing spaces",
"tags": [ "whitespace" ], "tags": [ "whitespace" ],
"aliases": [ "no-trailing-spaces" ],
"func": function MD009(params, errors) { "func": function MD009(params, errors) {
var brSpaces = params.options.br_spaces || 0; var brSpaces = params.options.br_spaces || 0;
params.lines.forEach(function forLine(line, lineIndex) { params.lines.forEach(function forLine(line, lineIndex) {
@ -293,6 +301,7 @@ module.exports = [
"name": "MD010", "name": "MD010",
"desc": "Hard tabs", "desc": "Hard tabs",
"tags": [ "whitespace", "hard_tab" ], "tags": [ "whitespace", "hard_tab" ],
"aliases": [ "no-hard-tabs" ],
"func": function MD010(params, errors) { "func": function MD010(params, errors) {
params.lines.forEach(function forLine(line, lineIndex) { params.lines.forEach(function forLine(line, lineIndex) {
if (/\t/.test(line)) { if (/\t/.test(line)) {
@ -306,6 +315,7 @@ module.exports = [
"name": "MD011", "name": "MD011",
"desc": "Reversed link syntax", "desc": "Reversed link syntax",
"tags": [ "links" ], "tags": [ "links" ],
"aliases": [ "no-reversed-links" ],
"func": function MD011(params, errors) { "func": function MD011(params, errors) {
forEachInlineChild(params, "text", function forToken(token) { forEachInlineChild(params, "text", function forToken(token) {
if (/\([^)]+\)\[[^\]]+\]/.test(token.content)) { if (/\([^)]+\)\[[^\]]+\]/.test(token.content)) {
@ -319,6 +329,7 @@ module.exports = [
"name": "MD012", "name": "MD012",
"desc": "Multiple consecutive blank lines", "desc": "Multiple consecutive blank lines",
"tags": [ "whitespace", "blank_lines" ], "tags": [ "whitespace", "blank_lines" ],
"aliases": [ "no-multiple-blanks" ],
"func": function MD012(params, errors) { "func": function MD012(params, errors) {
var prevLine = "-"; var prevLine = "-";
forEachLine(params, function forLine(line, lineIndex, inCode) { forEachLine(params, function forLine(line, lineIndex, inCode) {
@ -335,6 +346,7 @@ module.exports = [
"name": "MD013", "name": "MD013",
"desc": "Line length", "desc": "Line length",
"tags": [ "line_length" ], "tags": [ "line_length" ],
"aliases": [ "line-length" ],
"func": function MD013(params, errors) { "func": function MD013(params, errors) {
var lineLength = params.options.line_length || 80; var lineLength = params.options.line_length || 80;
var codeBlocks = params.options.code_blocks; var codeBlocks = params.options.code_blocks;
@ -357,6 +369,7 @@ module.exports = [
"name": "MD014", "name": "MD014",
"desc": "Dollar signs used before commands without showing output", "desc": "Dollar signs used before commands without showing output",
"tags": [ "code" ], "tags": [ "code" ],
"aliases": [ "commands-show-output" ],
"func": function MD014(params, errors) { "func": function MD014(params, errors) {
[ "code_block", "fence" ].forEach(function forType(type) { [ "code_block", "fence" ].forEach(function forType(type) {
filterTokens(params, type, function forToken(token) { filterTokens(params, type, function forToken(token) {
@ -376,6 +389,7 @@ module.exports = [
"name": "MD018", "name": "MD018",
"desc": "No space after hash on atx style header", "desc": "No space after hash on atx style header",
"tags": [ "headers", "atx", "spaces" ], "tags": [ "headers", "atx", "spaces" ],
"aliases": [ "no-missing-space-atx" ],
"func": function MD018(params, errors) { "func": function MD018(params, errors) {
forEachLine(params, function forLine(line, lineIndex, inCode) { forEachLine(params, function forLine(line, lineIndex, inCode) {
if (!inCode && /^#+[^#\s]/.test(line) && !/#$/.test(line)) { if (!inCode && /^#+[^#\s]/.test(line) && !/#$/.test(line)) {
@ -389,6 +403,7 @@ module.exports = [
"name": "MD019", "name": "MD019",
"desc": "Multiple spaces after hash on atx style header", "desc": "Multiple spaces after hash on atx style header",
"tags": [ "headers", "atx", "spaces" ], "tags": [ "headers", "atx", "spaces" ],
"aliases": [ "no-multiple-space-atx" ],
"func": function MD019(params, errors) { "func": function MD019(params, errors) {
filterTokens(params, "heading_open", function forToken(token) { filterTokens(params, "heading_open", function forToken(token) {
if ((headingStyleFor(token) === "atx") && if ((headingStyleFor(token) === "atx") &&
@ -403,6 +418,7 @@ module.exports = [
"name": "MD020", "name": "MD020",
"desc": "No space inside hashes on closed atx style header", "desc": "No space inside hashes on closed atx style header",
"tags": [ "headers", "atx_closed", "spaces" ], "tags": [ "headers", "atx_closed", "spaces" ],
"aliases": [ "no-missing-space-closed-atx" ],
"func": function MD020(params, errors) { "func": function MD020(params, errors) {
forEachLine(params, function forLine(line, lineIndex, inCode) { forEachLine(params, function forLine(line, lineIndex, inCode) {
if (!inCode && /^#+[^#]*[^\\]#+$/.test(line) && if (!inCode && /^#+[^#]*[^\\]#+$/.test(line) &&
@ -417,6 +433,7 @@ module.exports = [
"name": "MD021", "name": "MD021",
"desc": "Multiple spaces inside hashes on closed atx style header", "desc": "Multiple spaces inside hashes on closed atx style header",
"tags": [ "headers", "atx_closed", "spaces" ], "tags": [ "headers", "atx_closed", "spaces" ],
"aliases": [ "no-multiple-space-closed-atx" ],
"func": function MD021(params, errors) { "func": function MD021(params, errors) {
filterTokens(params, "heading_open", function forToken(token) { filterTokens(params, "heading_open", function forToken(token) {
if ((headingStyleFor(token) === "atx_closed") && if ((headingStyleFor(token) === "atx_closed") &&
@ -431,6 +448,7 @@ module.exports = [
"name": "MD022", "name": "MD022",
"desc": "Headers should be surrounded by blank lines", "desc": "Headers should be surrounded by blank lines",
"tags": [ "headers", "blank_lines" ], "tags": [ "headers", "blank_lines" ],
"aliases": [ "blanks-around-headers" ],
"func": function MD022(params, errors) { "func": function MD022(params, errors) {
var prevHeadingLineNumber = 0; var prevHeadingLineNumber = 0;
var prevMaxLineIndex = -1; var prevMaxLineIndex = -1;
@ -471,6 +489,7 @@ module.exports = [
"name": "MD023", "name": "MD023",
"desc": "Headers must start at the beginning of the line", "desc": "Headers must start at the beginning of the line",
"tags": [ "headers", "spaces" ], "tags": [ "headers", "spaces" ],
"aliases": [ "header-start-left" ],
"func": function MD023(params, errors) { "func": function MD023(params, errors) {
filterTokens(params, "heading_open", function forToken(token) { filterTokens(params, "heading_open", function forToken(token) {
if (/^\s/.test(token.line)) { if (/^\s/.test(token.line)) {
@ -484,6 +503,7 @@ module.exports = [
"name": "MD024", "name": "MD024",
"desc": "Multiple headers with the same content", "desc": "Multiple headers with the same content",
"tags": [ "headers" ], "tags": [ "headers" ],
"aliases": [ "no-duplicate-header" ],
"func": function MD024(params, errors) { "func": function MD024(params, errors) {
var knownContent = []; var knownContent = [];
forEachHeading(params, function forHeading(heading, content) { forEachHeading(params, function forHeading(heading, content) {
@ -500,6 +520,7 @@ module.exports = [
"name": "MD025", "name": "MD025",
"desc": "Multiple top level headers in the same document", "desc": "Multiple top level headers in the same document",
"tags": [ "headers" ], "tags": [ "headers" ],
"aliases": [ "single-h1" ],
"func": function MD025(params, errors) { "func": function MD025(params, errors) {
var hasTopLevelHeading = false; var hasTopLevelHeading = false;
filterTokens(params, "heading_open", function forToken(token) { filterTokens(params, "heading_open", function forToken(token) {
@ -518,6 +539,7 @@ module.exports = [
"name": "MD026", "name": "MD026",
"desc": "Trailing punctuation in header", "desc": "Trailing punctuation in header",
"tags": [ "headers" ], "tags": [ "headers" ],
"aliases": [ "no-trailing-punctuation" ],
"func": function MD026(params, errors) { "func": function MD026(params, errors) {
var punctuation = params.options.punctuation || ".,;:!?"; var punctuation = params.options.punctuation || ".,;:!?";
var re = new RegExp("[" + punctuation + "]$"); var re = new RegExp("[" + punctuation + "]$");
@ -533,6 +555,7 @@ module.exports = [
"name": "MD027", "name": "MD027",
"desc": "Multiple spaces after blockquote symbol", "desc": "Multiple spaces after blockquote symbol",
"tags": [ "blockquote", "whitespace", "indentation" ], "tags": [ "blockquote", "whitespace", "indentation" ],
"aliases": [ "no-multiple-space-blockquote" ],
"func": function MD027(params, errors) { "func": function MD027(params, errors) {
var inBlockquote = false; var inBlockquote = false;
params.tokens.forEach(function forToken(token) { params.tokens.forEach(function forToken(token) {
@ -557,6 +580,7 @@ module.exports = [
"name": "MD028", "name": "MD028",
"desc": "Blank line inside blockquote", "desc": "Blank line inside blockquote",
"tags": [ "blockquote", "whitespace" ], "tags": [ "blockquote", "whitespace" ],
"aliases": [ "no-blanks-blockquote" ],
"func": function MD028(params, errors) { "func": function MD028(params, errors) {
var prevToken = {}; var prevToken = {};
params.tokens.forEach(function forToken(token) { params.tokens.forEach(function forToken(token) {
@ -573,6 +597,7 @@ module.exports = [
"name": "MD029", "name": "MD029",
"desc": "Ordered list item prefix", "desc": "Ordered list item prefix",
"tags": [ "ol" ], "tags": [ "ol" ],
"aliases": [ "ol-prefix" ],
"func": function MD029(params, errors) { "func": function MD029(params, errors) {
var style = params.options.style || "one"; var style = params.options.style || "one";
flattenLists(params).forEach(function forList(list) { flattenLists(params).forEach(function forList(list) {
@ -596,6 +621,7 @@ module.exports = [
"name": "MD030", "name": "MD030",
"desc": "Spaces after list markers", "desc": "Spaces after list markers",
"tags": [ "ol", "ul", "whitespace" ], "tags": [ "ol", "ul", "whitespace" ],
"aliases": [ "list-marker-space" ],
"func": function MD030(params, errors) { "func": function MD030(params, errors) {
var ulSingle = params.options.ul_single || 1; var ulSingle = params.options.ul_single || 1;
var olSingle = params.options.ol_single || 1; var olSingle = params.options.ol_single || 1;
@ -621,6 +647,7 @@ module.exports = [
"name": "MD031", "name": "MD031",
"desc": "Fenced code blocks should be surrounded by blank lines", "desc": "Fenced code blocks should be surrounded by blank lines",
"tags": [ "code", "blank_lines" ], "tags": [ "code", "blank_lines" ],
"aliases": [ "blanks-around-fences" ],
"func": function MD031(params, errors) { "func": function MD031(params, errors) {
var lines = params.lines; var lines = params.lines;
forEachLine(params, function forLine(line, i, inCode, onFence) { forEachLine(params, function forLine(line, i, inCode, onFence) {
@ -636,6 +663,7 @@ module.exports = [
"name": "MD032", "name": "MD032",
"desc": "Lists should be surrounded by blank lines", "desc": "Lists should be surrounded by blank lines",
"tags": [ "bullet", "ul", "ol", "blank_lines" ], "tags": [ "bullet", "ul", "ol", "blank_lines" ],
"aliases": [ "blanks-around-lists" ],
"func": function MD032(params, errors) { "func": function MD032(params, errors) {
var inList = false; var inList = false;
var prevLine = ""; var prevLine = "";
@ -658,6 +686,7 @@ module.exports = [
"name": "MD033", "name": "MD033",
"desc": "Inline HTML", "desc": "Inline HTML",
"tags": [ "html" ], "tags": [ "html" ],
"aliases": [ "no-inline-html" ],
"func": function MD033(params, errors) { "func": function MD033(params, errors) {
var allowedElements = (params.options.allowed_elements || []) var allowedElements = (params.options.allowed_elements || [])
.map(function forElement(element) { .map(function forElement(element) {
@ -690,6 +719,7 @@ module.exports = [
"name": "MD034", "name": "MD034",
"desc": "Bare URL used", "desc": "Bare URL used",
"tags": [ "links", "url" ], "tags": [ "links", "url" ],
"aliases": [ "no-bare-urls" ],
"func": function MD034(params, errors) { "func": function MD034(params, errors) {
filterTokens(params, "inline", function forToken(token) { filterTokens(params, "inline", function forToken(token) {
var inLink = false; var inLink = false;
@ -712,6 +742,7 @@ module.exports = [
"name": "MD035", "name": "MD035",
"desc": "Horizontal rule style", "desc": "Horizontal rule style",
"tags": [ "hr" ], "tags": [ "hr" ],
"aliases": [ "hr-style" ],
"func": function MD035(params, errors) { "func": function MD035(params, errors) {
var style = params.options.style || "consistent"; var style = params.options.style || "consistent";
filterTokens(params, "hr", function forToken(token) { filterTokens(params, "hr", function forToken(token) {
@ -729,6 +760,7 @@ module.exports = [
"name": "MD036", "name": "MD036",
"desc": "Emphasis used instead of a header", "desc": "Emphasis used instead of a header",
"tags": [ "headers", "emphasis" ], "tags": [ "headers", "emphasis" ],
"aliases": [ "no-emphasis-as-header" ],
"func": function MD036(params, errors) { "func": function MD036(params, errors) {
function base(token) { function base(token) {
if (token.type === "paragraph_open") { if (token.type === "paragraph_open") {
@ -760,6 +792,7 @@ module.exports = [
"name": "MD037", "name": "MD037",
"desc": "Spaces inside emphasis markers", "desc": "Spaces inside emphasis markers",
"tags": [ "whitespace", "emphasis" ], "tags": [ "whitespace", "emphasis" ],
"aliases": [ "no-space-in-emphasis" ],
"func": function MD037(params, errors) { "func": function MD037(params, errors) {
forEachInlineChild(params, "text", function forToken(token) { forEachInlineChild(params, "text", function forToken(token) {
if (/\s(\*\*?|__?)\s.+\1/.test(token.content) || if (/\s(\*\*?|__?)\s.+\1/.test(token.content) ||
@ -774,6 +807,7 @@ module.exports = [
"name": "MD038", "name": "MD038",
"desc": "Spaces inside code span elements", "desc": "Spaces inside code span elements",
"tags": [ "whitespace", "code" ], "tags": [ "whitespace", "code" ],
"aliases": [ "no-space-in-code" ],
"func": function MD038(params, errors) { "func": function MD038(params, errors) {
forEachInlineChild(params, "code_inline", forEachInlineChild(params, "code_inline",
function forToken(token, inline) { function forToken(token, inline) {
@ -788,6 +822,7 @@ module.exports = [
"name": "MD039", "name": "MD039",
"desc": "Spaces inside link text", "desc": "Spaces inside link text",
"tags": [ "whitespace", "links" ], "tags": [ "whitespace", "links" ],
"aliases": [ "no-space-in-links" ],
"func": function MD039(params, errors) { "func": function MD039(params, errors) {
filterTokens(params, "inline", function forToken(token) { filterTokens(params, "inline", function forToken(token) {
var inLink = false; var inLink = false;
@ -821,6 +856,7 @@ module.exports = [
"name": "MD040", "name": "MD040",
"desc": "Fenced code blocks should have a language specified", "desc": "Fenced code blocks should have a language specified",
"tags": [ "code", "language" ], "tags": [ "code", "language" ],
"aliases": [ "fenced-code-language" ],
"func": function MD040(params, errors) { "func": function MD040(params, errors) {
filterTokens(params, "fence", function forToken(token) { filterTokens(params, "fence", function forToken(token) {
if (!token.info.trim()) { if (!token.info.trim()) {
@ -834,6 +870,7 @@ module.exports = [
"name": "MD041", "name": "MD041",
"desc": "First line in file should be a top level header", "desc": "First line in file should be a top level header",
"tags": [ "headers" ], "tags": [ "headers" ],
"aliases": [ "first-line-h1" ],
"func": function MD041(params, errors) { "func": function MD041(params, errors) {
var firstHeader = null; var firstHeader = null;
params.tokens.every(function forToken(token) { params.tokens.every(function forToken(token) {

View file

@ -8,7 +8,7 @@ module.exports.frontMatterRe = /^---$[^]*?^---$(\r\n|\r|\n)/m;
// Regular expression for matching inline disable/enable comments // Regular expression for matching inline disable/enable comments
module.exports.inlineCommentRe = module.exports.inlineCommentRe =
/<!--\s*markdownlint-(dis|en)able((?:\s+[a-z0-9_]+)*)\s*-->/ig; /<!--\s*markdownlint-(dis|en)able((?:\s+[a-z0-9_\-]+)*)\s*-->/ig;
// readFile options for reading with the UTF-8 encoding // readFile options for reading with the UTF-8 encoding
module.exports.utf8Encoding = { "encoding": "utf8" }; module.exports.utf8Encoding = { "encoding": "utf8" };

View file

@ -4,10 +4,10 @@
"default": true, "default": true,
"whitespace": false, "whitespace": false,
"line_length": false, "line_length": false,
"MD006": false, "ul-start-left": false,
"MD007": false, "ul-indent": false,
"MD033": false, "no-inline-html": false,
"MD034": false, "no-bare-urls": false,
"MD040": false, "fenced-code-language": false,
"MD041": false "first-line-h1": false
} }

View file

@ -60,9 +60,9 @@ hard tab / space * in * emphasis / space ` in ` code
hard tab {MD010} / space * in * emphasis {MD037} / space ` in ` code {MD038} hard tab {MD010} / space * in * emphasis {MD037} / space ` in ` code {MD038}
<!-- markdownlint-disable NotATag MD038 --> <!-- markdownlint-disable NotATag no-space-in-code -->
hard tab {MD010} / space * in * emphasis {MD037} / space ` in ` code hard tab {MD010} / space * in * emphasis {MD037} / space ` in ` code
<!-- markdownlint-enable NotATag MD038 --> <!-- markdownlint-enable NotATag nO-sPaCe-In-CoDe -->
hard tab {MD010} / space * in * emphasis {MD037} / space ` in ` code {MD038} hard tab {MD010} / space * in * emphasis {MD037} / space ` in ` code {MD038}

View file

@ -77,7 +77,8 @@ fs.readdirSync("./test").forEach(function forFile(file) {
module.exports.projectFiles = function projectFiles(test) { module.exports.projectFiles = function projectFiles(test) {
test.expect(2); test.expect(2);
var options = { var options = {
"files": [ "README.md" ] "files": [ "README.md" ],
"config": { "MD013": false }
}; };
markdownlint(options, function callback(err, actual) { markdownlint(options, function callback(err, actual) {
test.ifError(err); test.ifError(err);
@ -88,7 +89,7 @@ module.exports.projectFiles = function projectFiles(test) {
}; };
module.exports.resultFormatting = function resultFormatting(test) { module.exports.resultFormatting = function resultFormatting(test) {
test.expect(3); test.expect(4);
var options = { var options = {
"files": [ "files": [
"./test/atx_header_spacing.md", "./test/atx_header_spacing.md",
@ -121,13 +122,26 @@ module.exports.resultFormatting = function resultFormatting(test) {
" Multiple spaces after hash on atx style header\n" + " Multiple spaces after hash on atx style header\n" +
"./test/first_header_bad_atx.md: 1: MD002" + "./test/first_header_bad_atx.md: 1: MD002" +
" First header should be a h1 header"; " First header should be a h1 header";
test.equal(actualMessage, expectedMessage, "Incorrect message."); test.equal(actualMessage, expectedMessage, "Incorrect message (name).");
actualMessage = actualResult.toString(true);
expectedMessage =
"./test/atx_header_spacing.md: 3: first-header-h1" +
" First header should be a h1 header\n" +
"./test/atx_header_spacing.md: 1: no-missing-space-atx" +
" No space after hash on atx style header\n" +
"./test/atx_header_spacing.md: 3: no-multiple-space-atx" +
" Multiple spaces after hash on atx style header\n" +
"./test/atx_header_spacing.md: 5: no-multiple-space-atx" +
" Multiple spaces after hash on atx style header\n" +
"./test/first_header_bad_atx.md: 1: first-header-h1" +
" First header should be a h1 header";
test.equal(actualMessage, expectedMessage, "Incorrect message (alias).");
test.done(); test.done();
}); });
}; };
module.exports.resultFormattingSync = function resultFormattingSync(test) { module.exports.resultFormattingSync = function resultFormattingSync(test) {
test.expect(2); test.expect(3);
var options = { var options = {
"files": [ "files": [
"./test/atx_header_spacing.md", "./test/atx_header_spacing.md",
@ -159,7 +173,20 @@ module.exports.resultFormattingSync = function resultFormattingSync(test) {
" Multiple spaces after hash on atx style header\n" + " Multiple spaces after hash on atx style header\n" +
"./test/first_header_bad_atx.md: 1: MD002" + "./test/first_header_bad_atx.md: 1: MD002" +
" First header should be a h1 header"; " First header should be a h1 header";
test.equal(actualMessage, expectedMessage, "Incorrect message."); test.equal(actualMessage, expectedMessage, "Incorrect message (name).");
actualMessage = actualResult.toString(true);
expectedMessage =
"./test/atx_header_spacing.md: 3: first-header-h1" +
" First header should be a h1 header\n" +
"./test/atx_header_spacing.md: 1: no-missing-space-atx" +
" No space after hash on atx style header\n" +
"./test/atx_header_spacing.md: 3: no-multiple-space-atx" +
" Multiple spaces after hash on atx style header\n" +
"./test/atx_header_spacing.md: 5: no-multiple-space-atx" +
" Multiple spaces after hash on atx style header\n" +
"./test/first_header_bad_atx.md: 1: first-header-h1" +
" First header should be a h1 header";
test.equal(actualMessage, expectedMessage, "Incorrect message (alias).");
test.done(); test.done();
}; };
@ -302,7 +329,7 @@ module.exports.disableRules = function disableRules(test) {
"MD002": false, "MD002": false,
"default": true, "default": true,
"MD019": false, "MD019": false,
"MD041": false "first-line-h1": false
} }
}; };
markdownlint(options, function callback(err, actualResult) { markdownlint(options, function callback(err, actualResult) {
@ -328,7 +355,7 @@ module.exports.enableRules = function enableRules(test) {
"config": { "config": {
"MD002": true, "MD002": true,
"default": false, "default": false,
"MD019": true "no-multiple-space-atx": true
} }
}; };
markdownlint(options, function callback(err, actualResult) { markdownlint(options, function callback(err, actualResult) {
@ -357,7 +384,7 @@ module.exports.enableRulesMixedCase = function enableRulesMixedCase(test) {
"config": { "config": {
"Md002": true, "Md002": true,
"DeFaUlT": false, "DeFaUlT": false,
"Md019": true "nO-mUlTiPlE-sPaCe-AtX": true
} }
}; };
markdownlint(options, function callback(err, actualResult) { markdownlint(options, function callback(err, actualResult) {
@ -697,6 +724,23 @@ module.exports.ruleNamesUpperCase = function ruleNamesUpperCase(test) {
test.done(); test.done();
}; };
module.exports.uniqueAliases = function uniqueAliases(test) {
test.expect(74);
var tags = [];
rules.forEach(function forRule(rule) {
Array.prototype.push.apply(tags, rule.tags);
});
var aliases = [];
rules.forEach(function forRule(rule) {
rule.aliases.forEach(function forAlias(alias) {
test.ok(tags.indexOf(alias) === -1, "Alias not unique in tags.");
test.ok(aliases.indexOf(alias) === -1, "Alias not unique in aliases.");
aliases.push(alias);
});
});
test.done();
};
module.exports.readme = function readme(test) { module.exports.readme = function readme(test) {
test.expect(97); test.expect(97);
var tagToRules = {}; var tagToRules = {};
@ -733,7 +777,8 @@ module.exports.readme = function readme(test) {
test.ok(rule, test.ok(rule,
"Missing rule implementation for " + token.content + "."); "Missing rule implementation for " + token.content + ".");
if (rule) { if (rule) {
var expected = "**" + rule.name + "** - " + rule.desc; var expected = "**" + rule.name + "** *" +
rule.aliases.join(", ") + "* - " + rule.desc;
test.equal(token.content, expected, "Rule mismatch."); test.equal(token.content, expected, "Rule mismatch.");
} }
} else if (inTags) { } else if (inTags) {
@ -755,7 +800,7 @@ module.exports.readme = function readme(test) {
}; };
module.exports.doc = function doc(test) { module.exports.doc = function doc(test) {
test.expect(199); test.expect(274);
fs.readFile("doc/Rules.md", shared.utf8Encoding, fs.readFile("doc/Rules.md", shared.utf8Encoding,
function readFile(err, contents) { function readFile(err, contents) {
test.ifError(err); test.ifError(err);
@ -763,11 +808,14 @@ module.exports.doc = function doc(test) {
var inHeading = false; var inHeading = false;
var rule = null; var rule = null;
var ruleHasTags = true; var ruleHasTags = true;
var ruleHasAliases = true;
var ruleUsesParams = null; var ruleUsesParams = null;
var tagParameterRe = /, |: | /; var tagAliasParameterRe = /, |: | /;
function testTagsAndParams() { function testTagsAliasesParams() {
test.ok(ruleHasTags, test.ok(ruleHasTags,
"Missing tags for rule " + (rule || {}).name + "."); "Missing tags for rule " + (rule || {}).name + ".");
test.ok(ruleHasAliases,
"Missing aliases for rule " + (rule || {}).name + ".");
test.ok(!ruleUsesParams, test.ok(!ruleUsesParams,
"Missing parameters for rule " + (rule || {}).name + "."); "Missing parameters for rule " + (rule || {}).name + ".");
} }
@ -778,9 +826,9 @@ module.exports.doc = function doc(test) {
inHeading = false; inHeading = false;
} else if (token.type === "inline") { } else if (token.type === "inline") {
if (inHeading) { if (inHeading) {
testTagsAndParams(); testTagsAliasesParams();
rule = rulesLeft.shift(); rule = rulesLeft.shift();
ruleHasTags = false; ruleHasTags = ruleHasAliases = false;
test.ok(rule, test.ok(rule,
"Missing rule implementation for " + token.content + "."); "Missing rule implementation for " + token.content + ".");
test.equal(token.content, rule.name + " - " + rule.desc, test.equal(token.content, rule.name + " - " + rule.desc,
@ -793,13 +841,16 @@ module.exports.doc = function doc(test) {
}); });
} }
} else if (/^Tags: /.test(token.content) && rule) { } else if (/^Tags: /.test(token.content) && rule) {
var tags = token.content.split(tagParameterRe).slice(1); test.deepEqual(token.content.split(tagAliasParameterRe).slice(1),
test.deepEqual(tags, rule.tags, rule.tags, "Tag mismatch for rule " + rule.name + ".");
"Tag mismatch for rule " + rule.name + ".");
ruleHasTags = true; ruleHasTags = true;
} else if (/^Aliases: /.test(token.content) && rule) {
test.deepEqual(token.content.split(tagAliasParameterRe).slice(1),
rule.aliases, "Alias mismatch for rule " + rule.name + ".");
ruleHasAliases = true;
} else if (/^Parameters: /.test(token.content) && rule) { } else if (/^Parameters: /.test(token.content) && rule) {
var inDetails = false; var inDetails = false;
var parameters = token.content.split(tagParameterRe) var parameters = token.content.split(tagAliasParameterRe)
.slice(1) .slice(1)
.filter(function forPart(part) { .filter(function forPart(part) {
inDetails = inDetails || (part[0] === "("); inDetails = inDetails || (part[0] === "(");
@ -814,7 +865,7 @@ module.exports.doc = function doc(test) {
var ruleLeft = rulesLeft.shift(); var ruleLeft = rulesLeft.shift();
test.ok(!ruleLeft, test.ok(!ruleLeft,
"Missing rule documentation for " + (ruleLeft || {}).name + "."); "Missing rule documentation for " + (ruleLeft || {}).name + ".");
testTagsAndParams(); testTagsAliasesParams();
test.done(); test.done();
}); });
}; };