Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] Support new ESLint flat config #2556

Closed
Tracked by #922 ...
TomerAberbach opened this issue Sep 25, 2022 · 58 comments · Fixed by #3018
Closed
Tracked by #922 ...

[Feature Request] Support new ESLint flat config #2556

TomerAberbach opened this issue Sep 25, 2022 · 58 comments · Fixed by #3018

Comments

@TomerAberbach
Copy link

ESLint has a new experimental config file format and this plugin doesn't work with it. The plugin fails at this line because the new config format doesn't pass parsers via paths. Instead the parser object itself is passed.

I was able to mostly fix this in my fork, but because the parserPath doesn't exist anymore for this config format some of the keysFromParser logic won't work anymore.

Anyway, figured I'd open this issue to start discussion on the new config file format!

@TomerAberbach TomerAberbach changed the title [Feature Request] Support new ESLint flag config [Feature Request] Support new ESLint flat config Sep 25, 2022
@ljharb
Copy link
Member

ljharb commented Oct 9, 2022

If there's a way to support both configs at the same time, a PR would be appreciated.

Look to jsx-eslint/eslint-plugin-jsx-a11y#891 and jsx-eslint/eslint-plugin-react#3429 for some preferred direction.

@TomerAberbach
Copy link
Author

Yup, my fork does support both configs. The only thing I'm unsure about in my fork is how to adapt the keysFromParser logic to work without knowing the paths.

But based on this comment though, it sounds like maybe it's fine for the new config format not to set visitorKeys to anything? Let me know!

@ljharb
Copy link
Member

ljharb commented Nov 1, 2022

As long as the rules and configs work in both config systems, we're good.

@b0o
Copy link

b0o commented Dec 22, 2022

The plugin fails at this line because the new config format doesn't pass parsers via paths.

We're experiencing this issue as well. @TomerAberbach it would be really cool if you could submit a PR with your fixes!

@TomerAberbach
Copy link
Author

Thanks for the message! Been a bit busy with other stuff and kind of forgot about this 😅

I'll try to pick this up soon!

@goosewobbler
Copy link

goosewobbler commented Jan 31, 2023

As long as the rules and configs work in both config systems, we're good.

I'll test this on my fork of @TomerAberbach's fork when I get a chance.

@TomerAberbach
Copy link
Author

Thanks @goosewobbler! A little while ago I tried making it so that all the tests get run twice (once with the legacy and once with the new config system), but got stuck trying to get stuff working and then got busy 🙃

Would be awesome if you're able to figure out how to test both systems! Happy to answer any questions about the fork if that would help as well

@CallMeLaNN
Copy link

CallMeLaNN commented Feb 6, 2023

I managed to make it work without changes in parser.js source. Since parserPath can be parser name and keysFromParser can accept it, we can use the settings.import/parsers to add the missing parser from getParserPath function.

So that can pass the parserPath is required! error but actually I don't see parserPath is useful to keysFromParser because on my env it always doesn't hit the line mentioned in keysFromParser logic won't work anymore. So "espree" below is the default I guess from the source.

This is my working test config on node and typescript env. (EDIT: I just remove from using FlatCompat and directly construct the config)

import importPlugin from "eslint-plugin-import";

export default [
  // ... "eslint:recommended"
  {
    // All eslint-plugin-import config is here
    languageOptions: {
      parserOptions: {
        // Eslint doesn't supply ecmaVersion in `parser.js` `context.parserOptions`
        // This is required to avoid ecmaVersion < 2015 error or 'import' / 'export' error
        ecmaVersion: "latest",
        sourceType: "module",
      },
    },
    plugins: { import: importPlugin },
    settings: {
      // This will do the trick
      "import/parsers": {
        espree: [".js", ".cjs", ".mjs", ".jsx"],
      },
      "import/resolver": {
        typescript: true,
        node: true,
      },
    },
    rules: {
      ...importPlugin.configs["recommended"].rules,
    },
  },
  // ... add other plugins like typescript-eslint etc
  {
    // All my custom config
    languageOptions: {
      // This default get replaced by plugins, so I added back. Not related probably.
      ecmaVersion: "latest",
      sourceType: "module",
     // ... globals etc
    },
  },
  // ... custom rules etc
]

It was discovered and tested on a module js file with import/namespace rule throw like this. I lint the eslint.config.js itself lol

Parse errors in imported module '@eslint/eslintrc': parserPath is required! (undefined:undefined) eslint(import/namespace)

@ziimakc
Copy link

ziimakc commented May 20, 2023

Any way to make it work in meantime with flat config?

UPD. espree did the trick:

