@nx/vite
Version:
255 lines (171 loc) • 10.7 kB
Markdown
# Vite 8 Migration Instructions for LLM
## Overview
These instructions guide you through migrating an Nx workspace from Vite 7 to Vite 8. Vite 8 swaps Rollup for Rolldown as its bundler and updates a number of plugin APIs. Work through each section in order and run tests after each change.
## Pre-Migration Checklist
1. **Identify all Vite-using projects**:
```bash
nx show projects --with-target build
nx show projects --with-target serve
```
2. **Locate all Vite configuration files**:
- Search for `vite.config.{ts,js,mts,mjs,cts,cjs}`
- Check `project.json` files for inline Vite-related options
3. **Cypress Component Testing**: Cypress >= 15.14.0 supports Vite 8. The `nx migrate` step bumps Cypress automatically. If you have explicitly pinned Cypress below 15.14.0, upgrade it before bumping Vite.
## Migration Steps by Category
### 1. Rename `rollupOptions` to `rolldownOptions`
The `nx migrate` codemod handles this automatically for `vite.config.{ts,js,mts,mjs,cts,cjs}` files. If you have `rollupOptions` declared elsewhere (e.g., in helper modules imported by your config), rename them by hand.
**Search Pattern**: `rollupOptions` in any TypeScript/JavaScript file
```typescript
// ❌ BEFORE (Vite 7)
export default defineConfig({
build: {
rollupOptions: {
external: ['react'],
output: { manualChunks: { vendor: ['react', 'react-dom'] } },
},
},
});
// ✅ AFTER (Vite 8)
export default defineConfig({
build: {
rolldownOptions: {
external: ['react'],
output: { manualChunks: { vendor: ['react', 'react-dom'] } },
},
},
});
```
**Action Items**:
- [ ] Verify the codemod picked up every config file (`rg "rollupOptions"` should return zero hits inside vite configs)
- [ ] Rename any `rollupOptions` in helper modules or shared config builders
- [ ] Update CI scripts that parse `build.rollupOptions` (e.g., custom bundle-size assertions)
### 2. `@vitejs/plugin-react` v6 (Oxc Replaces Babel)
`@vitejs/plugin-react@^6` is required for Vite 8 and uses [Oxc](https://oxc.rs/) instead of Babel for JSX transformation. The plugin's `babel` option is gone.
```typescript
// ❌ BEFORE (Vite 7, plugin-react v4)
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
react({
babel: {
plugins: ['babel-plugin-styled-components'],
},
}),
],
});
// ✅ AFTER (Vite 8, plugin-react v6)
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
});
```
**Action Items**:
- [ ] Remove `babel` options from `react()` plugin invocations
- [ ] If you depended on a Babel plugin (e.g., styled-components, emotion, relay), find an Oxc-compatible replacement or switch to `@vitejs/plugin-react-swc` (still Babel-free). There is no drop-in for arbitrary Babel plugins.
- [ ] If you cannot drop your Babel plugin, stay on Vite 7 + plugin-react v4 for now (see "Project-Level Vite 7 Pinning")
- [ ] Run `pnpm install` (or your package manager equivalent) so the new plugin-react version resolves
### 3. Angular + Vitest (vitest-analog path): Add `@oxc-project/runtime`
Angular projects whose `test` target uses `@nx/vitest:test` (the vitest-analog setup wired by `@analogjs/vite-plugin-angular`) need `@oxc-project/runtime` declared in the workspace `devDependencies`.
`@analogjs/vite-plugin-angular` registers an `angularVitestPlugin` (active in test mode) whose `transform` hook matches `@angular/*` `fesm2022` modules that contain `async ` (plus any `@angular/cdk` file) and calls `vite.transformWithOxc(code, id, { target: 'es2016', … })`. The downlevel is deliberate: Zone.js relies on monkey-patching promise scheduling for `fakeAsync` and friends, which it cannot do on native `async`/`await`, so the plugin lowers them to a form Zone.js can intercept. With `target: 'es2016'`, oxc emits the helpers as external `@oxc-project/runtime/helpers/*` imports (oxc's default `HelperMode = 'Runtime'`). Nothing in the upstream chain (`analogjs`, `@angular/core`, `vite`, `rolldown`) declares `@oxc-project/runtime` in a way that's resolvable from the consumer's workspace, so `vite:import-analysis` fails to resolve those imports unless the dep is added explicitly.
Angular projects whose `test` target uses `@angular/build:unit-test` or `@nx/angular:unit-test` (the vitest-angular path) do **not** need this dependency — that path doesn't load `@analogjs/vite-plugin-angular`, sets `optimizeDeps.noDiscovery: true`, and uses an in-memory test provider, so the downlevel transform that emits the helper imports never runs.
**Search Pattern**: Projects with `test.executor` set to `@nx/vitest:test` that also have `@analogjs/vite-plugin-angular` in their `vite.config.*`.
```bash
rg '"@nx/vitest:test"' --type json
rg '@analogjs/vite-plugin-angular' --type ts --type js
```
**Action Items**:
- [ ] For each affected workspace, add `@oxc-project/runtime` to root `devDependencies` (the Nx Angular generators do this automatically on the vitest-analog path; check legacy workspaces that pre-date that)
- [ ] Run `pnpm install` (or equivalent)
- [ ] Run the project's tests to confirm the helper resolves
### 4. Type Resolution Under `moduleResolution: "node"`
Vite 8 ships its types only via conditional `exports` (it dropped the top-level `types` field that Vite 7 carried), which TypeScript cannot resolve under `moduleResolution: "node"`. Symptoms include type errors on `defineConfig`, `UserConfig`, or plugin return types.
**Action Items**:
- [ ] Update affected `tsconfig*.json` files: `"moduleResolution": "bundler"` (recommended) or `"node16"`/`"nodenext"`
- [ ] If you cannot change `moduleResolution`, narrow the impact with explicit `as any` casts at vite imports. The Nx-generated configs already do this in a handful of places.
- [ ] Run `tsc --noEmit` after the change to confirm types resolve cleanly
### 5. Bundle Validation Scripts
Rolldown produces different chunk and module counts than Rollup for the same input. Custom build validation (e.g., "bundle has exactly N chunks") will need to be re-baselined.
**Action Items**:
- [ ] Identify scripts that assert chunk/module counts or names
- [ ] Re-run the build and update expected values
- [ ] Prefer asserting on size budgets over exact counts going forward
### 6. Project-Level Vite 7 Pinning (Custom Babel Plugins)
If a project depends on a Babel plugin that has no Oxc equivalent, pin that project to Vite 7.
```jsonc
// package.json (workspace root)
{
"devDependencies": {
"vite": "^7.1.0",
"@vitejs/plugin-react": "^4.3.0",
},
}
```
If only some projects need to stay on 7 while the rest move to 8, use your package manager's overrides feature:
- pnpm: `pnpm.overrides` in root `package.json`
- npm/yarn: `overrides`/`resolutions`
**Action Items**:
- [ ] Document which projects are pinned to Vite 7 and why
- [ ] Track Oxc plugin equivalents so you can unpin later
## Post-Migration Validation
### 1. Run Tests Per Project
```bash
nx run-many -t test -p PROJECT_NAME
```
### 2. Build All Affected Projects
```bash
nx affected -t build
```
### 3. Validate Dev Server
```bash
nx serve PROJECT_NAME
```
Open the app and verify HMR still works for changes in source files.
### 4. Validate CI Pipeline
```bash
nx prepush
```
### 5. Review Migration Checklist
- [ ] All `rollupOptions` references renamed to `rolldownOptions`
- [ ] `@vitejs/plugin-react` upgraded to v6 (or pinned to v4 with a documented reason)
- [ ] No `babel: { ... }` options remain in `react()` calls (or those projects are pinned to Vite 7)
- [ ] Angular + vitest-analog projects have `@oxc-project/runtime` in root `devDependencies`
- [ ] Cypress upgraded by `nx migrate` (>= 15.14.0 for Vite 8 support)
- [ ] `tsc --noEmit` passes on all affected projects
- [ ] Build, test, and dev-server commands all succeed
## Common Issues and Solutions
### Issue: `Cannot find name 'rollupOptions'` or build options ignored
**Solution**: Rename to `rolldownOptions`. Vite 8 still accepts `rollupOptions` as a deprecated alias (it copies the value to `rolldownOptions` and logs a deprecation warning), but mixing both at the same level may cause precedence surprises (`rolldownOptions` wins).
### Issue: Babel plugin no longer applied (e.g., styled-components classNames missing)
**Solution**: `@vitejs/plugin-react@6` removed Babel. Find an Oxc-compatible alternative, switch to `@vitejs/plugin-react-swc`, or pin to Vite 7 + plugin-react v4.
### Issue: Angular + Vitest fails with `Failed to resolve import "@oxc-project/runtime/helpers/..."`
**Solution**: Add `@oxc-project/runtime` to root `devDependencies` and reinstall. This only affects projects whose `test` target uses `@nx/vitest:test` (the vitest-analog setup); projects using `@angular/build:unit-test` / `@nx/angular:unit-test` are not affected.
### Issue: Type errors on `defineConfig`, `UserConfig`, or `Plugin` imports from vite
**Solution**: Set `moduleResolution: "bundler"` in your tsconfig (or `nodenext` if you need Node-style resolution).
### Issue: Cypress CT fails to start under Vite 8
**Solution**: Confirm `cypress >= 15.14.0` is installed (Vite 8 support landed in that release). `nx migrate` bumps Cypress automatically; if you pinned it lower in `package.json`, remove the pin and reinstall.
### Issue: Bundle-size or chunk-count assertions fail after upgrade
**Solution**: Rolldown chunks differently than Rollup. Re-baseline expected values.
## Files to Review
```bash
# Vite config files
find . -name "vite.config.*" -not -path "*/node_modules/*"
# Cypress component testing setup
rg "@nx/(angular|react|next|remix)/plugins/component-testing"
# Babel plugin usage in plugin-react
rg "@vitejs/plugin-react.*babel|babel:\s*\{" --type ts --type js
# Angular projects with Vitest
rg "@angular/build" -l package.json
```
## Guard Rails
DO NOT
- Force tests to pass by removing assertions or replacing them with `expect(true).toBe(true)`
- Strip `react()` plugin options without finding an equivalent for what they did
- Roll Cypress back below 15.14.0 after the migrate. Older Cypress fails to start under Vite 8.
---
## Notes for LLM Execution
When executing this migration:
1. **Work systematically**: Complete one category before moving to the next
2. **Test after each change**: Build and test affected projects after each step
3. **Keep user informed**: Report which categories applied and which were skipped
4. **Use TodoWrite tool**: Track migration progress for visibility
5. **Stop and ask** if a project depends on a Babel plugin with no Oxc equivalent. Pinning to Vite 7 is a workspace decision.