From f81e77c8cbe73e63a22bbe4176584e6d38b33592 Mon Sep 17 00:00:00 2001 From: ntnyq Date: Sat, 8 Mar 2025 10:42:26 +0800 Subject: [PATCH 1/2] feat(utils): support `DeprecatedInfo` for `rule.meta.deprecated` --- packages/utils/src/ts-eslint/Rule.ts | 62 +++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/packages/utils/src/ts-eslint/Rule.ts b/packages/utils/src/ts-eslint/Rule.ts index 3b281090606b..35cd1c7f7a0c 100644 --- a/packages/utils/src/ts-eslint/Rule.ts +++ b/packages/utils/src/ts-eslint/Rule.ts @@ -27,6 +27,64 @@ export interface RuleMetaDataDocs { url?: string; } +export interface ExternalSpecifier { + /** + * Name of the referenced plugin / rule. + */ + name?: string; + /** + * URL pointing to documentation for the plugin / rule. + */ + url?: string; +} + +export interface ReplacedByInfo { + /** + * General message presented to the user, e.g. how to replace the rule + */ + message?: string; + /** + * URL to more information about this replacement in general + */ + url?: string; + /** + * Name should be "eslint" if the replacement is an ESLint core rule. Omit + * the property if the replacement is in the same plugin. + */ + plugin?: ExternalSpecifier; + /** + * Name and documentation of the replacement rule + */ + rule?: ExternalSpecifier; +} + +export interface DeprecatedInfo { + /** + * General message presented to the user, e.g. for the key rule why the rule + * is deprecated or for info how to replace the rule. + */ + message?: string; + /** + * URL to more information about this deprecation in general. + */ + url?: string; + /** + * An empty array explicitly states that there is no replacement. + */ + replacedBy?: ReplacedByInfo[]; + /** + * The package version since when the rule is deprecated (should use full + * semver without a leading "v"). + */ + deprecatedSince?: string; + /** + * The estimated version when the rule is removed (probably the next major + * version). null means the rule is "frozen" (will be available but will not + * be changed). + */ + availableUntil?: string | null; +} + export interface RuleMetaData< MessageIds extends string, PluginDocs = unknown, @@ -35,7 +93,7 @@ export interface RuleMetaData< /** * True if the rule is deprecated, false otherwise */ - deprecated?: boolean; + deprecated?: boolean | DeprecatedInfo; /** * Documentation for the rule */ @@ -56,6 +114,8 @@ export interface RuleMetaData< messages: Record; /** * The name of the rule this rule was replaced by, if it was deprecated. + * + * @deprecated since eslint 9.21.0, in favor of `RuleMetaData#deprecated.replacedBy` */ replacedBy?: readonly string[]; /** From ae1128ff9bafa40b881991c2d5b5a2db7570a3ea Mon Sep 17 00:00:00 2001 From: ntnyq Date: Mon, 10 Mar 2025 23:10:37 +0800 Subject: [PATCH 2/2] feat(eslint-plugin): add deprecated info to `rule.meta.deprecated` --- .../src/rules/no-empty-interface.ts | 13 ++++++++- .../src/rules/no-loss-of-precision.ts | 13 ++++++++- .../eslint-plugin/src/rules/no-type-alias.ts | 13 ++++++++- .../src/rules/no-var-requires.ts | 13 ++++++++- .../src/rules/prefer-ts-expect-error.ts | 13 ++++++++- .../src/rules/sort-type-constituents.ts | 27 ++++++++++++++++++- packages/website/rulesMeta.ts | 1 - 7 files changed, 86 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-empty-interface.ts b/packages/eslint-plugin/src/rules/no-empty-interface.ts index 1a2948bc7fb3..cbcb38f1092c 100644 --- a/packages/eslint-plugin/src/rules/no-empty-interface.ts +++ b/packages/eslint-plugin/src/rules/no-empty-interface.ts @@ -16,7 +16,18 @@ export default createRule({ name: 'no-empty-interface', meta: { type: 'suggestion', - deprecated: true, + deprecated: { + deprecatedSince: '8.0.0', + replacedBy: [ + { + rule: { + name: '@typescript-eslint/no-empty-object-type', + url: 'https://typescript-eslint.io/rules/no-empty-object-type', + }, + }, + ], + url: 'https://github.com/typescript-eslint/typescript-eslint/pull/8977', + }, docs: { description: 'Disallow the declaration of empty interfaces', }, diff --git a/packages/eslint-plugin/src/rules/no-loss-of-precision.ts b/packages/eslint-plugin/src/rules/no-loss-of-precision.ts index ba1c75823511..bd86e2f1ceed 100644 --- a/packages/eslint-plugin/src/rules/no-loss-of-precision.ts +++ b/packages/eslint-plugin/src/rules/no-loss-of-precision.ts @@ -20,7 +20,18 @@ export default createRule({ meta: { type: 'problem', // defaultOptions, -- base rule does not use defaultOptions - deprecated: true, + deprecated: { + deprecatedSince: '8.0.0', + replacedBy: [ + { + rule: { + name: 'no-loss-of-precision', + url: 'https://eslint.org/docs/latest/rules/no-loss-of-precision', + }, + }, + ], + url: 'https://github.com/typescript-eslint/typescript-eslint/pull/8832', + }, docs: { description: 'Disallow literal numbers that lose precision', extendsBaseRule: true, diff --git a/packages/eslint-plugin/src/rules/no-type-alias.ts b/packages/eslint-plugin/src/rules/no-type-alias.ts index 99ed9675f344..0dae356d03f8 100644 --- a/packages/eslint-plugin/src/rules/no-type-alias.ts +++ b/packages/eslint-plugin/src/rules/no-type-alias.ts @@ -37,7 +37,18 @@ export default createRule({ name: 'no-type-alias', meta: { type: 'suggestion', - deprecated: true, + deprecated: { + deprecatedSince: '6.0.0', + replacedBy: [ + { + rule: { + name: '@typescript-eslint/consistent-type-definitions', + url: 'https://typescript-eslint.io/rules/consistent-type-definitions', + }, + }, + ], + url: 'https://github.com/typescript-eslint/typescript-eslint/pull/6229', + }, docs: { description: 'Disallow type aliases', // too opinionated to be recommended diff --git a/packages/eslint-plugin/src/rules/no-var-requires.ts b/packages/eslint-plugin/src/rules/no-var-requires.ts index d677f35c3329..cd4ad15a24a1 100644 --- a/packages/eslint-plugin/src/rules/no-var-requires.ts +++ b/packages/eslint-plugin/src/rules/no-var-requires.ts @@ -15,7 +15,18 @@ export default createRule({ name: 'no-var-requires', meta: { type: 'problem', - deprecated: true, + deprecated: { + deprecatedSince: '8.0.0', + replacedBy: [ + { + rule: { + name: '@typescript-eslint/no-require-imports', + url: 'https://typescript-eslint.io/rules/no-require-imports', + }, + }, + ], + url: 'https://github.com/typescript-eslint/typescript-eslint/pull/8334', + }, docs: { description: 'Disallow `require` statements except in import statements', }, diff --git a/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts b/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts index 13541d4174ca..b2adbd5e7b67 100644 --- a/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts +++ b/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts @@ -11,7 +11,18 @@ export default createRule<[], MessageIds>({ name: 'prefer-ts-expect-error', meta: { type: 'problem', - deprecated: true, + deprecated: { + deprecatedSince: '7.11.0', + replacedBy: [ + { + rule: { + name: '@typescript-eslint/ban-ts-comment', + url: 'https://typescript-eslint.io/rules/ban-ts-comment', + }, + }, + ], + url: 'https://github.com/typescript-eslint/typescript-eslint/pull/9081', + }, docs: { description: 'Enforce using `@ts-expect-error` over `@ts-ignore`', }, diff --git a/packages/eslint-plugin/src/rules/sort-type-constituents.ts b/packages/eslint-plugin/src/rules/sort-type-constituents.ts index 2db45a55722c..5e3cdc13e114 100644 --- a/packages/eslint-plugin/src/rules/sort-type-constituents.ts +++ b/packages/eslint-plugin/src/rules/sort-type-constituents.ts @@ -121,7 +121,32 @@ export default createRule({ name: 'sort-type-constituents', meta: { type: 'suggestion', - deprecated: true, + deprecated: { + deprecatedSince: '7.13.0', + replacedBy: [ + { + plugin: { + name: 'eslint-plugin-perfectionist', + url: 'https://perfectionist.dev', + }, + rule: { + name: 'perfectionist/sort-intersection-types', + url: 'https://perfectionist.dev/rules/sort-intersection-types', + }, + }, + { + plugin: { + name: 'eslint-plugin-perfectionist', + url: 'https://perfectionist.dev', + }, + rule: { + name: 'perfectionist/sort-union-types', + url: 'https://perfectionist.dev/rules/sort-union-types', + }, + }, + ], + url: 'https://github.com/typescript-eslint/typescript-eslint/pull/9253', + }, docs: { description: 'Enforce constituents of a type union/intersection to be sorted alphabetically', diff --git a/packages/website/rulesMeta.ts b/packages/website/rulesMeta.ts index fae9f043e0b9..80d3b70e71ab 100644 --- a/packages/website/rulesMeta.ts +++ b/packages/website/rulesMeta.ts @@ -6,7 +6,6 @@ export const rulesMeta = Object.entries(rules).map(([name, content]) => ({ fixable: content.meta.fixable, hasSuggestions: content.meta.hasSuggestions, name, - replacedBy: content.meta.replacedBy, type: content.meta.type, }));