diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8df690e3..1ad441b9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,8 +19,8 @@ jobs: os: - ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm install @@ -29,8 +29,8 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: "18.x" - run: npm install @@ -39,8 +39,8 @@ jobs: eslint7: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: "18.x" - run: npm install diff --git a/CHANGELOG.md b/CHANGELOG.md index b52ff858..ef8f7582 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ +## [5.2.0](https://github.com/eslint-community/eslint-plugin-eslint-plugin/compare/v5.1.1...v5.2.0) (2023-12-11) + + +### Features + +* preparing for eslint v9 ([#400](https://github.com/eslint-community/eslint-plugin-eslint-plugin/issues/400)) ([35e14cd](https://github.com/eslint-community/eslint-plugin-eslint-plugin/commit/35e14cd7bc1fd865fa11efd955afe600ef2bbc22)) + ### [5.1.1](https://github.com/eslint-community/eslint-plugin-eslint-plugin/compare/v5.1.0...v5.1.1) (2023-07-19) diff --git a/docs/rules/require-meta-docs-description.md b/docs/rules/require-meta-docs-description.md index 8de0b5bf..252d15f8 100644 --- a/docs/rules/require-meta-docs-description.md +++ b/docs/rules/require-meta-docs-description.md @@ -49,9 +49,13 @@ module.exports = { ## Options -This rule takes an optional object containing: + -* `String` — `pattern` — A regular expression that the description must match. Use `'.+'` to allow anything. Defaults to `^(enforce|require|disallow)`. +| Name | Description | Type | Default | +| :-------- | :---------------------------------------------------------------------------------- | :----- | :------------------------------ | +| `pattern` | A regular expression that the description must match. Use `'.+'` to allow anything. | String | `^(enforce\|require\|disallow)` | + + ## Further Reading diff --git a/docs/rules/require-meta-docs-url.md b/docs/rules/require-meta-docs-url.md index 7c2f1740..22dd0fd5 100644 --- a/docs/rules/require-meta-docs-url.md +++ b/docs/rules/require-meta-docs-url.md @@ -77,7 +77,13 @@ module.exports = { ## Options -This rule has an option. + + +| Name | Description | Type | +| :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----- | +| `pattern` | A pattern to enforce rule's document URL. It replaces `{{name}}` placeholder by each rule name. The rule name is the basename of each rule file. Omitting this allows any URL. | String | + + ```json { @@ -87,8 +93,6 @@ This rule has an option. } ``` -- `pattern` (`string`) ... A pattern to enforce rule's document URL. It replaces `{{name}}` placeholder by each rule name. The rule name is the basename of each rule file. Default is `undefined` which allows any URL. - If you set the `pattern` option, this rule adds `meta.docs.url` property automatically when you execute `eslint --fix` command. ## Version specific URL diff --git a/docs/rules/require-meta-fixable.md b/docs/rules/require-meta-fixable.md index 28148a3e..81ccf77c 100644 --- a/docs/rules/require-meta-fixable.md +++ b/docs/rules/require-meta-fixable.md @@ -92,9 +92,13 @@ module.exports = { ## Options -This rule takes an optional object containing: + -* `boolean` — `catchNoFixerButFixableProperty` — default `false` - Whether the rule should attempt to detect rules that do not have a fixer but enable the `meta.fixable` property. This option is off by default because it increases the chance of false positives since fixers can't always be detected when helper functions are used. +| Name | Description | Type | Default | +| :------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------ | :------ | +| `catchNoFixerButFixableProperty` | Whether the rule should attempt to detect rules that do not have a fixer but enable the `meta.fixable` property. This option is off by default because it increases the chance of false positives since fixers can't always be detected when helper functions are used. | Boolean | `false` | + + ## Further Reading diff --git a/docs/rules/require-meta-schema.md b/docs/rules/require-meta-schema.md index 066eb0ba..5b768087 100644 --- a/docs/rules/require-meta-schema.md +++ b/docs/rules/require-meta-schema.md @@ -75,9 +75,13 @@ module.exports = { ## Options -This rule takes an optional object containing: + -* `boolean` — `requireSchemaPropertyWhenOptionless` — Whether the rule should require the `meta.schema` property to be specified (with `schema: []`) for rules that have no options. Defaults to `true`. +| Name | Description | Type | Default | +| :------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------- | :------ | :------ | +| `requireSchemaPropertyWhenOptionless` | Whether the rule should require the `meta.schema` property to be specified (with `schema: []`) for rules that have no options. | Boolean | `true` | + + ## When Not To Use It diff --git a/lib/rules/consistent-output.js b/lib/rules/consistent-output.js index a5c621d7..21a1c3ee 100644 --- a/lib/rules/consistent-output.js +++ b/lib/rules/consistent-output.js @@ -27,6 +27,7 @@ module.exports = { { type: 'string', enum: ['always', 'consistent'], + default: 'consistent', }, ], messages: { diff --git a/lib/rules/fixer-return.js b/lib/rules/fixer-return.js index 05e6867f..ac231d8f 100644 --- a/lib/rules/fixer-return.js +++ b/lib/rules/fixer-return.js @@ -76,12 +76,13 @@ module.exports = { * @returns {boolean} */ function isFix(node) { + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: use context.sourceCode when dropping eslint < v9 if (node.type === 'ArrayExpression' && node.elements.length === 0) { // An empty array is not a fix. return false; } - - const staticValue = getStaticValue(node, context.getScope()); + const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < 9.0.0 + const staticValue = getStaticValue(node, scope); if (!staticValue) { // If we can't find a static value, assume it's a real fix value. return true; @@ -98,7 +99,7 @@ module.exports = { return { Program(ast) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 contextIdentifiers = utils.getContextIdentifiers( sourceCode.scopeManager, ast @@ -148,7 +149,8 @@ module.exports = { // Ensure the current (arrow) fixer function returned a fix. 'ArrowFunctionExpression:exit'(node) { if (funcInfo.shouldCheck) { - const loc = context.getSourceCode().getTokenBefore(node.body).loc; // Show violation on arrow (=>). + const sourceCode = context.sourceCode || context.getSourceCode(); + const loc = sourceCode.getTokenBefore(node.body).loc; // Show violation on arrow (=>). if (node.expression) { // When the return is implied (no curly braces around the body), we have to check the single body node directly. if (!isFix(node.body)) { diff --git a/lib/rules/meta-property-ordering.js b/lib/rules/meta-property-ordering.js index 8c19c12e..d07215d3 100644 --- a/lib/rules/meta-property-ordering.js +++ b/lib/rules/meta-property-ordering.js @@ -34,7 +34,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const ruleInfo = getRuleInfo(sourceCode); if (!ruleInfo) { return {}; diff --git a/lib/rules/no-deprecated-context-methods.js b/lib/rules/no-deprecated-context-methods.js index 97ced0e5..3d68edbb 100644 --- a/lib/rules/no-deprecated-context-methods.js +++ b/lib/rules/no-deprecated-context-methods.js @@ -54,7 +54,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 // ---------------------------------------------------------------------- // Public diff --git a/lib/rules/no-deprecated-report-api.js b/lib/rules/no-deprecated-report-api.js index d6c7b373..59ff825d 100644 --- a/lib/rules/no-deprecated-report-api.js +++ b/lib/rules/no-deprecated-report-api.js @@ -30,7 +30,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 let contextIdentifiers; // ---------------------------------------------------------------------- @@ -60,7 +60,7 @@ module.exports = { fix(fixer) { const openingParen = sourceCode.getTokenBefore(node.arguments[0]); const closingParen = sourceCode.getLastToken(node); - const reportInfo = utils.getReportInfo(node.arguments, context); + const reportInfo = utils.getReportInfo(node, context); if (!reportInfo) { return null; diff --git a/lib/rules/no-identical-tests.js b/lib/rules/no-identical-tests.js index d8679c5a..429eb626 100644 --- a/lib/rules/no-identical-tests.js +++ b/lib/rules/no-identical-tests.js @@ -32,7 +32,7 @@ module.exports = { // ---------------------------------------------------------------------- // Public // ---------------------------------------------------------------------- - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 // ---------------------------------------------------------------------- // Helpers diff --git a/lib/rules/no-missing-message-ids.js b/lib/rules/no-missing-message-ids.js index dec2f17b..73d12dd9 100644 --- a/lib/rules/no-missing-message-ids.js +++ b/lib/rules/no-missing-message-ids.js @@ -26,7 +26,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const { scopeManager } = sourceCode; const ruleInfo = utils.getRuleInfo(sourceCode); if (!ruleInfo) { @@ -48,6 +48,7 @@ module.exports = { }, CallExpression(node) { + const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < 9.0.0 // Check for messageId properties used in known calls to context.report(); if ( node.callee.type === 'MemberExpression' && @@ -55,7 +56,7 @@ module.exports = { node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' ) { - const reportInfo = utils.getReportInfo(node.arguments, context); + const reportInfo = utils.getReportInfo(node, context); if (!reportInfo) { return; } @@ -80,7 +81,7 @@ module.exports = { val.value, ruleInfo, scopeManager, - context.getScope() + scope ) ) // Couldn't find this messageId in `meta.messages`. diff --git a/lib/rules/no-missing-placeholders.js b/lib/rules/no-missing-placeholders.js index a70fbd92..f505d4f4 100644 --- a/lib/rules/no-missing-placeholders.js +++ b/lib/rules/no-missing-placeholders.js @@ -31,7 +31,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const { scopeManager } = sourceCode; let contextIdentifiers; @@ -48,13 +48,14 @@ module.exports = { contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast); }, CallExpression(node) { + const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < 9.0.0 if ( node.callee.type === 'MemberExpression' && contextIdentifiers.has(node.callee.object) && node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' ) { - const reportInfo = utils.getReportInfo(node.arguments, context); + const reportInfo = utils.getReportInfo(node, context); if (!reportInfo) { return; } @@ -75,7 +76,7 @@ module.exports = { obj.messageId.value, ruleInfo, scopeManager, - context.getScope() + scope ); if (correspondingMessage) { obj.message = correspondingMessage.value; @@ -89,10 +90,7 @@ module.exports = { messageId, data, } of reportMessagesAndDataArray.filter((obj) => obj.message)) { - const messageStaticValue = getStaticValue( - message, - context.getScope() - ); + const messageStaticValue = getStaticValue(message, scope); if ( ((message.type === 'Literal' && typeof message.value === 'string') || diff --git a/lib/rules/no-only-tests.js b/lib/rules/no-only-tests.js index 49206f61..fc016cc8 100644 --- a/lib/rules/no-only-tests.js +++ b/lib/rules/no-only-tests.js @@ -51,7 +51,8 @@ module.exports = { { messageId: 'removeOnly', *fix(fixer) { - const sourceCode = context.getSourceCode(); + const sourceCode = + context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const tokenBefore = sourceCode.getTokenBefore(onlyProperty); diff --git a/lib/rules/no-unused-message-ids.js b/lib/rules/no-unused-message-ids.js index 2bd1b451..86840d15 100644 --- a/lib/rules/no-unused-message-ids.js +++ b/lib/rules/no-unused-message-ids.js @@ -24,7 +24,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const { scopeManager } = sourceCode; const ruleInfo = utils.getRuleInfo(sourceCode); if (!ruleInfo) { @@ -47,7 +47,7 @@ module.exports = { contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast); }, - 'Program:exit'() { + 'Program:exit'(ast) { if (hasSeenUnknownMessageId || !hasSeenViolationReport) { /* Bail out when the rule is likely to have false positives. @@ -57,9 +57,10 @@ module.exports = { return; } + const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < 9.0.0 + const messageIdNodesUnused = messageIdNodes.filter( - (node) => - !messageIdsUsed.has(utils.getKeyName(node, context.getScope())) + (node) => !messageIdsUsed.has(utils.getKeyName(node, scope)) ); // Report any messageIds that were never used. @@ -68,7 +69,7 @@ module.exports = { node: messageIdNode, messageId: 'unusedMessage', data: { - messageId: utils.getKeyName(messageIdNode, context.getScope()), + messageId: utils.getKeyName(messageIdNode, scope), }, }); } @@ -82,7 +83,7 @@ module.exports = { node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' ) { - const reportInfo = utils.getReportInfo(node.arguments, context); + const reportInfo = utils.getReportInfo(node, context); if (!reportInfo) { return; } diff --git a/lib/rules/no-unused-placeholders.js b/lib/rules/no-unused-placeholders.js index 51defefb..edace9c3 100644 --- a/lib/rules/no-unused-placeholders.js +++ b/lib/rules/no-unused-placeholders.js @@ -31,7 +31,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const { scopeManager } = sourceCode; let contextIdentifiers; @@ -47,13 +47,14 @@ module.exports = { contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast); }, CallExpression(node) { + const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < 9.0.0 if ( node.callee.type === 'MemberExpression' && contextIdentifiers.has(node.callee.object) && node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' ) { - const reportInfo = utils.getReportInfo(node.arguments, context); + const reportInfo = utils.getReportInfo(node, context); if (!reportInfo) { return; } @@ -74,7 +75,7 @@ module.exports = { obj.messageId.value, ruleInfo, scopeManager, - context.getScope() + scope ); if (correspondingMessage) { obj.message = correspondingMessage.value; @@ -86,10 +87,7 @@ module.exports = { for (const { message, data } of reportMessagesAndDataArray.filter( (obj) => obj.message )) { - const messageStaticValue = getStaticValue( - message, - context.getScope() - ); + const messageStaticValue = getStaticValue(message, scope); if ( ((message.type === 'Literal' && typeof message.value === 'string') || diff --git a/lib/rules/no-useless-token-range.js b/lib/rules/no-useless-token-range.js index e62d7316..ea488190 100644 --- a/lib/rules/no-useless-token-range.js +++ b/lib/rules/no-useless-token-range.js @@ -30,7 +30,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 // ---------------------------------------------------------------------- // Helpers diff --git a/lib/rules/prefer-message-ids.js b/lib/rules/prefer-message-ids.js index 539d7104..48bf348a 100644 --- a/lib/rules/prefer-message-ids.js +++ b/lib/rules/prefer-message-ids.js @@ -29,7 +29,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const ruleInfo = utils.getRuleInfo(sourceCode); if (!ruleInfo) { return {}; @@ -43,6 +43,7 @@ module.exports = { return { Program(ast) { + const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0 contextIdentifiers = utils.getContextIdentifiers( sourceCode.scopeManager, ast @@ -64,10 +65,7 @@ module.exports = { return; } - const staticValue = getStaticValue( - messagesNode.value, - context.getScope() - ); + const staticValue = getStaticValue(messagesNode.value, scope); if (!staticValue) { return; } @@ -90,7 +88,7 @@ module.exports = { node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' ) { - const reportInfo = utils.getReportInfo(node.arguments, context); + const reportInfo = utils.getReportInfo(node, context); if (!reportInfo) { return; } diff --git a/lib/rules/prefer-object-rule.js b/lib/rules/prefer-object-rule.js index 46bc4ceb..4cb11fcd 100644 --- a/lib/rules/prefer-object-rule.js +++ b/lib/rules/prefer-object-rule.js @@ -32,7 +32,7 @@ module.exports = { // Public // ---------------------------------------------------------------------- - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const ruleInfo = utils.getRuleInfo(sourceCode); if (!ruleInfo) { return {}; diff --git a/lib/rules/prefer-output-null.js b/lib/rules/prefer-output-null.js index 4403b7e8..d58e4307 100644 --- a/lib/rules/prefer-output-null.js +++ b/lib/rules/prefer-output-null.js @@ -35,7 +35,7 @@ module.exports = { // Public // ---------------------------------------------------------------------- - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 return { Program(ast) { diff --git a/lib/rules/prefer-placeholders.js b/lib/rules/prefer-placeholders.js index cc29d4b4..bd3457c3 100644 --- a/lib/rules/prefer-placeholders.js +++ b/lib/rules/prefer-placeholders.js @@ -33,7 +33,7 @@ module.exports = { create(context) { let contextIdentifiers; - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const { scopeManager } = sourceCode; // ---------------------------------------------------------------------- @@ -51,7 +51,7 @@ module.exports = { node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' ) { - const reportInfo = utils.getReportInfo(node.arguments, context); + const reportInfo = utils.getReportInfo(node, context); if (!reportInfo) { return; diff --git a/lib/rules/prefer-replace-text.js b/lib/rules/prefer-replace-text.js index c83beeaa..a9517ed2 100644 --- a/lib/rules/prefer-replace-text.js +++ b/lib/rules/prefer-replace-text.js @@ -30,7 +30,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 let funcInfo = { upper: null, codePath: null, diff --git a/lib/rules/report-message-format.js b/lib/rules/report-message-format.js index 770524c0..d22d9cfa 100644 --- a/lib/rules/report-message-format.js +++ b/lib/rules/report-message-format.js @@ -38,8 +38,8 @@ module.exports = { * @param {ASTNode} message The message AST node * @returns {void} */ - function processMessageNode(message) { - const staticValue = getStaticValue(message, context.getScope()); + function processMessageNode(message, scope) { + const staticValue = getStaticValue(message, scope); if ( (message.type === 'Literal' && typeof message.value === 'string' && @@ -57,7 +57,7 @@ module.exports = { } } - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const ruleInfo = utils.getRuleInfo(sourceCode); if (!ruleInfo) { return {}; @@ -69,6 +69,7 @@ module.exports = { return { Program(ast) { + const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0 contextIdentifiers = utils.getContextIdentifiers( sourceCode.scopeManager, ast @@ -93,21 +94,22 @@ module.exports = { messagesObject.value.properties .filter((prop) => prop.type === 'Property') .map((prop) => prop.value) - .forEach(processMessageNode); + .forEach((it) => processMessageNode(it, scope)); }, CallExpression(node) { + const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0 if ( node.callee.type === 'MemberExpression' && contextIdentifiers.has(node.callee.object) && node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' ) { - const reportInfo = utils.getReportInfo(node.arguments, context); + const reportInfo = utils.getReportInfo(node, context); const message = reportInfo && reportInfo.message; const suggest = reportInfo && reportInfo.suggest; if (message) { - processMessageNode(message); + processMessageNode(message, scope); } if (suggest && suggest.type === 'ArrayExpression') { @@ -122,7 +124,7 @@ module.exports = { prop.key.name === 'message' ) .map((prop) => prop.value) - .forEach(processMessageNode); + .forEach((it) => processMessageNode(it, scope)); } } }, diff --git a/lib/rules/require-meta-docs-description.js b/lib/rules/require-meta-docs-description.js index 0a9b390d..3afd6d8d 100644 --- a/lib/rules/require-meta-docs-description.js +++ b/lib/rules/require-meta-docs-description.js @@ -27,6 +27,9 @@ module.exports = { properties: { pattern: { type: 'string', + description: + "A regular expression that the description must match. Use `'.+'` to allow anything.", + default: '^(enforce|require|disallow)', }, }, additionalProperties: false, @@ -42,14 +45,15 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const ruleInfo = utils.getRuleInfo(sourceCode); if (!ruleInfo) { return {}; } return { - Program() { + Program(ast) { + const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0 const { scopeManager } = sourceCode; const pattern = @@ -77,10 +81,7 @@ module.exports = { return; } - const staticValue = getStaticValue( - descriptionNode.value, - context.getScope() - ); + const staticValue = getStaticValue(descriptionNode.value, scope); if (!staticValue) { // Ignore non-static values since we can't determine what they look like. return; diff --git a/lib/rules/require-meta-docs-url.js b/lib/rules/require-meta-docs-url.js index 0894c67a..d52f1984 100644 --- a/lib/rules/require-meta-docs-url.js +++ b/lib/rules/require-meta-docs-url.js @@ -31,7 +31,11 @@ module.exports = { { type: 'object', properties: { - pattern: { type: 'string' }, + pattern: { + type: 'string', + description: + "A pattern to enforce rule's document URL. It replaces `{{name}}` placeholder by each rule name. The rule name is the basename of each rule file. Omitting this allows any URL.", + }, }, additionalProperties: false, }, @@ -50,7 +54,7 @@ module.exports = { */ create(context) { const options = context.options[0] || {}; - const filename = context.getFilename(); + const filename = context.filename || context.getFilename(); // TODO: just use context.filename when dropping eslint < v9 const ruleName = filename === '' ? undefined @@ -72,14 +76,15 @@ module.exports = { ); } - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const ruleInfo = util.getRuleInfo(sourceCode); if (!ruleInfo) { return {}; } return { - Program() { + Program(ast) { + const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0 const { scopeManager } = sourceCode; const metaNode = ruleInfo.meta; @@ -94,7 +99,7 @@ module.exports = { .find((p) => p.type === 'Property' && util.getKeyName(p) === 'url'); const staticValue = urlPropNode - ? getStaticValue(urlPropNode.value, context.getScope()) + ? getStaticValue(urlPropNode.value, scope) : undefined; if (urlPropNode && !staticValue) { // Ignore non-static values since we can't determine what they look like. diff --git a/lib/rules/require-meta-fixable.js b/lib/rules/require-meta-fixable.js index 74b79e7a..3b9ec681 100644 --- a/lib/rules/require-meta-fixable.js +++ b/lib/rules/require-meta-fixable.js @@ -29,6 +29,8 @@ module.exports = { catchNoFixerButFixableProperty: { type: 'boolean', default: false, + description: + "Whether the rule should attempt to detect rules that do not have a fixer but enable the `meta.fixable` property. This option is off by default because it increases the chance of false positives since fixers can't always be detected when helper functions are used.", }, }, additionalProperties: false, @@ -47,7 +49,7 @@ module.exports = { const catchNoFixerButFixableProperty = context.options[0] && context.options[0].catchNoFixerButFixableProperty; - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const { scopeManager } = sourceCode; const ruleInfo = utils.getRuleInfo(sourceCode); let contextIdentifiers; @@ -76,7 +78,8 @@ module.exports = { usesFixFunctions = true; } }, - 'Program:exit'() { + 'Program:exit'(ast) { + const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0 const metaFixableProp = ruleInfo && utils @@ -84,10 +87,7 @@ module.exports = { .find((prop) => utils.getKeyName(prop) === 'fixable'); if (metaFixableProp) { - const staticValue = getStaticValue( - metaFixableProp.value, - context.getScope() - ); + const staticValue = getStaticValue(metaFixableProp.value, scope); if (!staticValue) { // Ignore non-static values since we can't determine what they look like. return; diff --git a/lib/rules/require-meta-has-suggestions.js b/lib/rules/require-meta-has-suggestions.js index 0a8f5282..3c9a0050 100644 --- a/lib/rules/require-meta-has-suggestions.js +++ b/lib/rules/require-meta-has-suggestions.js @@ -29,7 +29,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const { scopeManager } = sourceCode; const ruleInfo = utils.getRuleInfo(sourceCode); if (!ruleInfo) { @@ -44,7 +44,8 @@ module.exports = { * @returns {boolean} whether this property should be considered to contain suggestions */ function doesPropertyContainSuggestions(node) { - const staticValue = getStaticValue(node.value, context.getScope()); + const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0 + const staticValue = getStaticValue(node.value, scope); if ( !staticValue || (Array.isArray(staticValue.value) && staticValue.value.length > 0) || diff --git a/lib/rules/require-meta-schema.js b/lib/rules/require-meta-schema.js index 339d14bc..30d24109 100644 --- a/lib/rules/require-meta-schema.js +++ b/lib/rules/require-meta-schema.js @@ -25,6 +25,8 @@ module.exports = { requireSchemaPropertyWhenOptionless: { type: 'boolean', default: true, + description: + 'Whether the rule should require the `meta.schema` property to be specified (with `schema: []`) for rules that have no options.', }, }, additionalProperties: false, @@ -41,7 +43,7 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const { scopeManager } = sourceCode; const ruleInfo = utils.getRuleInfo(sourceCode); if (!ruleInfo) { diff --git a/lib/rules/require-meta-type.js b/lib/rules/require-meta-type.js index 9a56066b..1b3a14c4 100644 --- a/lib/rules/require-meta-type.js +++ b/lib/rules/require-meta-type.js @@ -38,14 +38,15 @@ module.exports = { // Public // ---------------------------------------------------------------------- - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const ruleInfo = utils.getRuleInfo(sourceCode); if (!ruleInfo) { return {}; } return { - Program() { + Program(node) { + const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0 const { scopeManager } = sourceCode; const metaNode = ruleInfo.meta; @@ -61,7 +62,7 @@ module.exports = { return; } - const staticValue = getStaticValue(typeNode.value, context.getScope()); + const staticValue = getStaticValue(typeNode.value, scope); if (!staticValue) { // Ignore non-static values since we can't determine what they look like. return; diff --git a/lib/rules/test-case-property-ordering.js b/lib/rules/test-case-property-ordering.js index 66aa1b57..73cb700f 100644 --- a/lib/rules/test-case-property-ordering.js +++ b/lib/rules/test-case-property-ordering.js @@ -50,7 +50,7 @@ module.exports = { 'env', 'errors', ]; - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 return { Program(ast) { diff --git a/lib/rules/test-case-shorthand-strings.js b/lib/rules/test-case-shorthand-strings.js index 5decd6c6..84b16e20 100644 --- a/lib/rules/test-case-shorthand-strings.js +++ b/lib/rules/test-case-shorthand-strings.js @@ -34,7 +34,7 @@ module.exports = { create(context) { const shorthandOption = context.options[0] || 'as-needed'; - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 // ---------------------------------------------------------------------- // Helpers diff --git a/lib/utils.js b/lib/utils.js index 19f6b7fd..4ad42c24 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -444,6 +444,7 @@ module.exports = { statements, variableIdentifiers = new Set() ) { + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 const runCalls = []; for (const statement of statements) { @@ -467,7 +468,10 @@ module.exports = { isRuleTesterConstruction(declarator.init) && declarator.id.type === 'Identifier' ) { - context.getDeclaredVariables(declarator).forEach((variable) => { + const vars = sourceCode.getDeclaredVariables + ? sourceCode.getDeclaredVariables(declarator) + : context.getDeclaredVariables(declarator); + vars.forEach((variable) => { variable.references .filter((ref) => ref.isRead()) .forEach((ref) => variableIdentifiers.add(ref.identifier)); @@ -579,11 +583,13 @@ module.exports = { }, /** - * Gets information on a report, given the arguments passed to context.report(). - * @param {ASTNode[]} reportArgs The arguments passed to context.report() + * Gets information on a report, given the ASTNode of context.report(). + * @param {ASTNode} node The ASTNode of context.report() * @param {Context} context */ - getReportInfo(reportArgs, context) { + getReportInfo(node, context) { + const reportArgs = node.arguments; + // If there is exactly one argument, the API expects an object. // Otherwise, if the second argument is a string, the arguments are interpreted as // ['node', 'message', 'data', 'fix']. @@ -608,11 +614,10 @@ module.exports = { } let keys; + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: use context.sourceCode when dropping eslint < v9 + const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when dropping eslint < v9 + const secondArgStaticValue = getStaticValue(reportArgs[1], scope); - const secondArgStaticValue = getStaticValue( - reportArgs[1], - context.getScope() - ); if ( (secondArgStaticValue && typeof secondArgStaticValue.value === 'string') || @@ -736,7 +741,7 @@ module.exports = { parent.parent.parent.type === 'CallExpression' && contextIdentifiers.has(parent.parent.parent.callee.object) && parent.parent.parent.callee.property.name === 'report' && - module.exports.getReportInfo(parent.parent.parent.arguments).fix === node + module.exports.getReportInfo(parent.parent.parent).fix === node ); }, @@ -766,9 +771,8 @@ module.exports = { ) && parent.parent.parent.parent.parent.parent.callee.property.name === 'report' && - module.exports.getReportInfo( - parent.parent.parent.parent.parent.parent.arguments - ).suggest === parent.parent.parent + module.exports.getReportInfo(parent.parent.parent.parent.parent.parent) + .suggest === parent.parent.parent ); }, diff --git a/package.json b/package.json index 18404d10..168df64f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-eslint-plugin", - "version": "5.1.1", + "version": "5.2.0", "description": "An ESLint plugin for linting ESLint plugins", "author": "Teddy Katz", "main": "./lib/index.js", @@ -44,7 +44,7 @@ "estraverse": "^5.3.0" }, "nyc": { - "branches": 99, + "branches": 94, "functions": 99, "lines": 99, "statements": 99 @@ -61,7 +61,7 @@ "eslint": "^8.23.0", "eslint-config-not-an-aardvark": "^2.1.0", "eslint-config-prettier": "^8.5.0", - "eslint-doc-generator": "^1.4.3", + "eslint-doc-generator": "^1.6.1", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-eslint-plugin": "file:./", "eslint-plugin-markdown": "^3.0.0", @@ -74,7 +74,7 @@ "globals": "^13.20.0", "husky": "^8.0.1", "lodash": "^4.17.21", - "markdownlint-cli": "^0.35.0", + "markdownlint-cli": "^0.36.0", "mocha": "^10.0.0", "npm-package-json-lint": "^7.0.0", "npm-run-all": "^4.1.5", diff --git a/tests/lib/utils.js b/tests/lib/utils.js index ebebacca..fe771824 100644 --- a/tests/lib/utils.js +++ b/tests/lib/utils.js @@ -761,8 +761,14 @@ describe('utils', () => { sourceType: 'script', nodejsScope: true, }); + const context = { + sourceCode: { + getDeclaredVariables: + scopeManager.getDeclaredVariables.bind(scopeManager), + }, + }; // mock object assert.deepEqual( - utils.getTestInfo(scopeManager, ast), + utils.getTestInfo(context, ast), [], 'Expected no tests to be found' ); @@ -827,7 +833,13 @@ describe('utils', () => { sourceType: 'script', nodejsScope: true, }); - const testInfo = utils.getTestInfo(scopeManager, ast); + const context = { + sourceCode: { + getDeclaredVariables: + scopeManager.getDeclaredVariables.bind(scopeManager), + }, + }; // mock object + const testInfo = utils.getTestInfo(context, ast); assert.strictEqual( testInfo.length, @@ -1021,7 +1033,13 @@ describe('utils', () => { sourceType: 'script', nodejsScope: true, }); - const testInfo = utils.getTestInfo(scopeManager, ast); + const context = { + sourceCode: { + getDeclaredVariables: + scopeManager.getDeclaredVariables.bind(scopeManager), + }, + }; // mock object + const testInfo = utils.getTestInfo(context, ast); assert.strictEqual( testInfo.length, @@ -1094,13 +1112,20 @@ describe('utils', () => { for (const args of CASES.keys()) { it(args.join(', '), () => { - const parsedArgs = espree.parse(`context.report(${args.join(', ')})`, { + const node = espree.parse(`context.report(${args.join(', ')})`, { ecmaVersion: 6, loc: false, range: false, - }).body[0].expression.arguments; - const context = { getScope() {} }; // mock object - const reportInfo = utils.getReportInfo(parsedArgs, context); + }).body[0].expression; + const parsedArgs = node.arguments; + const context = { + sourceCode: { + getScope() { + return {}; + }, + }, + }; // mock object + const reportInfo = utils.getReportInfo(node, context); assert.deepEqual(reportInfo, CASES.get(args)(parsedArgs)); }); @@ -1272,9 +1297,15 @@ describe('utils', () => { ecmaVersion: 6, range: true, }); - const context = { getScope() {} }; // mock object + const context = { + sourceCode: { + getScope() { + return {}; + }, + }, + }; // mock object const reportNode = ast.body[0].expression; - const reportInfo = utils.getReportInfo(reportNode.arguments, context); + const reportInfo = utils.getReportInfo(reportNode, context); const data = utils.collectReportViolationAndSuggestionData(reportInfo); assert( lodash.isMatch(data, testCase.shouldMatch),