Rashiq
Rashiq

Reputation: 429

Flat config file with configs from legacy eslintrc compat error

I am trying to get prettier, typescript, eslint with airbnb typescript syntax working with linting and auto formatting.

The minimal node package.json file.

{
  "name": "typescript-eslint-airbnb-compat",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "lint": "eslint .",
    "lint:fix": "npm run lint -- --fix",
    "format": "prettier . --check",
    "format:fix": "npm run format -- --write"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3.0.2",
    "@eslint/js": "^8.57.0",
    "@typescript-eslint/eslint-plugin": "^7.4.0",
    "@typescript-eslint/parser": "^7.4.0",
    "eslint": "^8.57.0",
    "eslint-config-airbnb-base": "^15.0.0",
    "eslint-config-airbnb-typescript": "^18.0.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-import": "^2.29.1",
    "typescript": "^5.4.3",
    "typescript-eslint": "^7.4.0",
    "vite": "^5.2.0"
  }
}

Here is the flat config file for eslint eslint.config.mjs.

import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import eslintConfigPrettier from "eslint-config-prettier";

import { FlatCompat } from "@eslint/eslintrc";
import path from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const compat = new FlatCompat({
  baseDirectory: __dirname
});

export default tseslint.config(
  eslint.configs.recommended,
  ...compat.extends("airbnb-base"),
  ...compat.extends("airbnb-typescript/base"),
  ...tseslint.configs.recommendedTypeChecked,
  eslintConfigPrettier,
  {
    languageOptions: {
      parserOptions: {
        project: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
  },
  {
      files: ['**/*.{js,jsx,cjs,mjs}'],
      extends: [tseslint.configs.disableTypeChecked],
  }
);

Airbnb and eslint airbnb ts config do not support the new flat config at the time of writing. Therefore, I am using the compat feature to transform the legacy config into the new syntax.

This is the error I am receiving and I haven't been able to solve it.

> typescript-eslint-airbnb-compat@0.0.0 lint
> eslint .


Oops! Something went wrong! :(

ESLint: 8.57.0

Error: Key "plugins": Cannot redefine plugin "@typescript-eslint".
    at .../typescript-eslint-airbnb-compat/node_modules/@humanwhocodes/object-schema/src/object-schema.js:250:27
    at Array.reduce (<anonymous>)
    at ObjectSchema.merge (.../typescript-eslint-airbnb-compat/node_modules/@humanwhocodes/object-schema/src/object-schema.js:237:24)
    at .../typescript-eslint-airbnb-compat/node_modules/@humanwhocodes/config-array/api.js:935:42
    at Array.reduce (<anonymous>)
    at FlatConfigArray.getConfig (.../typescript-eslint-airbnb-compat/node_modules/@humanwhocodes/config-array/api.js:934:39)
    at FlatConfigArray.isFileIgnored (.../typescript-eslint-airbnb-compat/node_modules/@humanwhocodes/config-array/api.js:962:15)
    at .../typescript-eslint-airbnb-compat/node_modules/eslint/lib/eslint/eslint-helpers.js:312:49
    at Array.reduce (<anonymous>)
    at entryFilter (.../typescript-eslint-airbnb-compat/node_modules/eslint/lib/eslint/eslint-helpers.js:299:28)

Is this a bug or am I taking the wrong step somewhere? I am open to any solution and trying different tools and configs if required.

Upvotes: 0

Views: 2761

Answers (1)

Brad Zacher
Brad Zacher

Reputation: 3243

This has been fixed in typescript-eslint v7.5.0

If you're stuck on an older version you can manually fix the issue by updating the code:

  ...compat.extends("airbnb-typescript/base").map(c => {
    if (c.plugins) {
      // @ts-expect-error
      c.plugins['@typescript-eslint'] = tseslint.plugin;
    }
    return c
  }),

Background:

The error is unculear but this is working as expected. TL;DR is that our config defines

{
  plugins: {
    '@typescript-eslint': require('typescript-eslint').plugin
  },
}

But eslint-config-airbnb-typescript defines

{
  plugins: {
    '@typescript-eslint': require('@typescript-eslint/eslint-plugin')
  },
}

ESLint enforces that whilst you may have multiple definitions of a plugin namespace - each definition must have exactly the same value.

The issue here is that

require('typescript-eslint').plugin !== require('@typescript-eslint/eslint-plugin')

tseslint.configs.recommendedTypeChecked uses the former and compat.extends("airbnb-typescript/base") uses the latter.

Hence the error.

Upvotes: 2

Related Questions