diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 470964ab..f4f9204f 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -12,6 +12,7 @@ jobs:
fail-fast: false
matrix:
node-version:
+ - 23
- 22
- 21
- 20
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 510e4cad..cd509eda 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,13 @@
* update dependency markdownlint-cli to ^0.38.0 ([#410](https://github.com/eslint-community/eslint-plugin-eslint-plugin/issues/410)) ([6b53c5b](https://github.com/eslint-community/eslint-plugin-eslint-plugin/commit/6b53c5b7b8bc9e19dcb86796ab29019f89c449fc))
* update dependency markdownlint-cli to ^0.39.0 ([#431](https://github.com/eslint-community/eslint-plugin-eslint-plugin/issues/431)) ([f005a2c](https://github.com/eslint-community/eslint-plugin-eslint-plugin/commit/f005a2c0231b8b77f6862dca81b4a6e3099e0493))
+## [6.5.0](https://github.com/eslint-community/eslint-plugin-eslint-plugin/compare/v6.4.0...v6.5.0) (2025-06-18)
+
+
+### Features
+
+* Add new rule `no-meta-replaced-by` ([105f05d](https://github.com/eslint-community/eslint-plugin-eslint-plugin/commit/105f05d73ebed88d2c78ae75cb6f3c6ab8c21aa9))
+
## [6.4.0](https://github.com/eslint-community/eslint-plugin-eslint-plugin/compare/v6.3.2...v6.4.0) (2024-12-18)
diff --git a/README.md b/README.md
index e9553dfc..f6eeb235 100644
--- a/README.md
+++ b/README.md
@@ -39,10 +39,11 @@ npm install eslint-plugin-eslint-plugin --save-dev
Here's an example ESLint configuration that:
-- Sets `sourceType` to `script` for CJS plugins (most users) (use `module` for ESM/TypeScript)
- Enables the `recommended` configuration
- Enables an optional/non-recommended rule
+Note: you might need to set `sourceType` to `script` (most users) (use `module` for ESM/TypeScript).
+
### **[.eslintrc.json](https://eslint.org/docs/latest/use/configure/configuration-files)**
```json
@@ -86,6 +87,7 @@ module.exports = [
| [meta-property-ordering](docs/rules/meta-property-ordering.md) | enforce the order of meta properties | | 🔧 | | |
| [no-deprecated-context-methods](docs/rules/no-deprecated-context-methods.md) | disallow usage of deprecated methods on rule context objects | ✅ | 🔧 | | |
| [no-deprecated-report-api](docs/rules/no-deprecated-report-api.md) | disallow the version of `context.report()` with multiple arguments | ✅ | 🔧 | | |
+| [no-meta-replaced-by](docs/rules/no-meta-replaced-by.md) | disallow using the `meta.replacedBy` rule property | | | | |
| [no-meta-schema-default](docs/rules/no-meta-schema-default.md) | disallow rules `meta.schema` properties to include defaults | | | | |
| [no-missing-message-ids](docs/rules/no-missing-message-ids.md) | disallow `messageId`s that are missing from `meta.messages` | ✅ | | | |
| [no-missing-placeholders](docs/rules/no-missing-placeholders.md) | disallow missing placeholders in rule report messages | ✅ | | | |
diff --git a/docs/rules/no-meta-replaced-by.md b/docs/rules/no-meta-replaced-by.md
new file mode 100644
index 00000000..070fa5ef
--- /dev/null
+++ b/docs/rules/no-meta-replaced-by.md
@@ -0,0 +1,62 @@
+# Disallow using the `meta.replacedBy` rule property (`eslint-plugin/no-meta-replaced-by`)
+
+
+
+As of ESLint v9.21.0, the rule property `meta.deprecated` can be either a boolean or an object of type `DeprecatedInfo`. The `DeprecatedInfo` type includes an optional `replacedBy` array that replaces the now-deprecated `meta.replacedBy` property.
+
+Examples of correct usage:
+
+- [array-bracket-newline](https://github.com/eslint/eslint/blob/4112fd09531092e9651e9981205bcd603dc56acf/lib/rules/array-bracket-newline.js#L18-L38)
+- [typescript-eslint/no-empty-interface](https://github.com/typescript-eslint/typescript-eslint/blob/af94f163a1d6447a84c5571fff5e38e4c700edb9/packages/eslint-plugin/src/rules/no-empty-interface.ts#L19-L30)
+
+## Rule Details
+
+This rule disallows the `meta.replacedBy` property in a rule.
+
+Examples of **incorrect** code for this rule:
+
+```js
+/* eslint eslint-plugin/no-meta-replaced-by: error */
+
+module.exports = {
+ meta: {
+ deprecated: true,
+ replacedBy: ['the-new-rule'],
+ },
+ create() {},
+};
+```
+
+Examples of **correct** code for this rule:
+
+```js
+/* eslint eslint-plugin/no-meta-replaced-by: error */
+
+module.exports = {
+ meta: {
+ deprecated: {
+ message: 'The new rule adds more functionality',
+ replacedBy: [
+ {
+ rule: {
+ name: 'the-new-rule',
+ },
+ },
+ ],
+ },
+ },
+ create() {},
+};
+
+module.exports = {
+ meta: {
+ deprecated: true,
+ },
+ create() {},
+};
+```
+
+## Further Reading
+
+- [ESLint docs: `DeprecatedInfo`](https://eslint.org/docs/latest/extend/rule-deprecation#-deprecatedinfo-type)
+- [RFC introducing `DeprecatedInfo` type](https://github.com/eslint/rfcs/tree/main/designs/2024-deprecated-rule-metadata)
diff --git a/docs/rules/prefer-replace-text.md b/docs/rules/prefer-replace-text.md
index 076665cf..881d544c 100644
--- a/docs/rules/prefer-replace-text.md
+++ b/docs/rules/prefer-replace-text.md
@@ -15,7 +15,7 @@ module.exports = {
create(context) {
context.report({
fix(fixer) {
- // error, can be written: return fixer.replaceText([node, '']);
+ // error, can be written: return fixer.replaceText(node, '');
return fixer.replaceTextRange([node.range[0], node.range[1]], '');
},
});
diff --git a/lib/rules/fixer-return.js b/lib/rules/fixer-return.js
index 0ea7c857..11e7d57f 100644
--- a/lib/rules/fixer-return.js
+++ b/lib/rules/fixer-return.js
@@ -76,12 +76,11 @@ 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 scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < 9.0.0
+ const scope = utils.getScope(context);
const staticValue = getStaticValue(node, scope);
if (!staticValue) {
// If we can't find a static value, assume it's a real fix value.
@@ -99,7 +98,7 @@ module.exports = {
return {
Program(ast) {
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
contextIdentifiers = utils.getContextIdentifiers(
sourceCode.scopeManager,
ast,
@@ -149,7 +148,7 @@ module.exports = {
// Ensure the current (arrow) fixer function returned a fix.
'ArrowFunctionExpression:exit'(node) {
if (funcInfo.shouldCheck) {
- const sourceCode = context.sourceCode || context.getSourceCode();
+ const sourceCode = utils.getSourceCode(context);
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.
diff --git a/lib/rules/meta-property-ordering.js b/lib/rules/meta-property-ordering.js
index 04142237..27e29218 100644
--- a/lib/rules/meta-property-ordering.js
+++ b/lib/rules/meta-property-ordering.js
@@ -4,7 +4,8 @@
'use strict';
-const { getKeyName, getRuleInfo } = require('../utils');
+const utils = require('../utils');
+const { getKeyName, getRuleInfo } = utils;
const defaultOrder = [
'type',
@@ -48,7 +49,7 @@ module.exports = {
},
create(context) {
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
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 e72580ca..c4f6134b 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
// ----------------------------------------------------------------------
// Public
diff --git a/lib/rules/no-deprecated-report-api.js b/lib/rules/no-deprecated-report-api.js
index 266cb71d..eb0c07c2 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
let contextIdentifiers;
// ----------------------------------------------------------------------
diff --git a/lib/rules/no-identical-tests.js b/lib/rules/no-identical-tests.js
index c0744fad..c7c5867f 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
// ----------------------------------------------------------------------
// Helpers
diff --git a/lib/rules/no-meta-replaced-by.js b/lib/rules/no-meta-replaced-by.js
new file mode 100644
index 00000000..36aa1297
--- /dev/null
+++ b/lib/rules/no-meta-replaced-by.js
@@ -0,0 +1,63 @@
+/**
+ * @fileoverview Disallows the usage of `meta.replacedBy` property
+ */
+
+'use strict';
+
+const utils = require('../utils');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow using the `meta.replacedBy` rule property',
+ category: 'Rules',
+ recommended: false,
+ url: 'https://github.com/eslint-community/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/no-meta-replaced-by.md',
+ },
+ schema: [],
+ messages: {
+ useNewFormat:
+ 'Use `meta.deprecated.replacedBy` instead of `meta.replacedBy`',
+ },
+ },
+ create(context) {
+ const sourceCode = utils.getSourceCode(context);
+ const ruleInfo = utils.getRuleInfo(sourceCode);
+
+ if (!ruleInfo) {
+ return {};
+ }
+
+ return {
+ Program() {
+ const metaNode = ruleInfo.meta;
+
+ if (!metaNode) {
+ return;
+ }
+
+ const replacedByNode = utils
+ .evaluateObjectProperties(metaNode, sourceCode.scopeManager)
+ .find(
+ (p) =>
+ p.type === 'Property' && utils.getKeyName(p) === 'replacedBy',
+ );
+
+ if (!replacedByNode) {
+ return;
+ }
+
+ context.report({
+ node: replacedByNode,
+ messageId: 'useNewFormat',
+ });
+ },
+ };
+ },
+};
diff --git a/lib/rules/no-meta-schema-default.js b/lib/rules/no-meta-schema-default.js
index 171d55e6..ccba978a 100644
--- a/lib/rules/no-meta-schema-default.js
+++ b/lib/rules/no-meta-schema-default.js
@@ -25,7 +25,7 @@ module.exports = {
},
create(context) {
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const { scopeManager } = sourceCode;
const ruleInfo = utils.getRuleInfo(sourceCode);
if (!ruleInfo) {
diff --git a/lib/rules/no-missing-message-ids.js b/lib/rules/no-missing-message-ids.js
index 1066b855..0bc2d9ed 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const { scopeManager } = sourceCode;
const ruleInfo = utils.getRuleInfo(sourceCode);
if (!ruleInfo) {
@@ -48,7 +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
+ const scope = utils.getScope(context);
// Check for messageId properties used in known calls to context.report();
if (
node.callee.type === 'MemberExpression' &&
diff --git a/lib/rules/no-missing-placeholders.js b/lib/rules/no-missing-placeholders.js
index 364c340f..996e8004 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const { scopeManager } = sourceCode;
let contextIdentifiers;
@@ -48,7 +48,7 @@ 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
+ const scope = utils.getScope(context);
if (
node.callee.type === 'MemberExpression' &&
contextIdentifiers.has(node.callee.object) &&
diff --git a/lib/rules/no-only-tests.js b/lib/rules/no-only-tests.js
index 816fedd1..3aa267d2 100644
--- a/lib/rules/no-only-tests.js
+++ b/lib/rules/no-only-tests.js
@@ -51,8 +51,7 @@ module.exports = {
{
messageId: 'removeOnly',
*fix(fixer) {
- const sourceCode =
- context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const tokenBefore =
sourceCode.getTokenBefore(onlyProperty);
diff --git a/lib/rules/no-property-in-node.js b/lib/rules/no-property-in-node.js
index ace20884..f21e0060 100644
--- a/lib/rules/no-property-in-node.js
+++ b/lib/rules/no-property-in-node.js
@@ -1,5 +1,7 @@
'use strict';
+const utils = require('../utils');
+
const defaultTypedNodeSourceFileTesters = [
/@types[/\\]estree[/\\]index\.d\.ts/,
/@typescript-eslint[/\\]types[/\\]dist[/\\]generated[/\\]ast-spec\.d\.ts/,
@@ -88,7 +90,7 @@ module.exports = {
'BinaryExpression[operator=in]'(node) {
// TODO: Switch this to ESLintUtils.getParserServices with typescript-eslint@>=6
// https://github.com/eslint-community/eslint-plugin-eslint-plugin/issues/269
- const services = (context.sourceCode || context).parserServices;
+ const services = utils.getparserServices(context);
if (!services.program) {
throw new Error(
'You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.',
diff --git a/lib/rules/no-unused-message-ids.js b/lib/rules/no-unused-message-ids.js
index 978fad10..69036140 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const { scopeManager } = sourceCode;
const ruleInfo = utils.getRuleInfo(sourceCode);
if (!ruleInfo) {
@@ -47,7 +47,7 @@ module.exports = {
contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast);
},
- 'Program:exit'(ast) {
+ 'Program:exit'() {
if (hasSeenUnknownMessageId || !hasSeenViolationReport) {
/*
Bail out when the rule is likely to have false positives.
@@ -57,7 +57,7 @@ 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 scope = utils.getScope(context);
const messageIdNodesUnused = messageIdNodes.filter(
(node) => !messageIdsUsed.has(utils.getKeyName(node, scope)),
diff --git a/lib/rules/no-unused-placeholders.js b/lib/rules/no-unused-placeholders.js
index ca95c952..d7169c9a 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const { scopeManager } = sourceCode;
let contextIdentifiers;
@@ -47,7 +47,7 @@ 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
+ const scope = utils.getScope(context);
if (
node.callee.type === 'MemberExpression' &&
contextIdentifiers.has(node.callee.object) &&
diff --git a/lib/rules/no-useless-token-range.js b/lib/rules/no-useless-token-range.js
index 998c0059..6c6d6214 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
// ----------------------------------------------------------------------
// Helpers
diff --git a/lib/rules/prefer-message-ids.js b/lib/rules/prefer-message-ids.js
index 1d932748..cdf66642 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const ruleInfo = utils.getRuleInfo(sourceCode);
if (!ruleInfo) {
return {};
@@ -43,7 +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
+ const scope = utils.getScope(context);
contextIdentifiers = utils.getContextIdentifiers(
sourceCode.scopeManager,
ast,
diff --git a/lib/rules/prefer-object-rule.js b/lib/rules/prefer-object-rule.js
index 4a9351bf..1b0e98f1 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
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 d9be111b..4d9e90e5 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
return {
Program(ast) {
diff --git a/lib/rules/prefer-placeholders.js b/lib/rules/prefer-placeholders.js
index a1555945..f5c88fc1 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const { scopeManager } = sourceCode;
// ----------------------------------------------------------------------
diff --git a/lib/rules/prefer-replace-text.js b/lib/rules/prefer-replace-text.js
index 100c618c..173cc509 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
let funcInfo = {
upper: null,
codePath: null,
diff --git a/lib/rules/report-message-format.js b/lib/rules/report-message-format.js
index d6d308f5..4fce24b8 100644
--- a/lib/rules/report-message-format.js
+++ b/lib/rules/report-message-format.js
@@ -63,7 +63,7 @@ module.exports = {
}
}
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const ruleInfo = utils.getRuleInfo(sourceCode);
if (!ruleInfo) {
return {};
@@ -75,7 +75,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
+ const scope = utils.getScope(context);
contextIdentifiers = utils.getContextIdentifiers(
sourceCode.scopeManager,
ast,
@@ -103,7 +103,7 @@ module.exports = {
.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
+ const scope = utils.getScope(context);
if (
node.callee.type === 'MemberExpression' &&
contextIdentifiers.has(node.callee.object) &&
diff --git a/lib/rules/require-meta-default-options.js b/lib/rules/require-meta-default-options.js
index e1166b08..9ac37575 100644
--- a/lib/rules/require-meta-default-options.js
+++ b/lib/rules/require-meta-default-options.js
@@ -26,7 +26,7 @@ module.exports = {
},
create(context) {
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const { scopeManager } = sourceCode;
const ruleInfo = utils.getRuleInfo(sourceCode);
if (!ruleInfo) {
diff --git a/lib/rules/require-meta-docs-description.js b/lib/rules/require-meta-docs-description.js
index 0ff4ac38..b3cf5272 100644
--- a/lib/rules/require-meta-docs-description.js
+++ b/lib/rules/require-meta-docs-description.js
@@ -46,15 +46,15 @@ module.exports = {
},
create(context) {
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const ruleInfo = utils.getRuleInfo(sourceCode);
if (!ruleInfo) {
return {};
}
return {
- Program(ast) {
- const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
+ Program() {
+ const scope = utils.getScope(context);
const { scopeManager } = sourceCode;
const {
diff --git a/lib/rules/require-meta-docs-recommended.js b/lib/rules/require-meta-docs-recommended.js
index b4ae768b..67851ffc 100644
--- a/lib/rules/require-meta-docs-recommended.js
+++ b/lib/rules/require-meta-docs-recommended.js
@@ -57,7 +57,7 @@ module.exports = {
},
create(context) {
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const ruleInfo = utils.getRuleInfo(sourceCode);
if (!ruleInfo) {
return {};
diff --git a/lib/rules/require-meta-docs-url.js b/lib/rules/require-meta-docs-url.js
index 90650626..cb3e35d3 100644
--- a/lib/rules/require-meta-docs-url.js
+++ b/lib/rules/require-meta-docs-url.js
@@ -55,7 +55,7 @@ module.exports = {
*/
create(context) {
const options = context.options[0] || {};
- const filename = context.filename || context.getFilename(); // TODO: just use context.filename when dropping eslint < v9
+ const filename = utils.getFilename(context);
const ruleName =
filename === ''
? undefined
@@ -77,15 +77,15 @@ module.exports = {
);
}
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const ruleInfo = utils.getRuleInfo(sourceCode);
if (!ruleInfo) {
return {};
}
return {
- Program(ast) {
- const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
+ Program() {
+ const scope = utils.getScope(context);
const { scopeManager } = sourceCode;
const {
diff --git a/lib/rules/require-meta-fixable.js b/lib/rules/require-meta-fixable.js
index 76ab6032..e69ca4fc 100644
--- a/lib/rules/require-meta-fixable.js
+++ b/lib/rules/require-meta-fixable.js
@@ -50,7 +50,7 @@ module.exports = {
const catchNoFixerButFixableProperty =
context.options[0] && context.options[0].catchNoFixerButFixableProperty;
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const { scopeManager } = sourceCode;
const ruleInfo = utils.getRuleInfo(sourceCode);
let contextIdentifiers;
@@ -79,8 +79,8 @@ module.exports = {
usesFixFunctions = true;
}
},
- 'Program:exit'(ast) {
- const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
+ 'Program:exit'() {
+ const scope = utils.getScope(context);
const metaFixableProp =
ruleInfo &&
utils
diff --git a/lib/rules/require-meta-has-suggestions.js b/lib/rules/require-meta-has-suggestions.js
index 1759eecf..c8f75b3f 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.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const { scopeManager } = sourceCode;
const ruleInfo = utils.getRuleInfo(sourceCode);
if (!ruleInfo) {
@@ -44,7 +44,7 @@ module.exports = {
* @returns {boolean} whether this property should be considered to contain suggestions
*/
function doesPropertyContainSuggestions(node) {
- const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
+ const scope = utils.getScope(context);
const staticValue = getStaticValue(node.value, scope);
if (
!staticValue ||
@@ -95,8 +95,8 @@ module.exports = {
ruleReportsSuggestions = true;
}
},
- 'Program:exit'(node) {
- const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
+ 'Program:exit'() {
+ const scope = utils.getScope(context);
const metaNode = ruleInfo && ruleInfo.meta;
const hasSuggestionsProperty = utils
.evaluateObjectProperties(metaNode, scopeManager)
diff --git a/lib/rules/require-meta-schema-description.js b/lib/rules/require-meta-schema-description.js
index 4606a489..842d576f 100644
--- a/lib/rules/require-meta-schema-description.js
+++ b/lib/rules/require-meta-schema-description.js
@@ -25,7 +25,7 @@ module.exports = {
},
create(context) {
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const { scopeManager } = sourceCode;
const ruleInfo = utils.getRuleInfo(sourceCode);
if (!ruleInfo) {
diff --git a/lib/rules/require-meta-schema.js b/lib/rules/require-meta-schema.js
index f885aa4b..59cf4575 100644
--- a/lib/rules/require-meta-schema.js
+++ b/lib/rules/require-meta-schema.js
@@ -43,7 +43,7 @@ module.exports = {
},
create(context) {
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
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 24036071..a6aec027 100644
--- a/lib/rules/require-meta-type.js
+++ b/lib/rules/require-meta-type.js
@@ -38,15 +38,15 @@ module.exports = {
// Public
// ----------------------------------------------------------------------
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
const ruleInfo = utils.getRuleInfo(sourceCode);
if (!ruleInfo) {
return {};
}
return {
- Program(node) {
- const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
+ Program() {
+ const scope = utils.getScope(context);
const { scopeManager } = sourceCode;
const metaNode = ruleInfo.meta;
diff --git a/lib/rules/test-case-property-ordering.js b/lib/rules/test-case-property-ordering.js
index 6bf7a4a0..f6b4dd2f 100644
--- a/lib/rules/test-case-property-ordering.js
+++ b/lib/rules/test-case-property-ordering.js
@@ -55,7 +55,7 @@ module.exports = {
// Public
// ----------------------------------------------------------------------
const order = context.options[0] || defaultOrder;
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
return {
Program(ast) {
diff --git a/lib/rules/test-case-shorthand-strings.js b/lib/rules/test-case-shorthand-strings.js
index 68e813e3..0cf22333 100644
--- a/lib/rules/test-case-shorthand-strings.js
+++ b/lib/rules/test-case-shorthand-strings.js
@@ -39,7 +39,7 @@ module.exports = {
create(context) {
const shorthandOption = context.options[0] || 'as-needed';
- const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
+ const sourceCode = utils.getSourceCode(context);
// ----------------------------------------------------------------------
// Helpers
diff --git a/lib/utils.js b/lib/utils.js
index b049eacb..bc15d2c0 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -1003,4 +1003,25 @@ module.exports = {
return variable?.defs[0]?.type === 'Parameter';
},
+
+ getSourceCode(context) {
+ // TODO: remove contet.getSourceCode() when dropping eslint < v9
+ return context.sourceCode || context.getSourceCode();
+ },
+
+ getScope(context) {
+ // TODO: remove contet.getScope() when dropping eslint < v9
+ const sourceCode = context.sourceCode || context.getSourceCode();
+ return sourceCode.getScope?.(sourceCode.ast) || context.getScope();
+ },
+
+ getparserServices(context) {
+ // TODO: remove context.parserServices when dropping eslint < v9
+ return (context.sourceCode || context).parserServices;
+ },
+
+ getFilename(context) {
+ // TODO: just use context.filename when dropping eslint < v9
+ return context.filename || context.getFilename();
+ },
};
diff --git a/package.json b/package.json
index c29b6e39..be4d50fc 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-eslint-plugin",
- "version": "6.4.0",
+ "version": "6.5.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": 90,
+ "branches": 95,
"functions": 99,
"lines": 99,
"statements": 99
diff --git a/tests/lib/rules/no-meta-replaced-by.js b/tests/lib/rules/no-meta-replaced-by.js
new file mode 100644
index 00000000..d72f41ac
--- /dev/null
+++ b/tests/lib/rules/no-meta-replaced-by.js
@@ -0,0 +1,139 @@
+/**
+ * @fileoverview Disallows the usage of `meta.replacedBy` property
+ */
+
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-meta-replaced-by');
+const RuleTester = require('../eslint-rule-tester').RuleTester;
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const valid = [
+ 'module.exports = {};',
+ `
+ module.exports = {
+ create(context) {},
+ };
+ `,
+ `
+ module.exports = {
+ meta: {},
+ create(context) {},
+ };
+ `,
+ `
+ module.exports = {
+ meta: {
+ deprecated: true,
+ },
+ create(context) {},
+ };
+ `,
+ {
+ code: `
+ module.exports = {
+ meta: {
+ deprecated: {
+ replacedBy: [
+ {
+ rule: {
+ name: 'foo',
+ },
+ },
+ ],
+ },
+ },
+ create(context) {},
+ };
+ `,
+ errors: 0,
+ },
+];
+
+const invalid = [
+ {
+ code: `
+ module.exports = {
+ meta: {
+ replacedBy: ['the-new-rule'],
+ },
+ create(context) {},
+ };
+ `,
+ errors: [
+ {
+ messageId: 'useNewFormat',
+ line: 4,
+ endLine: 4,
+ },
+ ],
+ },
+ {
+ code: `
+ const meta = {
+ replacedBy: null,
+ };
+
+ module.exports = {
+ meta,
+ create(context) {},
+ };
+ `,
+ errors: [
+ {
+ messageId: 'useNewFormat',
+ line: 3,
+ endLine: 3,
+ },
+ ],
+ },
+ {
+ code: `
+ const spread = {
+ replacedBy: null,
+ };
+
+ module.exports = {
+ meta: {
+ ...spread,
+ },
+ create(context) {},
+ };
+ `,
+ errors: [{ messageId: 'useNewFormat' }],
+ },
+];
+
+const testToESM = (test) => {
+ if (typeof test === 'string') {
+ return test.replace('module.exports =', 'export default');
+ }
+
+ const code = test.code.replace('module.exports =', 'export default');
+
+ return {
+ ...test,
+ code,
+ };
+};
+
+new RuleTester({
+ languageOptions: { sourceType: 'commonjs' },
+}).run('no-meta-replaced-by', rule, {
+ valid,
+ invalid,
+});
+
+new RuleTester({
+ languageOptions: { sourceType: 'module' },
+}).run('no-meta-replaced-by', rule, {
+ valid: valid.map(testToESM),
+ invalid: invalid.map(testToESM),
+});