From 8090bcb2a9baaeec213dd8f73083c666f1b54fc7 Mon Sep 17 00:00:00 2001 From: Vladyslav Soldatenko Date: Sat, 22 Nov 2025 18:31:55 +0200 Subject: [PATCH 01/14] expiring-todo-comments: should not flag eslint disable comments --- rules/expiring-todo-comments.js | 11 +++++++++++ test/expiring-todo-comments.js | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/rules/expiring-todo-comments.js b/rules/expiring-todo-comments.js index 5fe07c21f2..369043a4a4 100644 --- a/rules/expiring-todo-comments.js +++ b/rules/expiring-todo-comments.js @@ -284,6 +284,13 @@ const create = context => { const {sourceCode} = context; const comments = sourceCode.getAllComments(); + + const isEslintDirectiveComment = comment => { + // Strip leading whitespace and optional `*` from block comments + const normalizedComment = comment.value.trimStart().replace(/^\*\s*/, ''); + return /^eslint(?:-(?:en|dis)able)?(?:-(?:next-)?line)?\b/.test(normalizedComment); + }; + const unusedComments = comments .filter(token => token.type !== 'Shebang') // Block comments come as one. @@ -319,6 +326,10 @@ const create = context => { // eslint-disable-next-line complexity function processComment(comment) { + if (isEslintDirectiveComment(comment)) { + return; + } + if (ignoreRegexes.some(ignore => ignore.test(comment.value))) { return; } diff --git a/test/expiring-todo-comments.js b/test/expiring-todo-comments.js index 57728e3cb6..9c81b9124e 100644 --- a/test/expiring-todo-comments.js +++ b/test/expiring-todo-comments.js @@ -114,6 +114,18 @@ test({ code: '// TODO [2001-01-01]: quite old', options: [{date: '2000-01-01'}], }, + { + code: `// eslint-disable-next-line rule-to-test/expiring-todo-comments + // TODO without a date`, + options: [{allowWarningComments: false}], + }, + { + code: `/* eslint-disable rule-to-test/expiring-todo-comments */ + // TODO without a date + // fixme [2000-01-01]: too old' + /* eslint-enable rule-to-test/expiring-todo-comments */`, + options: [{allowWarningComments: false}], + }, ], invalid: [ { From d0df7af5b80a82677576c647eb19dfd7a0b6e8a7 Mon Sep 17 00:00:00 2001 From: Vladyslav Soldatenko Date: Sat, 22 Nov 2025 20:32:28 +0200 Subject: [PATCH 02/14] expiring-todo-comments: use ConfigCommentParser to test for eslint directives --- rules/expiring-todo-comments.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/rules/expiring-todo-comments.js b/rules/expiring-todo-comments.js index 369043a4a4..42a36fa687 100644 --- a/rules/expiring-todo-comments.js +++ b/rules/expiring-todo-comments.js @@ -1,3 +1,4 @@ +import {ConfigCommentParser} from '@eslint/plugin-kit'; import path from 'node:path'; import {isRegExp} from 'node:util/types'; import semver from 'semver'; @@ -267,6 +268,8 @@ const DEFAULT_OPTIONS = { allowWarningComments: true, }; +let configCommentParser; + /** @param {import('eslint').Rule.RuleContext} context */ const create = context => { const options = { @@ -284,13 +287,6 @@ const create = context => { const {sourceCode} = context; const comments = sourceCode.getAllComments(); - - const isEslintDirectiveComment = comment => { - // Strip leading whitespace and optional `*` from block comments - const normalizedComment = comment.value.trimStart().replace(/^\*\s*/, ''); - return /^eslint(?:-(?:en|dis)able)?(?:-(?:next-)?line)?\b/.test(normalizedComment); - }; - const unusedComments = comments .filter(token => token.type !== 'Shebang') // Block comments come as one. @@ -326,7 +322,10 @@ const create = context => { // eslint-disable-next-line complexity function processComment(comment) { - if (isEslintDirectiveComment(comment)) { + configCommentParser ??= new ConfigCommentParser(); + + const directive = configCommentParser.parseDirective(comment.value); + if (directive?.label?.startsWith('eslint')) { return; } From 00bf48ab434e75c684bd32a774d735be4cd84ac2 Mon Sep 17 00:00:00 2001 From: Vladyslav Soldatenko Date: Sat, 22 Nov 2025 22:56:44 +0200 Subject: [PATCH 03/14] create and use parse-directive helper --- rules/expiring-todo-comments.js | 10 ++----- rules/no-abusive-eslint-disable.js | 17 +++-------- rules/utils/index.js | 1 + rules/utils/parse-directive.js | 47 ++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 20 deletions(-) create mode 100644 rules/utils/parse-directive.js diff --git a/rules/expiring-todo-comments.js b/rules/expiring-todo-comments.js index 42a36fa687..f371fd4fe5 100644 --- a/rules/expiring-todo-comments.js +++ b/rules/expiring-todo-comments.js @@ -1,8 +1,8 @@ -import {ConfigCommentParser} from '@eslint/plugin-kit'; import path from 'node:path'; import {isRegExp} from 'node:util/types'; import semver from 'semver'; import * as ci from 'ci-info'; +import parseDirective from './utils/parse-directive.js'; import getBuiltinRule from './utils/get-builtin-rule.js'; import {readPackageJson} from './shared/package-json.js'; @@ -268,8 +268,6 @@ const DEFAULT_OPTIONS = { allowWarningComments: true, }; -let configCommentParser; - /** @param {import('eslint').Rule.RuleContext} context */ const create = context => { const options = { @@ -322,10 +320,8 @@ const create = context => { // eslint-disable-next-line complexity function processComment(comment) { - configCommentParser ??= new ConfigCommentParser(); - - const directive = configCommentParser.parseDirective(comment.value); - if (directive?.label?.startsWith('eslint')) { + const directive = parseDirective(comment); + if (directive?.isEslintDisableDirective || directive?.isEslintEnableDirective) { return; } diff --git a/rules/no-abusive-eslint-disable.js b/rules/no-abusive-eslint-disable.js index d665ed4f58..a824771ad9 100644 --- a/rules/no-abusive-eslint-disable.js +++ b/rules/no-abusive-eslint-disable.js @@ -1,30 +1,21 @@ -import {ConfigCommentParser} from '@eslint/plugin-kit'; +import parseDirective from './utils/parse-directive.js'; const MESSAGE_ID = 'no-abusive-eslint-disable'; const messages = { [MESSAGE_ID]: 'Specify the rules you want to disable.', }; -// https://github.com/eslint/eslint/blob/ecd0ede7fd2ccbb4c0daf0e4732e97ea0f49db1b/lib/linter/linter.js#L509-L512 -const eslintDisableDirectives = new Set([ - 'eslint-disable', - 'eslint-disable-line', - 'eslint-disable-next-line', -]); - -let commentParser; /** @param {import('eslint').Rule.RuleContext} context */ const create = context => { context.on('Program', function * (node) { for (const comment of node.comments) { - commentParser ??= new ConfigCommentParser(); - const result = commentParser.parseDirective(comment.value); + const directive = parseDirective(comment); if (!( // It's a eslint-disable comment - eslintDisableDirectives.has(result?.label) + directive?.isEslintDisableDirective // But it did not specify any rules - && !result?.value + && !directive?.value )) { return; } diff --git a/rules/utils/index.js b/rules/utils/index.js index 424a06a2f1..d91215b75a 100644 --- a/rules/utils/index.js +++ b/rules/utils/index.js @@ -49,6 +49,7 @@ export {default as isShorthandImportLocal} from './is-shorthand-import-local.js' export {default as isShorthandPropertyValue} from './is-shorthand-property-value.js'; export {default as isValueNotUsable} from './is-value-not-usable.js'; export {default as needsSemicolon} from './needs-semicolon.js'; +export {default as parseDirective} from './parse-directive.js'; export {checkVueTemplate} from './rule.js'; export {default as shouldAddParenthesesToAwaitExpressionArgument} from './should-add-parentheses-to-await-expression-argument.js'; export {default as shouldAddParenthesesToCallExpressionCallee} from './should-add-parentheses-to-call-expression-callee.js'; diff --git a/rules/utils/parse-directive.js b/rules/utils/parse-directive.js new file mode 100644 index 0000000000..f10dab73ae --- /dev/null +++ b/rules/utils/parse-directive.js @@ -0,0 +1,47 @@ +import {ConfigCommentParser} from '@eslint/plugin-kit'; + +const ESLINT_DISABLE_DIRECTIVES = new Set([ + 'eslint-disable', + 'eslint-disable-line', + 'eslint-disable-next-line', +]); + +const ESLINT_ENABLE_DIRECTIVES = new Set([ + 'eslint-enable', + 'eslint-enable-line', + 'eslint-enable-next-line', +]); + +let configCommentParser; + +/** +Parse a directive comment value and return directive meta info. + +@param {ESTree.Comment} comment +@returns {{ + label: string, + value: string, + justification: string, + isEslintDisableDirective: boolean, + isEslintEnableDirective: boolean, +}|undefined} +*/ +export default function parseDirective(comment) { + configCommentParser ??= new ConfigCommentParser(); + + const result = configCommentParser.parseDirective(comment.value); + + if (!result) { + return; + } + + const {label} = result; + const isEslintDisableDirective = ESLINT_DISABLE_DIRECTIVES.has(label); + const isEslintEnableDirective = ESLINT_ENABLE_DIRECTIVES.has(label); + + return { + ...result, + isEslintDisableDirective, + isEslintEnableDirective, + }; +} From 11a71ed521859f0fa718a6cf0f6dea0d551bb92a Mon Sep 17 00:00:00 2001 From: Vladyslav Soldatenko Date: Sun, 23 Nov 2025 00:01:27 +0200 Subject: [PATCH 04/14] remove unnessesary directives --- rules/utils/parse-directive.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/rules/utils/parse-directive.js b/rules/utils/parse-directive.js index f10dab73ae..933179fd2e 100644 --- a/rules/utils/parse-directive.js +++ b/rules/utils/parse-directive.js @@ -6,12 +6,6 @@ const ESLINT_DISABLE_DIRECTIVES = new Set([ 'eslint-disable-next-line', ]); -const ESLINT_ENABLE_DIRECTIVES = new Set([ - 'eslint-enable', - 'eslint-enable-line', - 'eslint-enable-next-line', -]); - let configCommentParser; /** @@ -37,7 +31,7 @@ export default function parseDirective(comment) { const {label} = result; const isEslintDisableDirective = ESLINT_DISABLE_DIRECTIVES.has(label); - const isEslintEnableDirective = ESLINT_ENABLE_DIRECTIVES.has(label); + const isEslintEnableDirective = label === 'eslint-enable'; return { ...result, From 859710c30fef73443cb2e0b2dff2c5bdd1cc8ebb Mon Sep 17 00:00:00 2001 From: Vladyslav Soldatenko Date: Sun, 23 Nov 2025 00:03:09 +0200 Subject: [PATCH 05/14] add link --- rules/utils/parse-directive.js | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/utils/parse-directive.js b/rules/utils/parse-directive.js index 933179fd2e..e2e5031495 100644 --- a/rules/utils/parse-directive.js +++ b/rules/utils/parse-directive.js @@ -1,5 +1,6 @@ import {ConfigCommentParser} from '@eslint/plugin-kit'; +// https://github.com/eslint/eslint/blob/ecd0ede7fd2ccbb4c0daf0e4732e97ea0f49db1b/lib/linter/linter.js#L509-L512 const ESLINT_DISABLE_DIRECTIVES = new Set([ 'eslint-disable', 'eslint-disable-line', From 47e0ef63011d835cd12cc02a525fe84bd791686c Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 23 Nov 2025 23:10:15 +0800 Subject: [PATCH 06/14] Minor tweak --- rules/expiring-todo-comments.js | 8 +++--- rules/no-abusive-eslint-disable.js | 6 +++-- rules/utils/index.js | 2 +- ...irective.js => parse-directive-comment.js} | 26 ++++++++++--------- 4 files changed, 24 insertions(+), 18 deletions(-) rename rules/utils/{parse-directive.js => parse-directive-comment.js} (60%) diff --git a/rules/expiring-todo-comments.js b/rules/expiring-todo-comments.js index f371fd4fe5..127329af00 100644 --- a/rules/expiring-todo-comments.js +++ b/rules/expiring-todo-comments.js @@ -2,8 +2,10 @@ import path from 'node:path'; import {isRegExp} from 'node:util/types'; import semver from 'semver'; import * as ci from 'ci-info'; -import parseDirective from './utils/parse-directive.js'; -import getBuiltinRule from './utils/get-builtin-rule.js'; +import { + parseDirectiveComment, + getBuiltinRule, +} from './utils/index.js'; import {readPackageJson} from './shared/package-json.js'; const baseRule = getBuiltinRule('no-warning-comments'); @@ -320,7 +322,7 @@ const create = context => { // eslint-disable-next-line complexity function processComment(comment) { - const directive = parseDirective(comment); + const directive = parseDirectiveComment(comment); if (directive?.isEslintDisableDirective || directive?.isEslintEnableDirective) { return; } diff --git a/rules/no-abusive-eslint-disable.js b/rules/no-abusive-eslint-disable.js index a824771ad9..2ff0552eed 100644 --- a/rules/no-abusive-eslint-disable.js +++ b/rules/no-abusive-eslint-disable.js @@ -1,4 +1,6 @@ -import parseDirective from './utils/parse-directive.js'; +import { + parseDirectiveComment, +} from './utils/index.js'; const MESSAGE_ID = 'no-abusive-eslint-disable'; const messages = { @@ -9,7 +11,7 @@ const messages = { const create = context => { context.on('Program', function * (node) { for (const comment of node.comments) { - const directive = parseDirective(comment); + const directive = parseDirectiveComment(comment); if (!( // It's a eslint-disable comment diff --git a/rules/utils/index.js b/rules/utils/index.js index d91215b75a..c9f23f166c 100644 --- a/rules/utils/index.js +++ b/rules/utils/index.js @@ -49,7 +49,7 @@ export {default as isShorthandImportLocal} from './is-shorthand-import-local.js' export {default as isShorthandPropertyValue} from './is-shorthand-property-value.js'; export {default as isValueNotUsable} from './is-value-not-usable.js'; export {default as needsSemicolon} from './needs-semicolon.js'; -export {default as parseDirective} from './parse-directive.js'; +export {default as parseDirectiveComment} from './parse-directive-comment.js'; export {checkVueTemplate} from './rule.js'; export {default as shouldAddParenthesesToAwaitExpressionArgument} from './should-add-parentheses-to-await-expression-argument.js'; export {default as shouldAddParenthesesToCallExpressionCallee} from './should-add-parentheses-to-call-expression-callee.js'; diff --git a/rules/utils/parse-directive.js b/rules/utils/parse-directive-comment.js similarity index 60% rename from rules/utils/parse-directive.js rename to rules/utils/parse-directive-comment.js index e2e5031495..7e0ff8d346 100644 --- a/rules/utils/parse-directive.js +++ b/rules/utils/parse-directive-comment.js @@ -1,5 +1,9 @@ import {ConfigCommentParser} from '@eslint/plugin-kit'; +/** +@typedef {Exclude, undefined>} DirectiveComment +*/ + // https://github.com/eslint/eslint/blob/ecd0ede7fd2ccbb4c0daf0e4732e97ea0f49db1b/lib/linter/linter.js#L509-L512 const ESLINT_DISABLE_DIRECTIVES = new Set([ 'eslint-disable', @@ -13,15 +17,15 @@ let configCommentParser; Parse a directive comment value and return directive meta info. @param {ESTree.Comment} comment -@returns {{ - label: string, - value: string, - justification: string, - isEslintDisableDirective: boolean, - isEslintEnableDirective: boolean, -}|undefined} +@returns { + | DirectiveComment & { + isEslintDisableDirective: boolean, + isEslintEnableDirective: boolean, + } + | undefined +} */ -export default function parseDirective(comment) { +export default function parseDirectiveComment(comment) { configCommentParser ??= new ConfigCommentParser(); const result = configCommentParser.parseDirective(comment.value); @@ -31,12 +35,10 @@ export default function parseDirective(comment) { } const {label} = result; - const isEslintDisableDirective = ESLINT_DISABLE_DIRECTIVES.has(label); - const isEslintEnableDirective = label === 'eslint-enable'; return { ...result, - isEslintDisableDirective, - isEslintEnableDirective, + isEslintDisableDirective: ESLINT_DISABLE_DIRECTIVES.has(label), + isEslintEnableDirective: label === 'eslint-enable', }; } From 72472abda96c5291ffe520544ac2b78a51fb724c Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 23 Nov 2025 23:15:48 +0800 Subject: [PATCH 07/14] Update reference --- rules/utils/parse-directive-comment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/utils/parse-directive-comment.js b/rules/utils/parse-directive-comment.js index 7e0ff8d346..2187b1ee0f 100644 --- a/rules/utils/parse-directive-comment.js +++ b/rules/utils/parse-directive-comment.js @@ -4,7 +4,7 @@ import {ConfigCommentParser} from '@eslint/plugin-kit'; @typedef {Exclude, undefined>} DirectiveComment */ -// https://github.com/eslint/eslint/blob/ecd0ede7fd2ccbb4c0daf0e4732e97ea0f49db1b/lib/linter/linter.js#L509-L512 +// https://github.com/eslint/eslint/blob/df5566f826d9f5740546e473aa6876b1f7d2f12c/lib/languages/js/source-code/source-code.js#L914-L917 const ESLINT_DISABLE_DIRECTIVES = new Set([ 'eslint-disable', 'eslint-disable-line', From 074ea48c4930a9dd33930d3ad12843e20571a595 Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 23 Nov 2025 23:43:56 +0800 Subject: [PATCH 08/14] Use `sourceCode.getDisableDirectives()` --- package.json | 1 - rules/expiring-todo-comments.js | 9 ++---- rules/no-abusive-eslint-disable.js | 32 +++++++++++-------- rules/utils/eslint-directive.js | 8 +++++ rules/utils/index.js | 6 +++- rules/utils/parse-directive-comment.js | 44 -------------------------- 6 files changed, 33 insertions(+), 67 deletions(-) create mode 100644 rules/utils/eslint-directive.js delete mode 100644 rules/utils/parse-directive-comment.js diff --git a/package.json b/package.json index d7e94dd73b..c054526dea 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,6 @@ "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "@eslint-community/eslint-utils": "^4.9.0", - "@eslint/plugin-kit": "^0.4.0", "change-case": "^5.4.4", "ci-info": "^4.3.1", "clean-regexp": "^1.0.0", diff --git a/rules/expiring-todo-comments.js b/rules/expiring-todo-comments.js index 127329af00..5f903242fc 100644 --- a/rules/expiring-todo-comments.js +++ b/rules/expiring-todo-comments.js @@ -3,7 +3,7 @@ import {isRegExp} from 'node:util/types'; import semver from 'semver'; import * as ci from 'ci-info'; import { - parseDirectiveComment, + isEslintDisableOrEnableDirective, getBuiltinRule, } from './utils/index.js'; import {readPackageJson} from './shared/package-json.js'; @@ -288,7 +288,7 @@ const create = context => { const {sourceCode} = context; const comments = sourceCode.getAllComments(); const unusedComments = comments - .filter(token => token.type !== 'Shebang') + .filter(comment => comment.type !== 'Shebang' && !isEslintDisableOrEnableDirective(context, comment)) // Block comments come as one. // Split for situations like this: // /* @@ -322,11 +322,6 @@ const create = context => { // eslint-disable-next-line complexity function processComment(comment) { - const directive = parseDirectiveComment(comment); - if (directive?.isEslintDisableDirective || directive?.isEslintEnableDirective) { - return; - } - if (ignoreRegexes.some(ignore => ignore.test(comment.value))) { return; } diff --git a/rules/no-abusive-eslint-disable.js b/rules/no-abusive-eslint-disable.js index 2ff0552eed..9bdd0b6418 100644 --- a/rules/no-abusive-eslint-disable.js +++ b/rules/no-abusive-eslint-disable.js @@ -1,28 +1,32 @@ -import { - parseDirectiveComment, -} from './utils/index.js'; - const MESSAGE_ID = 'no-abusive-eslint-disable'; const messages = { [MESSAGE_ID]: 'Specify the rules you want to disable.', }; +// https://github.com/eslint/eslint/blob/df5566f826d9f5740546e473aa6876b1f7d2f12c/lib/languages/js/source-code/source-code.js#L914-L917 +const ESLINT_DISABLE_DIRECTIVE_TYPES = new Set([ + 'disable', + 'disable-next-line', + 'disable-line', +]); + /** @param {import('eslint').Rule.RuleContext} context */ const create = context => { - context.on('Program', function * (node) { - for (const comment of node.comments) { - const directive = parseDirectiveComment(comment); - - if (!( + context.on('Program', function * () { + const {sourceCode} = context; + const {directives} = sourceCode.getDisableDirectives(); + for (const directive of directives) { + if ( + !( // It's a eslint-disable comment - directive?.isEslintDisableDirective - // But it did not specify any rules - && !directive?.value - )) { + ESLINT_DISABLE_DIRECTIVE_TYPES.has(directive.type) + // But it did not specify any rules + && !directive.value + )) { return; } - const {sourceCode} = context; + const comment = directive.node; yield { // Can't set it at the given location as the warning diff --git a/rules/utils/eslint-directive.js b/rules/utils/eslint-directive.js new file mode 100644 index 0000000000..05ef49bb3f --- /dev/null +++ b/rules/utils/eslint-directive.js @@ -0,0 +1,8 @@ +function isEslintDisableOrEnableDirective(context, comment) { + const {directives} = context.sourceCode.getDisableDirectives(); + return directives.some(directive => directive.node === comment); +} + +export { + isEslintDisableOrEnableDirective, +}; diff --git a/rules/utils/index.js b/rules/utils/index.js index c9f23f166c..8ec38bd192 100644 --- a/rules/utils/index.js +++ b/rules/utils/index.js @@ -49,7 +49,10 @@ export {default as isShorthandImportLocal} from './is-shorthand-import-local.js' export {default as isShorthandPropertyValue} from './is-shorthand-property-value.js'; export {default as isValueNotUsable} from './is-value-not-usable.js'; export {default as needsSemicolon} from './needs-semicolon.js'; -export {default as parseDirectiveComment} from './parse-directive-comment.js'; +export { + getEslintDisableDirective, + isEslintDisableOrEnableDirective, +} from './eslint-directive.js'; export {checkVueTemplate} from './rule.js'; export {default as shouldAddParenthesesToAwaitExpressionArgument} from './should-add-parentheses-to-await-expression-argument.js'; export {default as shouldAddParenthesesToCallExpressionCallee} from './should-add-parentheses-to-call-expression-callee.js'; @@ -65,3 +68,4 @@ export {default as getAncestor} from './get-ancestor.js'; export {getPreviousNode, getNextNode} from './get-sibling-node.js'; export * from './string-cases.js'; export * from './numeric.js'; +export {default as getBuiltinRule} from './get-builtin-rule.js'; diff --git a/rules/utils/parse-directive-comment.js b/rules/utils/parse-directive-comment.js deleted file mode 100644 index 2187b1ee0f..0000000000 --- a/rules/utils/parse-directive-comment.js +++ /dev/null @@ -1,44 +0,0 @@ -import {ConfigCommentParser} from '@eslint/plugin-kit'; - -/** -@typedef {Exclude, undefined>} DirectiveComment -*/ - -// https://github.com/eslint/eslint/blob/df5566f826d9f5740546e473aa6876b1f7d2f12c/lib/languages/js/source-code/source-code.js#L914-L917 -const ESLINT_DISABLE_DIRECTIVES = new Set([ - 'eslint-disable', - 'eslint-disable-line', - 'eslint-disable-next-line', -]); - -let configCommentParser; - -/** -Parse a directive comment value and return directive meta info. - -@param {ESTree.Comment} comment -@returns { - | DirectiveComment & { - isEslintDisableDirective: boolean, - isEslintEnableDirective: boolean, - } - | undefined -} -*/ -export default function parseDirectiveComment(comment) { - configCommentParser ??= new ConfigCommentParser(); - - const result = configCommentParser.parseDirective(comment.value); - - if (!result) { - return; - } - - const {label} = result; - - return { - ...result, - isEslintDisableDirective: ESLINT_DISABLE_DIRECTIVES.has(label), - isEslintEnableDirective: label === 'eslint-enable', - }; -} From 38ac0f535a88d38126e0af35a129743ebe306fd7 Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 23 Nov 2025 23:44:50 +0800 Subject: [PATCH 09/14] Fix --- rules/utils/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/rules/utils/index.js b/rules/utils/index.js index 8ec38bd192..7287200f7c 100644 --- a/rules/utils/index.js +++ b/rules/utils/index.js @@ -50,7 +50,6 @@ export {default as isShorthandPropertyValue} from './is-shorthand-property-value export {default as isValueNotUsable} from './is-value-not-usable.js'; export {default as needsSemicolon} from './needs-semicolon.js'; export { - getEslintDisableDirective, isEslintDisableOrEnableDirective, } from './eslint-directive.js'; export {checkVueTemplate} from './rule.js'; From d62bd28a1bc26484ba415865c21cf62f8a168cf3 Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 23 Nov 2025 23:49:18 +0800 Subject: [PATCH 10/14] Refactor --- rules/no-abusive-eslint-disable.js | 24 +++++++----------------- rules/utils/eslint-directive.js | 13 +++++++++++++ rules/utils/index.js | 1 + 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/rules/no-abusive-eslint-disable.js b/rules/no-abusive-eslint-disable.js index 9bdd0b6418..17f83623e2 100644 --- a/rules/no-abusive-eslint-disable.js +++ b/rules/no-abusive-eslint-disable.js @@ -1,32 +1,22 @@ +import { + getEslintDisableDirectives, +} from './utils/index.js'; + const MESSAGE_ID = 'no-abusive-eslint-disable'; const messages = { [MESSAGE_ID]: 'Specify the rules you want to disable.', }; -// https://github.com/eslint/eslint/blob/df5566f826d9f5740546e473aa6876b1f7d2f12c/lib/languages/js/source-code/source-code.js#L914-L917 -const ESLINT_DISABLE_DIRECTIVE_TYPES = new Set([ - 'disable', - 'disable-next-line', - 'disable-line', -]); - /** @param {import('eslint').Rule.RuleContext} context */ const create = context => { context.on('Program', function * () { - const {sourceCode} = context; - const {directives} = sourceCode.getDisableDirectives(); - for (const directive of directives) { - if ( - !( - // It's a eslint-disable comment - ESLINT_DISABLE_DIRECTIVE_TYPES.has(directive.type) - // But it did not specify any rules - && !directive.value - )) { + for (const directive of getEslintDisableDirectives(context)) { + if (directive.value) { return; } const comment = directive.node; + const {sourceCode} = context; yield { // Can't set it at the given location as the warning diff --git a/rules/utils/eslint-directive.js b/rules/utils/eslint-directive.js index 05ef49bb3f..402352ba99 100644 --- a/rules/utils/eslint-directive.js +++ b/rules/utils/eslint-directive.js @@ -1,8 +1,21 @@ +// https://github.com/eslint/eslint/blob/df5566f826d9f5740546e473aa6876b1f7d2f12c/lib/languages/js/source-code/source-code.js#L914-L917 +const ESLINT_DISABLE_DIRECTIVE_TYPES = new Set([ + 'disable', + 'disable-next-line', + 'disable-line', +]); + +function getEslintDisableDirectives(context) { + const {directives} = context.sourceCode.getDisableDirectives(); + return directives.filters(({type}) => ESLINT_DISABLE_DIRECTIVE_TYPES.has(type)); +} + function isEslintDisableOrEnableDirective(context, comment) { const {directives} = context.sourceCode.getDisableDirectives(); return directives.some(directive => directive.node === comment); } export { + getEslintDisableDirectives, isEslintDisableOrEnableDirective, }; diff --git a/rules/utils/index.js b/rules/utils/index.js index 7287200f7c..7792657351 100644 --- a/rules/utils/index.js +++ b/rules/utils/index.js @@ -50,6 +50,7 @@ export {default as isShorthandPropertyValue} from './is-shorthand-property-value export {default as isValueNotUsable} from './is-value-not-usable.js'; export {default as needsSemicolon} from './needs-semicolon.js'; export { + getEslintDisableDirectives, isEslintDisableOrEnableDirective, } from './eslint-directive.js'; export {checkVueTemplate} from './rule.js'; From 752fd39996e43faa1d61b67d9a376e74caa4761d Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 23 Nov 2025 23:53:55 +0800 Subject: [PATCH 11/14] Typo --- rules/utils/eslint-directive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/utils/eslint-directive.js b/rules/utils/eslint-directive.js index 402352ba99..29e81364d4 100644 --- a/rules/utils/eslint-directive.js +++ b/rules/utils/eslint-directive.js @@ -7,7 +7,7 @@ const ESLINT_DISABLE_DIRECTIVE_TYPES = new Set([ function getEslintDisableDirectives(context) { const {directives} = context.sourceCode.getDisableDirectives(); - return directives.filters(({type}) => ESLINT_DISABLE_DIRECTIVE_TYPES.has(type)); + return directives.filter(({type}) => ESLINT_DISABLE_DIRECTIVE_TYPES.has(type)); } function isEslintDisableOrEnableDirective(context, comment) { From af8688e69a70d52aa0ba03b24c21f90dd51e3663 Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 23 Nov 2025 23:58:12 +0800 Subject: [PATCH 12/14] `getLoc` once! --- rules/no-abusive-eslint-disable.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rules/no-abusive-eslint-disable.js b/rules/no-abusive-eslint-disable.js index 17f83623e2..107902526c 100644 --- a/rules/no-abusive-eslint-disable.js +++ b/rules/no-abusive-eslint-disable.js @@ -15,18 +15,17 @@ const create = context => { return; } - const comment = directive.node; - const {sourceCode} = context; + const {start, end} = context.sourceCode.getLoc(directive.node); yield { // Can't set it at the given location as the warning // will be ignored due to the disable comment loc: { start: { - ...sourceCode.getLoc(comment).start, + ...start, column: -1, }, - end: sourceCode.getLoc(comment).end, + end, }, messageId: MESSAGE_ID, }; From 632b0def996452d9f97ceb30e073e65cf666aebe Mon Sep 17 00:00:00 2001 From: fisker Date: Mon, 24 Nov 2025 00:00:38 +0800 Subject: [PATCH 13/14] Fix BUG --- rules/no-abusive-eslint-disable.js | 2 +- test/no-abusive-eslint-disable.js | 5 +++++ .../snapshots/no-abusive-eslint-disable.js.md | 20 ++++++++++++++++++ .../no-abusive-eslint-disable.js.snap | Bin 509 -> 573 bytes 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/rules/no-abusive-eslint-disable.js b/rules/no-abusive-eslint-disable.js index 107902526c..b3bf860e98 100644 --- a/rules/no-abusive-eslint-disable.js +++ b/rules/no-abusive-eslint-disable.js @@ -12,7 +12,7 @@ const create = context => { context.on('Program', function * () { for (const directive of getEslintDisableDirectives(context)) { if (directive.value) { - return; + continue; } const {start, end} = context.sourceCode.getLoc(directive.node); diff --git a/test/no-abusive-eslint-disable.js b/test/no-abusive-eslint-disable.js index aba5ace11f..4ee5297572 100644 --- a/test/no-abusive-eslint-disable.js +++ b/test/no-abusive-eslint-disable.js @@ -96,5 +96,10 @@ test.snapshot({ // eslint-disable-next-line -- reason eval(); `, + outdent` + // eslint-disable-next-line no-eval + eval(); + // eslint-disable-next-line + `, ], }); diff --git a/test/snapshots/no-abusive-eslint-disable.js.md b/test/snapshots/no-abusive-eslint-disable.js.md index 830a7f6f2b..6e507f57a7 100644 --- a/test/snapshots/no-abusive-eslint-disable.js.md +++ b/test/snapshots/no-abusive-eslint-disable.js.md @@ -129,3 +129,23 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Specify the rules you want to disable.␊ 2 | eval();␊ ` + +## invalid(8): // eslint-disable-next-line no-eval eval(); // eslint-disable-next-line + +> Input + + `␊ + 1 | // eslint-disable-next-line no-eval␊ + 2 | eval();␊ + 3 | // eslint-disable-next-line␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + 1 | // eslint-disable-next-line no-eval␊ + 2 | eval();␊ + > 3 | // eslint-disable-next-line␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Specify the rules you want to disable.␊ + ` diff --git a/test/snapshots/no-abusive-eslint-disable.js.snap b/test/snapshots/no-abusive-eslint-disable.js.snap index d8238e7fb6f61073750909826cb86b85356f8b0a..473454efd379cf9d01dd442f082c034674987862 100644 GIT binary patch literal 573 zcmV-D0>b@4RzVOmG^S75`~^ge|W?MxAa zF&7XBbRUZd00000000BM)iF=oKokdX(+XAb)}7%^h(t1$6DX~bQdKGgLl?4Cs)TeQ zm*&LjF7jPckTTH@zz7RcC$b9WlE5t8c3~9roA4~2}2N4>ygg9_Lu8!?Qk&GblgTyIFoSO9z`cls7smk>Uwb_cf=3y>C^PlXl z9*4djcoAcH!hD&Qt=%dB^gM_tQBo|X+(nez#gu7iG3pIMy{dwWjw!RH&jH;@ zpx+lj3+c_WEdPuTzs-+;p%Hv5F@pHGOc(R}XIoUmi0;sc{^Y(V)yrrXmhp7=B|Er# LN+ed*nF;^^JSP>Y literal 509 zcmV!H|bJ4?@mFKf;WmbL{BNJb?fJ6F5^pa5BJM(z^oQ#}EuDW^m;X;SzZq zxDR3Ab~qhr1%V%cZP}5N9XVAD2YS9gpJ!RF17Xc-$n_bf1600cH~kvAa<9dBLVPn% z{J%&-Bv~~au*wUAK-o&dl?AxXa5LGvGNF<1c7L&5(inxrPxrT?{cCZPWHUq`E6LFk z7OKm7l(JMc?to0{QRvc*x=VlMCYv&;xECqzC=^W(kNHQnm7d3f=b_BAsLl?DE*2HI zZvyVK8aMmA95;1I^2O;D)!m(6-NZj6o-mbqU!b<5o17jc{ugDgPlD^Mmbl-_@GQps zgvl`#t*vJOnkEDoG2ct4Yzvf)Y|1z^8}(H{oi2ka9FwP|zX|;gn~kmf;0OQ!Ct~OV From ab49dc12702073c4ccf9dde9ec7d8ea3efd73fd2 Mon Sep 17 00:00:00 2001 From: fisker Date: Mon, 24 Nov 2025 00:03:40 +0800 Subject: [PATCH 14/14] Update test --- test/no-abusive-eslint-disable.js | 1 + test/snapshots/no-abusive-eslint-disable.js.md | 4 +++- .../snapshots/no-abusive-eslint-disable.js.snap | Bin 573 -> 575 bytes 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/test/no-abusive-eslint-disable.js b/test/no-abusive-eslint-disable.js index 4ee5297572..e5a8e1fc9c 100644 --- a/test/no-abusive-eslint-disable.js +++ b/test/no-abusive-eslint-disable.js @@ -100,6 +100,7 @@ test.snapshot({ // eslint-disable-next-line no-eval eval(); // eslint-disable-next-line + eval(); `, ], }); diff --git a/test/snapshots/no-abusive-eslint-disable.js.md b/test/snapshots/no-abusive-eslint-disable.js.md index 6e507f57a7..6805e488ac 100644 --- a/test/snapshots/no-abusive-eslint-disable.js.md +++ b/test/snapshots/no-abusive-eslint-disable.js.md @@ -130,7 +130,7 @@ Generated by [AVA](https://avajs.dev). 2 | eval();␊ ` -## invalid(8): // eslint-disable-next-line no-eval eval(); // eslint-disable-next-line +## invalid(8): // eslint-disable-next-line no-eval eval(); // eslint-disable-next-line eval(); > Input @@ -138,6 +138,7 @@ Generated by [AVA](https://avajs.dev). 1 | // eslint-disable-next-line no-eval␊ 2 | eval();␊ 3 | // eslint-disable-next-line␊ + 4 | eval();␊ ` > Error 1/1 @@ -148,4 +149,5 @@ Generated by [AVA](https://avajs.dev). 2 | eval();␊ > 3 | // eslint-disable-next-line␊ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Specify the rules you want to disable.␊ + 4 | eval();␊ ` diff --git a/test/snapshots/no-abusive-eslint-disable.js.snap b/test/snapshots/no-abusive-eslint-disable.js.snap index 473454efd379cf9d01dd442f082c034674987862..20e0bb1397dc76b38631dad293a86560b2466050 100644 GIT binary patch literal 575 zcmV-F0>J%2RzVj2dxv+bNF-yKq@_)yRFz<0=mHB8Ql(3B zAtz3Ek?-3z}jW1^h8ob|g&+~hq_x!ihoPy&05hKnH|xmg?a9Kow^~du^sjlT(i~L75Vyr@B(BU$^Zx(d4MQ=Dc^N zUt!(ZD13AQ0+?i|xRjG7<#b>4`a06NLOK)o2UUuk(3;!GBM1O6fuHsd4tuo4I%CjX z3Spm91!LKVArTt1gh+5Ja>w?3GR- Np8z&)L2|tc000`H6TScd literal 573 zcmV-D0>b@4RzVOmG^S75`~^ge|W?MxAa zF&7XBbRUZd00000000BM)iF=oKokdX(+XAb)}7%^h(t1$6DX~bQdKGgLl?4Cs)TeQ zm*&LjF7jPckTTH@zz7RcC$b9WlE5t8c3~9roA4~2}2N4>ygg9_Lu8!?Qk&GblgTyIFoSO9z`cls7smk>Uwb_cf=3y>C^PlXl z9*4djcoAcH!hD&Qt=%dB^gM_tQBo|X+(nez#gu7iG3pIMy{dwWjw!RH&jH;@ zpx+lj3+c_WEdPuTzs-+;p%Hv5F@pHGOc(R}XIoUmi0;sc{^Y(V)yrrXmhp7=B|Er# LN+ed*nF;^^JSP>Y