Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: skip processor code blocks that match only universal patterns #18880

Merged
merged 2 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix: skip processor code blocks that match only universal patterns
  • Loading branch information
mdjermanovic committed Sep 10, 2024
commit 961134694e7bf3ff69a9a0524f08a7b92d50c1af
36 changes: 36 additions & 0 deletions docs/src/use/configure/configuration-files-new.md
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,42 @@ export default [
];
```

ESLint only lints named code blocks when they are JavaScript files or if they match a `files` entry in a config object. Be sure to add a config object with a matching `files` entry if you want to lint non-JavaScript named code blocks. Also note that [global ignores](#globally-ignoring-files-with-ignores) apply to named code blocks as well.

```js
// eslint.config.js
import markdown from "eslint-plugin-markdown";

export default [

// applies to Markdown files
{
files: ["**/*.md"],
plugins: {
markdown
},
processor: "markdown/markdown"
},

// applies to all .jsx files, including jsx blocks inside of Markdown files
{
files: ["**/*.jsx"],
languageOptions: {
parserOptions: {
ecmaFeatures: {
jsx: true
}
}
}
},

// ignore jsx blocks inside of test.md files
{
ignores: ["**/test.md/*.jsx"]
}
];
```

### Configuring rules

You can configure any number of rules in a configuration object by add a `rules` property containing an object with your rule configurations. The names in this object are the names of the rules and the values are the configurations for each of those rules. Here's an example:
Expand Down
2 changes: 1 addition & 1 deletion lib/eslint/flat-eslint.js
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ function verifyText({
* @returns {boolean} `true` if the linter should adopt the code block.
*/
filterCodeBlock(blockFilename) {
return configs.isExplicitMatch(blockFilename);
return configs.getConfig(blockFilename) !== void 0;
}
}
);
Expand Down
178 changes: 178 additions & 0 deletions tests/lib/eslint/flat-eslint.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ describe("FlatESLint", () => {
}
};
const examplePreprocessorName = "eslint-plugin-processor";
const patternProcessor = require("../../fixtures/processors/pattern-processor");
const exampleMarkdownPlugin = {
processors: {
markdown: patternProcessor.defineProcessor(/```(\w+)\n(.+?)\n```(?:\n|$)/gsu)
}
};
const originalDir = process.cwd();
const fixtureDir = path.resolve(fs.realpathSync(os.tmpdir()), "eslint/fixtures");

Expand Down Expand Up @@ -3590,6 +3596,178 @@ describe("FlatESLint", () => {
assert(!Object.prototype.hasOwnProperty.call(results[0], "output"));
});
});

describe("matching and ignoring code blocks", () => {
const pluginConfig = {
files: ["**/*.md"],
plugins: {
markdown: exampleMarkdownPlugin
},
processor: "markdown/markdown"
};
const text = unIndent`
\`\`\`js
foo_js
\`\`\`
\`\`\`ts
foo_ts
\`\`\`
\`\`\`cjs
foo_cjs
\`\`\`
\`\`\`mjs
foo_mjs
\`\`\`
`;

it("should by default lint only .js, .mjs, and .cjs virtual files", async () => {
eslint = new FlatESLint({
overrideConfigFile: true,
overrideConfig: [
pluginConfig,
{
rules: {
"no-undef": 2
}
}
]
});
const [result] = await eslint.lintText(text, { filePath: "foo.md" });

assert.strictEqual(result.messages.length, 3);
assert.strictEqual(result.messages[0].ruleId, "no-undef");
assert.match(result.messages[0].message, /foo_js/u);
assert.strictEqual(result.messages[0].line, 2);
assert.strictEqual(result.messages[1].ruleId, "no-undef");
assert.match(result.messages[1].message, /foo_cjs/u);
assert.strictEqual(result.messages[1].line, 10);
assert.strictEqual(result.messages[2].ruleId, "no-undef");
assert.match(result.messages[2].message, /foo_mjs/u);
assert.strictEqual(result.messages[2].line, 14);
});