"import/parsers": {
    espree: [".js", ".cjs", ".mjs", ".jsx"],
    "@typescript-eslint/parser": [".ts"],
},

@500-internal-server-error

Hello, I'm still having problems with mixing this plugin with my ESLint flat config. The fork mentioned in the OP seems to be a bit old. Is there any way I can use this plugin with the new flat config?

$ npx eslint src/index.ts

Oops! Something went wrong! :(

ESLint: 8.42.0

Configuration for rule "plugins" is invalid. Expected severity of "off", 0, "warn", 1, "error", or 2.

You passed '"import"'.

See https://eslint.org/docs/latest/use/configure/rules#using-configuration-files for configuring rules.

Thanks in advance.

@ljharb
Copy link
Member

ljharb commented Jun 8, 2023

Not until that support is released.

500-internal-server-error added a commit to binusgdc/VCPA that referenced this issue Jun 9, 2023
Downgraded ESLint config file to use the old cascading config instead of the new flat config. This allows eslint-plugin-import to be used, as it seems to only support the old config.

See:

- import-js/eslint-plugin-import#2556

- eslint/eslint#13481
500-internal-server-error added a commit to binusgdc/VCPA that referenced this issue Jun 9, 2023
Downgraded ESLint config file to use the old cascading config instead of the new flat config. This allows eslint-plugin-import to be used, as it seems to only support the old config.

See:
- import-js/eslint-plugin-import#2556
- eslint/eslint#13481
@OlivierZal
Copy link

OlivierZal commented Aug 2, 2023

Hi @ljharb and @timmywil,

I guess my issue is related to this one but I'm not sure: I've recently migrated my .eslintrc to the new eslint.config.js eslint flat config:

import js from '@eslint/js'
/* eslint-disable import/no-unresolved */
import tsParser from '@typescript-eslint/parser'
import tsPlugin from '@typescript-eslint/eslint-plugin'
/* eslint-enable import/no-unresolved */
import prettierConfig from 'eslint-config-prettier'
import importPlugin from 'eslint-plugin-import'
import globals from 'globals'

export default [
  {
    ignores: ['.homeybuild/'],
  },
  js.configs.recommended,
  {
    languageOptions: {
      ecmaVersion: 'latest',
      globals: {
        ...globals.browser,
        ...globals.es2024,
        ...globals.node,
      },
      sourceType: 'module',
    },
    linterOptions: {
      reportUnusedDisableDirectives: true,
    },
    plugins: {
      import: importPlugin,
    },
    rules: importPlugin.configs.recommended.rules,
  },
  {
    files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'],
    languageOptions: {
      parser: tsParser,
      parserOptions: {
        project: './tsconfig.json',
      },
    },
    plugins: {
      '@typescript-eslint': tsPlugin,
    },
    rules: {
      ...tsPlugin.configs['eslint-recommended'].overrides[0].rules,
      ...tsPlugin.configs['strict-type-checked'].rules,
      ...tsPlugin.configs['stylistic-type-checked'].rules,
      ...importPlugin.configs.typescript.rules,
      '@typescript-eslint/no-unsafe-argument': 'off',
      '@typescript-eslint/no-unsafe-assignment': 'off',
      '@typescript-eslint/no-unsafe-call': 'off',
      '@typescript-eslint/no-unsafe-member-access': 'off',
      '@typescript-eslint/no-unsafe-return': 'off',
      '@typescript-eslint/no-unused-vars': [
        'error',
        {
          varsIgnorePattern: 'onHomeyReady',
        },
      ],
    },
    settings: {
      ...importPlugin.configs.typescript.settings,
      'import/resolver': {
        ...importPlugin.configs.typescript.settings['import/resolver'],
        typescript: {
          alwaysTryTypes: true,
        },
      },
    },
  },
  prettierConfig,
]

and I got the following errors:

  2:19  error    Parse errors in imported module 'axios': parserPath or languageOptions.parser is required! (undefined:undefined)  import/namespace
  2:19  error    Parse errors in imported module 'axios': parserPath or languageOptions.parser is required! (undefined:undefined)  import/default
  2:19  warning  Parse errors in imported module 'axios': parserPath or languageOptions.parser is required! (undefined:undefined)  import/no-named-as-default
  2:19  warning  Parse errors in imported module 'axios': parserPath or languageOptions.parser is required! (undefined:undefined)  import/no-named-as-default-member

Should it be solved with #2829, or is it another issue?

I'm also wondering why I'm getting this error (only for @typescript-eslint/* imports):

  2:22  error  Unable to resolve path to module '@typescript-eslint/parser'         import/no-unresolved
  3:22  error  Unable to resolve path to module '@typescript-eslint/eslint-plugin'  import/no-unresolved

@ljharb
Copy link
Member

ljharb commented Aug 4, 2023

@OlivierZal yes, i suspect #2829 will solve your issue.

For the other one, are those packages installed on disk and in package.json?

@OlivierZal
Copy link

OlivierZal commented Aug 4, 2023

Thanks @ljharb for your answer.

Yes, they are installed and work very well: @typescript-eslint errors are correctly raised when the linter is run.

So do you think it is worth raising a separate issue on this one?

@ljharb
Copy link
Member

ljharb commented Aug 5, 2023

Yes, I think so, thanks

@simlu
Copy link

simlu commented Aug 14, 2023

Running into the same error here parserPath or languageOptions.parser is required! when trying to use FlatESLint.

I'll keep an eye on #2829 for sure 👍

@ljharb
Copy link
Member

ljharb commented Jun 7, 2024

@Mustachipleb it's not up to me whether it's acceptable. i just wouldn't want to allow an unofficial solution to be spammed here.

Yes, you're right, the library is in limbo until it's addressed.

@michaelfaith
Copy link
Contributor

michaelfaith commented Jun 18, 2024

Just catching up on this, it seems like the main blocking issue is that the legacy config uses a parser by name, whereas the flat config needs the parser object? And in the typescript config we're just setting the parser by name. What if, in a new flat config version of the typescript config we setting the parser as the typescript parser object, and added the @typescript-eslint/parser package as an optional peer dependency?

@ljharb
Copy link
Member

ljharb commented Jun 18, 2024

@michaelfaith adding an optional peer dependency is still a breaking change, both because it constrains the version range of one that might already be installed, and because not every npm version we support, supports optional peer deps.

@Goldziher

This comment was marked as outdated.

@goosewobbler

This comment was marked as spam.

@Goldziher

This comment was marked as off-topic.

@ljharb

This comment was marked as resolved.

@vinicius-sabadim
Copy link

I was able to make it work on my setup following this official guide.

A summary of the fix:

import { fixupPluginRules } from '@eslint/compat'
import eslintPluginImport from 'eslint-plugin-import'

export default [
  {
      // code omit
     plugins: { import: fixupPluginRules(eslintPluginImport) }
  }
]

@glitch452
Copy link

I was able to make it work on my setup following this official guide.

A summary of the fix:

import { fixupPluginRules } from '@eslint/compat'
import eslintPluginImport from 'eslint-plugin-import'

export default [
  {
      // code omit
     plugins: { import: fixupPluginRules(eslintPluginImport) }
  }
]

I gave this a try. It doesn't seem to work with rules like import/namespace and import/export.

I'm getting the error parserPath or languageOptions.parser is required. I do have the @typescript-eslint parser setup.

@vinicius-sabadim
Copy link

vinicius-sabadim commented Aug 2, 2024

@glitch452 I tested the import/export: 'error' here and it's working without problems. I'll share my whole eslint.config.mjs for you to compare:

import path from 'node:path'
import { fileURLToPath } from 'node:url'

import { fixupConfigRules, fixupPluginRules } from '@eslint/compat'
import { FlatCompat } from '@eslint/eslintrc'
import js from '@eslint/js'
import tsParser from '@typescript-eslint/parser'
import eslintPluginImport from 'eslint-plugin-import'
import prettier from 'eslint-plugin-prettier'
import simpleImportSort from 'eslint-plugin-simple-import-sort'
import globals from 'globals'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const compat = new FlatCompat({
  baseDirectory: __dirname,
  recommendedConfig: js.configs.recommended,
  allConfig: js.configs.all,
})

export default [
  ...fixupConfigRules(
    compat.extends(
      'eslint:recommended',
      'plugin:@typescript-eslint/recommended',
      'plugin:react/recommended',
      'plugin:jsx-a11y/recommended',
      'plugin:prettier/recommended',
      'plugin:storybook/recommended',
      'plugin:jsonc/recommended-with-jsonc',
    ),
  ),
  {
    plugins: {
      import: fixupPluginRules(eslintPluginImport),
      'simple-import-sort': simpleImportSort,
      prettier: fixupPluginRules(prettier),
    },

    languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.amd,
        ...globals.node,
      },

      parser: tsParser,
      ecmaVersion: 'latest',
      sourceType: 'module',

      parserOptions: {
        ecmaFeatures: {
          jsx: true,
        },
      },
    },

    settings: {
      react: {
        version: 'detect',
      },

      'import/resolver': {
        typescript: true,

        node: {
          paths: ['src'],
          extensions: ['.js', '.jsx', '.ts', '.tsx'],
        },
      },
    },

    rules: {
      // omit other rules
      'import/export': 'error',
      'import/newline-after-import': ['error', { count: 1, considerComments: true }],
    },
  },
]

if I run the ESLint in this code:

export const foo = function () {
  /*...*/
} // Multiple exports of name 'foo'.

function bar() {
  /*...*/
}
export { bar as foo } // Multiple exports of name 'foo'.

I get error Multiple exports of name 'foo' import/export

michaelfaith added a commit to michaelfaith/eslint-plugin-import that referenced this issue Aug 4, 2024
This change adds support for ESLint's new Flat config system.  It maintains backwards compatibility with eslintrc style configs as well.

To achieve this, we're now dynamically creating flat configs on a new `flatConfigs` export.  I was a bit on the fence about using this convention, or the other convention that's become prevalent in the community: adding the flat configs directly to the `configs` object, but with a 'flat/' prefix.  I like this better, since it's slightly more ergonomic when using it in practice.  e.g. `...importX.flatConfigs.recommended` vs `...importX.configs['flat/recommended']`, but i'm open to changing that.

Example Usage

```js
import importPlugin from 'eslint-plugin-import';
import js from '@eslint/js';
import tsParser from '@typescript-eslint/parser';

export default [
  js.configs.recommended,
  importPlugin.flatConfigs.recommended,
  importPlugin.flatConfigs.react,
  importPlugin.flatConfigs.typescript,
  {
    files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
    languageOptions: {
      parser: tsParser,
      ecmaVersion: 'latest',
      sourceType: 'module',
    },
    ignores: ['eslint.config.js'],
    rules: {
      'no-unused-vars': 'off',
      'import/no-dynamic-require': 'warn',
      'import/no-nodejs-modules': 'warn',
    },
  },
];
```

Note: in order to fill a gap in a future API gap for the `no-unused-module`, this takes advantage of a *proposed* new API on the ESLint context, that currently only exists in a POC state (unreleased).

Closes import-js#2556
@kubosho
Copy link

kubosho commented Aug 4, 2024

By using fixupConfigRules() and fixupPluginRules() provided by @eslint/compat, and FlatCompat provided by @eslint/eslintrc, I able to narrow down the scope of using ESLint compatibility utilities and migrate to Flat config.

Sample code is as follows:

import { fixupConfigRules, fixupPluginRules } from "@eslint/compat";
import { FlatCompat } from "@eslint/eslintrc";
import eslintPluginImport from "eslint-plugin-import";
import importRecommendedConfig from "eslint-plugin-import/config/recommended.js";

const flatCompat = new FlatCompat();

export default [
  ...fixupConfigRules(flatCompat.config(importRecommendedConfig)),
  {
    plugins: {
      import: fixupPluginRules(eslintPluginImport),
    },
    rules: {
      // some rewrite rules
    },
    settings: {
      // some rewrite settings
    },
  },
];

By writing as above, can migrate import-related configurations to flat config without waiting for eslint-plugin-import to support flat config.

That code related to imports including other configurations is in my repository.

@mshima
Copy link

mshima commented Aug 7, 2024

One more example:

import importPlugin from 'eslint-plugin-import';
import importRecommented from 'eslint-plugin-import/config/recommended.js';
import { fixupPluginRules } from '@eslint/compat';

export default [
  {
    languageOptions: {
      // import plugin does not use ecmaVersion and sourceType from languageOptions object
      parserOptions: {
        ecmaVersion: 2022,
        sourceType: 'module',
      },
    },
    plugins: {
      import: fixupPluginRules(importPlugin),
    },
    settings: {
      'import/parsers': {
        espree: ['.js', '.cjs', '.mjs'],
        '@typescript-eslint/parser': ['.ts'],
      },
      'import/resolver': {
        node: true,
        typescript: true,
      },
    },
    rules: {
      ...importRecommented.rules,
      'import/no-named-as-default-member': 'off',
      // Custom rules
    },
  },
];

michaelfaith added a commit to michaelfaith/eslint-plugin-import that referenced this issue Aug 18, 2024
This change adds support for ESLint's new Flat config system.  It maintains backwards compatibility with eslintrc style configs as well.

To achieve this, we're now dynamically creating flat configs on a new `flatConfigs` export.  I was a bit on the fence about using this convention, or the other convention that's become prevalent in the community: adding the flat configs directly to the `configs` object, but with a 'flat/' prefix.  I like this better, since it's slightly more ergonomic when using it in practice.  e.g. `...importX.flatConfigs.recommended` vs `...importX.configs['flat/recommended']`, but i'm open to changing that.

Example Usage

```js
import importPlugin from 'eslint-plugin-import';
import js from '@eslint/js';
import tsParser from '@typescript-eslint/parser';

export default [
  js.configs.recommended,
  importPlugin.flatConfigs.recommended,
  importPlugin.flatConfigs.react,
  importPlugin.flatConfigs.typescript,
  {
    files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
    languageOptions: {
      parser: tsParser,
      ecmaVersion: 'latest',
      sourceType: 'module',
    },
    ignores: ['eslint.config.js'],
    rules: {
      'no-unused-vars': 'off',
      'import/no-dynamic-require': 'warn',
      'import/no-nodejs-modules': 'warn',
    },
  },
];
```

Note: in order to fill a gap in a future API gap for the `no-unused-module`, this takes advantage of a *proposed* new API on the ESLint context, that currently only exists in a POC state (unreleased).

Closes import-js#2556
michaelfaith added a commit to michaelfaith/eslint-plugin-import that referenced this issue Aug 18, 2024
This change adds support for ESLint's new Flat config system.  It maintains backwards compatibility with eslintrc style configs as well.

To achieve this, we're now dynamically creating flat configs on a new `flatConfigs` export.  I was a bit on the fence about using this convention, or the other convention that's become prevalent in the community: adding the flat configs directly to the `configs` object, but with a 'flat/' prefix.  I like this better, since it's slightly more ergonomic when using it in practice.  e.g. `...importX.flatConfigs.recommended` vs `...importX.configs['flat/recommended']`, but i'm open to changing that.

Example Usage

```js
import importPlugin from 'eslint-plugin-import';
import js from '@eslint/js';
import tsParser from '@typescript-eslint/parser';

export default [
  js.configs.recommended,
  importPlugin.flatConfigs.recommended,
  importPlugin.flatConfigs.react,
  importPlugin.flatConfigs.typescript,
  {
    files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
    languageOptions: {
      parser: tsParser,
      ecmaVersion: 'latest',
      sourceType: 'module',
    },
    ignores: ['eslint.config.js'],
    rules: {
      'no-unused-vars': 'off',
      'import/no-dynamic-require': 'warn',
      'import/no-nodejs-modules': 'warn',
    },
  },
];
```

Note: in order to fill a gap in a future API gap for the `no-unused-module`, this takes advantage of a *proposed* new API on the ESLint context, that currently only exists in a POC state (unreleased).

Closes import-js#2556
michaelfaith added a commit to michaelfaith/eslint-plugin-import that referenced this issue Aug 18, 2024
This change adds support for ESLint's new Flat config system.  It maintains backwards compatibility with eslintrc style configs as well.

To achieve this, we're now dynamically creating flat configs on a new `flatConfigs` export.  I was a bit on the fence about using this convention, or the other convention that's become prevalent in the community: adding the flat configs directly to the `configs` object, but with a 'flat/' prefix.  I like this better, since it's slightly more ergonomic when using it in practice.  e.g. `...importX.flatConfigs.recommended` vs `...importX.configs['flat/recommended']`, but i'm open to changing that.

Example Usage

```js
import importPlugin from 'eslint-plugin-import';
import js from '@eslint/js';
import tsParser from '@typescript-eslint/parser';

export default [
  js.configs.recommended,
  importPlugin.flatConfigs.recommended,
  importPlugin.flatConfigs.react,
  importPlugin.flatConfigs.typescript,
  {
    files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
    languageOptions: {
      parser: tsParser,
      ecmaVersion: 'latest',
      sourceType: 'module',
    },
    ignores: ['eslint.config.js'],
    rules: {
      'no-unused-vars': 'off',
      'import/no-dynamic-require': 'warn',
      'import/no-nodejs-modules': 'warn',
    },
  },
];
```

Note: in order to fill a gap in a future API gap for the `no-unused-module`, this takes advantage of a *proposed* new API on the ESLint context, that currently only exists in a POC state (unreleased).

Closes import-js#2556
@marikaner
Copy link

A release for this would be wonderful!

@kfsass
Copy link

kfsass commented Sep 10, 2024

Strong work! 💖 Loving reminder to update the package.json's dependencies as well - package managers still report an unmet peer for eslint@9 with version 2.30.0

@ljharb
Copy link
Member

ljharb commented Sep 10, 2024

@kfsass that's intentional; flat config works in eslint 8 and we don't yet support eslint 9.

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

Successfully merging a pull request may close this issue.