# Known issue: `import` plugin namespace collision

On the `10.x` and `11.x` lines this config registers `eslint-plugin-import-x` under the **`import`**
namespace, which is the canonical namespace of a different package (`eslint-plugin-import`). ESLint flat
config rejects two different plugin objects claiming one key, so if anything else also claims `import` the
run aborts:

```text
Config (unnamed): Key "plugins": Cannot redefine plugin "import".
```

This is a hard failure - the run aborts and no rules are reported. On `8.x` and `9.x` the config
registered the real `eslint-plugin-import`, whose canonical namespace `import` is, so those lines do not
have this defect. When it fires depends on how the config is assembled:

- When **both** claimants sit inside `/nuxt`'s composer, which is the case for a block you `.append()` to
  it, the abort is glob-independent: the composer rejects the second claimant while building the config,
  before any file is linted, reporting `Different instances of plugin "import"`.
- Otherwise ESLint checks per file, and the abort happens only when it lints a file both registrations
  apply to, reporting `Cannot redefine plugin "import"`. That covers a plain flat-config array such as
  this package's base config, and also the case where you spread `await bitfactoryNuxt.toConfigs()` into
  an array and add your own block after it.

Not having this defect did not make the older lines safe with `/nuxt`. On `8.4.x` and `9.0.x`, composing
the base config with `/nuxt` aborts on every `@nuxt/eslint-config` their `^1.0.0` peer admits, because no
`1.x` depends on `eslint-plugin-import` at all, so Nuxt's chunk always claims `import` for a different
package. Two different packages
cannot dedupe to one object, so workaround 2 cannot reach that shape. The workarounds below are written
on `10.x`/`11.x` premises; those lines carry their own, and the caveats differ:
[`8.4.4`](https://unpkg.com/@bitfactory/eslint-config@8.4.4/docs/known-issue-import-namespace.md), whose
remedy 1 also needs `@babel/eslint-parser` and `@babel/core` pinned to `^7.26.10`, and
[`9.0.1`](https://unpkg.com/@bitfactory/eslint-config@9.0.1/docs/known-issue-import-namespace.md).
ADR-0006 records why no fix landed on either line.

## Whether this affects you

This section covers `10.x` and `11.x`. It needs a **second** claimant of the `import` key. You are
affected if either applies:

- Your flat config **registers** an import plugin under `import` itself, for rules this config does not
  ship. Usually that is `eslint-plugin-import`; a second copy of `eslint-plugin-import-x` that does not
  dedupe with this config's collides the same way. Merely having either in `devDependencies` is not
  enough; the collision comes from the registration.
- You compose this package's base config together with `/nuxt`. `@nuxt/eslint-config` claims `import` too,
  so this throws whenever the two claimants are different objects: on 1.0.0-1.4.1 and 1.6+ that means the
  two `eslint-plugin-import-x` copies did not resolve to a single instance, while 1.5.x claims the key for
  `eslint-plugin-import-lite` and so throws regardless of dedupe.

You are **not** affected if:

- You use `/nuxt` on its own. One claimant, no collision.
- You use a legacy `.eslintrc` config. Plugins are referenced there by name string, so no two plugin
  objects can contend for one key.
- You use this package's base config without registering an import plugin of your own.

## Workaround 1: use the import plugin this config already provides

This is the preferred fix, and usually a net deletion. On `10.x`/`11.x` the config registers
`eslint-plugin-import-x` under `import`, so every `import/*` rule ID resolves **without you registering
anything**. Remove your own registration and keep the rules:

```diff
- import importPlugin from 'eslint-plugin-import';
-
  export default [
      ...bitfactoryBase,
      {
-         plugins: {
-             'import': importPlugin,
-         },
          rules: {
              'import/first': 'error',
              'import/no-mutable-exports': 'error',
              'import/no-named-default': 'error',
          },
      },
  ];
```

`eslint-plugin-import-x` is a drop-in for the rules in question, so the rule IDs and behaviour are
unchanged. You can drop the `eslint-plugin-import` devDependency at the same time, unless something else
in your toolchain needs it.

## Workaround 2: resolve `eslint-plugin-import-x` to one copy

If the collision comes from two `eslint-plugin-import-x` copies, pin the package so every registration
resolves to the same object:

```jsonc
// package.json, pnpm
{
  "pnpm": {
    "overrides": {
      "eslint-plugin-import-x": "4.17.1"
    }
  }
}
```

Use `overrides` on npm, or `resolutions` on Yarn. Match the version to whatever this config's peer
resolves to. A pin cannot help against `@nuxt/eslint-config` 1.5.x, whose `import` key holds
`eslint-plugin-import-lite`, a different package: upgrade it to >= 1.6.0, or use workaround 3.

## Workaround 3: let one side own the key

If you must keep your own instance registered, remove the other claimant and re-add the rules it
contributed. For a Nuxt project that means dropping `@nuxt/eslint-config`'s import chunk and restating its
rules against your own plugin. Restate rather than copy, because the plugins disagree on some option
values: 1.5.x sets `consistent-type-specifier-style: ['error', 'top-level']`, which `eslint-plugin-import`
rejects. This is the most invasive option - prefer workaround 1, unless your `@nuxt/eslint-config` is
1.5.x, where the two claimants are this config and Nuxt, so removing your own registration changes
nothing.

## Fixed in 12.0.0

`12.0.0` registers the plugin under `import-x`, which leaves `import` free, and rehomes Nuxt's chunk the
same way (ADR-0004); `docs/migration-import-x-namespace.md` covers the rename it asks of you. The `/nuxt`
entrypoint requires `@nuxt/eslint-config` >= 1.15.0; the rehome itself is dependable from 1.6.0
(1.0.0-1.4.1 and 1.6.0-1.14.0 sit outside the required range but are normally rehomed too, since their
import-x resolves new enough to be identified).

Two residual cases leave Nuxt's chunk on `import`, where it can still collide with a registration of your
own there:

- `@nuxt/eslint-config` 1.5.x registers `eslint-plugin-import-lite`, which the rehome never touches.
  Upgrading to >= 1.6.0 clears it.
- The `eslint-plugin-import-x` Nuxt registers resolves below 4.10.3, too old to identify itself. Ordinary
  resolution reaches that low only on `@nuxt/eslint-config` 1.0.0-1.3.0; inside the supported range it
  takes an explicit `overrides`/`resolutions` pin to force, and raising that pin (workaround 2) is the
  fix.

An un-rehomed chunk is not by itself an error: with no second claimant the run succeeds, only reporting
each import violation under two rule IDs (`import/*` and `import-x/*`) instead of one.

`12.0.0` also opens one case of its own: this config now owns `import-x`, so a second
`eslint-plugin-import-x` copy you register yourself collides on that key unless the copies dedupe, on the
same two timings as above. Workaround 2's pin is the remedy, or drop your own registration where this
config already supplies `import-x`. On `12.x` and `13.x` that is `**/*.{js,mjs,cjs}` with the base config,
and every linted file under `/nuxt`, whose rehomed chunk declares no `files` of its own. From `14.0.0` it is
also `**/*.{ts,tsx,mts,cts}` with `/typescript`, and `**/*.vue` with `/vue` and `/vue3`.

This is deliberately **not** backported to `10.x` or `11.x`: the fix renames a consumer-visible rule ID,
and a consumer pinned to `^10` or `^11` should be able to take every patch and minor within that major
without breakage. The remedy on these lines is a workaround above, or upgrading to `12.0.0`.

## `/nuxt` does not reach this case from `14.0.0`

`/nuxt` composes Nuxt's layer with `features.standalone: false`, which registers only the `nuxt` plugin and
leaves `import` unclaimed (ADR-0023). A configuration that keeps the standalone layer still reaches everything
above, and `@nuxt/eslint-config` offers two narrower controls for it:

- `features: { import: false }` drops the import chunk entirely, which frees the `import` key.
- `features: { import: { package: 'eslint-plugin-import-x' } }` selects which plugin backs the chunk. It does
  **not** free the key: the chunk is still registered as `import`, so a second claimant still collides.

## Links

- ADR-0004 - the decision this records
- ADR-0006 - the different `/nuxt` collision on `8.4.x` and `9.0.x`
- Issue #204 (tracked in the private repository)