it("should lint additional virtual files that match non-universal patterns", async () => {
eslint = new FlatESLint({
overrideConfigFile: true,
overrideConfig: [
pluginConfig,
{
rules: {
"no-undef": 2
}
},
{
files: ["**/*.ts"]
}
]
});
const [result] = await eslint.lintText(text, { filePath: "foo.md" });

assert.strictEqual(result.messages.length, 4);
assert.strictEqual(result.messages[0].ruleId, "no-undef");
assert.match(result.messages[0].message, /foo_js/u);
assert.strictEqual(result.messages[0].line, 2);
assert.strictEqual(result.messages[1].ruleId, "no-undef");
assert.match(result.messages[1].message, /foo_ts/u);
assert.strictEqual(result.messages[1].line, 6);
assert.strictEqual(result.messages[2].ruleId, "no-undef");
assert.match(result.messages[2].message, /foo_cjs/u);
assert.strictEqual(result.messages[2].line, 10);
assert.strictEqual(result.messages[3].ruleId, "no-undef");
assert.match(result.messages[3].message, /foo_mjs/u);
assert.strictEqual(result.messages[3].line, 14);
});

// https://github.com/eslint/eslint/issues/18493
it("should silently skip virtual files that match only universal patterns", async () => {
eslint = new FlatESLint({
overrideConfigFile: true,
overrideConfig: [
pluginConfig,
{
files: ["**/*"],
rules: {
"no-undef": 2
}
}
]
});
const [result] = await eslint.lintText(text, { filePath: "foo.md" });

assert.strictEqual(result.messages.length, 3);
assert.strictEqual(result.messages[0].ruleId, "no-undef");
assert.match(result.messages[0].message, /foo_js/u);
assert.strictEqual(result.messages[0].line, 2);
assert.strictEqual(result.messages[1].ruleId, "no-undef");
assert.match(result.messages[1].message, /foo_cjs/u);
assert.strictEqual(result.messages[1].line, 10);
assert.strictEqual(result.messages[2].ruleId, "no-undef");
assert.match(result.messages[2].message, /foo_mjs/u);
assert.strictEqual(result.messages[2].line, 14);
});

it("should silently skip virtual files that are ignored by global ignores", async () => {
eslint = new FlatESLint({
overrideConfigFile: true,
overrideConfig: [
pluginConfig,
{
rules: {
"no-undef": 2
}
},
{
ignores: ["**/*.cjs"]
}
]
});
const [result] = await eslint.lintText(text, { filePath: "foo.md" });

assert.strictEqual(result.messages.length, 2);
assert.strictEqual(result.messages[0].ruleId, "no-undef");
assert.match(result.messages[0].message, /foo_js/u);
assert.strictEqual(result.messages[0].line, 2);
assert.strictEqual(result.messages[1].ruleId, "no-undef");
assert.match(result.messages[1].message, /foo_mjs/u);
assert.strictEqual(result.messages[1].line, 14);
});

// https://github.com/eslint/eslint/issues/15949
it("should silently skip virtual files that are ignored by global ignores even if they match non-universal patterns", async () => {
eslint = new FlatESLint({
overrideConfigFile: true,
overrideConfig: [
pluginConfig,
{
rules: {
"no-undef": 2
}
},
{
files: ["**/*.ts"]
},
{
ignores: ["**/*.md/*.ts"]
}
]
});
const [result] = await eslint.lintText(text, { filePath: "foo.md" });

assert.strictEqual(result.messages.length, 3);
assert.strictEqual(result.messages[0].ruleId, "no-undef");
assert.match(result.messages[0].message, /foo_js/u);
assert.strictEqual(result.messages[0].line, 2);
assert.strictEqual(result.messages[1].ruleId, "no-undef");
assert.match(result.messages[1].message, /foo_cjs/u);
assert.strictEqual(result.messages[1].line, 10);
assert.strictEqual(result.messages[2].ruleId, "no-undef");
assert.match(result.messages[2].message, /foo_mjs/u);
assert.strictEqual(result.messages[2].line, 14);
});
});
});

describe("Patterns which match no file should throw errors.", () => {
Expand Down