diff --git a/lib/internal/disabled-area.js b/lib/internal/disabled-area.js index bad87a8..f7dca6a 100644 --- a/lib/internal/disabled-area.js +++ b/lib/internal/disabled-area.js @@ -8,25 +8,7 @@ const utils = require("./utils") const DELIMITER = /[\s,]+/gu const pool = new WeakMap() -module.exports = class DisabledArea { - /** - * Get singleton instance for the given source code. - * - * @param {eslint.SourceCode} sourceCode - The source code to get. - * @returns {DisabledArea} The singleton object for the source code. - */ - static get(sourceCode) { - let retv = pool.get(sourceCode.ast) - - if (retv == null) { - retv = new DisabledArea() - retv._scan(sourceCode) - pool.set(sourceCode.ast, retv) - } - - return retv - } - +class DisabledArea { /** * Constructor. */ @@ -45,7 +27,7 @@ module.exports = class DisabledArea { * @param {string[]|null} ruleIds - The ruleId names to disable. * @param {string} kind - The kind of disable-comments. * @returns {void} - * @private + * @protected */ _disable(comment, location, ruleIds, kind) { if (ruleIds) { @@ -85,7 +67,7 @@ module.exports = class DisabledArea { * @param {string[]|null} ruleIds - The ruleId names to enable. * @param {string} kind - The kind of disable-comments. * @returns {void} - * @private + * @protected */ _enable(comment, location, ruleIds, kind) { const relatedDisableDirectives = new Set() @@ -159,13 +141,62 @@ module.exports = class DisabledArea { return null } +} +class DisabledAreaForLanguagePlugin extends DisabledArea { + /** + * Scan the source code and setup disabled area list. + * + * @param {import('@eslint/core').TextSourceCode} sourceCode - The source code to scan. + * @returns {void} + */ + _scan(sourceCode) { + const disableDirectives = sourceCode.getDisableDirectives() + for (const directive of disableDirectives.directives) { + if ( + ![ + "disable", + "enable", + "disable-line", + "disable-next-line", + ].includes(directive.type) + ) { + continue + } + const ruleIds = directive.value + ? directive.value.split(DELIMITER) + : null + + const loc = sourceCode.getLoc(directive.node) + if (directive.type === "disable") { + this._disable(directive.node, loc.start, ruleIds, "block") + } else if (directive.type === "enable") { + this._enable(directive.node, loc.start, ruleIds, "block") + } else if (directive.type === "disable-line") { + const line = loc.start.line + const start = { line, column: 0 } + const end = { line: line + 1, column: -1 } + + this._disable(directive.node, start, ruleIds, "line") + this._enable(directive.node, end, ruleIds, "line") + } else if (directive.type === "disable-next-line") { + const line = loc.start.line + const start = { line: line + 1, column: 0 } + const end = { line: line + 2, column: -1 } + + this._disable(directive.node, start, ruleIds, "line") + this._enable(directive.node, end, ruleIds, "line") + } + } + } +} + +class DisabledAreaForLegacy extends DisabledArea { /** * Scan the source code and setup disabled area list. * * @param {eslint.SourceCode} sourceCode - The source code to scan. * @returns {void} - * @private */ _scan(sourceCode) { for (const comment of sourceCode.getAllComments()) { @@ -176,10 +207,12 @@ module.exports = class DisabledArea { const kind = directiveComment.kind if ( - kind !== "eslint-disable" && - kind !== "eslint-enable" && - kind !== "eslint-disable-line" && - kind !== "eslint-disable-next-line" + ![ + "eslint-disable", + "eslint-enable", + "eslint-disable-line", + "eslint-disable-next-line", + ].includes(kind) ) { continue } @@ -209,3 +242,27 @@ module.exports = class DisabledArea { } } } + +module.exports = { + /** + * Get singleton instance for the given rule context. + * + * @param {import("@eslint/core").RuleContext} context - The rule context code to get. + * @returns {DisabledArea} The singleton object for the rule context. + */ + getDisabledArea(context) { + const sourceCode = context.sourceCode || context.getSourceCode() + let retv = pool.get(sourceCode.ast) + + if (retv == null) { + retv = + typeof sourceCode.getDisableDirectives === "function" + ? new DisabledAreaForLanguagePlugin() + : new DisabledAreaForLegacy() + retv._scan(sourceCode) + pool.set(sourceCode.ast, retv) + } + + return retv + }, +} diff --git a/lib/internal/get-all-directive-comments.js b/lib/internal/get-all-directive-comments.js new file mode 100644 index 0000000..f471c0a --- /dev/null +++ b/lib/internal/get-all-directive-comments.js @@ -0,0 +1,154 @@ +"use strict" + +const utils = require("./utils") + +/** + * @typedef {object} DirectiveComment + * @property {string} kind The kind of directive comment. + * @property {string} [value] The directive value if it is `eslint-` comment. + * @property {string} description The description of the directive comment. + * @property {object} node The node of the directive comment. + * @property {import("@eslint/core").SourceRange} range The range of the directive comment. + * @property {import("@eslint/core").SourceLocation} loc The location of the directive comment. + */ + +const pool = new WeakMap() + +/** + * @param {import('eslint').SourceCode} sourceCode - The source code to scan. + * @returns {DirectiveComment[]} The directive comments. + */ +function getAllDirectiveCommentsFromAllComments(sourceCode) { + return sourceCode + .getAllComments() + .map((comment) => ({ + comment, + directiveComment: utils.parseDirectiveComment(comment), + })) + .filter(({ directiveComment }) => Boolean(directiveComment)) + .map( + ({ comment, directiveComment }) => + /** @type {DirectiveComment} */ ({ + kind: directiveComment.kind, + value: directiveComment.value, + description: directiveComment.description, + node: comment, + range: comment.range, + loc: comment.loc, + }) + ) +} + +/** + * @param {import('@eslint/core').TextSourceCode} sourceCode - The source code to scan. + * @returns {DirectiveComment[]} The directive comments. + */ +function getAllDirectiveCommentsFromInlineConfigNodes(sourceCode) { + const result = sourceCode.getDisableDirectives().directives.map( + (directive) => + /** @type {DirectiveComment} */ ({ + kind: `eslint-${directive.type}`, + value: directive.value, + description: directive.justification, + node: directive.node, + range: sourceCode.getRange(directive.node), + get loc() { + return sourceCode.getLoc(directive.node) + }, + }) + ) + + return result.concat( + sourceCode + .getInlineConfigNodes() + .map((node) => ({ + node, + range: sourceCode.getRange(node), + })) + .filter( + ({ range }) => + // The node has intersection of the directive comment. + // So, we need to skip it. + !result.some( + (comment) => + comment.range[0] <= range[1] && + range[0] <= comment.range[1] + ) + ) + .map(({ node, range }) => { + const nodeText = sourceCode.text.slice(range[0], range[1]) + const commentValue = extractCommentContent(nodeText) + const directiveComment = utils.parseDirectiveText(commentValue) + + return { + directiveComment, + node, + range, + } + }) + .filter( + ({ directiveComment }) => + directiveComment != null && + ![ + "eslint-disable", + "eslint-disable-line", + "eslint-disable-next-line", + "eslint-enable", + ].includes(directiveComment.kind) + ) + .map( + ({ directiveComment, node, range }) => + /** @type {DirectiveComment} */ ({ + kind: directiveComment.kind, + value: directiveComment.value, + description: directiveComment.description, + node, + range, + get loc() { + return sourceCode.getLoc(node) + }, + }) + ) + ) +} + +function extractCommentContent(text) { + // Extract comment content from the comment text. + // The comment format was based on the language comment definition in vscode-eslint. + // See https://github.com/microsoft/vscode-eslint/blob/c0e753713ea9935667e849d91e549adbff213e7e/server/src/languageDefaults.ts#L14 + return text.startsWith("/*") && text.endsWith("*/") + ? text.slice(2, -2) + : text.startsWith("//") + ? text.slice(2) + : text.startsWith("") + ? text.slice(4, -3) + : text.startsWith("###") && text.endsWith("###") + ? text.slice(3, -3) + : text.startsWith("#") + ? text.slice(1) + : text +} + +module.exports = { + /** + * Get all directive comments for the given rule context. + * + * @param {import("@eslint/core").RuleContext} context - The rule context to get. + * @returns {DirectiveComment[]} The all directive comments object for the rule context. + */ + getAllDirectiveComments(context) { + const sourceCode = context.sourceCode || context.getSourceCode() + let result = pool.get(sourceCode.ast) + + if (result == null) { + result = + typeof sourceCode.getInlineConfigNodes === "function" && + typeof sourceCode.getDisableDirectives === "function" + ? getAllDirectiveCommentsFromInlineConfigNodes(sourceCode) + : getAllDirectiveCommentsFromAllComments(sourceCode) + pool.set(sourceCode.ast, result) + } + + return result + }, +} diff --git a/lib/internal/utils.js b/lib/internal/utils.js index 931d483..bf88709 100644 --- a/lib/internal/utils.js +++ b/lib/internal/utils.js @@ -31,13 +31,15 @@ module.exports = { /** * Calculate the location of the given rule in the given comment token. * + * @param {Partial} context - The rule context code. * @param {Token} comment - The comment token to calculate. * @param {string|null} ruleId - The rule name to calculate. * @returns {object} The location of the given information. */ - toRuleIdLocation(comment, ruleId) { + toRuleIdLocation(context, comment, ruleId) { + const commentLoc = getLoc(context, comment) if (ruleId == null) { - return module.exports.toForceLocation(comment.loc) + return module.exports.toForceLocation(commentLoc) } const lines = comment.value.match(LINE_PATTERN) @@ -49,7 +51,7 @@ module.exports = { { const m = ruleIdPattern.exec(lines[0]) if (m != null) { - const start = comment.loc.start + const start = commentLoc.start return { start: { line: start.line, @@ -71,7 +73,7 @@ module.exports = { for (let i = 1; i < lines.length; ++i) { const m = ruleIdPattern.exec(lines[i]) if (m != null) { - const start = comment.loc.start + const start = commentLoc.start return { start: { line: start.line + i, @@ -86,7 +88,7 @@ module.exports = { } /*istanbul ignore next : foolproof */ - return comment.loc + return commentLoc }, /** @@ -107,35 +109,54 @@ module.exports = { * @returns {{kind: string, value: string, description: string | null}|null} The parsed data of the given comment. If `null`, it is not a directive comment. */ parseDirectiveComment(comment) { - const { text, description } = divideDirectiveComment(comment.value) - const match = DIRECTIVE_PATTERN.exec(text) - - if (!match) { + const parsed = parseDirectiveText(comment.value) + if (!parsed) { return null } - const directiveText = match[1] - const lineCommentSupported = LINE_COMMENT_PATTERN.test(directiveText) + + const lineCommentSupported = LINE_COMMENT_PATTERN.test(parsed.kind) if (comment.type === "Line" && !lineCommentSupported) { return null } if ( - lineCommentSupported && + parsed.kind === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line ) { // disable-line comment should not span multiple lines. return null } - const directiveValue = text.slice(match.index + directiveText.length) - - return { - kind: directiveText, - value: directiveValue.trim(), - description, - } + return parsed }, + parseDirectiveText, + + getLoc, +} + +/** + * Parse the given text as a directive comment. + * + * @param {string} textToParse - The text to parse. + * @returns {{kind: string, value: string, description: string | null}|null} The parsed data of the given comment. If `null`, it is not a directive comment. + */ +function parseDirectiveText(textToParse) { + const { text, description } = divideDirectiveComment(textToParse) + const match = DIRECTIVE_PATTERN.exec(text) + + if (!match) { + return null + } + const directiveText = match[1] + + const directiveValue = text.slice(match.index + directiveText.length) + + return { + kind: directiveText, + value: directiveValue.trim(), + description, + } } /** @@ -151,3 +172,18 @@ function divideDirectiveComment(value) { description: divided.length > 1 ? divided[1].trim() : null, } } + +/** + * Get source code location from the given node. + * + * @param {Partial} context - The rule context code. + * @param {unknown} nodeOrToken - The node or token to get. + * @returns {object} The source code location. + */ +function getLoc(context, nodeOrToken) { + const sourceCode = + context.sourceCode || (context.getSourceCode && context.getSourceCode()) + return sourceCode && typeof sourceCode.getLoc === "function" + ? sourceCode.getLoc(nodeOrToken) + : nodeOrToken.loc +} diff --git a/lib/rules/disable-enable-pair.js b/lib/rules/disable-enable-pair.js index bb28be2..ce0330e 100644 --- a/lib/rules/disable-enable-pair.js +++ b/lib/rules/disable-enable-pair.js @@ -4,7 +4,7 @@ */ "use strict" -const DisabledArea = require("../internal/disabled-area") +const { getDisabledArea } = require("../internal/disabled-area") const utils = require("../internal/utils") module.exports = { @@ -39,35 +39,35 @@ module.exports = { create(context) { const allowWholeFile = context.options[0] && context.options[0].allowWholeFile - const sourceCode = context.getSourceCode() - const disabledArea = DisabledArea.get(sourceCode) + const disabledArea = getDisabledArea(context) - return { - Program(node) { - if (allowWholeFile && node.body.length === 0) { - return - } + /** @type {import('@eslint/core').TextSourceCode} */ + const sourceCode = context.sourceCode || context.getSourceCode() - for (const area of disabledArea.areas) { - if (area.end != null) { - continue - } - if ( - allowWholeFile && - utils.lte(area.start, node.loc.start) - ) { - continue - } + const firstToken = + sourceCode.ast && sourceCode.ast.tokens && sourceCode.ast.tokens[0] - context.report({ - loc: utils.toRuleIdLocation(area.comment, area.ruleId), - messageId: area.ruleId - ? "missingRulePair" - : "missingPair", - data: area, - }) - } - }, + if (allowWholeFile && !firstToken) { + return {} + } + + for (const area of disabledArea.areas) { + if (area.end != null) { + continue + } + if ( + allowWholeFile && + utils.lte(area.start, utils.getLoc(context, firstToken).start) + ) { + continue + } + + context.report({ + loc: utils.toRuleIdLocation(context, area.comment, area.ruleId), + messageId: area.ruleId ? "missingRulePair" : "missingPair", + data: area, + }) } + return {} }, } diff --git a/lib/rules/no-aggregating-enable.js b/lib/rules/no-aggregating-enable.js index 6f34616..d7c1f94 100644 --- a/lib/rules/no-aggregating-enable.js +++ b/lib/rules/no-aggregating-enable.js @@ -4,7 +4,7 @@ */ "use strict" -const DisabledArea = require("../internal/disabled-area") +const { getDisabledArea } = require("../internal/disabled-area") const utils = require("../internal/utils") module.exports = { @@ -26,24 +26,20 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode() - const disabledArea = DisabledArea.get(sourceCode) + const disabledArea = getDisabledArea(context) - return { - Program() { - for (const entry of disabledArea.numberOfRelatedDisableDirectives) { - const comment = entry[0] - const count = entry[1] + for (const entry of disabledArea.numberOfRelatedDisableDirectives) { + const comment = entry[0] + const count = entry[1] - if (count >= 2) { - context.report({ - loc: utils.toForceLocation(comment.loc), - messageId: "aggregatingEnable", - data: { count }, - }) - } - } - }, + if (count >= 2) { + context.report({ + loc: utils.toForceLocation(utils.getLoc(context, comment)), + messageId: "aggregatingEnable", + data: { count }, + }) + } } + return {} }, } diff --git a/lib/rules/no-duplicate-disable.js b/lib/rules/no-duplicate-disable.js index f5e7846..78a7aaa 100644 --- a/lib/rules/no-duplicate-disable.js +++ b/lib/rules/no-duplicate-disable.js @@ -4,7 +4,7 @@ */ "use strict" -const DisabledArea = require("../internal/disabled-area") +const { getDisabledArea } = require("../internal/disabled-area") const utils = require("../internal/utils") module.exports = { @@ -25,19 +25,15 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode() - const disabledArea = DisabledArea.get(sourceCode) + const disabledArea = getDisabledArea(context) - return { - Program() { - for (const item of disabledArea.duplicateDisableDirectives) { - context.report({ - loc: utils.toRuleIdLocation(item.comment, item.ruleId), - messageId: item.ruleId ? "duplicateRule" : "duplicate", - data: item, - }) - } - }, + for (const item of disabledArea.duplicateDisableDirectives) { + context.report({ + loc: utils.toRuleIdLocation(context, item.comment, item.ruleId), + messageId: item.ruleId ? "duplicateRule" : "duplicate", + data: item, + }) } + return {} }, } diff --git a/lib/rules/no-restricted-disable.js b/lib/rules/no-restricted-disable.js index a620d50..b96ab9d 100644 --- a/lib/rules/no-restricted-disable.js +++ b/lib/rules/no-restricted-disable.js @@ -5,7 +5,7 @@ "use strict" const ignore = require("ignore") -const DisabledArea = require("../internal/disabled-area") +const { getDisabledArea } = require("../internal/disabled-area") const utils = require("../internal/utils") module.exports = { @@ -30,8 +30,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode() - const disabledArea = DisabledArea.get(sourceCode) + const disabledArea = getDisabledArea(context) if (context.options.length === 0) { return {} @@ -42,23 +41,21 @@ module.exports = { ig.add(pattern) } - return { - Program() { - for (const area of disabledArea.areas) { - if (area.ruleId == null || ig.ignores(area.ruleId)) { - context.report({ - loc: utils.toRuleIdLocation( - area.comment, - area.ruleId - ), - messageId: "disallow", - data: { - ruleId: area.ruleId || String(context.options), - }, - }) - } - } - }, + for (const area of disabledArea.areas) { + if (area.ruleId == null || ig.ignores(area.ruleId)) { + context.report({ + loc: utils.toRuleIdLocation( + context, + area.comment, + area.ruleId + ), + messageId: "disallow", + data: { + ruleId: area.ruleId || String(context.options), + }, + }) + } } + return {} }, } diff --git a/lib/rules/no-unlimited-disable.js b/lib/rules/no-unlimited-disable.js index 942c6d3..87ae0f0 100644 --- a/lib/rules/no-unlimited-disable.js +++ b/lib/rules/no-unlimited-disable.js @@ -4,6 +4,9 @@ */ "use strict" +const { + getAllDirectiveComments, +} = require("../internal/get-all-directive-comments") const utils = require("../internal/utils") module.exports = { @@ -25,34 +28,23 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode() - - return { - Program() { - for (const comment of sourceCode.getAllComments()) { - const directiveComment = - utils.parseDirectiveComment(comment) - if (directiveComment == null) { - continue - } - - const kind = directiveComment.kind - if ( - kind !== "eslint-disable" && - kind !== "eslint-disable-line" && - kind !== "eslint-disable-next-line" - ) { - continue - } - if (!directiveComment.value) { - context.report({ - loc: utils.toForceLocation(comment.loc), - messageId: "unexpected", - data: { kind: directiveComment.kind }, - }) - } - } - }, + for (const directiveComment of getAllDirectiveComments(context)) { + const kind = directiveComment.kind + if ( + kind !== "eslint-disable" && + kind !== "eslint-disable-line" && + kind !== "eslint-disable-next-line" + ) { + continue + } + if (!directiveComment.value) { + context.report({ + loc: utils.toForceLocation(directiveComment.loc), + messageId: "unexpected", + data: { kind: directiveComment.kind }, + }) + } } + return {} }, } diff --git a/lib/rules/no-unused-enable.js b/lib/rules/no-unused-enable.js index 39feec1..c4e4fba 100644 --- a/lib/rules/no-unused-enable.js +++ b/lib/rules/no-unused-enable.js @@ -4,7 +4,7 @@ */ "use strict" -const DisabledArea = require("../internal/disabled-area") +const { getDisabledArea } = require("../internal/disabled-area") const utils = require("../internal/utils") module.exports = { @@ -26,19 +26,15 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode() - const disabledArea = DisabledArea.get(sourceCode) + const disabledArea = getDisabledArea(context) - return { - Program() { - for (const item of disabledArea.unusedEnableDirectives) { - context.report({ - loc: utils.toRuleIdLocation(item.comment, item.ruleId), - messageId: item.ruleId ? "unusedRule" : "unused", - data: item, - }) - } - }, + for (const item of disabledArea.unusedEnableDirectives) { + context.report({ + loc: utils.toRuleIdLocation(context, item.comment, item.ruleId), + messageId: item.ruleId ? "unusedRule" : "unused", + data: item, + }) } + return {} }, } diff --git a/lib/rules/no-use.js b/lib/rules/no-use.js index 99f841f..5b1afc0 100644 --- a/lib/rules/no-use.js +++ b/lib/rules/no-use.js @@ -4,6 +4,9 @@ */ "use strict" +const { + getAllDirectiveComments, +} = require("../internal/get-all-directive-comments") const utils = require("../internal/utils") module.exports = { @@ -48,28 +51,18 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode() const allowed = new Set( (context.options[0] && context.options[0].allow) || [] ) - return { - Program() { - for (const comment of sourceCode.getAllComments()) { - const directiveComment = - utils.parseDirectiveComment(comment) - if (directiveComment == null) { - continue - } - - if (!allowed.has(directiveComment.kind)) { - context.report({ - loc: utils.toForceLocation(comment.loc), - messageId: "disallow", - }) - } - } - }, + for (const directiveComment of getAllDirectiveComments(context)) { + if (!allowed.has(directiveComment.kind)) { + context.report({ + loc: utils.toForceLocation(directiveComment.loc), + messageId: "disallow", + }) + } } + return {} }, } diff --git a/lib/rules/require-description.js b/lib/rules/require-description.js index b751aef..149840f 100644 --- a/lib/rules/require-description.js +++ b/lib/rules/require-description.js @@ -4,6 +4,9 @@ */ "use strict" +const { + getAllDirectiveComments, +} = require("../internal/get-all-directive-comments") const utils = require("../internal/utils") module.exports = { @@ -50,30 +53,21 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode() const ignores = new Set( (context.options[0] && context.options[0].ignore) || [] ) - return { - Program() { - for (const comment of sourceCode.getAllComments()) { - const directiveComment = - utils.parseDirectiveComment(comment) - if (directiveComment == null) { - continue - } - if (ignores.has(directiveComment.kind)) { - continue - } - if (!directiveComment.description) { - context.report({ - loc: utils.toForceLocation(comment.loc), - messageId: "missingDescription", - }) - } - } - }, + for (const directiveComment of getAllDirectiveComments(context)) { + if (ignores.has(directiveComment.kind)) { + continue + } + if (!directiveComment.description) { + context.report({ + loc: utils.toForceLocation(directiveComment.loc), + messageId: "missingDescription", + }) + } } + return {} }, } diff --git a/lib/utils/patch.js b/lib/utils/patch.js index 8acbff9..404b44c 100644 --- a/lib/utils/patch.js +++ b/lib/utils/patch.js @@ -90,7 +90,7 @@ function createNoUnusedDisableError(ruleId, severity, message, comment) { if (comment != null) { if (targetRuleId) { - const loc = toRuleIdLocation(comment, targetRuleId) + const loc = toRuleIdLocation({}, comment, targetRuleId) clone.line = loc.start.line clone.column = loc.start.column + 1 clone.endLine = loc.end.line diff --git a/package.json b/package.json index d9d91a6..e0e4d78 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,9 @@ "configs.js", "lib" ], - "exports": { - "./configs": "./configs.js", - ".": "./index.js" + "exports": { + "./configs": "./configs.js", + ".": "./index.js" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" @@ -26,6 +26,8 @@ "@babel/core": "^7.22.9", "@babel/eslint-parser": "^7.22.9", "@eslint-community/eslint-plugin-mysticatea": "^15.5.1", + "@eslint/core": "^0.13.0", + "@eslint/css": "^0.6.0", "@types/node": "^14.18.54", "@vuepress/plugin-pwa": "^1.9.9", "cross-spawn": "^7.0.3", @@ -49,7 +51,8 @@ "docs:build": "vitepress build docs", "docs:watch": "vitepress dev docs", "lint": "eslint lib scripts tests", - "test": "nyc mocha \"tests/lib/**/*.js\" --reporter dot --timeout 8000", + "test": "nyc npm run debug", + "debug": "mocha \"tests/lib/**/*.js\" --reporter dot --timeout 8000", "coverage": "nyc report --reporter lcov && opener coverage/lcov-report/index.html", "watch": "npm run -s test -- --watch --growl" }, diff --git a/tests/lib/illegal-eslint-disable-line.js b/tests/lib/illegal-eslint-disable-line.js index b05a6dd..495d68c 100644 --- a/tests/lib/illegal-eslint-disable-line.js +++ b/tests/lib/illegal-eslint-disable-line.js @@ -77,8 +77,6 @@ describe("multi-line eslint-disable-line comments", () => { for (const code of [ `/* eslint @eslint-community/eslint-comments/no-use:[error, {allow: ['eslint']}] */ /* eslint-disable-line -*/ -/* eslint-disable-next-line */`, `/* eslint @eslint-community/eslint-comments/no-duplicate-disable:error */ /*eslint-disable no-undef*/ @@ -92,7 +90,7 @@ no-undef*/ const normalMessages = messages.filter( (message) => message.ruleId != null ) - assert.strictEqual(normalMessages.length, 0) + assert.deepStrictEqual(normalMessages, []) }) ) } diff --git a/tests/lib/rules/disable-enable-pair.js b/tests/lib/rules/disable-enable-pair.js index a79f162..b1e3197 100644 --- a/tests/lib/rules/disable-enable-pair.js +++ b/tests/lib/rules/disable-enable-pair.js @@ -92,6 +92,22 @@ var foo = 1 `, ] : []), + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: ` +/*eslint-disable no-undef*/ +/*eslint-enable no-undef*/ +a {} +`, + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + }, + ] + : []), ], invalid: [ { @@ -232,5 +248,27 @@ console.log(); }, ] : []), + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: "/* eslint-disable no-unused-vars */ a {}", + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + errors: [ + { + message: + "Requires 'eslint-enable' directive for 'no-unused-vars'.", + line: 1, + column: 19, + endLine: 1, + endColumn: 33, + }, + ], + }, + ] + : []), ], }) diff --git a/tests/lib/rules/no-aggregating-enable.js b/tests/lib/rules/no-aggregating-enable.js index c9cde5a..979ddce 100644 --- a/tests/lib/rules/no-aggregating-enable.js +++ b/tests/lib/rules/no-aggregating-enable.js @@ -32,6 +32,22 @@ tester.run("no-aggregating-enable", rule, { /*eslint-enable no-redeclare*/ /*eslint-enable no-shadow*/ `, + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: ` + /*eslint-disable no-redeclare, no-shadow*/ + /*eslint-enable no-redeclare*/ + /*eslint-enable no-shadow*/ + a {}`, + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + }, + ] + : []), ], invalid: [ { @@ -80,5 +96,24 @@ tester.run("no-aggregating-enable", rule, { }, ] : []), + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: ` + /*eslint-disable no-redeclare*/ + /*eslint-disable no-shadow*/ + /*eslint-enable*/ + a {}`, + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + errors: [ + "This `eslint-enable` comment affects 2 `eslint-disable` comments. An `eslint-enable` comment should be for an `eslint-disable` comment.", + ], + }, + ] + : []), ], }) diff --git a/tests/lib/rules/no-duplicate-disable.js b/tests/lib/rules/no-duplicate-disable.js index cb278c7..3ba9652 100644 --- a/tests/lib/rules/no-duplicate-disable.js +++ b/tests/lib/rules/no-duplicate-disable.js @@ -29,6 +29,23 @@ tester.run("no-duplicate-disable", rule, { /*eslint-disable-next-line semi*/ /*eslint-disable eqeqeq*/ `, + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: ` +/*eslint-disable no-undef*/ +/*eslint-disable-line no-unused-vars*/ +/*eslint-disable-next-line semi*/ +/*eslint-disable eqeqeq*/ +a {}`, + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + }, + ] + : []), ], invalid: [ { @@ -142,5 +159,30 @@ tester.run("no-duplicate-disable", rule, { }, ] : []), + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: ` +/* eslint-disable-next-line no-undef */ +/* eslint-disable-line no-undef */ +a {}`, + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + errors: [ + { + message: + "'no-undef' rule has been disabled already.", + line: 3, + column: 24, + endLine: 3, + endColumn: 32, + }, + ], + }, + ] + : []), ], }) diff --git a/tests/lib/rules/no-restricted-disable.js b/tests/lib/rules/no-restricted-disable.js index 569fed7..e515838 100644 --- a/tests/lib/rules/no-restricted-disable.js +++ b/tests/lib/rules/no-restricted-disable.js @@ -48,6 +48,19 @@ tester.run("no-restricted-disable", rule, { code: "/*eslint-disable eqeqeq*/", options: ["*", "!eqeqeq"], }, + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: "/*eslint-disable eqeqeq*/ a {}", + options: ["*", "!eqeqeq"], + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + }, + ] + : []), ], invalid: [ { @@ -179,5 +192,19 @@ tester.run("no-restricted-disable", rule, { }, ] : []), + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: "/*eslint-disable eqeqeq*/ a {}", + options: ["eqeqeq"], + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + errors: ["Disabling 'eqeqeq' is not allowed."], + }, + ] + : []), ], }) diff --git a/tests/lib/rules/no-unlimited-disable.js b/tests/lib/rules/no-unlimited-disable.js index 24aad0b..34f7427 100644 --- a/tests/lib/rules/no-unlimited-disable.js +++ b/tests/lib/rules/no-unlimited-disable.js @@ -19,6 +19,18 @@ tester.run("no-unlimited-disable", rule, { "/*eslint-disable-next-line eqeqeq*/", "var foo;\n//eslint-disable-line eqeqeq", "var foo;\n/*eslint-disable-line eqeqeq*/", + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: "/*eslint-disable-line eqeqeq*/ a {}", + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + }, + ] + : []), ], invalid: [ { @@ -104,5 +116,20 @@ tester.run("no-unlimited-disable", rule, { }, ] : []), + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: "/* eslint-disable */ a {}", + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + errors: [ + "Unexpected unlimited 'eslint-disable' comment. Specify some rule names to disable.", + ], + }, + ] + : []), ], }) diff --git a/tests/lib/rules/no-use.js b/tests/lib/rules/no-use.js index bcd14f8..861f372 100644 --- a/tests/lib/rules/no-use.js +++ b/tests/lib/rules/no-use.js @@ -4,7 +4,8 @@ */ "use strict" -const { RuleTester } = require("eslint") +const semver = require("semver") +const { Linter, RuleTester } = require("eslint") const rule = require("../../../lib/rules/no-use") const tester = new RuleTester() @@ -62,6 +63,19 @@ tester.run("no-use", rule, { code: "/* globals */", options: [{ allow: ["globals"] }], }, + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: "/* eslint-disable */ a {}", + options: [{ allow: ["eslint-disable"] }], + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + }, + ] + : []), ], invalid: [ { @@ -108,5 +122,18 @@ tester.run("no-use", rule, { code: "/* globals */", errors: ["Unexpected ESLint directive comment."], }, + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: "/* eslint-disable */ a {}", + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + errors: ["Unexpected ESLint directive comment."], + }, + ] + : []), ], }) diff --git a/tests/lib/rules/require-description.js b/tests/lib/rules/require-description.js index f5f7325..56b6f08 100644 --- a/tests/lib/rules/require-description.js +++ b/tests/lib/rules/require-description.js @@ -74,6 +74,19 @@ tester.run("require-description", rule, { code: "/* globals */", options: [{ ignore: ["globals"] }], }, + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: "/* eslint-disable */ a {}", + options: [{ ignore: ["eslint-disable"] }], + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + }, + ] + : []), ], invalid: [ { @@ -209,5 +222,20 @@ tester.run("require-description", rule, { "Unexpected undescribed directive comment. Include descriptions to explain why the comment is necessary.", ], }, + // Language plugin + ...(semver.satisfies(Linter.version, ">=9.6.0") + ? [ + { + code: "/* eslint-disable */ a {}", + plugins: { + css: require("@eslint/css").default, + }, + language: "css/css", + errors: [ + "Unexpected undescribed directive comment. Include descriptions to explain why the comment is necessary.", + ], + }, + ] + : []), ], })