Skip to content

feat: definePlugin#132

Open
JoshuaKGoldberg wants to merge 26 commits intoeslint:mainfrom
JoshuaKGoldberg:2025-define-plugin
Open

feat: definePlugin#132
JoshuaKGoldberg wants to merge 26 commits intoeslint:mainfrom
JoshuaKGoldberg:2025-define-plugin

Conversation

@JoshuaKGoldberg
Copy link
Copy Markdown
Contributor

@JoshuaKGoldberg JoshuaKGoldberg marked this pull request as ready for review April 23, 2025 19:21
@fasttime fasttime added the Initial Commenting This RFC is in the initial feedback stage label Apr 24, 2025
This RFC's scope intentionally does include a "rule creator" factory akin to [`@typescript-eslint/rule-creator`](https://typescript-eslint.io/developers/custom-rules/#rulecreator).
Creating such a factory in ESLint could be useful but does not seem to be required for any of the problems `definePlugin` aims to solve.

Should a "rule creator" factory be tackled separately?
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like an adjacent but separate conversation.

Copy link
Copy Markdown
Member

@nzakas nzakas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the work and research you put into this.

At a high-level, I can see the value of a definePlugin() helper function that:

  • provides type safety
  • makes it easier to define configs
  • enforces best practices (requiring meta.name, meta.version, and meta.namspace

I'm less convinced with some of the more ambitious parts of this proposal such as:

  • lazy-loading rules
  • autocreation of configs

By way of example, defineConfig()s goals were primary to provide type safety and add an extends key, so I'm wondering if maybe it makes sense to scale down definePlugin()'s MVP?

JoshuaKGoldberg and others added 2 commits May 1, 2025 16:37
Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
Co-authored-by: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com>
@JoshuaKGoldberg
Copy link
Copy Markdown
Contributor Author

JoshuaKGoldberg commented Jun 16, 2025

I'm less convinced with some of the more ambitious parts of this proposal such as:

Added:

`configs` is the only property `definePlugin` receives with a different shape than the output plugin object.

- If `configs` is not provided, a default config is generated.
- `configs` may be provided as a shape that does not include the plugin's name.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not totally following this... Are we saying that if I provide

definePlugin({
   meta,
   rules,
   configs: {
     [`${pluginName}/recommended`]: { /* ... */  },
     config1: { /* ... */ },
     config2: { /* ... */ },
   }
});

that all these configs should be transformed? Or that only the `${pluginName}/recommended` one will be? Or maybe something else?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you're right, this is weirdly phrased and kind of wrong. It's the first one. Each individual config will be transformed. Edited:

  • This part just says it's an object whose properties describe the configs to create.
  • Generated Configs describes the key-value mapping in more detail

cc4e515

Is that more clear? I'm having a hard time describing this.

JoshuaKGoldberg and others added 3 commits June 30, 2025 11:06
Co-authored-by: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com>
Copy link
Copy Markdown
Member

@fasttime fasttime left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your work, and apologies for the delayed review. I've added some comments throughout. Given the length of the text, I may have missed something. Feel free to let me know if anything in my remarks seems off.

JoshuaKGoldberg and others added 4 commits September 25, 2025 12:51
Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
@JoshuaKGoldberg
Copy link
Copy Markdown
Contributor Author

@fasttime @nzakas I removed the config rules generation points and noted them as out of scope, similar to lazy-loading rules. The only modification the definePlugin function now would do is merging in a plugins array with the plugin set by meta.name.

@fasttime
Copy link
Copy Markdown
Member

fasttime commented Apr 2, 2026

I think this RFC slipped off our radar for a bit while we were busy with the v10 release. Reading the updated proposal, it seems the main behavior change is that definePlugin would automatically inject the plugin into each provided config (i.e. add plugins: { [meta.name]: plugin }).

That seems useful for the common case. My only concern is that there will be cases where this behavior is undesirable or at least surprising, where plugin authors don't want plugins populated automatically.

To avoid having an automatic behavior that can't be opted out of, maybe we could make this explicit via an option (e.g. addPluginToConfigs: true)? That would keep the helper purely pass-through + validation by default, while still supporting this convenience when required, and it leaves room for additional opt-in behavior later.

For comparison, defineConfig doesn't mutate the passed in config by default; it adds new syntax (extends) to opt into extra functionality. Thoughts?

@bradzacher
Copy link
Copy Markdown

My only concern is that there will be cases where this behavior is undesirable or at least surprising, where plugin authors don't want plugins populated automatically

Is it worth designing around the 0.0001% case? Couldn't those users just not use the function?

@fasttime
Copy link
Copy Markdown
Member

fasttime commented Apr 3, 2026

Is it worth designing around the 0.0001% case? Couldn't those users just not use the function?

For example, a plugin like @eslint/markdown that provides a "recommended-legacy" config in eslintrc format couldn't be implemented using only definePlugin as it's currently proposed.

Previous versions of this RFC also included other useful features, such as automatically generating a recommended config. Those ideas were ultimately dismissed because there was no way to opt out of the proposed behavior, which made the helper function less adoptable. That's why I'd suggest avoiding modifying the plugin automatically. If we want to apply defaults for common cases, it would be good to provide at least opt-outs for each of them.

@JoshuaKGoldberg
Copy link
Copy Markdown
Contributor Author

that provides a "recommended-legacy" config in eslintrc format

Is that a problem, though? This RFC has no intention of supporting eslintrc. I'm under the impression that's out-of-scope for general ESLint projects as of ESLint 10.

dismissed ... avoiding modifying the plugin automatically

In my mind, the difference between this still-in-the-RFC name merging behavior and those past utilities is that this name merging behavior is seen as useful 100% of the time, whereas those were more subjective. Without the eslintrc use case, is there any time when someone wouldn't want that behavior?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Initial Commenting This RFC is in the initial feedback stage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants