diff --git a/README.md b/README.md index 3d484c7..87dd3d4 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,18 @@ Install the plugin: npm install --save-dev eslint eslint-plugin-markdown ``` -Add it to your `.eslintrc`: +Add it to your `.eslintrc` and enable the processor on Markdown files: ```json { "plugins": [ "markdown" + ], + "overrides": [ + { + "files": ["*.md"], + "processor": "markdown/markdown" + } ] } ``` @@ -30,10 +36,10 @@ Add it to your `.eslintrc`: Run ESLint on `.md` files: ```sh -eslint --ext md . +eslint --ext js,md . ``` -It will lint `js`, `javascript`, `jsx`, or `node` [fenced code blocks](https://help.github.com/articles/github-flavored-markdown/#fenced-code-blocks) in your Markdown documents: +It will lint [fenced code blocks](https://help.github.com/articles/github-flavored-markdown/#fenced-code-blocks) in your Markdown documents: ````markdown ```js @@ -42,7 +48,9 @@ var answer = 6 * 7; console.log(answer); ``` -```JavaScript +Here is some regular Markdown text that will be ignored. + +```js // This also gets linted /* eslint quotes: [2, "double"] */ @@ -54,17 +62,12 @@ hello(); ``` ```jsx -// This gets linted too +// This gets linted too if you enable --ext jsx var div =
; ``` - -```node -// And this -console.log(process.version); -``` ```` -Blocks that don't specify either `js`, `javascript`, `jsx`, or `node` syntax are ignored: +Blocks that don't specify a syntax are ignored: ````markdown ``` diff --git a/lib/index.js b/lib/index.js index e27aa16..6d96ac4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,9 +9,6 @@ const processor = require("./processor"); module.exports = { processors: { - ".markdown": processor, - ".mdown": processor, - ".mkdn": processor, - ".md": processor + markdown: processor } }; diff --git a/lib/processor.js b/lib/processor.js index 5873f75..618db7e 100644 --- a/lib/processor.js +++ b/lib/processor.js @@ -8,7 +8,6 @@ const unified = require("unified"); const remarkParse = require("remark-parse"); -const SUPPORTED_SYNTAXES = ["js", "javascript", "node", "jsx"]; const UNSATISFIABLE_RULES = [ "eol-last", // The Markdown parser strips trailing newlines in code fences "unicode-bom" // Code blocks will begin in the middle of Markdown files @@ -222,7 +221,7 @@ function preprocess(text) { code(node, parent) { const comments = []; - if (node.lang && SUPPORTED_SYNTAXES.indexOf(node.lang.split(" ")[0].toLowerCase()) >= 0) { + if (node.lang) { let index = parent.children.indexOf(node) - 1; let previousNode = parent.children[index]; @@ -252,11 +251,14 @@ function preprocess(text) { } }); - return blocks.map(block => [ - ...block.comments, - block.value, - "" - ].join("\n")); + return blocks.map((block, index) => ({ + filename: `${index}.${block.lang}`, + text: [ + ...block.comments, + block.value, + "" + ].join("\n") + })); } /** diff --git a/tests/fixtures/eslintrc.json b/tests/fixtures/eslintrc.json new file mode 100644 index 0000000..3e64f12 --- /dev/null +++ b/tests/fixtures/eslintrc.json @@ -0,0 +1,20 @@ +{ + "root": true, + "env": { + "browser": true + }, + "plugins": ["markdown"], + "overrides": [ + { + "files": ["*.md", "*.mkdn", "*.mdown", "*.markdown", "*.custom"], + "processor": "markdown/markdown" + } + ], + "rules": { + "eol-last": "error", + "no-console": "error", + "no-undef": "error", + "quotes": "error", + "spaced-comment": "error" + } +} diff --git a/tests/fixtures/long.md b/tests/fixtures/long.md index 31e99e4..c1f5c3f 100644 --- a/tests/fixtures/long.md +++ b/tests/fixtures/long.md @@ -10,7 +10,7 @@ This is some code: console.log(42); ``` -```JavaScript +```js // Comment function foo() { console.log("Hello"); @@ -20,7 +20,7 @@ function foo() { -```node +```js console.log(process.version); ``` @@ -34,7 +34,7 @@ How about some JSX? --> -```jsx +```js console.log("Error!"); ``` diff --git a/tests/lib/plugin.js b/tests/lib/plugin.js index f8e04d7..f9a520c 100644 --- a/tests/lib/plugin.js +++ b/tests/lib/plugin.js @@ -18,19 +18,10 @@ const plugin = require("../.."); function initCLI(isAutofixEnabled) { const fix = isAutofixEnabled || false; const cli = new CLIEngine({ - envs: ["browser"], - extensions: ["md", "mkdn", "mdown", "markdown"], - plugins: ["markdown"], fix, ignore: false, - rules: { - "eol-last": 2, - "no-console": 2, - "no-undef": 2, - quotes: 2, - "spaced-comment": 2 - }, - useEslintrc: false + useEslintrc: false, + configFile: path.resolve(__dirname, "../fixtures/eslintrc.json") }); cli.addPlugin("markdown", plugin); @@ -133,6 +124,15 @@ describe("plugin", () => { assert.strictEqual(report.results[0].messages[0].line, 2); }); + it("should run on files with any custom extension", () => { + const report = cli.executeOnText(shortText, "test.custom"); + + assert.strictEqual(report.results.length, 1); + assert.strictEqual(report.results[0].messages.length, 1); + assert.strictEqual(report.results[0].messages[0].message, "Unexpected console statement."); + assert.strictEqual(report.results[0].messages[0].line, 2); + }); + it("should extract blocks and remap messages", () => { const report = cli.executeOnFiles([path.resolve(__dirname, "../fixtures/long.md")]); @@ -342,27 +342,6 @@ describe("plugin", () => { assert.strictEqual(actual, expected); }); - it("in blocks with uncommon tags", () => { - const input = [ - "This is Markdown.", - "", - "```JavaScript", - "console.log('Hello, world!')", - "```" - ].join("\n"); - const expected = [ - "This is Markdown.", - "", - "```JavaScript", - "console.log(\"Hello, world!\")", - "```" - ].join("\n"); - const report = cli.executeOnText(input, "test.md"); - const actual = report.results[0].output; - - assert.strictEqual(actual, expected); - }); - it("in blocks with extra backticks", () => { const input = [ "This is Markdown.", diff --git a/tests/lib/processor.js b/tests/lib/processor.js index 0cd7a12..9b6e500 100644 --- a/tests/lib/processor.js +++ b/tests/lib/processor.js @@ -5,8 +5,8 @@ "use strict"; -const assert = require("chai").assert, - processor = require("../../lib/processor"); +const assert = require("chai").assert; +const processor = require("../../lib/processor"); describe("processor", () => { @@ -73,7 +73,8 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 1); - assert.strictEqual(blocks[0], "var answer = 6 * 7;\n ```\nGoodbye\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "var answer = 6 * 7;\n ```\nGoodbye\n"); }); it("should ignore tab-indented code blocks", () => { @@ -98,7 +99,8 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 1); - assert.strictEqual(blocks[0], "var answer = 6 * 7;\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "var answer = 6 * 7;\n"); }); it("should allow backticks or tildes", () => { @@ -113,8 +115,10 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 2); - assert.strictEqual(blocks[0], "backticks\n"); - assert.strictEqual(blocks[1], "tildes\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "backticks\n"); + assert.strictEqual(blocks[1].filename, "1.javascript"); + assert.strictEqual(blocks[1].text, "tildes\n"); }); it("should allow more than three fence characters", () => { @@ -126,7 +130,8 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 1); - assert.strictEqual(blocks[0], "four\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "four\n"); }); it("should require end fences at least as long as the starting fence", () => { @@ -145,9 +150,12 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 3); - assert.strictEqual(blocks[0], "four\n```\n"); - assert.strictEqual(blocks[1], "five\n"); - assert.strictEqual(blocks[2], "six\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "four\n```\n"); + assert.strictEqual(blocks[1].filename, "1.js"); + assert.strictEqual(blocks[1].text, "five\n"); + assert.strictEqual(blocks[2].filename, "2.js"); + assert.strictEqual(blocks[2].text, "six\n"); }); it("should not allow other content on ending fence line", () => { @@ -160,7 +168,8 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 1); - assert.strictEqual(blocks[0], "test();\n``` end\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "test();\n``` end\n"); }); it("should allow empty blocks", () => { @@ -172,7 +181,8 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 1); - assert.strictEqual(blocks[0], "\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "\n"); }); it("should allow whitespace-only blocks", () => { @@ -188,7 +198,8 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 1); - assert.strictEqual(blocks[0], "\n\n \n \n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "\n\n \n \n"); }); it("should ignore code fences with unspecified info string", () => { @@ -292,7 +303,8 @@ describe("processor", () => { ].join("\n"); const blocks = processor.preprocess(code); - assert.strictEqual(blocks[0], "var answer = 6 * 7;\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "var answer = 6 * 7;\n"); }); it("should allow multi-line source code", () => { @@ -304,7 +316,8 @@ describe("processor", () => { ].join("\n"); const blocks = processor.preprocess(code); - assert.strictEqual(blocks[0], "var answer = 6 * 7;\nconsole.log(answer);\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "var answer = 6 * 7;\nconsole.log(answer);\n"); }); it("should preserve original line endings", () => { @@ -316,7 +329,8 @@ describe("processor", () => { ].join("\r\n"); const blocks = processor.preprocess(code); - assert.strictEqual(blocks[0], "var answer = 6 * 7;\nconsole.log(answer);\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "var answer = 6 * 7;\nconsole.log(answer);\n"); }); it("should unindent space-indented code fences", () => { @@ -329,7 +343,8 @@ describe("processor", () => { ].join("\n"); const blocks = processor.preprocess(code); - assert.strictEqual(blocks[0], "var answer = 6 * 7;\n console.log(answer);\n// Fin.\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "var answer = 6 * 7;\n console.log(answer);\n// Fin.\n"); }); it("should find multiple code fences", () => { @@ -349,8 +364,10 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 2); - assert.strictEqual(blocks[0], "var answer = 6 * 7;\n"); - assert.strictEqual(blocks[1], "console.log(answer);\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "var answer = 6 * 7;\n"); + assert.strictEqual(blocks[1].filename, "1.javascript"); + assert.strictEqual(blocks[1].text, "console.log(answer);\n"); }); it("should insert leading configuration comments", () => { @@ -370,7 +387,8 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 1); - assert.strictEqual(blocks[0], [ + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, [ "/* eslint-env browser */", "/*", " eslint quotes: [", @@ -395,7 +413,8 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 1); - assert.strictEqual(blocks[0], [ + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, [ "/* global foo */", "/* global bar:false, baz:true */", "alert(foo, bar, baz);", @@ -415,7 +434,8 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 1); - assert.strictEqual(blocks[0], [ + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, [ "alert('Hello, world!');", "" ].join("\n")); @@ -433,7 +453,8 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 1); - assert.strictEqual(blocks[0], [ + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, [ "alert('Hello, world!');", "" ].join("\n")); @@ -469,7 +490,8 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 1); - assert.strictEqual(blocks[0], "var answer = 6 * 7;\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "var answer = 6 * 7;\n"); }); it("should still work surrounded by other comments", () => { @@ -489,7 +511,8 @@ describe("processor", () => { const blocks = processor.preprocess(code); assert.strictEqual(blocks.length, 1); - assert.strictEqual(blocks[0], "var answer = 6 * 7;\n"); + assert.strictEqual(blocks[0].filename, "0.js"); + assert.strictEqual(blocks[0].text, "var answer = 6 * 7;\n"); }); });