From 9546cc520ee9c9bfbf2ea5bf1ca5b00bf86cae81 Mon Sep 17 00:00:00 2001 From: David Anson Date: Tue, 12 Jan 2016 21:29:17 -0800 Subject: [PATCH] Add rule aliases, support throughout (fixes #1). --- README.md | 114 +++++++++++++++++++--------------- doc/Rules.md | 74 ++++++++++++++++++++++ example/standalone.js | 4 +- lib/markdownlint.js | 48 ++++++++------ lib/rules.js | 37 +++++++++++ lib/shared.js | 2 +- style/relaxed.json | 12 ++-- test/inline-disable-enable.md | 4 +- test/markdownlint-test.js | 89 ++++++++++++++++++++------ 9 files changed, 283 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index ff9df1ee..2a3fb59f 100644 --- a/README.md +++ b/README.md @@ -38,45 +38,45 @@ cases come directly from that project. [`markdownlint` demo](http://dlaa.me/markdownlint/), an interactive, in-browser playground for learning and exploring. -## Rules +## Rules / Aliases -* **MD001** - Header levels should only increment by one level at a time -* **MD002** - First header should be a h1 header -* **MD003** - Header style -* **MD004** - Unordered list style -* **MD005** - Inconsistent indentation for list items at the same level -* **MD006** - Consider starting bulleted lists at the beginning of the line -* **MD007** - Unordered list indentation -* **MD009** - Trailing spaces -* **MD010** - Hard tabs -* **MD011** - Reversed link syntax -* **MD012** - Multiple consecutive blank lines -* **MD013** - Line length -* **MD014** - Dollar signs used before commands without showing output -* **MD018** - No space after hash on atx style header -* **MD019** - Multiple spaces after hash on atx style header -* **MD020** - No space inside hashes on closed atx style header -* **MD021** - Multiple spaces inside hashes on closed atx style header -* **MD022** - Headers should be surrounded by blank lines -* **MD023** - Headers must start at the beginning of the line -* **MD024** - Multiple headers with the same content -* **MD025** - Multiple top level headers in the same document -* **MD026** - Trailing punctuation in header -* **MD027** - Multiple spaces after blockquote symbol -* **MD028** - Blank line inside blockquote -* **MD029** - Ordered list item prefix -* **MD030** - Spaces after list markers -* **MD031** - Fenced code blocks should be surrounded by blank lines -* **MD032** - Lists should be surrounded by blank lines -* **MD033** - Inline HTML -* **MD034** - Bare URL used -* **MD035** - Horizontal rule style -* **MD036** - Emphasis used instead of a header -* **MD037** - Spaces inside emphasis markers -* **MD038** - Spaces inside code span elements -* **MD039** - Spaces inside link text -* **MD040** - Fenced code blocks should have a language specified -* **MD041** - First line in file should be a top level header +* **MD001** *header-increment* - Header levels should only increment by one level at a time +* **MD002** *first-header-h1* - First header should be a h1 header +* **MD003** *header-style* - Header style +* **MD004** *ul-style* - Unordered list style +* **MD005** *list-indent* - Inconsistent indentation for list items at the same level +* **MD006** *ul-start-left* - Consider starting bulleted lists at the beginning of the line +* **MD007** *ul-indent* - Unordered list indentation +* **MD009** *no-trailing-spaces* - Trailing spaces +* **MD010** *no-hard-tabs* - Hard tabs +* **MD011** *no-reversed-links* - Reversed link syntax +* **MD012** *no-multiple-blanks* - Multiple consecutive blank lines +* **MD013** *line-length* - Line length +* **MD014** *commands-show-output* - Dollar signs used before commands without showing output +* **MD018** *no-missing-space-atx* - No space after hash on atx style header +* **MD019** *no-multiple-space-atx* - Multiple spaces after hash on atx style header +* **MD020** *no-missing-space-closed-atx* - No space inside hashes on closed atx style header +* **MD021** *no-multiple-space-closed-atx* - Multiple spaces inside hashes on closed atx style header +* **MD022** *blanks-around-headers* - Headers should be surrounded by blank lines +* **MD023** *header-start-left* - Headers must start at the beginning of the line +* **MD024** *no-duplicate-header* - Multiple headers with the same content +* **MD025** *single-h1* - Multiple top level headers in the same document +* **MD026** *no-trailing-punctuation* - Trailing punctuation in header +* **MD027** *no-multiple-space-blockquote* - Multiple spaces after blockquote symbol +* **MD028** *no-blanks-blockquote* - Blank line inside blockquote +* **MD029** *ol-prefix* - Ordered list item prefix +* **MD030** *list-marker-space* - Spaces after list markers +* **MD031** *blanks-around-fences* - Fenced code blocks should be surrounded by blank lines +* **MD032** *blanks-around-lists* - Lists should be surrounded by blank lines +* **MD033** *no-inline-html* - Inline HTML +* **MD034** *no-bare-urls* - Bare URL used +* **MD035** *hr-style* - Horizontal rule style +* **MD036** *no-emphasis-as-header* - Emphasis used instead of a header +* **MD037** *no-space-in-emphasis* - Spaces inside emphasis markers +* **MD038** *no-space-in-code* - Spaces inside code span elements +* **MD039** *no-space-in-links* - Spaces inside link text +* **MD040** *fenced-code-language* - Fenced code blocks should have a language specified +* **MD041** *first-line-h1* - First line in file should be a top level header 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. -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, and passing an object customizes its settings. Setting the special `default` 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 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: @@ -246,7 +246,7 @@ Example: "default": true, "MD003": { "style": "atx_closed" }, "MD007": { "indent": 4 }, - "MD009": false, + "no-hard-tabs": false, "whitespace": false } ``` @@ -276,7 +276,8 @@ Standard completion callback. Type: `Object` 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 @@ -300,14 +301,7 @@ markdownlint(options, function callback(err, result) { }); ``` -Or invoke `markdownlint.sync` for a synchronous call: - -```js -var result = markdownlint.sync(options); -console.log(result.toString()); -``` - -Output of both calls: +Output: ```text 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 ``` +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: ```js @@ -420,7 +432,7 @@ bad.md: 3: MD018 No space after hash on atx style header ## Browser -`markdownlint` works in the browser. +`markdownlint` also works in the browser. Generate normal and minified scripts with: diff --git a/doc/Rules.md b/doc/Rules.md index b02045b1..33e65389 100644 --- a/doc/Rules.md +++ b/doc/Rules.md @@ -8,6 +8,8 @@ versions of the examples. Tags: headers +Aliases: header-increment + This rule is triggered when you skip header levels in a markdown document, for example: @@ -37,6 +39,8 @@ level at a time: Tags: headers +Aliases: first-header-h1 + This rule is triggered when the first header in the document 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 +Aliases: header-style + Parameters: style ("consistent", "atx", "atx_closed", "setext", "setext_with_atx"; default "consistent") This rule is triggered when different header styles (atx, setext, and 'closed' @@ -90,6 +96,8 @@ consistent within the document. Tags: bullet, ul +Aliases: ul-style + Parameters: style ("consistent", "asterisk", "plus", "dash"; default "consistent") This rule is triggered when the symbols used in the document for unordered @@ -114,6 +122,8 @@ document. Tags: bullet, ul, indentation +Aliases: list-indent + This rule is triggered when list items are parsed as being at the same level, but don't have the same indentation: @@ -134,6 +144,8 @@ for the list to fix it: Tags: bullet, ul, indentation +Aliases: ul-start-left + This rule is triggered when top level lists don't start at the beginning of a 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 +Aliases: ul-indent + Parameters: indent (number; default 2) 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 +Aliases: no-trailing-spaces + Parameters: br_spaces (number; default: 0) 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 +Aliases: no-hard-tabs + 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 with spaces instead. @@ -231,6 +249,8 @@ Corrected example: Tags: links +Aliases: no-reversed-links + 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 reversed): @@ -245,6 +265,8 @@ To fix this, swap the `[]` and `()` around: Tags: whitespace, blank_lines +Aliases: no-multiple-blanks + This rule is triggered when there are multiple consecutive blank lines in the document: @@ -266,6 +288,8 @@ lines inside code blocks. Tags: line_length +Aliases: line-length + 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 @@ -287,6 +311,8 @@ rules. Still, some languages do not lend themselves to short lines. Tags: code +Aliases: commands-show-output + This rule is triggered when there are code blocks showing shell commands to be typed, and the shell commands are preceded by dollar signs ($): @@ -320,6 +346,8 @@ for more information. Tags: headers, atx, spaces +Aliases: no-missing-space-atx + This rule is triggered when spaces are missing after the hash characters in an atx style header: @@ -338,6 +366,8 @@ space: Tags: headers, atx, spaces +Aliases: no-multiple-space-atx + 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: @@ -356,6 +386,8 @@ space: Tags: headers, atx_closed, spaces +Aliases: no-missing-space-closed-atx + This rule is triggered when spaces are missing inside the hash characters 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 +Aliases: no-multiple-space-closed-atx + 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: @@ -397,6 +431,8 @@ spaces. Tags: headers, blank_lines +Aliases: blanks-around-headers + This rule is triggered when headers (any style) are either not preceded or not followed by a blank line: @@ -425,6 +461,8 @@ regular text. Tags: headers, spaces +Aliases: header-start-left + This rule is triggered when a header is indented by one or more spaces: Some text @@ -444,6 +482,8 @@ parsed as headers, and will instead appear as regular text. Tags: headers +Aliases: no-duplicate-header + This rule is triggered if there are multiple headers in the document that have the same text: @@ -465,6 +505,8 @@ this. Tags: headers +Aliases: single-h1 + 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 document: @@ -492,6 +534,8 @@ should be contained within this header. Tags: headers +Aliases: no-trailing-punctuation + Parameters: punctuation (string; default ".,;:!?") 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 +Aliases: no-multiple-space-blockquote + This rule is triggered when blockquotes have more than one space after the blockquote (`>`) symbol: @@ -527,6 +573,8 @@ To fix, remove any extraneous space: Tags: blockquote, whitespace +Aliases: no-blanks-blockquote + This rule is triggered when two blockquote blocks are separated by nothing except for a blank line: @@ -560,6 +608,8 @@ separate blockquotes. Tags: ol +Aliases: ol-prefix + Parameters: style ("one", "ordered"; default "one") 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 +Aliases: list-marker-space + 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. '`-`', @@ -641,6 +693,8 @@ for your selected document style. Tags: code, blank_lines +Aliases: blanks-around-fences + This rule is triggered when fenced code blocks are either not preceded or not 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 +Aliases: blanks-around-lists + This rule is triggered when lists (of any kind) are either not preceded or not followed by a blank line: @@ -716,6 +772,8 @@ items with hanging indents are okay: Tags: html +Aliases: no-inline-html + Parameters: allowed_elements (array of string; default empty) 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 +Aliases: no-bare-urls + This rule is triggered whenever a URL is given that isn't surrounded by angle brackets: @@ -758,6 +818,8 @@ converted: Tags: hr +Aliases: hr-style + Parameters: style ("consistent", "---", "***", or other string specifying the horizontal rule; default "consistent") @@ -792,6 +854,8 @@ is allowed. Tags: headers, emphasis +Aliases: no-emphasis-as-header + 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: @@ -821,6 +885,8 @@ It won't fire on emphasis used within regular text. Tags: whitespace, emphasis +Aliases: no-space-in-emphasis + This rule is triggered when emphasis markers (bold, italic) are used, but they have spaces between the markers and the text: @@ -851,6 +917,8 @@ intended by the author. Tags: whitespace, code +Aliases: no-space-in-code + This rule is triggered on code span elements that have spaces right inside the backticks: @@ -868,6 +936,8 @@ To fix this, remove the spaces inside the codespan markers: Tags: whitespace, links +Aliases: no-space-in-links + This rule is triggered on links that have spaces surrounding the link text: [ a link ](http://www.example.com/) @@ -880,6 +950,8 @@ To fix this, remove the spaces surrounding the link text: Tags: code, language +Aliases: fenced-code-language + This rule is triggered when fenced code blocks are used, but a language isn't specified: @@ -899,6 +971,8 @@ To fix this, add a language specifier to the code block: Tags: headers +Aliases: first-line-h1 + This rule is triggered when the first line in the file isn't a top level (h1) header: diff --git a/example/standalone.js b/example/standalone.js index f283bb20..d695e299 100644 --- a/example/standalone.js +++ b/example/standalone.js @@ -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); -console.log(result.toString()); +console.log(result.toString(true)); diff --git a/lib/markdownlint.js b/lib/markdownlint.js index 6201b21a..1603625f 100644 --- a/lib/markdownlint.js +++ b/lib/markdownlint.js @@ -7,38 +7,46 @@ var shared = require("./shared"); // Mappings from rule to description and tag to rules var allRuleNames = []; -var ruleToDescription = {}; -var tagUpperToRules = {}; +var ruleNameToRule = {}; +var idUpperToRuleNames = {}; rules.forEach(function forRule(rule) { allRuleNames.push(rule.name); - ruleToDescription[rule.name] = rule.desc; + ruleNameToRule[rule.name] = rule; // 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) { var tagUpper = tag.toUpperCase(); - var tags = tagUpperToRules[tagUpper] || []; - tags.push(rule.name); - tagUpperToRules[tagUpper] = tags; + var ruleNames = idUpperToRuleNames[tagUpper] || []; + ruleNames.push(rule.name); + 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 -// Object.keys(tagToRules).sort().forEach(function forTag(tag) { -// console.log("* **" + tag + "** - " + tagToRules[tag].join(", ")); +// Object.keys(idUpperToRuleNames).sort().forEach(function forTag(tag) { +// console.log("* **" + tag + "** - " + idUpperToRuleNames[tag].join(", ")); // }); // Class for results with toString for pretty display function Results() { } -Results.prototype.toString = function resultsToString() { +Results.prototype.toString = function resultsToString(useAlias) { var that = this; var results = []; Object.keys(that).forEach(function forFile(file) { var fileResults = that[file]; - Object.keys(fileResults).forEach(function forRule(rule) { - var ruleResults = fileResults[rule]; + Object.keys(fileResults).forEach(function forRule(ruleName) { + var rule = ruleNameToRule[ruleName]; + var ruleResults = fileResults[ruleName]; ruleResults.forEach(function forLine(lineNumber) { var result = - file + ": " + lineNumber + ": " + - rule + " " + ruleToDescription[rule]; + file + ": " + + lineNumber + ": " + + (useAlias ? rule.aliases[0] : rule.name) + " " + + rule.desc; results.push(result); }); }); @@ -114,10 +122,10 @@ function lintContent(content, config, frontMatter) { // eslint-disable-line value = false; } var keyUpper = key.toUpperCase(); - if (ruleToDescription[keyUpper]) { + if (ruleNameToRule[keyUpper]) { mergedRules[keyUpper] = value; - } else if (tagUpperToRules[keyUpper]) { - tagUpperToRules[keyUpper].forEach(function forRule(ruleName) { + } else if (idUpperToRuleNames[keyUpper]) { + idUpperToRuleNames[keyUpper].forEach(function forRule(ruleName) { mergedRules[ruleName] = value; }); } @@ -133,10 +141,10 @@ function lintContent(content, config, frontMatter) { // eslint-disable-line match[2].trim().toUpperCase().split(/\s+/) : allRuleNames; items.forEach(function forItem(nameUpper) { - if (ruleToDescription[nameUpper]) { + if (ruleNameToRule[nameUpper]) { enabledRules[nameUpper] = enabled; - } else if (tagUpperToRules[nameUpper]) { - tagUpperToRules[nameUpper].forEach(function forRule(ruleName) { + } else if (idUpperToRuleNames[nameUpper]) { + idUpperToRuleNames[nameUpper].forEach(function forRule(ruleName) { enabledRules[ruleName] = enabled; }); } diff --git a/lib/rules.js b/lib/rules.js index 7a387af3..c19e4523 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -153,6 +153,7 @@ module.exports = [ "name": "MD001", "desc": "Header levels should only increment by one level at a time", "tags": [ "headers" ], + "aliases": [ "header-increment" ], "func": function MD001(params, errors) { var prevLevel = 0; filterTokens(params, "heading_open", function forToken(token) { @@ -169,6 +170,7 @@ module.exports = [ "name": "MD002", "desc": "First header should be a h1 header", "tags": [ "headers" ], + "aliases": [ "first-header-h1" ], "func": function MD002(params, errors) { params.tokens.every(function forToken(token) { if (token.type === "heading_open") { @@ -186,6 +188,7 @@ module.exports = [ "name": "MD003", "desc": "Header style", "tags": [ "headers" ], + "aliases": [ "header-style" ], "func": function MD003(params, errors) { var style = params.options.style || "consistent"; filterTokens(params, "heading_open", function forToken(token) { @@ -207,6 +210,7 @@ module.exports = [ "name": "MD004", "desc": "Unordered list style", "tags": [ "bullet", "ul" ], + "aliases": [ "ul-style" ], "func": function MD004(params, errors) { var style = params.options.style || "consistent"; flattenLists(params).forEach(function forList(list) { @@ -228,6 +232,7 @@ module.exports = [ "name": "MD005", "desc": "Inconsistent indentation for list items at the same level", "tags": [ "bullet", "ul", "indentation" ], + "aliases": [ "list-indent" ], "func": function MD005(params, errors) { flattenLists(params).forEach(function forList(list) { var indent = indentFor(list.items[0]); @@ -244,6 +249,7 @@ module.exports = [ "name": "MD006", "desc": "Consider starting bulleted lists at the beginning of the line", "tags": [ "bullet", "ul", "indentation" ], + "aliases": [ "ul-start-left" ], "func": function MD006(params, errors) { flattenLists(params).forEach(function forList(list) { if (!list.ordered && !list.nesting && indentFor(list.open)) { @@ -257,6 +263,7 @@ module.exports = [ "name": "MD007", "desc": "Unordered list indentation", "tags": [ "bullet", "ul", "indentation" ], + "aliases": [ "ul-indent" ], "func": function MD007(params, errors) { var optionsIndent = params.options.indent || 2; var prevIndent = 0; @@ -277,6 +284,7 @@ module.exports = [ "name": "MD009", "desc": "Trailing spaces", "tags": [ "whitespace" ], + "aliases": [ "no-trailing-spaces" ], "func": function MD009(params, errors) { var brSpaces = params.options.br_spaces || 0; params.lines.forEach(function forLine(line, lineIndex) { @@ -293,6 +301,7 @@ module.exports = [ "name": "MD010", "desc": "Hard tabs", "tags": [ "whitespace", "hard_tab" ], + "aliases": [ "no-hard-tabs" ], "func": function MD010(params, errors) { params.lines.forEach(function forLine(line, lineIndex) { if (/\t/.test(line)) { @@ -306,6 +315,7 @@ module.exports = [ "name": "MD011", "desc": "Reversed link syntax", "tags": [ "links" ], + "aliases": [ "no-reversed-links" ], "func": function MD011(params, errors) { forEachInlineChild(params, "text", function forToken(token) { if (/\([^)]+\)\[[^\]]+\]/.test(token.content)) { @@ -319,6 +329,7 @@ module.exports = [ "name": "MD012", "desc": "Multiple consecutive blank lines", "tags": [ "whitespace", "blank_lines" ], + "aliases": [ "no-multiple-blanks" ], "func": function MD012(params, errors) { var prevLine = "-"; forEachLine(params, function forLine(line, lineIndex, inCode) { @@ -335,6 +346,7 @@ module.exports = [ "name": "MD013", "desc": "Line length", "tags": [ "line_length" ], + "aliases": [ "line-length" ], "func": function MD013(params, errors) { var lineLength = params.options.line_length || 80; var codeBlocks = params.options.code_blocks; @@ -357,6 +369,7 @@ module.exports = [ "name": "MD014", "desc": "Dollar signs used before commands without showing output", "tags": [ "code" ], + "aliases": [ "commands-show-output" ], "func": function MD014(params, errors) { [ "code_block", "fence" ].forEach(function forType(type) { filterTokens(params, type, function forToken(token) { @@ -376,6 +389,7 @@ module.exports = [ "name": "MD018", "desc": "No space after hash on atx style header", "tags": [ "headers", "atx", "spaces" ], + "aliases": [ "no-missing-space-atx" ], "func": function MD018(params, errors) { forEachLine(params, function forLine(line, lineIndex, inCode) { if (!inCode && /^#+[^#\s]/.test(line) && !/#$/.test(line)) { @@ -389,6 +403,7 @@ module.exports = [ "name": "MD019", "desc": "Multiple spaces after hash on atx style header", "tags": [ "headers", "atx", "spaces" ], + "aliases": [ "no-multiple-space-atx" ], "func": function MD019(params, errors) { filterTokens(params, "heading_open", function forToken(token) { if ((headingStyleFor(token) === "atx") && @@ -403,6 +418,7 @@ module.exports = [ "name": "MD020", "desc": "No space inside hashes on closed atx style header", "tags": [ "headers", "atx_closed", "spaces" ], + "aliases": [ "no-missing-space-closed-atx" ], "func": function MD020(params, errors) { forEachLine(params, function forLine(line, lineIndex, inCode) { if (!inCode && /^#+[^#]*[^\\]#+$/.test(line) && @@ -417,6 +433,7 @@ module.exports = [ "name": "MD021", "desc": "Multiple spaces inside hashes on closed atx style header", "tags": [ "headers", "atx_closed", "spaces" ], + "aliases": [ "no-multiple-space-closed-atx" ], "func": function MD021(params, errors) { filterTokens(params, "heading_open", function forToken(token) { if ((headingStyleFor(token) === "atx_closed") && @@ -431,6 +448,7 @@ module.exports = [ "name": "MD022", "desc": "Headers should be surrounded by blank lines", "tags": [ "headers", "blank_lines" ], + "aliases": [ "blanks-around-headers" ], "func": function MD022(params, errors) { var prevHeadingLineNumber = 0; var prevMaxLineIndex = -1; @@ -471,6 +489,7 @@ module.exports = [ "name": "MD023", "desc": "Headers must start at the beginning of the line", "tags": [ "headers", "spaces" ], + "aliases": [ "header-start-left" ], "func": function MD023(params, errors) { filterTokens(params, "heading_open", function forToken(token) { if (/^\s/.test(token.line)) { @@ -484,6 +503,7 @@ module.exports = [ "name": "MD024", "desc": "Multiple headers with the same content", "tags": [ "headers" ], + "aliases": [ "no-duplicate-header" ], "func": function MD024(params, errors) { var knownContent = []; forEachHeading(params, function forHeading(heading, content) { @@ -500,6 +520,7 @@ module.exports = [ "name": "MD025", "desc": "Multiple top level headers in the same document", "tags": [ "headers" ], + "aliases": [ "single-h1" ], "func": function MD025(params, errors) { var hasTopLevelHeading = false; filterTokens(params, "heading_open", function forToken(token) { @@ -518,6 +539,7 @@ module.exports = [ "name": "MD026", "desc": "Trailing punctuation in header", "tags": [ "headers" ], + "aliases": [ "no-trailing-punctuation" ], "func": function MD026(params, errors) { var punctuation = params.options.punctuation || ".,;:!?"; var re = new RegExp("[" + punctuation + "]$"); @@ -533,6 +555,7 @@ module.exports = [ "name": "MD027", "desc": "Multiple spaces after blockquote symbol", "tags": [ "blockquote", "whitespace", "indentation" ], + "aliases": [ "no-multiple-space-blockquote" ], "func": function MD027(params, errors) { var inBlockquote = false; params.tokens.forEach(function forToken(token) { @@ -557,6 +580,7 @@ module.exports = [ "name": "MD028", "desc": "Blank line inside blockquote", "tags": [ "blockquote", "whitespace" ], + "aliases": [ "no-blanks-blockquote" ], "func": function MD028(params, errors) { var prevToken = {}; params.tokens.forEach(function forToken(token) { @@ -573,6 +597,7 @@ module.exports = [ "name": "MD029", "desc": "Ordered list item prefix", "tags": [ "ol" ], + "aliases": [ "ol-prefix" ], "func": function MD029(params, errors) { var style = params.options.style || "one"; flattenLists(params).forEach(function forList(list) { @@ -596,6 +621,7 @@ module.exports = [ "name": "MD030", "desc": "Spaces after list markers", "tags": [ "ol", "ul", "whitespace" ], + "aliases": [ "list-marker-space" ], "func": function MD030(params, errors) { var ulSingle = params.options.ul_single || 1; var olSingle = params.options.ol_single || 1; @@ -621,6 +647,7 @@ module.exports = [ "name": "MD031", "desc": "Fenced code blocks should be surrounded by blank lines", "tags": [ "code", "blank_lines" ], + "aliases": [ "blanks-around-fences" ], "func": function MD031(params, errors) { var lines = params.lines; forEachLine(params, function forLine(line, i, inCode, onFence) { @@ -636,6 +663,7 @@ module.exports = [ "name": "MD032", "desc": "Lists should be surrounded by blank lines", "tags": [ "bullet", "ul", "ol", "blank_lines" ], + "aliases": [ "blanks-around-lists" ], "func": function MD032(params, errors) { var inList = false; var prevLine = ""; @@ -658,6 +686,7 @@ module.exports = [ "name": "MD033", "desc": "Inline HTML", "tags": [ "html" ], + "aliases": [ "no-inline-html" ], "func": function MD033(params, errors) { var allowedElements = (params.options.allowed_elements || []) .map(function forElement(element) { @@ -690,6 +719,7 @@ module.exports = [ "name": "MD034", "desc": "Bare URL used", "tags": [ "links", "url" ], + "aliases": [ "no-bare-urls" ], "func": function MD034(params, errors) { filterTokens(params, "inline", function forToken(token) { var inLink = false; @@ -712,6 +742,7 @@ module.exports = [ "name": "MD035", "desc": "Horizontal rule style", "tags": [ "hr" ], + "aliases": [ "hr-style" ], "func": function MD035(params, errors) { var style = params.options.style || "consistent"; filterTokens(params, "hr", function forToken(token) { @@ -729,6 +760,7 @@ module.exports = [ "name": "MD036", "desc": "Emphasis used instead of a header", "tags": [ "headers", "emphasis" ], + "aliases": [ "no-emphasis-as-header" ], "func": function MD036(params, errors) { function base(token) { if (token.type === "paragraph_open") { @@ -760,6 +792,7 @@ module.exports = [ "name": "MD037", "desc": "Spaces inside emphasis markers", "tags": [ "whitespace", "emphasis" ], + "aliases": [ "no-space-in-emphasis" ], "func": function MD037(params, errors) { forEachInlineChild(params, "text", function forToken(token) { if (/\s(\*\*?|__?)\s.+\1/.test(token.content) || @@ -774,6 +807,7 @@ module.exports = [ "name": "MD038", "desc": "Spaces inside code span elements", "tags": [ "whitespace", "code" ], + "aliases": [ "no-space-in-code" ], "func": function MD038(params, errors) { forEachInlineChild(params, "code_inline", function forToken(token, inline) { @@ -788,6 +822,7 @@ module.exports = [ "name": "MD039", "desc": "Spaces inside link text", "tags": [ "whitespace", "links" ], + "aliases": [ "no-space-in-links" ], "func": function MD039(params, errors) { filterTokens(params, "inline", function forToken(token) { var inLink = false; @@ -821,6 +856,7 @@ module.exports = [ "name": "MD040", "desc": "Fenced code blocks should have a language specified", "tags": [ "code", "language" ], + "aliases": [ "fenced-code-language" ], "func": function MD040(params, errors) { filterTokens(params, "fence", function forToken(token) { if (!token.info.trim()) { @@ -834,6 +870,7 @@ module.exports = [ "name": "MD041", "desc": "First line in file should be a top level header", "tags": [ "headers" ], + "aliases": [ "first-line-h1" ], "func": function MD041(params, errors) { var firstHeader = null; params.tokens.every(function forToken(token) { diff --git a/lib/shared.js b/lib/shared.js index 93dbe36d..25c7d354 100644 --- a/lib/shared.js +++ b/lib/shared.js @@ -8,7 +8,7 @@ module.exports.frontMatterRe = /^---$[^]*?^---$(\r\n|\r|\n)/m; // Regular expression for matching inline disable/enable comments module.exports.inlineCommentRe = - //ig; + //ig; // readFile options for reading with the UTF-8 encoding module.exports.utf8Encoding = { "encoding": "utf8" }; diff --git a/style/relaxed.json b/style/relaxed.json index f2e51faf..590d9193 100644 --- a/style/relaxed.json +++ b/style/relaxed.json @@ -4,10 +4,10 @@ "default": true, "whitespace": false, "line_length": false, - "MD006": false, - "MD007": false, - "MD033": false, - "MD034": false, - "MD040": false, - "MD041": false + "ul-start-left": false, + "ul-indent": false, + "no-inline-html": false, + "no-bare-urls": false, + "fenced-code-language": false, + "first-line-h1": false } diff --git a/test/inline-disable-enable.md b/test/inline-disable-enable.md index 6b8eebf9..7b6ef7b8 100644 --- a/test/inline-disable-enable.md +++ b/test/inline-disable-enable.md @@ -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 - + hard tab {MD010} / space * in * emphasis {MD037} / space ` in ` code {MD038} diff --git a/test/markdownlint-test.js b/test/markdownlint-test.js index d59c75a6..74806d3e 100644 --- a/test/markdownlint-test.js +++ b/test/markdownlint-test.js @@ -77,7 +77,8 @@ fs.readdirSync("./test").forEach(function forFile(file) { module.exports.projectFiles = function projectFiles(test) { test.expect(2); var options = { - "files": [ "README.md" ] + "files": [ "README.md" ], + "config": { "MD013": false } }; markdownlint(options, function callback(err, actual) { test.ifError(err); @@ -88,7 +89,7 @@ module.exports.projectFiles = function projectFiles(test) { }; module.exports.resultFormatting = function resultFormatting(test) { - test.expect(3); + test.expect(4); var options = { "files": [ "./test/atx_header_spacing.md", @@ -121,13 +122,26 @@ module.exports.resultFormatting = function resultFormatting(test) { " Multiple spaces after hash on atx style header\n" + "./test/first_header_bad_atx.md: 1: MD002" + " 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(); }); }; module.exports.resultFormattingSync = function resultFormattingSync(test) { - test.expect(2); + test.expect(3); var options = { "files": [ "./test/atx_header_spacing.md", @@ -159,7 +173,20 @@ module.exports.resultFormattingSync = function resultFormattingSync(test) { " Multiple spaces after hash on atx style header\n" + "./test/first_header_bad_atx.md: 1: MD002" + " 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(); }; @@ -302,7 +329,7 @@ module.exports.disableRules = function disableRules(test) { "MD002": false, "default": true, "MD019": false, - "MD041": false + "first-line-h1": false } }; markdownlint(options, function callback(err, actualResult) { @@ -328,7 +355,7 @@ module.exports.enableRules = function enableRules(test) { "config": { "MD002": true, "default": false, - "MD019": true + "no-multiple-space-atx": true } }; markdownlint(options, function callback(err, actualResult) { @@ -357,7 +384,7 @@ module.exports.enableRulesMixedCase = function enableRulesMixedCase(test) { "config": { "Md002": true, "DeFaUlT": false, - "Md019": true + "nO-mUlTiPlE-sPaCe-AtX": true } }; markdownlint(options, function callback(err, actualResult) { @@ -697,6 +724,23 @@ module.exports.ruleNamesUpperCase = function ruleNamesUpperCase(test) { 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) { test.expect(97); var tagToRules = {}; @@ -733,7 +777,8 @@ module.exports.readme = function readme(test) { test.ok(rule, "Missing rule implementation for " + token.content + "."); if (rule) { - var expected = "**" + rule.name + "** - " + rule.desc; + var expected = "**" + rule.name + "** *" + + rule.aliases.join(", ") + "* - " + rule.desc; test.equal(token.content, expected, "Rule mismatch."); } } else if (inTags) { @@ -755,7 +800,7 @@ module.exports.readme = function readme(test) { }; module.exports.doc = function doc(test) { - test.expect(199); + test.expect(274); fs.readFile("doc/Rules.md", shared.utf8Encoding, function readFile(err, contents) { test.ifError(err); @@ -763,11 +808,14 @@ module.exports.doc = function doc(test) { var inHeading = false; var rule = null; var ruleHasTags = true; + var ruleHasAliases = true; var ruleUsesParams = null; - var tagParameterRe = /, |: | /; - function testTagsAndParams() { + var tagAliasParameterRe = /, |: | /; + function testTagsAliasesParams() { test.ok(ruleHasTags, "Missing tags for rule " + (rule || {}).name + "."); + test.ok(ruleHasAliases, + "Missing aliases for rule " + (rule || {}).name + "."); test.ok(!ruleUsesParams, "Missing parameters for rule " + (rule || {}).name + "."); } @@ -778,9 +826,9 @@ module.exports.doc = function doc(test) { inHeading = false; } else if (token.type === "inline") { if (inHeading) { - testTagsAndParams(); + testTagsAliasesParams(); rule = rulesLeft.shift(); - ruleHasTags = false; + ruleHasTags = ruleHasAliases = false; test.ok(rule, "Missing rule implementation for " + token.content + "."); 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) { - var tags = token.content.split(tagParameterRe).slice(1); - test.deepEqual(tags, rule.tags, - "Tag mismatch for rule " + rule.name + "."); + test.deepEqual(token.content.split(tagAliasParameterRe).slice(1), + rule.tags, "Tag mismatch for rule " + rule.name + "."); 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) { var inDetails = false; - var parameters = token.content.split(tagParameterRe) + var parameters = token.content.split(tagAliasParameterRe) .slice(1) .filter(function forPart(part) { inDetails = inDetails || (part[0] === "("); @@ -814,7 +865,7 @@ module.exports.doc = function doc(test) { var ruleLeft = rulesLeft.shift(); test.ok(!ruleLeft, "Missing rule documentation for " + (ruleLeft || {}).name + "."); - testTagsAndParams(); + testTagsAliasesParams(); test.done(); }); };