From 9458735381269d12b24f76e1b2b6fda1bc5a509b Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Sun, 4 Feb 2024 18:04:40 +0100 Subject: [PATCH] docs: fix malformed `eslint` config comments in rule examples (#18078) * fix: fix malformed `eslint` config comments in rule examples * extend rule example validation * fix test for runtime-specific error message --- docs/src/rules/lines-around-comment.md | 8 +++---- tests/fixtures/bad-examples.md | 12 ++++++++++- tests/tools/check-rule-examples.js | 17 +++++++++++---- tools/check-rule-examples.js | 30 +++++++++++++++----------- 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/docs/src/rules/lines-around-comment.md b/docs/src/rules/lines-around-comment.md index 146224fa9af..23582fefd60 100644 --- a/docs/src/rules/lines-around-comment.md +++ b/docs/src/rules/lines-around-comment.md @@ -233,7 +233,7 @@ class C { switch (foo) { /* what a great and wonderful day */ - case 1: + case 1: bar(); break; } @@ -317,7 +317,7 @@ class C { } switch (foo) { - case 1: + case 1: bar(); break; @@ -663,7 +663,7 @@ Examples of **correct** code for the `ignorePattern` option: /*eslint lines-around-comment: ["error"]*/ foo(); -/* eslint mentioned in this comment */ +/* jshint mentioned in this comment */ bar(); /*eslint lines-around-comment: ["error", { "ignorePattern": "pragma" }] */ @@ -712,7 +712,7 @@ Examples of **incorrect** code for the `{ "applyDefaultIgnorePatterns": false }` /*eslint lines-around-comment: ["error", { "applyDefaultIgnorePatterns": false }] */ foo(); -/* eslint mentioned in comment */ +/* jshint mentioned in comment */ ``` diff --git a/tests/fixtures/bad-examples.md b/tests/fixtures/bad-examples.md index 2cc35a19d88..872a1f79bf8 100644 --- a/tests/fixtures/bad-examples.md +++ b/tests/fixtures/bad-examples.md @@ -2,7 +2,7 @@ title: no-restricted-syntax --- -This file contains rule example code with syntax errors. +This file contains rule example code with syntax errors and other problems. @@ -32,3 +32,13 @@ const foo = "baz"; ``` ::: + +:::correct + +```js +/* eslint no-restricted-syntax: "error" */ + +/* eslint doesn't allow this comment */ +``` + +::: diff --git a/tests/tools/check-rule-examples.js b/tests/tools/check-rule-examples.js index 1ee68168f9e..d93bcbe1931 100644 --- a/tests/tools/check-rule-examples.js +++ b/tests/tools/check-rule-examples.js @@ -55,10 +55,18 @@ describe("check-rule-examples", () => { assert.strictEqual(code, 1); assert.strictEqual(stdout, ""); - // Remove OS-dependent path except base name. + /* eslint-disable no-control-regex -- escaping control characters */ + const normalizedStderr = - // eslint-disable-next-line no-control-regex -- escaping control character - stderr.replace(/(?<=\x1B\[4m).*(?=bad-examples\.md)/u, ""); + stderr + + // Remove OS-dependent path except base name. + .replace(/(?<=\x1B\[4m).*(?=bad-examples\.md)/u, "") + + // Remove runtime-specific error message part (different in Node.js 18, 20 and 21). + .replace(/(?<=' doesn't allow this comment'):.*(?=\x1B\[0m)/u, ""); + + /* eslint-enable no-control-regex -- re-enable rule */ const expectedStderr = "\x1B[0m\x1B[0m\n" + @@ -68,8 +76,9 @@ describe("check-rule-examples", () => { "\x1B[0m \x1B[2m20:5\x1B[22m \x1B[31merror\x1B[39m Nonstandard language tag 'ts': use one of 'javascript', 'js' or 'jsx'\x1B[0m\n" + "\x1B[0m \x1B[2m23:7\x1B[22m \x1B[31merror\x1B[39m Syntax error: Identifier 'foo' has already been declared\x1B[0m\n" + "\x1B[0m \x1B[2m31:1\x1B[22m \x1B[31merror\x1B[39m Example code should contain a configuration comment like /* eslint no-restricted-syntax: \"error\" */\x1B[0m\n" + + "\x1B[0m \x1B[2m41:1\x1B[22m \x1B[31merror\x1B[39m Failed to parse JSON from ' doesn't allow this comment'\x1B[0m\n" + "\x1B[0m\x1B[0m\n" + - "\x1B[0m\x1B[31m\x1B[1m✖ 5 problems (5 errors, 0 warnings)\x1B[22m\x1B[39m\x1B[0m\n" + + "\x1B[0m\x1B[31m\x1B[1m✖ 6 problems (6 errors, 0 warnings)\x1B[22m\x1B[39m\x1B[0m\n" + "\x1B[0m\x1B[31m\x1B[1m\x1B[22m\x1B[39m\x1B[0m\n"; assert.strictEqual(normalizedStderr, expectedStderr); diff --git a/tools/check-rule-examples.js b/tools/check-rule-examples.js index 7373737cc02..b475d46b872 100644 --- a/tools/check-rule-examples.js +++ b/tools/check-rule-examples.js @@ -38,7 +38,7 @@ const commentParser = new ConfigCommentParser(); */ function tryParseForPlayground(code, parserOptions) { try { - const ast = parse(code, { ecmaVersion: "latest", ...parserOptions, comment: true }); + const ast = parse(code, { ecmaVersion: "latest", ...parserOptions, comment: true, loc: true }); return { ast }; } catch (error) { @@ -81,20 +81,26 @@ async function findProblems(filename) { const { ast, error } = tryParseForPlayground(code, parserOptions); - if (ast && !isRuleRemoved) { - const hasRuleConfigComment = ast.comments.some( - comment => { - if (comment.type !== "Block" || !/^\s*eslint(?!\S)/u.test(comment.value)) { - return false; - } - const { directiveValue } = commentParser.parseDirective(comment); - const parseResult = commentParser.parseJsonConfig(directiveValue, comment.loc); + if (ast) { + let hasRuleConfigComment = false; - return parseResult.success && Object.hasOwn(parseResult.config, title); + for (const comment of ast.comments) { + if (comment.type !== "Block" || !/^\s*eslint(?!\S)/u.test(comment.value)) { + continue; } - ); + const { directiveValue } = commentParser.parseDirective(comment); + const parseResult = commentParser.parseJsonConfig(directiveValue, comment.loc); + const parseError = parseResult.error; + + if (parseError) { + parseError.line += codeBlockToken.map[0] + 1; + problems.push(parseError); + } else if (Object.hasOwn(parseResult.config, title)) { + hasRuleConfigComment = true; + } + } - if (!hasRuleConfigComment) { + if (!isRuleRemoved && !hasRuleConfigComment) { const message = `Example code should contain a configuration comment like /* eslint ${title}: "error" */`; problems.push({