diff options
Diffstat (limited to 'tools/node_modules/eslint/node_modules/eslint-plugin-markdown/lib/processor.js')
-rw-r--r-- | tools/node_modules/eslint/node_modules/eslint-plugin-markdown/lib/processor.js | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/tools/node_modules/eslint/node_modules/eslint-plugin-markdown/lib/processor.js b/tools/node_modules/eslint/node_modules/eslint-plugin-markdown/lib/processor.js new file mode 100644 index 0000000000..8df09ef614 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/eslint-plugin-markdown/lib/processor.js @@ -0,0 +1,164 @@ +/** + * @fileoverview Processes Markdown files for consumption by ESLint. + * @author Brandon Mills + */ + +"use strict"; + +var assign = require("object-assign"); +var unified = require("unified"); +var remarkParse = require("remark-parse"); + +var SUPPORTED_SYNTAXES = ["js", "javascript", "node", "jsx"]; +var UNSATISFIABLE_RULES = [ + "eol-last" // The Markdown parser strips trailing newlines in code fences +]; + +var markdown = unified().use(remarkParse); + +var blocks = []; + +/** + * Performs a depth-first traversal of the Markdown AST. + * @param {ASTNode} node A Markdown AST node. + * @param {object} callbacks A map of node types to callbacks. + * @param {object} [parent] The node's parent AST node. + * @returns {void} + */ +function traverse(node, callbacks, parent) { + var i; + + if (callbacks[node.type]) { + callbacks[node.type](node, parent); + } + + if (typeof node.children !== "undefined") { + for (i = 0; i < node.children.length; i++) { + traverse(node.children[i], callbacks, node); + } + } +} + +/** + * Converts leading HTML comments to JS block comments. + * @param {string} html The text content of an HTML AST node. + * @returns {string[]} An array of JS block comments. + */ +function getComment(html) { + var commentStart = "<!--"; + var commentEnd = "-->"; + var prefix = "eslint"; + + if ( + html.slice(0, commentStart.length) !== commentStart || + html.slice(-commentEnd.length) !== commentEnd + ) { + return ""; + } + + html = html.slice(commentStart.length, -commentEnd.length); + + if (html.trim().slice(0, prefix.length) !== prefix) { + return ""; + } + + return html; +} + +/** + * Extracts lintable JavaScript code blocks from Markdown text. + * @param {string} text The text of the file. + * @returns {string[]} Source code strings to lint. + */ +function preprocess(text) { + var ast = markdown.parse(text); + + blocks = []; + traverse(ast, { + "code": function(node, parent) { + var comments = []; + var index, previousNode, comment; + + if (node.lang && SUPPORTED_SYNTAXES.indexOf(node.lang.toLowerCase()) >= 0) { + index = parent.children.indexOf(node) - 1; + previousNode = parent.children[index]; + while (previousNode && previousNode.type === "html") { + comment = getComment(previousNode.value); + + if (!comment) { + break; + } + + if (comment.trim() === "eslint-skip") { + return; + } + + comments.unshift("/*" + comment + "*/"); + index--; + previousNode = parent.children[index]; + } + + blocks.push(assign({}, node, { comments: comments })); + } + } + }); + + return blocks.map(function(block) { + return block.comments.concat(block.value).join("\n"); + }); +} + +/** + * Creates a map function that adjusts messages in a code block. + * @param {Block} block A code block. + * @returns {function} A function that adjusts messages in a code block. + */ +function adjustBlock(block) { + var leadingCommentLines = block.comments.reduce(function(count, comment) { + return count + comment.split("\n").length; + }, 0); + + /** + * Adjusts ESLint messages to point to the correct location in the Markdown. + * @param {Message} message A message from ESLint. + * @returns {Message} The same message, but adjusted ot the correct location. + */ + return function adjustMessage(message) { + var lineInCode = message.line - leadingCommentLines; + if (lineInCode < 1) { + return null; + } + + return assign({}, message, { + line: lineInCode + block.position.start.line, + column: message.column + block.position.indent[lineInCode - 1] - 1 + }); + }; +} + +/** + * Excludes unsatisfiable rules from the list of messages. + * @param {Message} message A message from the linter. + * @returns {boolean} True if the message should be included in output. + */ +function excludeUnsatisfiableRules(message) { + return message && UNSATISFIABLE_RULES.indexOf(message.ruleId) < 0; +} + +/** + * Transforms generated messages for output. + * @param {Array<Message[]>} messages An array containing one array of messages + * for each code block returned from `preprocess`. + * @returns {Message[]} A flattened array of messages with mapped locations. + */ +function postprocess(messages) { + return [].concat.apply([], messages.map(function(group, i) { + var adjust = adjustBlock(blocks[i]); + return group.map(adjust).filter(excludeUnsatisfiableRules); + })); +} + +module.exports = { + preprocess: preprocess, + postprocess: postprocess +}; |