diff --git a/lib/rules.js b/lib/rules.js index f990f5fc..5f623d0d 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -31,10 +31,12 @@ function unorderedListStyleFor(token) { } } -// Filters a list of tokens by type -function filterTokens(tokens, typeA, typeB) { - return tokens.filter(function filterToken(token) { - return ((token.type === typeA) || (token.type === typeB)); +// Calls the provided function for each matching token +function filterTokens(tokens, type, callback) { + tokens.forEach(function forToken(token) { + if (token.type === type) { + callback(token); + } }); } @@ -42,12 +44,11 @@ function filterTokens(tokens, typeA, typeB) { function forEachLine(params, callback) { // Identify lines in code blocks var codeLines = []; - filterTokens(params.tokens, "code_block") - .forEach(function forToken(token) { - for (var i = token.map[0]; i < token.map[1]; i++) { - codeLines.push(i); - } - }); + filterTokens(params.tokens, "code_block", function forToken(token) { + for (var i = token.map[0]; i < token.map[1]; i++) { + codeLines.push(i); + } + }); // Identify lines in code fences (with info about transitions) var inFence = false; params.lines.forEach(function forLine(line, lineIndex) { @@ -62,13 +63,11 @@ function forEachLine(params, callback) { // Calls the provided function for each specified inline child token function forEachInlineChild(params, type, callback) { - filterTokens(params.tokens, "inline") - .forEach(function forToken(token) { - filterTokens(token.children, type) - .forEach(function forChild(child) { - callback(child, token); - }); + filterTokens(params.tokens, "inline", function forToken(token) { + filterTokens(token.children, type, function forChild(child) { + callback(child, token); }); + }); } // Calls the provided function for each heading's content @@ -131,14 +130,13 @@ module.exports = [ "tags": [ "headers" ], "func": function MD001(params, errors) { var prevLevel = 0; - filterTokens(params.tokens, "heading_open") - .forEach(function forToken(token) { - var level = parseInt(token.tag.slice(1), 10); - if (prevLevel && (level > prevLevel + 1)) { - errors.push(token.lineNumber); - } - prevLevel = level; - }); + filterTokens(params.tokens, "heading_open", function forToken(token) { + var level = parseInt(token.tag.slice(1), 10); + if (prevLevel && (level > prevLevel + 1)) { + errors.push(token.lineNumber); + } + prevLevel = level; + }); } }, @@ -165,11 +163,10 @@ module.exports = [ "tags": [ "headers" ], "func": function MD003(params, errors) { var style = params.options.style || "consistent"; - var headings = filterTokens(params.tokens, "heading_open"); - if ((style === "consistent") && headings.length) { - style = headingStyleFor(headings[0]); - } - headings.forEach(function forToken(token) { + filterTokens(params.tokens, "heading_open", function forToken(token) { + if (style === "consistent") { + style = headingStyleFor(token); + } if (headingStyleFor(token) !== style) { errors.push(token.lineNumber); } @@ -321,8 +318,8 @@ module.exports = [ "desc": "Dollar signs used before commands without showing output", "tags": [ "code" ], "func": function MD014(params, errors) { - filterTokens(params.tokens, "code_block", "fence") - .forEach(function forToken(token) { + [ "code_block", "fence" ].forEach(function forType(type) { + filterTokens(params.tokens, type, function forToken(token) { if (token.content && token.content.split(shared.newLineRe) .filter(function filterLine(line) { return line; @@ -332,6 +329,7 @@ module.exports = [ errors.push(token.lineNumber); } }); + }); } }, @@ -353,13 +351,12 @@ module.exports = [ "desc": "Multiple spaces after hash on atx style header", "tags": [ "headers", "atx", "spaces" ], "func": function MD019(params, errors) { - filterTokens(params.tokens, "heading_open") - .forEach(function forToken(token) { - if ((headingStyleFor(token) === "atx") && - /^#+\s\s/.test(token.line)) { - errors.push(token.lineNumber); - } - }); + filterTokens(params.tokens, "heading_open", function forToken(token) { + if ((headingStyleFor(token) === "atx") && + /^#+\s\s/.test(token.line)) { + errors.push(token.lineNumber); + } + }); } }, @@ -382,13 +379,12 @@ module.exports = [ "desc": "Multiple spaces inside hashes on closed atx style header", "tags": [ "headers", "atx_closed", "spaces" ], "func": function MD021(params, errors) { - filterTokens(params.tokens, "heading_open") - .forEach(function forToken(token) { - if ((headingStyleFor(token) === "atx_closed") && - (/^#+\s\s/.test(token.line) || /\s\s#+$/.test(token.line))) { - errors.push(token.lineNumber); - } - }); + filterTokens(params.tokens, "heading_open", function forToken(token) { + if ((headingStyleFor(token) === "atx_closed") && + (/^#+\s\s/.test(token.line) || /\s\s#+$/.test(token.line))) { + errors.push(token.lineNumber); + } + }); } }, @@ -434,12 +430,11 @@ module.exports = [ "desc": "Headers must start at the beginning of the line", "tags": [ "headers", "spaces" ], "func": function MD023(params, errors) { - filterTokens(params.tokens, "heading_open") - .forEach(function forToken(token) { - if (/^\s/.test(token.line)) { - errors.push(token.lineNumber); - } - }); + filterTokens(params.tokens, "heading_open", function forToken(token) { + if (/^\s/.test(token.line)) { + errors.push(token.lineNumber); + } + }); } }, @@ -465,16 +460,15 @@ module.exports = [ "tags": [ "headers" ], "func": function MD025(params, errors) { var hasTopLevelHeading = false; - filterTokens(params.tokens, "heading_open") - .forEach(function forToken(token) { - if (token.tag === "h1") { - if (hasTopLevelHeading) { - errors.push(token.lineNumber); - } else if (token.lineNumber === 1) { - hasTopLevelHeading = true; - } + filterTokens(params.tokens, "heading_open", function forToken(token) { + if (token.tag === "h1") { + if (hasTopLevelHeading) { + errors.push(token.lineNumber); + } else if (token.lineNumber === 1) { + hasTopLevelHeading = true; } - }); + } + }); } }, @@ -624,10 +618,9 @@ module.exports = [ "desc": "Inline HTML", "tags": [ "html" ], "func": function MD033(params, errors) { - filterTokens(params.tokens, "html_block") - .forEach(function forToken(token) { - errors.push(token.lineNumber); - }); + filterTokens(params.tokens, "html_block", function forToken(token) { + errors.push(token.lineNumber); + }); forEachInlineChild(params, "html_inline", function forToken(token) { errors.push(token.lineNumber); }); @@ -639,21 +632,20 @@ module.exports = [ "desc": "Bare URL used", "tags": [ "links", "url" ], "func": function MD034(params, errors) { - filterTokens(params.tokens, "inline") - .forEach(function forToken(token) { - var inLink = false; - token.children.forEach(function forChild(child) { - if (child.type === "link_open") { - inLink = true; - } else if (child.type === "link_close") { - inLink = false; - } else if ((child.type === "text") && - !inLink && - /https?:\/\//.test(child.content)) { - errors.push(child.lineNumber); - } - }); + filterTokens(params.tokens, "inline", function forToken(token) { + var inLink = false; + token.children.forEach(function forChild(child) { + if (child.type === "link_open") { + inLink = true; + } else if (child.type === "link_close") { + inLink = false; + } else if ((child.type === "text") && + !inLink && + /https?:\/\//.test(child.content)) { + errors.push(child.lineNumber); + } }); + }); } }, @@ -663,11 +655,10 @@ module.exports = [ "tags": [ "hr" ], "func": function MD035(params, errors) { var style = params.options.style || "consistent"; - var horizontalRules = filterTokens(params.tokens, "hr"); - if ((style === "consistent") && horizontalRules.length) { - style = horizontalRules[0].line; - } - horizontalRules.forEach(function forToken(token) { + filterTokens(params.tokens, "hr", function forToken(token) { + if (style === "consistent") { + style = token.line; + } if (token.line !== style) { errors.push(token.lineNumber); } @@ -739,32 +730,31 @@ module.exports = [ "desc": "Spaces inside link text", "tags": [ "whitespace", "links" ], "func": function MD039(params, errors) { - filterTokens(params.tokens, "inline") - .forEach(function forToken(token) { - var inLink = false; - var index = 0; - var lastChildRightSpaceLineNumber = 0; - token.children.forEach(function forChild(child) { - if (child.type === "link_open") { - inLink = true; - index = 0; - } else if (child.type === "link_close") { - inLink = false; - if (lastChildRightSpaceLineNumber) { - errors.push(lastChildRightSpaceLineNumber); - } - } else if (inLink) { - if ((index === 0) && - (child.content.trimLeft().length !== child.content.length)) { - errors.push(child.lineNumber); - } - lastChildRightSpaceLineNumber = - (child.content.trimRight().length !== child.content.length) ? - child.lineNumber : 0; - index++; + filterTokens(params.tokens, "inline", function forToken(token) { + var inLink = false; + var index = 0; + var lastChildRightSpaceLineNumber = 0; + token.children.forEach(function forChild(child) { + if (child.type === "link_open") { + inLink = true; + index = 0; + } else if (child.type === "link_close") { + inLink = false; + if (lastChildRightSpaceLineNumber) { + errors.push(lastChildRightSpaceLineNumber); } - }); + } else if (inLink) { + if ((index === 0) && + (child.content.trimLeft().length !== child.content.length)) { + errors.push(child.lineNumber); + } + lastChildRightSpaceLineNumber = + (child.content.trimRight().length !== child.content.length) ? + child.lineNumber : 0; + index++; + } }); + }); } }, @@ -773,12 +763,11 @@ module.exports = [ "desc": "Fenced code blocks should have a language specified", "tags": [ "code", "language" ], "func": function MD040(params, errors) { - filterTokens(params.tokens, "fence") - .forEach(function forToken(token) { - if (!token.info.trim()) { - errors.push(token.lineNumber); - } - }); + filterTokens(params.tokens, "fence", function forToken(token) { + if (!token.info.trim()) { + errors.push(token.lineNumber); + } + }); } } ];