eslint / eslint

Find and fix problems in your JavaScript code.
https://eslint.org
MIT License
25.06k stars 4.53k forks source link

Change Request: Support config arrays in plugins that use flat config style #18752

Closed alecgibson closed 2 months ago

alecgibson commented 2 months ago

ESLint version

v8.57.0

What problem do you want to solve?

In "classic config" world, it was possible to define a plugin that had extends and overrides options:

module.exports = {
  configs: {
    recommended: {
      extends: [/** extends stuff */], 
      overrides: [/** can also override */],
    },
  },
};

When I tried to move an existing plugin to use flat config, I first tried to do this using an array for my config:

module.exports = {
  configs: {
    recommended: [ // throws an error - not allowed to be an array
      baseConfig,
      ...overrides,
      // Define new rules
      {...},
    ], 
  },
};

Indeed, checking the documentation, I see that recommended should be defined as an object.

My question is simple: how is a plugin meant to define a config that extends other configs?.

I don't think I'm unusual in wanting to do this. For example, @typescript-eslint extends its own configs and will presumably need this feature in order to migrate their configs to flat config?

What do you think is the correct solution?

Allow plugins to define configs as an array, which is more consistent with the flat config style anyway.

Participation

Additional comments

No response

alecgibson commented 2 months ago

I assume the workaround is to export arrays of configs, and then the consumer would import these arrays using spread syntax, but I don't know if this is necessarily expected or user-friendly syntax:

/** plugin */

module.exports = {
  configGroups: {
    recommended: [],
  },
};
/** eslint.config.js */

module.exports = [
  ...plugin.configGroups.recommended,
];
alecgibson commented 2 months ago

I actually wonder if my request is a bigger, more general request of why not let us define configs as arrays of arrays?

Something like:

type AssembledConfig = Array<Config | AssembledConfig>;

const config: AssembledConfig = [
  [
    {rules: {}}, 
    {rules: {}},
  ],
  {
    rules: {},
  },
];

I realise that this may run antithetical to the idea of a flat config, but adding this level of flexibility would simplify plugin definition, as well as using FlatCompat (no more need for spread operators).

Presumably internally eslint would just need to call config.flat() and this would all "just work"...?

Sorry if this has already been discussed and dismissed; I raised this issue searching specifically for plugin-related issues, and have only just realised it may be more complicated (or simpler...?) than I first thought.

I guess a consumer workaround is that we could ourselves call .flat() to get this behaviour today?

/** eslint.config.js */
export default [
  compat.extends('legacy-plugin'),
  plugin.configs.definedAsArray,
  {
    rules: {},
  },
].flat();

Maybe this could be as simple as a documentation change?

nzakas commented 2 months ago

Please see the documention on combining configs. If you'd like to add an example using .flat(), please feel free.

We did discuss allowing arrays inside of the config array, and even designed and built that feature, but it turned out to create more confusion because instead of wondering if a config was an array (so you could use .map() to work with) or an object, you also need to worry if the array contains arrays.

alecgibson commented 2 months ago

@nzakas thanks — I assumed it had already been discussed!

I guess my question is now: what's the advice to plugin authors who want their configs to extend other configs? Or is it acceptable to expect consumers to have to spread a plugin config? (I think I'm unclear on what the contract is here)

nzakas commented 2 months ago

I guess my question is now: what's the advice to plugin authors who want their configs to extend other configs? Or is it acceptable to expect consumers to have to spread a plugin config? (I think I'm unclear on what the contract is here)

Export an array and let the consumers spread it. This is in the plugin docs

alecgibson commented 2 months ago

Aha RTFM 🤦🏼

Thanks for being patient! Have raised a PR to help future documentationally-challenged people like me https://github.com/eslint/eslint/pull/18753