Skip to content

Commit

Permalink
Fix: use blocksCache instead of single blocks instance (fixes #181) (#…
Browse files Browse the repository at this point in the history
…183)

* Fix: use blocksCache instead of single blocks instance

pass through lint messages from other plugins

* refactor: use Map and delete map after postprocess

remove unnecessary mdx example

* Chore: add test case for blocksCache

* Revert: downgrade remark-parse and unified

* chore: remove unnecessary fallback check

* refactor: simplify return statement
  • Loading branch information
JounQin authored Apr 5, 2021
1 parent a09a645 commit d23d5f7
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 12 deletions.
2 changes: 1 addition & 1 deletion examples/typescript/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# React Example
# TypeScript Example

The `@typescript-eslint` parser and the `recommended` config's rules will work in `ts` code blocks. However, [type-aware rules](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/TYPED_LINTING.md) will not work because the code blocks are not part of a compilable `tsconfig.json` project.

Expand Down
18 changes: 13 additions & 5 deletions lib/processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ const SUPPORTS_AUTOFIX = true;
const markdown = unified().use(remarkParse);

/**
* @type {Block[]}
* @type {Map<string, Block[]>}
*/
let blocks = [];
const blocksCache = new Map();

/**
* Performs a depth-first traversal of the Markdown AST.
Expand Down Expand Up @@ -235,10 +235,14 @@ function getBlockRangeMap(text, node, comments) {
/**
* Extracts lintable code blocks from Markdown text.
* @param {string} text The text of the file.
* @param {string} filename The filename of the file
* @returns {Array<{ filename: string, text: string }>} Source code blocks to lint.
*/
function preprocess(text) {
function preprocess(text, filename) {
const ast = markdown.parse(text);
const blocks = [];

blocksCache.set(filename, blocks);

/**
* During the depth-first traversal, keep track of any sequences of HTML
Expand All @@ -250,7 +254,6 @@ function preprocess(text) {
*/
let htmlComments = [];

blocks = [];
traverse(ast, {
"*"() {
htmlComments = [];
Expand Down Expand Up @@ -370,9 +373,14 @@ function excludeUnsatisfiableRules(message) {
* Transforms generated messages for output.
* @param {Array<Message[]>} messages An array containing one array of messages
* for each code block returned from `preprocess`.
* @param {string} filename The filename of the file
* @returns {Message[]} A flattened array of messages with mapped locations.
*/
function postprocess(messages) {
function postprocess(messages, filename) {
const blocks = blocksCache.get(filename);

blocksCache.delete(filename);

return [].concat(...messages.map((group, i) => {
const adjust = adjustBlock(blocks[i]);

Expand Down
54 changes: 48 additions & 6 deletions tests/lib/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,23 @@ const CLIEngine = require("eslint").CLIEngine;
const path = require("path");
const plugin = require("../..");

/**
* @typedef {import('eslint/lib/cli-engine/cli-engine').CLIEngineOptions} CLIEngineOptions
*/

/**
* Helper function which creates CLIEngine instance with enabled/disabled autofix feature.
* @param {string} fixtureConfigName ESLint JSON config fixture filename.
* @param {boolean} [isAutofixEnabled=false] Whether to enable autofix feature.
* @param {CLIEngineOptions} [options={}] Whether to enable autofix feature.
* @returns {CLIEngine} CLIEngine instance to execute in tests.
*/
function initCLI(fixtureConfigName, isAutofixEnabled) {
const fix = isAutofixEnabled || false;
function initCLI(fixtureConfigName, options = {}) {
const cli = new CLIEngine({
cwd: path.resolve(__dirname, "../fixtures/"),
fix,
ignore: false,
useEslintrc: false,
configFile: path.resolve(__dirname, "../fixtures/", fixtureConfigName)
configFile: path.resolve(__dirname, "../fixtures/", fixtureConfigName),
...options
});

cli.addPlugin("markdown", plugin);
Expand Down Expand Up @@ -242,6 +245,45 @@ describe("plugin", () => {
assert.strictEqual(report.results[0].messages[4].column, 2);
});

// https://github.com/eslint/eslint-plugin-markdown/issues/181
it("should work when called on nested code blocks in the same file", () => {

/*
* As of this writing, the nested code block, though it uses the same
* Markdown processor, must use a different extension or ESLint will not
* re-apply the processor on the nested code block. To work around that,
* a file named `test.md` contains a nested `markdown` code block in
* this test.
*
* https://github.com/eslint/eslint/pull/14227/files#r602802758
*/
const code = [
"<!-- test.md -->",
"",
"````markdown",
"<!-- test.md/0_0.markdown -->",
"",
"This test only repros if the MD files have a different number of lines before code blocks.",
"",
"```js",
"// test.md/0_0.markdown/0_0.js",
"console.log('single quotes')",
"```",
"````"
].join("\n");
const recursiveCli = initCLI("eslintrc.json", {
extensions: [".js", ".markdown", ".md"]
});
const report = recursiveCli.executeOnText(code, "test.md");

assert.strictEqual(report.results.length, 1);
assert.strictEqual(report.results[0].messages.length, 2);
assert.strictEqual(report.results[0].messages[0].message, "Unexpected console statement.");
assert.strictEqual(report.results[0].messages[0].line, 10);
assert.strictEqual(report.results[0].messages[1].message, "Strings must use doublequote.");
assert.strictEqual(report.results[0].messages[1].line, 10);
});

describe("configuration comments", () => {

it("apply only to the code block immediately following", () => {
Expand Down Expand Up @@ -282,7 +324,7 @@ describe("plugin", () => {
describe("should fix code", () => {

before(() => {
cli = initCLI("eslintrc.json", true);
cli = initCLI("eslintrc.json", { fix: true });
});

it("in the simplest case", () => {
Expand Down

0 comments on commit d23d5f7

Please sign in to comment.