Nuxt Compatibility
Vite+ scaffolds Nuxt projects and runs alongside one, and that is not the same as supporting one. The honest statement is narrower than the marketing and narrower than the earlier version of this proposal implied: the framework-agnostic half of Vite+ works here completely, and the Vite-application half does not apply to the app at all.
Setting that out explicitly matters because the two halves fail differently. A command that does not apply is a command nobody runs. A command that half-applies — linting that reads a .vue file and silently skips most of it — is the one that produces false confidence.
What holds and what does not
| Surface | Status here |
|---|---|
vp run --cache | Applies, unproven. Framework-agnostic — it runs nuxt build as a task — but the traced key is what Phase 0 measures, and worker fan-out and the virrun overlay are the two ways it can be wrong |
vp fmt | Works fully. oxfmt already formats every file type in the repo |
vp env, vp install, vp pm | Works. Runtime and package-manager management are indifferent to the framework |
vp lint | Partial, and the partiality is the risk — oxlint parses a .vue script block, not its template |
vp test | Unproven. Two specific blockers below |
vp build, vp dev | Does not apply to the app. Nuxt owns the build, the dev server and the module graph |
vp pack | Applies to the libraries, which are already tsdown packages |
The config-file consequence of that last row is the seam described in configuration: Vite+ needs a root vite.config.ts to recognise a workspace, Nuxt wraps Vite and discourages a standalone one, and upstream declined to let nuxt.config.ts stand in (that page holds the issue). The resulting warning is a warning rather than a defect — a separate config works (Nuxt discussion) — but the arrangement is two config trees where one would do.
The two test blockers
Neither is speculative and both are measurable before anything is changed.
- The Nuxt Vitest environment. Well over a hundred test files opt into it with a
@vitest-environment nuxtpragma, and that environment is supplied by Nuxt's own test utilities rather than by Vitest. Whether it still registers under avp testinvocation is the question, and a large share of the suite depends on the answer. - The sharded blob pipeline. The suite runs as one root Vitest
projectsconfig so it shares one run, one coverage report and one--shardaxis, and CI fans it across shards with--reporter=bloband recombines with--merge-reports. A wrapper that does not forward those flags cleanly does not merely run slower — it silently stops being one coverage report, and the aggregate gate that means "every shard passed" is exactly the check this repository has already had go green while a shard did not.
Until both are answered, the suite stays as task runner leaves it — Vitest invoked directly, its wrapper the task. That is not a compromise; running a tool through a cached task runner is where the value is, and rewriting how the tool is imported buys nothing on top of it.
vp migrate cannot be used here
This is worth stating as a flat conclusion rather than a caveat, because the command's name invites reaching for it first.
Three properties rule it out, and any one of them would be enough:
- It is workspace-root only. The documentation is explicit that a monorepo's migration target must be the workspace root and that Vite+ cannot migrate a single workspace member, because it rewrites shared package-manager configuration and the lockfile. So the one thing wanted from it — a small first increment — is the one thing it does not do.
- It rewrites imports across the repository. It converts
vitetovite-plusandvitesttovite-plus/test. Many hundreds of files here import fromvitest, and the Vitest config type is imported in the shared configuration factory that every package's config calls. That rewrite does not adopt a runner; it makes Vite+ a source-level dependency of the entire test suite, which is a far larger commitment than caching tasks, and it is the commitment hardest to reverse. - It merges tool configs into one
vite.config.ts. That is the monolith this migration is explicitly organised to avoid, and unpicking a generated merge into per-concern modules afterwards is more work than composing them correctly in the first place.
It also has no documented meta-framework handling, which for this repository is the least of the three problems but confirms the shape of the tool: it is built for a Vite application, and the app here is not one.
So the migration is performed by hand. That is the recommendation, not a fallback.
The adoption ladder
Ordered by how little of Vite+ each rung requires, which is also the order of decreasing certainty. Each rung is useful alone and reversible alone, and the ladder is what the phases schedule against.
- Tasks and caching only. Tasks invoke the existing scripts verbatim. No import rewrites, no config merges, Nuxt untouched, and every check runs the same binary it runs today. All of the measurable value sits on this rung.
- Lint and format configuration. The oxlint and oxfmt settings relocate into the Vite+ config, decomposed per concern. Behaviour unchanged; only who reads the settings.
- Runtime and package manager.
vp envandvp install, with the caveat that the repository's second runtime pin does not transfer. - The test runner — only if both blockers above clear. Otherwise this rung is never climbed, and nothing below it is affected.
- The app build — never, while Nuxt owns the module graph. This is not a pending item; it is the seam.
The ladder's shape is the argument against vp migrate restated as a plan: the value is concentrated on the first rung, the risk is concentrated on the fourth, and a workspace-wide rewrite would take all of it at once.
Where ESLint sits
Oxlint reading a .vue file's script and not its template is the reason ESLint does not leave, and it is worth being precise about the consequence: vp lint replaces the oxlint invocation, not the ESLint one, and the two continue side by side.
That is also why this proposal leaves the rule migration where configuration leaves it: Vite+ changes who invokes the linter; it does not change what the linter can parse. Treating adoption as progress on that migration would retire ESLint rules that nothing has replaced, over templates that nothing is reading.
Key files
| File | Role after the change |
|---|---|
apps/web/nuxt.config.ts | the app build Nuxt keeps, outside vp build |
apps/web/vitest.config.ts | the app's tests — Vitest invoked directly as a task, and vp test only once both test blockers clear |
apps/web/package.json | the scripts the adoption ladder moves one rung at a time |
Sources
- Nuxt discussion 34857 — Vite+ against a Nuxt application, the support matrix this page reads.