diff --git a/.eslintrc.json b/.eslintrc.json index 3d5739a4..75470021 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -64,7 +64,7 @@ "max-lines": "off", "max-nested-callbacks": "error", "max-params": ["error", 7], - "max-statements": ["error", 32], + "max-statements": ["error", 20], "max-statements-per-line": "error", "multiline-comment-style": ["error", "separate-lines"], "multiline-ternary": "off", diff --git a/lib/markdownlint.js b/lib/markdownlint.js index 52a7defa..157ed948 100644 --- a/lib/markdownlint.js +++ b/lib/markdownlint.js @@ -8,33 +8,6 @@ var md = require("markdown-it")({ "html": true }); var rules = require("./rules"); var shared = require("./shared"); -// Mapping from rule names/tags to canonical rule name -var aliasToRuleNames = {}; -// var tagToRuleNames = {}; -rules.forEach(function forRule(rule) { - var ruleName = rule.names[0].toUpperCase(); - // The following is useful for updating README.md - // console.log( - // "* **[" + ruleName + "](doc/Rules.md#" + ruleName.toLowerCase() + - // ")** *" + rule.names.slice(1).join(", ") + "* - " + rule.description); - rule.names.forEach(function forName(name) { - var nameUpper = name.toUpperCase(); - aliasToRuleNames[nameUpper] = [ ruleName ]; - }); - rule.tags.forEach(function forTag(tag) { - var tagUpper = tag.toUpperCase(); - var ruleNames = aliasToRuleNames[tagUpper] || []; - ruleNames.push(ruleName); - aliasToRuleNames[tagUpper] = ruleNames; - // tagToRuleNames[tag] = ruleName; - }); -}); -// The following is useful for updating README.md -// Object.keys(tagToRuleNames).sort().forEach(function forTag(tag) { -// console.log("* **" + tag + "** - " + -// aliasToRuleNames[tag.toUpperCase()].join(", ")); -// }); - // Class for results with toString for pretty display function Results() {} Results.prototype.toString = function resultsToString(useAlias) { @@ -85,24 +58,8 @@ Results.prototype.toString = function resultsToString(useAlias) { return results.join("\n"); }; -// Array.sort comparison for objects in errors array -function lineNumberComparison(a, b) { - return a.lineNumber - b.lineNumber; -} - -// Function to return unique values from a sorted errors array -function uniqueFilterForSortedErrors(value, index, array) { - return (index === 0) || (value.lineNumber > array[index - 1].lineNumber); -} - -// Lints a single string -function lintContent( - content, config, frontMatter, noInlineConfig, resultVersion) { - // Remove UTF-8 byte order marker (if present) - if (content.charCodeAt(0) === 0xfeff) { - content = content.slice(1); - } - // Remove front matter (if present at beginning of content) +// Remove front matter (if present at beginning of content) +function removeFrontMatter(content, frontMatter) { var frontMatterLines = []; if (frontMatter) { var frontMatterMatch = content.match(frontMatter); @@ -116,14 +73,16 @@ function lintContent( } } } - // Ignore the content of HTML comments - content = shared.clearHtmlCommentText(content); - // Parse content into tokens and lines - var tokens = md.parse(content, {}); - var lines = content.split(shared.newLineRe); - var tokenLists = {}; + return { + "content": content, + "frontMatterLines": frontMatterLines + }; +} + +// Annotate tokens with line/lineNumber +function annotateTokens(tokens, lines) { var tbodyMap = null; - // Annotate tokens with line/lineNumber + var tokenLists = {}; tokens.forEach(function forToken(token) { // Handle missing maps for table body if (token.type === "tbody_open") { @@ -159,7 +118,41 @@ function lintContent( } tokenLists[token.type].push(token); }); - // Merge rules/tags and sanitize config + return tokenLists; +} + +// Map rule names/tags to canonical rule name +function mapAliasToRuleNames(ruleList) { + var aliasToRuleNames = {}; + // var tagToRuleNames = {}; + ruleList.forEach(function forRule(rule) { + var ruleName = rule.names[0].toUpperCase(); + // The following is useful for updating README.md: + // console.log( + // "* **[" + ruleName + "](doc/Rules.md#" + ruleName.toLowerCase() + + // ")** *" + rule.names.slice(1).join(", ") + "* - " + rule.description); + rule.names.forEach(function forName(name) { + var nameUpper = name.toUpperCase(); + aliasToRuleNames[nameUpper] = [ ruleName ]; + }); + rule.tags.forEach(function forTag(tag) { + var tagUpper = tag.toUpperCase(); + var ruleNames = aliasToRuleNames[tagUpper] || []; + ruleNames.push(ruleName); + aliasToRuleNames[tagUpper] = ruleNames; + // tagToRuleNames[tag] = ruleName; + }); + }); + // The following is useful for updating README.md: + // Object.keys(tagToRuleNames).sort().forEach(function forTag(tag) { + // console.log("* **" + tag + "** - " + + // aliasToRuleNames[tag.toUpperCase()].join(", ")); + // }); + return aliasToRuleNames; +} + +// Merge rules/tags and sanitize config +function mergeRulesAndSanitize(config, aliasToRuleNames) { var defaultKey = Object.keys(config).filter(function forKey(key) { return key.toUpperCase() === "DEFAULT"; }); @@ -183,7 +176,12 @@ function lintContent( mergedRules[ruleName] = value; }); }); - // Create mapping of enabled rules per line + return mergedRules; +} + +// Create mapping of enabled rules per line +function getEnabledRulesPerLineNumber( + lines, frontMatterLines, noInlineConfig, mergedRules, aliasToRuleNames) { var enabledRules = {}; var allRuleNames = []; rules.forEach(function forRule(rule) { @@ -216,6 +214,40 @@ function lintContent( } enabledRulesPerLineNumber.push(enabledRules); }); + return enabledRulesPerLineNumber; +} + +// Array.sort comparison for objects in errors array +function lineNumberComparison(a, b) { + return a.lineNumber - b.lineNumber; +} + +// Function to return unique values from a sorted errors array +function uniqueFilterForSortedErrors(value, index, array) { + return (index === 0) || (value.lineNumber > array[index - 1].lineNumber); +} + +// Lints a single string +function lintContent( + content, config, frontMatter, noInlineConfig, resultVersion) { + // Remove UTF-8 byte order marker (if present) + if (content.charCodeAt(0) === 0xfeff) { + content = content.slice(1); + } + // Remove front matter + var removeFrontMatterResult = removeFrontMatter(content, frontMatter); + content = removeFrontMatterResult.content; + var frontMatterLines = removeFrontMatterResult.frontMatterLines; + // Ignore the content of HTML comments + content = shared.clearHtmlCommentText(content); + // Parse content into tokens and lines + var tokens = md.parse(content, {}); + var lines = content.split(shared.newLineRe); + var tokenLists = annotateTokens(tokens, lines); + var aliasToRuleNames = mapAliasToRuleNames(rules); + var mergedRules = mergeRulesAndSanitize(config, aliasToRuleNames); + var enabledRulesPerLineNumber = getEnabledRulesPerLineNumber( + lines, frontMatterLines, noInlineConfig, mergedRules, aliasToRuleNames); // Create parameters for rules var params = { "tokens": tokens, diff --git a/test/markdownlint-test.js b/test/markdownlint-test.js index 8520e71f..5eb161f8 100644 --- a/test/markdownlint-test.js +++ b/test/markdownlint-test.js @@ -1071,15 +1071,12 @@ module.exports.readme = function readme(test) { if (!seenRelated) { seenRelated = true; } else if (seenRelated && !seenRules) { - seenRules = true; - inRules = true; + seenRules = inRules = true; } else if (seenRelated && seenRules && !seenTags) { - seenTags = true; - inTags = true; + seenTags = inTags = true; } } else if (token.type === "bullet_list_close") { - inRules = false; - inTags = false; + inRules = inTags = false; } else if (token.type === "inline") { if (inRules) { var rule = rulesLeft.shift(); @@ -1145,17 +1142,15 @@ module.exports.doc = function doc(test) { ruleHasTags = ruleHasAliases = false; test.ok(rule, "Missing rule implementation for " + token.content + "."); - if (rule) { - test.equal(token.content, - rule.names[0] + " - " + rule.description, - "Rule mismatch."); - ruleUsesParams = rule.function.toString() - .match(/params\.config\.[_a-z]*/gi); - if (ruleUsesParams) { - ruleUsesParams = ruleUsesParams.map(function forUse(use) { - return use.split(".").pop(); - }); - } + test.equal(token.content, + rule.names[0] + " - " + rule.description, + "Rule mismatch."); + ruleUsesParams = rule.function.toString() + .match(/params\.config\.[_a-z]*/gi); + if (ruleUsesParams) { + ruleUsesParams = ruleUsesParams.map(function forUse(use) { + return use.split(".").pop(); + }); } } else if (/^Tags: /.test(token.content) && rule) { test.deepEqual(token.content.split(tagAliasParameterRe).slice(1),