# Vue 3 and Nuxt

This package ships two opt-in entrypoints for Vue 3 projects:

- `@bitfactory/eslint-config/vue3` for plain Vue 3 projects (Vite, Laravel Inertia, or any non-Nuxt setup).
- `@bitfactory/eslint-config/nuxt` for Nuxt projects, layered on top of `@nuxt/eslint-config`.

Each owns every file type it lints and applies the same house rule set to all of them, so neither needs pairing with another entrypoint. Order matters in one case: nothing carrying the house rule set may follow `/nuxt`, which switches `no-undef` off last for Nuxt's auto-imports.

## Vue 3 (non-Nuxt)

Use `/vue3` for plain Vue 3 single-file components. It composes the base config and lints `.vue` and the TypeScript extensions itself, with `vue-eslint-parser` for the SFC and `@typescript-eslint/parser` as its script sub-parser, so `<script setup lang="ts">` is parsed.

```js
import bitfactoryVue3 from '@bitfactory/eslint-config/vue3';
import gitignore from 'eslint-config-flat-gitignore';

export default [
    gitignore(),
    ...bitfactoryVue3,
];
```

> [!IMPORTANT]
> `/vue3` uses `@typescript-eslint/parser` as the `.vue` script sub-parser and for the TypeScript extensions. It is declared as an optional peer, so a Vue 3 project must have it installed even if it is not otherwise a TypeScript project.

Adding `/typescript` alongside `/vue3` still works and changes nothing, so an existing configuration that writes it does not need editing.

## Nuxt 3 and Nuxt 4

Use `/nuxt` for Nuxt projects. It is `/vue3` plus Nuxt's own layer, selected with `createConfigForNuxt({ features: { standalone: false } })`: this package owns the JavaScript, TypeScript and Vue layers exactly as it does everywhere else, and Nuxt owns its own rules, its page and component conventions, and the disables that follow from its file layout (ADR-0023). It also lints `.jsx`, which Nuxt's own globs name.

```js
import bitfactoryNuxt from '@bitfactory/eslint-config/nuxt';
import gitignore from 'eslint-config-flat-gitignore';

export default [
    gitignore(),
    ...bitfactoryNuxt,
];
```

One `/nuxt` export covers both Nuxt 3 and Nuxt 4: `createConfigForNuxt()`'s own root-directory default already handles both directory layouts.

> [!NOTE]
> `/nuxt` requires the optional peer `@nuxt/eslint-config` (`>=1.15.0 <2.0.0`). Install it alongside this package in Nuxt projects.

Two things changed for `/nuxt` in `14.0.0`. The export is now a plain array rather than a `FlatConfigComposer`, so spread it and add your own blocks after it instead of calling `.append()`. And the community layer's automatic `.gitignore` block is no longer contributed, so call `gitignore()` yourself as the example above does (ADR-0027); without it, the first run reports on generated files.

The `import` namespace is left unclaimed on this path, so each import violation reports once under `import-x/*` and a project registering its own `eslint-plugin-import` under `import` does not conflict. The rehome that used to be needed here (ADR-0004) is gone from this package: `features.standalone: false` means Nuxt contributes no `import` registration to rehome. A configuration that keeps Nuxt's standalone layer still meets the collision, and handles it itself. [Known issue: `import` plugin namespace collision](known-issue-import-namespace.md) covers that case.

### Adding Project Overrides

Spread the export and append your own blocks. Nuxt's own layer already switches `vue/multi-word-component-names` off for `pages/` and `layouts/`, so a project that keeps single-word component names elsewhere - a `components/Icon.vue`, say - extends that to its own directories:

```js
import bitfactoryNuxt from '@bitfactory/eslint-config/nuxt';
import gitignore from 'eslint-config-flat-gitignore';

export default [
    gitignore(),
    ...bitfactoryNuxt,
    {
        files: ['app/components/**', 'components/**'],
        rules: {
            'vue/multi-word-component-names': 'off',
        },
    },
];
```

This override is a documented example only. It is not baked into the `/nuxt` export, so projects that do not need it are unaffected.

## Getting `no-undef` back on

`/vue`, `/vue3` and `/nuxt` all switch `no-undef` off for `.vue`, for two reasons that arrive together. An SFC's
script block may be TypeScript, and then every ambient declaration a project writes - a `declare global` type, a
`declare const` for a script-tag global - reports as a name nothing lexically declares; `@typescript-eslint`
turns the rule off on `.ts` for exactly this reason, but its glob never matches a `.vue` file. On top of that,
Nuxt auto-imports hundreds of names that are generated per project (ADR-0023).

`vue-eslint-parser` already keeps the rule out of a template, so what this gives up is the script block: a
genuine typo in an untyped `<script>` is not reported.

Turn it back on if your project declares the names it uses. Scope it to `.vue` and the JavaScript family, never
to `.ts` - the TypeScript preset disables it there deliberately, and re-enabling it reports every ambient type:

```js
import bitfactoryVue3 from '@bitfactory/eslint-config/vue3';
import gitignore from 'eslint-config-flat-gitignore';

export default [
    gitignore(),
    ...bitfactoryVue3,
    {
        files: ['**/*.vue', '**/*.js', '**/*.mjs'],
        languageOptions: {
            globals: {
                App: 'readonly',
                route: 'readonly',
            },
        },
        rules: {
            'no-undef': 'error',
        },
    },
];
```

Name every ambient type and runtime global your SFCs reference. On Nuxt, spread your generated
`.nuxt/eslint.config.mjs` in place of that globals block to get the auto-imports as well.

## `/nuxt` needs an ESM configuration file

`/nuxt` resolves Nuxt's own layer at load time, which is asynchronous, so it is the one entrypoint that a
CommonJS `eslint.config.js` cannot `require()` - the attempt fails with `ERR_REQUIRE_ASYNC_MODULE`. Name the
file `eslint.config.mjs`, or set `"type": "module"` in your `package.json`. Every other entrypoint loads either
way.

## Peer Dependencies

Each row lists what the export imports on top of the base entrypoint's own peers. Every one is imported
unconditionally, so a missing package fails the run with `ERR_MODULE_NOT_FOUND` rather than degrading.

| Export | Peers it adds |
| -------- | ---------------- |
| `/vue3` | `@typescript-eslint/eslint-plugin`, `@typescript-eslint/parser`, `eslint-plugin-vue`, `eslint-plugin-vuejs-accessibility`, `vue-eslint-parser` |
| `/nuxt` | everything `/vue3` adds, which it composes, plus `@nuxt/eslint-config` |
| `/vue` | the same set as `/vue3` |

`/nuxt` composing `/vue3` (ADR-0023) is what makes its row the longer one. `@nuxt/eslint-config` does not ship
`eslint-plugin-vuejs-accessibility`, and that package is an optional peer here, so nothing installs it for you:
a `/nuxt` project that skips it fails to load.
