Skip to content

Change Request: Support additional rule metadata for deprecations #18061

@bmish

Description

@bmish

ESLint version

8.56.0

What problem do you want to solve?

There are long-time rule properties meta.deprecated and meta.replacedBy that have been intended to document when rules are deprecated and what their replacement rule(s) are. For the most part, usage would look something like this:

module.exports = { meta: { deprecated: true, replacedBy: ['replacement-rule-name'] } };

These properties are often used for generating plugin/rule documentation websites and in documentation tooling like eslint-doc-generator.

But there are some limitations to this current format.

  • Simply providing the replacement rule name as a string doesn't yield much context/explanation of the replacement/deprecation.
  • Some rules provide the replacement rule name with the plugin prefix as in prefix/rule-name while others just provide it as rule-name, which can result in ambiguity about whether the replacement rule is in the same plugin, a different third-party plugin, or ESLint core. For third-party plugins, there's no easy way to automatically determine where their documentation is located or how to link to them.

What do you think is the correct solution?

I would like to propose an extended meta.deprecated / meta.replacedBy rule property schema to reduce ambiguity and allow additional key details to be represented in it, described below as a TypeScript type for clarity:

type RuleMeta = {
  deprecated:
    | boolean // Existing boolean option, backwards compatible.
    | string // General deprecation message, such as why the deprecation occurred. Any truthy value implies deprecated.
    | {
        message?: string; // General deprecation message, such as why the deprecation occurred.
        url?: string; // URL to more information about this deprecation in general.
      };
  replacedBy:
    | readonly string[] // Existing string array option of rule names, backwards compatible.
    | readonly {
        /**
         * Plugin containing the replacement.
         * Use "eslint" if the replacement is an ESLint core rule.
         * Omit if the replacement is in the same plugin.
         */
        plugin?:
          | string // Plugin name i.e. "eslint-plugin-example" that contains the replacement rule.
          | {
              name?: string; // Plugin name i.e. "eslint-plugin-example" that contains the replacement rule.
              url?: string; // URL to plugin documentation.
            };
        rule:
          | string // Replacement rule name (without plugin prefix).
          | {
              name?: string; // Replacement rule name (without plugin prefix).
              url?: string; // URL to rule documentation.
            };
        deprecation?: {
          message?: string; // Message about this specific replacement, such as how to use/configure the replacement rule to achieve the same results as the rule being replaced.
          url?: string; // URL to more information about this specific deprecation/replacement.
        };
      }[]
    | undefined;
};

Real-world example of how this could be used based on the situation in #18053:

// lib/rules/semi.js
module.exports = {
  meta: {
    deprecated: {
      message: 'Stylistic rules are being moved out of ESLint core.',
      url: 'https://eslint.org/blog/2023/10/deprecating-formatting-rules/',
    },
    replacedBy: [
      {
        plugin: {
          name: '@stylistic/js',
          url: 'https://eslint.style/',
        },
        rule: {
          name: 'semi',
          url: 'https://eslint.style/rules/js/semi',
        },
      },
    ],
  },
};

This data could be used by documentation websites and tooling like eslint-doc-generator to generate notices like:

semi (deprecated)
Replaced by semi from @stylistic/js.
Stylistic rules are being moved out of ESLint core. Read more.

We can also support the same meta.deprecated and meta.replacedBy properties on configurations and processors (the other kinds of objects exported by ESLint plugins), replacing rule with config or processor as needed.

In terms of actual changes needed for this:

This proposal is inspired by:

Related:

Participation

  • I am willing to submit a pull request for this change.

Additional comments

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedThere is consensus among the team that this change meets the criteria for inclusioncoreRelates to ESLint's core APIs and featuresenhancementThis change enhances an existing feature of ESLint

    Type

    No type

    Projects

    Status

    Complete

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions