chore: remove tools/ai-migrations generated by nx migrate
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
8439c5d5e0
commit
629d609792
@@ -1,845 +0,0 @@
|
||||
# Next.js 16 Migration Instructions for LLM
|
||||
|
||||
## Overview
|
||||
|
||||
These instructions guide you through migrating an Nx workspace containing Next.js projects from Next.js 15 to Next.js 16. Work systematically through each breaking change category.
|
||||
|
||||
## Pre-Migration Checklist
|
||||
|
||||
1. **Identify all Next.js projects**:
|
||||
|
||||
```bash
|
||||
nx show projects --with-target build | xargs -I {} nx show project {} --json | jq -r 'select(.targets.build.executor | contains("next")) | .name'
|
||||
```
|
||||
|
||||
Or search for Next.js configuration files:
|
||||
|
||||
```bash
|
||||
find . -name "next.config.*" -not -path "*/node_modules/*"
|
||||
```
|
||||
|
||||
2. **Update packages**:
|
||||
|
||||
```bash
|
||||
npm install next@latest react@latest react-dom@latest
|
||||
npm install -D @types/react @types/react-dom # if using TypeScript
|
||||
```
|
||||
|
||||
3. **Verify minimum requirements**:
|
||||
- Node.js 20.9+ (Node.js 18 is no longer supported)
|
||||
- TypeScript 5.1.0+
|
||||
- Browser support: Chrome 111+, Edge 111+, Firefox 111+, Safari 16.4+
|
||||
|
||||
## Migration Steps by Category
|
||||
|
||||
### 1. Async Request APIs (Major Breaking Change)
|
||||
|
||||
This is the most impactful change in Next.js 16. All dynamic request APIs are now asynchronous.
|
||||
|
||||
**Search Patterns**:
|
||||
|
||||
- `cookies()` usage in server components
|
||||
- `headers()` usage in server components
|
||||
- `draftMode()` usage
|
||||
- `params` in page, layout, route handlers, and metadata files
|
||||
- `searchParams` in page components
|
||||
|
||||
#### 1.1 Page Components with params
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```tsx
|
||||
// BEFORE (Next.js 15)
|
||||
export default function Page({ params }) {
|
||||
const { slug } = params;
|
||||
return <h1>{slug}</h1>;
|
||||
}
|
||||
|
||||
// AFTER (Next.js 16)
|
||||
export default async function Page(props) {
|
||||
const { slug } = await props.params;
|
||||
return <h1>{slug}</h1>;
|
||||
}
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Make all page components that use `params` async
|
||||
- [ ] Add `await` before accessing `props.params`
|
||||
- [ ] Update TypeScript types if applicable
|
||||
|
||||
#### 1.2 Page Components with searchParams
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```tsx
|
||||
// BEFORE (Next.js 15)
|
||||
export default function Page({ searchParams }) {
|
||||
const query = searchParams.q;
|
||||
return <Results query={query} />;
|
||||
}
|
||||
|
||||
// AFTER (Next.js 16)
|
||||
export default async function Page(props) {
|
||||
const searchParams = await props.searchParams;
|
||||
const query = searchParams.q;
|
||||
return <Results query={query} />;
|
||||
}
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Make all page components that use `searchParams` async
|
||||
- [ ] Add `await` before accessing `props.searchParams`
|
||||
|
||||
#### 1.3 Layout Components with params
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```tsx
|
||||
// BEFORE (Next.js 15)
|
||||
export default function Layout({ children, params }) {
|
||||
const { locale } = params;
|
||||
return <div data-locale={locale}>{children}</div>;
|
||||
}
|
||||
|
||||
// AFTER (Next.js 16)
|
||||
export default async function Layout(props) {
|
||||
const { locale } = await props.params;
|
||||
return <div data-locale={locale}>{props.children}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.4 Route Handlers
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```tsx
|
||||
// BEFORE (Next.js 15)
|
||||
export async function GET(request, { params }) {
|
||||
const { id } = params;
|
||||
return Response.json({ id });
|
||||
}
|
||||
|
||||
// AFTER (Next.js 16)
|
||||
export async function GET(request, props) {
|
||||
const { id } = await props.params;
|
||||
return Response.json({ id });
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.5 cookies() and headers()
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```tsx
|
||||
// BEFORE (Next.js 15)
|
||||
import { cookies, headers } from 'next/headers';
|
||||
|
||||
export default function Page() {
|
||||
const cookieStore = cookies();
|
||||
const headersList = headers();
|
||||
const theme = cookieStore.get('theme');
|
||||
const userAgent = headersList.get('user-agent');
|
||||
return <div>...</div>;
|
||||
}
|
||||
|
||||
// AFTER (Next.js 16)
|
||||
import { cookies, headers } from 'next/headers';
|
||||
|
||||
export default async function Page() {
|
||||
const cookieStore = await cookies();
|
||||
const headersList = await headers();
|
||||
const theme = cookieStore.get('theme');
|
||||
const userAgent = headersList.get('user-agent');
|
||||
return <div>...</div>;
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.6 draftMode()
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```tsx
|
||||
// BEFORE (Next.js 15)
|
||||
import { draftMode } from 'next/headers';
|
||||
|
||||
export default function Page() {
|
||||
const { isEnabled } = draftMode();
|
||||
return <div>{isEnabled ? 'Draft' : 'Published'}</div>;
|
||||
}
|
||||
|
||||
// AFTER (Next.js 16)
|
||||
import { draftMode } from 'next/headers';
|
||||
|
||||
export default async function Page() {
|
||||
const { isEnabled } = await draftMode();
|
||||
return <div>{isEnabled ? 'Draft' : 'Published'}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.7 generateMetadata with params
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```tsx
|
||||
// BEFORE (Next.js 15)
|
||||
export async function generateMetadata({ params }) {
|
||||
const { slug } = params;
|
||||
return { title: slug };
|
||||
}
|
||||
|
||||
// AFTER (Next.js 16)
|
||||
export async function generateMetadata(props) {
|
||||
const { slug } = await props.params;
|
||||
return { title: slug };
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.8 Automated Migration
|
||||
|
||||
Run the Next.js codemod for automated migration:
|
||||
|
||||
```bash
|
||||
npx @next/codemod@canary upgrade latest
|
||||
```
|
||||
|
||||
Generate type helpers for safer migrations (Next.js 15.5+):
|
||||
|
||||
```bash
|
||||
npx next typegen
|
||||
```
|
||||
|
||||
This generates `PageProps`, `LayoutProps`, and `RouteContext` helpers.
|
||||
|
||||
### 2. Image Generation Functions
|
||||
|
||||
**Search Pattern**: `generateImageMetadata`, `default function Image` in opengraph-image or twitter-image files
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```tsx
|
||||
// BEFORE (Next.js 15)
|
||||
export function generateImageMetadata({ params }) {
|
||||
const { slug } = params;
|
||||
return [{ id: '1' }];
|
||||
}
|
||||
|
||||
export default function Image({ params, id }) {
|
||||
const slug = params.slug;
|
||||
return new ImageResponse(/* ... */);
|
||||
}
|
||||
|
||||
// AFTER (Next.js 16)
|
||||
export async function generateImageMetadata({ params }) {
|
||||
const { slug } = await params;
|
||||
return [{ id: '1' }];
|
||||
}
|
||||
|
||||
export default async function Image({ params, id }) {
|
||||
const { slug } = await params;
|
||||
const imageId = await id;
|
||||
return new ImageResponse(/* ... */);
|
||||
}
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Make `generateImageMetadata` functions async
|
||||
- [ ] Make Image components async
|
||||
- [ ] Add `await` for both `params` and `id` access
|
||||
|
||||
### 3. Sitemap Generation
|
||||
|
||||
**Search Pattern**: `sitemap` functions with `id` parameter
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```tsx
|
||||
// BEFORE (Next.js 15)
|
||||
export default async function sitemap({ id }) {
|
||||
const start = id * 50000;
|
||||
// ...
|
||||
}
|
||||
|
||||
// AFTER (Next.js 16)
|
||||
export default async function sitemap({ id }) {
|
||||
const resolvedId = await id;
|
||||
const start = resolvedId * 50000;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Turbopack Configuration
|
||||
|
||||
Turbopack is now the default bundler for development.
|
||||
|
||||
**Search Pattern**: `--turbo` or `--turbopack` flags in package.json scripts, `turbopack` in next.config
|
||||
|
||||
#### 4.1 Remove Explicit Turbopack Flags
|
||||
|
||||
```json
|
||||
// BEFORE (Next.js 15)
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "next dev --turbo"
|
||||
}
|
||||
}
|
||||
|
||||
// AFTER (Next.js 16) - Turbopack is default
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "next dev"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2 Opt Out to Webpack (if needed)
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build": "next build --webpack"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.3 Move Turbopack Config Out of Experimental
|
||||
|
||||
```ts
|
||||
// BEFORE (Next.js 15)
|
||||
const nextConfig = {
|
||||
experimental: {
|
||||
turbopack: {
|
||||
/* options */
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// AFTER (Next.js 16)
|
||||
const nextConfig = {
|
||||
turbopack: {
|
||||
/* options */
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
#### 4.4 Update Sass Imports (Turbopack Specific)
|
||||
|
||||
```scss
|
||||
/* BEFORE */
|
||||
@import '~bootstrap/dist/css/bootstrap.min.css';
|
||||
|
||||
/* AFTER - Remove tilde prefix */
|
||||
@import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Remove `--turbo` and `--turbopack` flags from scripts
|
||||
- [ ] Move `turbopack` config from `experimental` to root level
|
||||
- [ ] Remove tilde (`~`) prefix from Sass imports
|
||||
- [ ] Add `--webpack` flag if Webpack is required
|
||||
|
||||
### 5. Middleware to Proxy Rename
|
||||
|
||||
**Search Pattern**: `middleware.ts` or `middleware.js` files
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```bash
|
||||
# Rename the file
|
||||
mv middleware.ts proxy.ts
|
||||
```
|
||||
|
||||
```ts
|
||||
// BEFORE (middleware.ts)
|
||||
export function middleware(request) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// AFTER (proxy.ts)
|
||||
export function proxy(request) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Config Updates**:
|
||||
|
||||
```js
|
||||
// BEFORE
|
||||
{
|
||||
skipMiddlewareUrlNormalize: true;
|
||||
}
|
||||
|
||||
// AFTER
|
||||
{
|
||||
skipProxyUrlNormalize: true;
|
||||
}
|
||||
```
|
||||
|
||||
**Important**: The Edge runtime is no longer supported in `proxy`. It now uses Node.js runtime.
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Rename `middleware.ts/js` to `proxy.ts/js`
|
||||
- [ ] Rename exported function from `middleware` to `proxy`
|
||||
- [ ] Update config option names
|
||||
- [ ] Remove Edge runtime usage from proxy files
|
||||
|
||||
### 6. Parallel Routes default.js Requirement
|
||||
|
||||
**Search Pattern**: Directories starting with `@` in the app folder (parallel route slots)
|
||||
|
||||
All parallel route slots now require an explicit `default.js` file.
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```tsx
|
||||
// Create app/@modal/default.tsx for each parallel route slot
|
||||
import { notFound } from 'next/navigation';
|
||||
|
||||
export default function Default() {
|
||||
notFound(); // or return null
|
||||
}
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Find all parallel route slots (`app/@*/`)
|
||||
- [ ] Create `default.tsx` in each slot that doesn't have one
|
||||
|
||||
### 7. Image Optimization Changes
|
||||
|
||||
#### 7.1 Local Images with Query Strings
|
||||
|
||||
```tsx
|
||||
// Now requires explicit configuration
|
||||
<Image src="/assets/photo?v=1" alt="Photo" width="100" height="100" />
|
||||
```
|
||||
|
||||
```js
|
||||
// next.config.js
|
||||
module.exports = {
|
||||
images: {
|
||||
localPatterns: [
|
||||
{
|
||||
pathname: '/assets/**',
|
||||
search: '?v=1',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
#### 7.2 Default Value Changes
|
||||
|
||||
Add these to `next.config.js` if you need the old defaults:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
images: {
|
||||
// minimumCacheTTL changed from 60 to 14400 seconds
|
||||
minimumCacheTTL: 60,
|
||||
|
||||
// Value 16 removed from default imageSizes
|
||||
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
|
||||
|
||||
// qualities now defaults to [75] only
|
||||
qualities: [50, 75, 100],
|
||||
|
||||
// Local IP now blocked by default
|
||||
dangerouslyAllowLocalIP: true, // only for private networks
|
||||
|
||||
// Maximum redirects changed from unlimited to 3
|
||||
maximumRedirects: 5,
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
#### 7.3 Deprecated images.domains
|
||||
|
||||
```js
|
||||
// BEFORE - Remove this
|
||||
module.exports = {
|
||||
images: {
|
||||
domains: ['example.com'],
|
||||
},
|
||||
};
|
||||
|
||||
// AFTER - Use remotePatterns instead
|
||||
module.exports = {
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'example.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Add `localPatterns` for images with query strings
|
||||
- [ ] Migrate `images.domains` to `images.remotePatterns`
|
||||
- [ ] Review and update default values if needed
|
||||
|
||||
### 8. Caching API Updates
|
||||
|
||||
#### 8.1 Remove unstable\_ Prefix
|
||||
|
||||
```ts
|
||||
// BEFORE (Next.js 15)
|
||||
import {
|
||||
unstable_cacheLife as cacheLife,
|
||||
unstable_cacheTag as cacheTag,
|
||||
} from 'next/cache';
|
||||
|
||||
// AFTER (Next.js 16)
|
||||
import { cacheLife, cacheTag } from 'next/cache';
|
||||
```
|
||||
|
||||
#### 8.2 New Cache Functions
|
||||
|
||||
**revalidateTag with cacheLife profile**:
|
||||
|
||||
```ts
|
||||
'use server';
|
||||
import { revalidateTag } from 'next/cache';
|
||||
|
||||
export async function updateArticle(articleId: string) {
|
||||
revalidateTag(`article-${articleId}`, 'max');
|
||||
}
|
||||
```
|
||||
|
||||
**updateTag (new)**:
|
||||
|
||||
```ts
|
||||
'use server';
|
||||
import { updateTag } from 'next/cache';
|
||||
|
||||
export async function updateUserProfile(userId: string, profile: Profile) {
|
||||
await db.users.update(userId, profile);
|
||||
updateTag(`user-${userId}`);
|
||||
}
|
||||
```
|
||||
|
||||
**refresh (new)**:
|
||||
|
||||
```ts
|
||||
'use server';
|
||||
import { refresh } from 'next/cache';
|
||||
|
||||
export async function markNotificationAsRead(notificationId: string) {
|
||||
await db.notifications.markAsRead(notificationId);
|
||||
refresh();
|
||||
}
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Remove `unstable_` prefix from `cacheLife` and `cacheTag` imports
|
||||
- [ ] Consider using new `updateTag` and `refresh` functions
|
||||
|
||||
### 9. React Compiler Support
|
||||
|
||||
React Compiler is now stable and supported:
|
||||
|
||||
```ts
|
||||
// next.config.ts
|
||||
const nextConfig = {
|
||||
reactCompiler: true,
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
```
|
||||
|
||||
Install the plugin:
|
||||
|
||||
```bash
|
||||
npm install -D babel-plugin-react-compiler
|
||||
```
|
||||
|
||||
**Note**: Expect higher compile times with React Compiler enabled.
|
||||
|
||||
### 10. Scroll Behavior Override
|
||||
|
||||
Next.js no longer overrides `scroll-behavior: smooth` during navigation.
|
||||
|
||||
To restore previous behavior:
|
||||
|
||||
```tsx
|
||||
// app/layout.tsx
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en" data-scroll-behavior="smooth">
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 11. ESLint Migration
|
||||
|
||||
The `next lint` command has been removed. Migrate to ESLint CLI directly.
|
||||
|
||||
```bash
|
||||
# Run migration codemod
|
||||
npx @next/codemod@canary next-lint-to-eslint-cli .
|
||||
```
|
||||
|
||||
Remove from `next.config.js`:
|
||||
|
||||
```js
|
||||
// Remove this
|
||||
{
|
||||
eslint: {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Run the ESLint migration codemod
|
||||
- [ ] Remove `eslint` config from `next.config.js`
|
||||
- [ ] Update CI scripts to use `eslint` directly instead of `next lint`
|
||||
|
||||
### 12. Feature Removals
|
||||
|
||||
#### 12.1 AMP Support Removed
|
||||
|
||||
- All AMP APIs have been deleted
|
||||
- Remove `useAmp` hook usage
|
||||
- Remove `amp` config option
|
||||
- Delete AMP-specific pages
|
||||
|
||||
#### 12.2 Runtime Configuration Removed
|
||||
|
||||
```js
|
||||
// BEFORE - Remove these
|
||||
module.exports = {
|
||||
serverRuntimeConfig: { dbUrl: process.env.DATABASE_URL },
|
||||
publicRuntimeConfig: { apiUrl: '/api' },
|
||||
};
|
||||
```
|
||||
|
||||
**Migration for server-side config**:
|
||||
|
||||
```tsx
|
||||
// Use environment variables directly
|
||||
async function fetchData() {
|
||||
const dbUrl = process.env.DATABASE_URL;
|
||||
return await db.query(dbUrl, 'SELECT * FROM users');
|
||||
}
|
||||
```
|
||||
|
||||
**Migration for client-side config**:
|
||||
|
||||
```bash
|
||||
# .env.local
|
||||
NEXT_PUBLIC_API_URL="/api"
|
||||
```
|
||||
|
||||
```tsx
|
||||
'use client';
|
||||
export default function Component() {
|
||||
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### 12.3 devIndicators Options Removed
|
||||
|
||||
Remove these from `next.config.js`:
|
||||
|
||||
- `appIsrStatus`
|
||||
- `buildActivity`
|
||||
- `buildActivityPosition`
|
||||
|
||||
#### 12.4 experimental.dynamicIO Renamed
|
||||
|
||||
```js
|
||||
// BEFORE
|
||||
{
|
||||
experimental: {
|
||||
dynamicIO: true;
|
||||
}
|
||||
}
|
||||
|
||||
// AFTER
|
||||
{
|
||||
cacheComponents: true;
|
||||
}
|
||||
```
|
||||
|
||||
#### 12.5 unstable_rootParams Removed
|
||||
|
||||
This API is removed. Await alternative API in a future minor release.
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Remove all AMP-related code
|
||||
- [ ] Migrate runtime configuration to environment variables
|
||||
- [ ] Remove deprecated devIndicators options
|
||||
- [ ] Rename `dynamicIO` to `cacheComponents`
|
||||
|
||||
### 13. Development Changes
|
||||
|
||||
#### 13.1 Concurrent dev and build
|
||||
|
||||
Development now outputs to `.next/dev` (separate from build).
|
||||
|
||||
**Update Turbopack tracing command**:
|
||||
|
||||
```bash
|
||||
npx next internal trace .next/dev/trace-turbopack
|
||||
```
|
||||
|
||||
## Post-Migration Validation
|
||||
|
||||
### 1. Run Build Per Project
|
||||
|
||||
```bash
|
||||
# Build each Next.js project individually
|
||||
nx run PROJECT_NAME:build
|
||||
```
|
||||
|
||||
### 2. Run Development Server
|
||||
|
||||
```bash
|
||||
# Start dev server to verify Turbopack works
|
||||
nx run PROJECT_NAME:serve
|
||||
```
|
||||
|
||||
### 3. Run All Affected Builds
|
||||
|
||||
```bash
|
||||
# Build all affected projects
|
||||
nx affected -t build
|
||||
```
|
||||
|
||||
### 4. Run Full Validation
|
||||
|
||||
```bash
|
||||
# Run full CI validation
|
||||
nx prepush
|
||||
```
|
||||
|
||||
### 5. Review Migration Checklist
|
||||
|
||||
- [ ] All async request APIs updated
|
||||
- [ ] All page/layout components using params are async
|
||||
- [ ] Turbopack configuration updated
|
||||
- [ ] Middleware renamed to proxy
|
||||
- [ ] Parallel routes have default.js files
|
||||
- [ ] Image configuration updated
|
||||
- [ ] Cache imports updated (removed unstable\_ prefix)
|
||||
- [ ] AMP code removed
|
||||
- [ ] Runtime config migrated to env vars
|
||||
- [ ] ESLint configuration migrated
|
||||
- [ ] All projects build successfully
|
||||
- [ ] Development servers start correctly
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### Issue: "cookies() expects to be called in a synchronous context"
|
||||
|
||||
**Solution**: Make the function async and await `cookies()`
|
||||
|
||||
### Issue: "params should be awaited before accessing properties"
|
||||
|
||||
**Solution**: Add `await` before accessing `props.params`
|
||||
|
||||
### Issue: Build fails with Turbopack
|
||||
|
||||
**Solution**: Add `--webpack` flag to build script, then gradually address Turbopack compatibility
|
||||
|
||||
### Issue: Middleware not working after rename
|
||||
|
||||
**Solution**: Ensure both file and function are renamed from `middleware` to `proxy`
|
||||
|
||||
### Issue: Parallel route not rendering
|
||||
|
||||
**Solution**: Add `default.tsx` file to the parallel route slot
|
||||
|
||||
### Issue: Images with query strings not loading
|
||||
|
||||
**Solution**: Add `localPatterns` configuration for those images
|
||||
|
||||
### Issue: TypeScript errors with params types
|
||||
|
||||
**Solution**: Run `npx next typegen` to generate type helpers, then use `PageProps`, `LayoutProps` types
|
||||
|
||||
## Files to Review
|
||||
|
||||
Create a checklist of all files that need review:
|
||||
|
||||
```bash
|
||||
# Find all pages with potential params usage
|
||||
find . -path "*/app/*" -name "page.tsx" -o -name "page.ts" | xargs grep -l "params\|searchParams"
|
||||
|
||||
# Find all layouts
|
||||
find . -path "*/app/*" -name "layout.tsx" -o -name "layout.ts"
|
||||
|
||||
# Find all route handlers
|
||||
find . -path "*/app/*" -name "route.ts" -o -name "route.tsx"
|
||||
|
||||
# Find middleware files
|
||||
find . -name "middleware.ts" -o -name "middleware.js"
|
||||
|
||||
# Find files using cookies/headers
|
||||
rg "from 'next/headers'" --type ts --type tsx
|
||||
|
||||
# Find next.config files
|
||||
find . -name "next.config.*" -not -path "*/node_modules/*"
|
||||
|
||||
# Find parallel routes
|
||||
find . -path "*/app/@*" -type d
|
||||
```
|
||||
|
||||
## Migration Strategy for Large Workspaces
|
||||
|
||||
1. **Migrate in phases**: Start with a small project, validate, then expand
|
||||
2. **Use the codemod**: Run `npx @next/codemod@canary upgrade latest` for automated fixes
|
||||
3. **Generate types**: Run `npx next typegen` for type-safe migrations
|
||||
4. **Run tests frequently**: After each configuration change, run affected tests
|
||||
5. **Document issues**: Keep track of project-specific issues and solutions
|
||||
|
||||
## Useful Commands During Migration
|
||||
|
||||
```bash
|
||||
# Find all Next.js projects
|
||||
nx show projects --with-target build
|
||||
|
||||
# Build specific project
|
||||
nx build PROJECT_NAME
|
||||
|
||||
# Serve specific project
|
||||
nx serve PROJECT_NAME
|
||||
|
||||
# Build all affected
|
||||
nx affected -t build
|
||||
|
||||
# View project details
|
||||
nx show project PROJECT_NAME --web
|
||||
|
||||
# Clear Nx cache if needed
|
||||
nx reset
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes for LLM Execution
|
||||
|
||||
When executing this migration:
|
||||
|
||||
1. **Work systematically**: Complete one category before moving to the next
|
||||
2. **Test after each change**: Don't batch all changes without validation
|
||||
3. **Keep user informed**: Report progress through each section
|
||||
4. **Handle errors promptly**: If builds fail, fix immediately before proceeding
|
||||
5. **Use the codemod first**: Let `@next/codemod` handle repetitive async/await changes
|
||||
6. **Prioritize breaking changes**: Focus on async APIs first as they're most impactful
|
||||
7. **Create meaningful commits**: Group related changes together with clear messages
|
||||
8. **Use TodoWrite tool**: Track migration progress for visibility
|
||||
@@ -1,725 +0,0 @@
|
||||
# Vitest 4.0 Migration Instructions for LLM
|
||||
|
||||
## Overview
|
||||
|
||||
These instructions guide you through migrating an Nx workspace containing multiple Vitest projects from Vitest 3.x to Vitest 4.0. Work systematically through each breaking change category.
|
||||
|
||||
## Pre-Migration Checklist
|
||||
|
||||
1. **Identify all Vitest projects**:
|
||||
|
||||
```bash
|
||||
nx show projects --with-target test
|
||||
```
|
||||
|
||||
2. **Locate all Vitest configuration files**:
|
||||
- Search for `vitest.config.{ts,js,mjs}`
|
||||
- Search for `vitest.workspace.{ts,js,mjs}`
|
||||
- Check `project.json` files for inline Vitest configuration
|
||||
|
||||
3. **Identify affected code**:
|
||||
- Test files: `**/*.{spec,test}.{ts,js,tsx,jsx}`
|
||||
- Mock usage: Files using `vi.fn()`, `vi.spyOn()`, `vi.mock()`
|
||||
- Coverage configuration references
|
||||
|
||||
## Migration Steps by Category
|
||||
|
||||
### 1. Configuration File Updates
|
||||
|
||||
#### 1.1 Coverage Configuration
|
||||
|
||||
**Search Pattern**: `coverage` in all `vitest.config.*` files and `project.json` test target options
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
export default defineConfig({
|
||||
test: {
|
||||
coverage: {
|
||||
all: true,
|
||||
extensions: ['.ts', '.tsx'],
|
||||
ignoreEmptyLines: false,
|
||||
experimentalAstAwareRemapping: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
export default defineConfig({
|
||||
test: {
|
||||
coverage: {
|
||||
// Explicitly define files to include in coverage
|
||||
include: ['src/**/*.{ts,tsx}'],
|
||||
// Remove: all, extensions, ignoreEmptyLines, experimentalAstAwareRemapping
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Remove `coverage.all` option
|
||||
- [ ] Remove `coverage.extensions` option
|
||||
- [ ] Remove `coverage.ignoreEmptyLines` option
|
||||
- [ ] Remove `coverage.experimentalAstAwareRemapping` option
|
||||
- [ ] Add explicit `coverage.include` patterns based on project structure
|
||||
- [ ] Update any documentation referencing these options
|
||||
|
||||
#### 1.2 Pool Options Restructuring
|
||||
|
||||
**Search Pattern**: `poolOptions`, `maxThreads`, `maxForks`, `singleThread`, `singleFork` in all Vitest config files
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
export default defineConfig({
|
||||
test: {
|
||||
maxThreads: 4,
|
||||
maxForks: 2,
|
||||
singleThread: false,
|
||||
poolOptions: {
|
||||
threads: {
|
||||
useAtomics: true,
|
||||
},
|
||||
vmThreads: {
|
||||
memoryLimit: '512MB',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
export default defineConfig({
|
||||
test: {
|
||||
maxWorkers: 4, // Consolidates maxThreads and maxForks
|
||||
isolate: true, // Replaces singleThread: false
|
||||
// Remove: poolOptions, threads.useAtomics
|
||||
vmMemoryLimit: '512MB', // Moved to top-level
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Replace `maxThreads` and `maxForks` with single `maxWorkers` option
|
||||
- [ ] Replace `singleThread: true` or `singleFork: true` with `maxWorkers: 1, isolate: false`
|
||||
- [ ] Move all `poolOptions.*` nested options to top-level (e.g., `poolOptions.vmThreads.memoryLimit` → `vmMemoryLimit`)
|
||||
- [ ] Remove `threads.useAtomics` option
|
||||
- [ ] Update CI environment variables: `VITEST_MAX_THREADS` and `VITEST_MAX_FORKS` → `VITEST_MAX_WORKERS`
|
||||
|
||||
#### 1.3 Workspace to Projects Rename
|
||||
|
||||
**Search Pattern**: `workspace` property in Vitest config files
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
export default defineConfig({
|
||||
test: {
|
||||
workspace: ['apps/*', 'libs/*'],
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
export default defineConfig({
|
||||
test: {
|
||||
projects: ['apps/*', 'libs/*'],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Rename `workspace` property to `projects` in all config files
|
||||
- [ ] Remove external workspace file references (must be inline in config)
|
||||
- [ ] Update `poolMatchGlobs` to use `projects` pattern matching instead
|
||||
- [ ] Update `environmentMatchGlobs` to use `projects` pattern matching instead
|
||||
|
||||
#### 1.4 Browser Configuration
|
||||
|
||||
**Search Pattern**: `browser.provider`, `browser.testerScripts`, imports from `@vitest/browser`
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
export default defineConfig({
|
||||
test: {
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: 'playwright', // String value
|
||||
testerScripts: ['./setup.js'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Import changes
|
||||
import { page } from '@vitest/browser';
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
export default defineConfig({
|
||||
test: {
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: { name: 'playwright' }, // Object value
|
||||
testerHtmlPath: './test-setup.html', // Renamed from testerScripts
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Import changes
|
||||
import { page } from 'vitest/browser';
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Convert `browser.provider` string values to object format: `{ name: 'provider-name' }`
|
||||
- [ ] Replace `browser.testerScripts` with `browser.testerHtmlPath`
|
||||
- [ ] Update all imports from `@vitest/browser` to `vitest/browser`
|
||||
- [ ] Remove `@vitest/browser` from dependencies if no longer needed
|
||||
|
||||
#### 1.5 Deprecated Configuration Options
|
||||
|
||||
**Search Pattern**: `deps.external`, `deps.inline`, `deps.fallbackCJS` in config files
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
export default defineConfig({
|
||||
test: {
|
||||
deps: {
|
||||
external: ['some-package'],
|
||||
inline: ['inline-package'],
|
||||
fallbackCJS: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
export default defineConfig({
|
||||
test: {
|
||||
server: {
|
||||
deps: {
|
||||
external: ['some-package'],
|
||||
inline: ['inline-package'],
|
||||
fallbackCJS: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Move `deps.*` options under `server.deps` namespace
|
||||
- [ ] Remove `poolMatchGlobs` (use `projects` with conditions instead)
|
||||
- [ ] Remove `environmentMatchGlobs` (use `projects` with conditions instead)
|
||||
|
||||
### 2. Test Code Updates
|
||||
|
||||
#### 2.1 Mock Function Name Changes
|
||||
|
||||
**Search Pattern**: `.getMockName()` calls in test files
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
const mockFn = vi.fn();
|
||||
expect(mockFn.getMockName()).toBe('spy'); // Old default
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
const mockFn = vi.fn();
|
||||
expect(mockFn.getMockName()).toBe('vi.fn()'); // New default
|
||||
|
||||
// If you need custom names, set them explicitly
|
||||
const namedMock = vi.fn().mockName('myCustomName');
|
||||
expect(namedMock.getMockName()).toBe('myCustomName');
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Update test assertions checking default mock names from `'spy'` to `'vi.fn()'`
|
||||
- [ ] Add explicit `.mockName()` calls where specific names are required
|
||||
|
||||
#### 2.2 Mock Invocation Call Order
|
||||
|
||||
**Search Pattern**: `.mock.invocationCallOrder` in test files
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
const mockFn = vi.fn();
|
||||
mockFn();
|
||||
expect(mockFn.mock.invocationCallOrder[0]).toBe(0); // Started at 0
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
const mockFn = vi.fn();
|
||||
mockFn();
|
||||
expect(mockFn.mock.invocationCallOrder[0]).toBe(1); // Now starts at 1 (Jest-compatible)
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Update assertions on `invocationCallOrder` to account for 1-based indexing
|
||||
- [ ] Search for off-by-one errors in call order comparisons
|
||||
|
||||
#### 2.3 Constructor Spies and Mocks
|
||||
|
||||
**Search Pattern**: `vi.spyOn` on constructors, `vi.fn()` used as constructors
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x) - Arrow function constructors might have worked
|
||||
const MockConstructor = vi.fn(() => ({ value: 42 }));
|
||||
new MockConstructor(); // May have worked in v3
|
||||
|
||||
// ✅ AFTER (Vitest 4.0) - Must use function or class
|
||||
const MockConstructor = vi.fn(function () {
|
||||
return { value: 42 };
|
||||
});
|
||||
new MockConstructor(); // Correctly supports 'new'
|
||||
|
||||
// Or use class syntax
|
||||
class MockClass {
|
||||
value = 42;
|
||||
}
|
||||
const MockConstructor = vi.fn(MockClass);
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Convert arrow function mocks used as constructors to `function` keyword or `class` syntax
|
||||
- [ ] Test all constructor spies to ensure `new` keyword works correctly
|
||||
- [ ] Update any mocks that expect constructor behavior
|
||||
|
||||
#### 2.4 RestoreAllMocks Behavior
|
||||
|
||||
**Search Pattern**: `vi.restoreAllMocks()` in test files
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
vi.mock('./module', () => ({ fn: vi.fn() }));
|
||||
vi.restoreAllMocks(); // Would restore automocks
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
vi.mock('./module', () => ({ fn: vi.fn() }));
|
||||
vi.restoreAllMocks(); // Only restores manual spies, NOT automocks
|
||||
|
||||
// To reset automocks, use:
|
||||
vi.unmock('./module');
|
||||
// or
|
||||
vi.resetModules();
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Review all `vi.restoreAllMocks()` usage
|
||||
- [ ] Add explicit `vi.unmock()` or `vi.resetModules()` calls for automocked modules
|
||||
- [ ] Ensure test isolation is maintained after this change
|
||||
|
||||
#### 2.5 SpyOn Return Value Changes
|
||||
|
||||
**Search Pattern**: `vi.spyOn()` on already mocked functions
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
const mock = vi.fn();
|
||||
const spy = vi.spyOn({ method: mock }, 'method');
|
||||
// spy !== mock (created new spy)
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
const mock = vi.fn();
|
||||
const spy = vi.spyOn({ method: mock }, 'method');
|
||||
// spy === mock (returns same instance)
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Review code that creates spies on existing mocks
|
||||
- [ ] Remove redundant spy creation if same instance is returned
|
||||
- [ ] Update assertions that check spy identity
|
||||
|
||||
#### 2.6 Automock Behavior Changes
|
||||
|
||||
**Search Pattern**: `vi.mock()` with factory functions, `.mockRestore()` on automocks
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
vi.mock('./utils', () => ({
|
||||
get value() {
|
||||
return 42;
|
||||
}, // Would call getter
|
||||
}));
|
||||
|
||||
import { value } from './utils';
|
||||
console.log(value); // Would execute getter logic
|
||||
|
||||
// Restore might have worked
|
||||
const spy = vi.spyOn(obj, 'method');
|
||||
spy.mockRestore(); // Might work on automocks
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
vi.mock('./utils', () => ({
|
||||
get value() {
|
||||
return 42;
|
||||
},
|
||||
}));
|
||||
|
||||
import { value } from './utils';
|
||||
console.log(value); // Returns undefined (doesn't call getter)
|
||||
|
||||
// Explicitly return value if needed
|
||||
vi.mock('./utils', () => ({
|
||||
value: 42, // Not a getter
|
||||
}));
|
||||
|
||||
// mockRestore no longer works on automocks
|
||||
const spy = vi.spyOn(obj, 'method');
|
||||
spy.mockRestore(); // Throws error if method is automocked
|
||||
|
||||
// Use unmock instead
|
||||
vi.unmock('./module');
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Convert automocked getters to plain property values where needed
|
||||
- [ ] Remove `.mockRestore()` calls on automocked methods
|
||||
- [ ] Use `vi.unmock()` to clear automocks instead
|
||||
- [ ] Test instance method isolation (they now share state with prototype)
|
||||
|
||||
#### 2.7 Settled Results Immediate Population
|
||||
|
||||
**Search Pattern**: `.mock.settledResults` in test files
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
const asyncMock = vi.fn(async () => 'result');
|
||||
const promise = asyncMock();
|
||||
|
||||
// settledResults is immediately populated with 'incomplete' status
|
||||
expect(asyncMock.mock.settledResults[0]).toEqual({
|
||||
type: 'incomplete',
|
||||
value: undefined,
|
||||
});
|
||||
|
||||
// After promise resolves
|
||||
await promise;
|
||||
expect(asyncMock.mock.settledResults[0]).toEqual({
|
||||
type: 'fulfilled',
|
||||
value: 'result',
|
||||
});
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Update tests that check `settledResults` before promise resolution
|
||||
- [ ] Handle `'incomplete'` status in assertions
|
||||
- [ ] Ensure tests properly await promises before checking settled results
|
||||
|
||||
### 3. Reporter and CLI Changes
|
||||
|
||||
#### 3.1 Reporter API Changes
|
||||
|
||||
**Search Pattern**: Custom reporters, `onCollected`, `onTaskUpdate`, `onFinished`
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
export default {
|
||||
onCollected(files) {
|
||||
// Handle collected files
|
||||
},
|
||||
onTaskUpdate(task) {
|
||||
// Handle task update
|
||||
},
|
||||
onFinished(files) {
|
||||
// Handle completion
|
||||
},
|
||||
};
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
// Use new reporter API - consult Vitest 4 docs for replacement methods
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Review custom reporters for removed API usage
|
||||
- [ ] Consult Vitest 4 documentation for new reporter API
|
||||
- [ ] Update or rewrite custom reporters to use new APIs
|
||||
|
||||
#### 3.2 Built-in Reporter Changes
|
||||
|
||||
**Search Pattern**: `reporters: ['basic']`, `reporters: ['verbose']`
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
export default defineConfig({
|
||||
test: {
|
||||
reporters: ['basic'],
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
export default defineConfig({
|
||||
test: {
|
||||
reporters: [['default', { summary: false }]], // Equivalent to 'basic'
|
||||
},
|
||||
});
|
||||
|
||||
// For verbose (tree output)
|
||||
reporters: ['tree']; // Use 'tree' for hierarchical output
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Replace `'basic'` reporter with `['default', { summary: false }]`
|
||||
- [ ] Replace `'verbose'` reporter with `'tree'` for hierarchical output
|
||||
- [ ] Update CI configuration if reporters are specified there
|
||||
|
||||
### 4. Snapshot Changes
|
||||
|
||||
#### 4.1 Custom Elements Shadow Root
|
||||
|
||||
**Search Pattern**: Snapshot tests involving custom elements or Web Components
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
// Shadow root contents now printed by default in snapshots
|
||||
|
||||
// If you want old behavior (don't print shadow root):
|
||||
export default defineConfig({
|
||||
test: {
|
||||
printShadowRoot: false,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Review snapshot tests for custom elements
|
||||
- [ ] Update snapshots if shadow root contents are now included
|
||||
- [ ] Add `printShadowRoot: false` if old behavior is required
|
||||
|
||||
### 5. Environment Variable Updates
|
||||
|
||||
**Search Pattern**: CI/CD configuration files, `.env` files, documentation
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```bash
|
||||
# ❌ BEFORE (Vitest 3.x)
|
||||
VITEST_MAX_THREADS=4
|
||||
VITEST_MAX_FORKS=2
|
||||
VITE_NODE_DEPS_MODULE_DIRECTORIES=/custom/path
|
||||
|
||||
# ✅ AFTER (Vitest 4.0)
|
||||
VITEST_MAX_WORKERS=4
|
||||
VITEST_MODULE_DIRECTORIES=/custom/path
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Update CI/CD pipeline environment variables
|
||||
- [ ] Update `.env` files
|
||||
- [ ] Update documentation referencing old environment variables
|
||||
- [ ] Search for `VITEST_MAX_THREADS`, `VITEST_MAX_FORKS`, `VITE_NODE_DEPS_MODULE_DIRECTORIES`
|
||||
|
||||
### 6. Advanced: Module Runner Changes
|
||||
|
||||
**Search Pattern**: `vitest/execute`, `__vitest_executor`, `vite-node`
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// ❌ BEFORE (Vitest 3.x)
|
||||
import { execute } from 'vitest/execute';
|
||||
// Access to __vitest_executor
|
||||
|
||||
// ✅ AFTER (Vitest 4.0)
|
||||
// Use Vite's Module Runner API instead
|
||||
// Consult Vite Module Runner documentation
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] If using `vitest/execute`, migrate to Vite Module Runner
|
||||
- [ ] Remove dependencies on `__vitest_executor`
|
||||
- [ ] Update custom pool implementations (complete rewrite needed)
|
||||
|
||||
### 7. Type Definition Updates
|
||||
|
||||
**Search Pattern**: TypeScript imports from `vitest`, type errors after upgrade
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
```typescript
|
||||
// All deprecated type exports removed
|
||||
// If you get TypeScript errors about missing types:
|
||||
// - Check if you're using deprecated type names
|
||||
// - Update to current type names from Vitest 4 API
|
||||
// - Remove explicit @types/node if it was only needed due to Vitest bug
|
||||
```
|
||||
|
||||
**Action Items**:
|
||||
|
||||
- [ ] Run TypeScript compilation on all test files
|
||||
- [ ] Fix any type errors related to removed Vitest type definitions
|
||||
- [ ] Review `@types/node` usage (may no longer be accidentally included)
|
||||
|
||||
## Post-Migration Validation
|
||||
|
||||
### 1. Run Tests Per Project
|
||||
|
||||
```bash
|
||||
# Test each project individually
|
||||
nx run-many -t test -p PROJECT_NAME
|
||||
```
|
||||
|
||||
### 2. Run All Tests
|
||||
|
||||
```bash
|
||||
# Run tests across all affected projects
|
||||
nx affected -t test
|
||||
```
|
||||
|
||||
### 3. Check Coverage
|
||||
|
||||
```bash
|
||||
# Verify coverage generation works with new config
|
||||
nx affected -t test --coverage
|
||||
```
|
||||
|
||||
### 4. Validate CI Pipeline
|
||||
|
||||
```bash
|
||||
# Run full CI validation
|
||||
nx prepush
|
||||
```
|
||||
|
||||
### 5. Review Migration Checklist
|
||||
|
||||
- [ ] All configuration files updated
|
||||
- [ ] All test files pass
|
||||
- [ ] Coverage reports generate correctly
|
||||
- [ ] CI/CD pipeline runs successfully
|
||||
- [ ] Environment variables updated
|
||||
- [ ] Documentation updated
|
||||
- [ ] No deprecated API warnings in console
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### Issue: Coverage includes too many files
|
||||
|
||||
**Solution**: Add explicit `coverage.include` patterns to match your source files
|
||||
|
||||
### Issue: Tests fail with "arrow function constructors not supported"
|
||||
|
||||
**Solution**: Convert arrow functions used as constructors to `function` keyword or `class` syntax
|
||||
|
||||
### Issue: Automocks not resetting between tests
|
||||
|
||||
**Solution**: Use `vi.unmock()` or `vi.resetModules()` instead of `vi.restoreAllMocks()`
|
||||
|
||||
### Issue: Mock call order assertions failing
|
||||
|
||||
**Solution**: Update to 1-based indexing for `invocationCallOrder`
|
||||
|
||||
### Issue: Browser tests failing after upgrade
|
||||
|
||||
**Solution**: Check browser provider is object format and imports use `vitest/browser`
|
||||
|
||||
### Issue: TypeScript errors in test files
|
||||
|
||||
**Solution**: Update to new type definitions and remove usage of deprecated types
|
||||
|
||||
## Files to Review
|
||||
|
||||
Create a checklist of all files that need review:
|
||||
|
||||
```bash
|
||||
# Configuration files
|
||||
find . -name "vitest.config.*" -o -name "vitest.workspace.*"
|
||||
find . -name "project.json" -exec grep -l "vitest" {} \;
|
||||
|
||||
# Test files
|
||||
find . -name "*.spec.*" -o -name "*.test.*"
|
||||
|
||||
# Files with mock usage
|
||||
rg "vi\.(fn|spyOn|mock|restoreAllMocks)" --type ts --type tsx --type js
|
||||
|
||||
# Files with coverage config
|
||||
rg "coverage\.(all|extensions|ignoreEmptyLines)" --type ts --type js
|
||||
|
||||
# CI configuration
|
||||
find . -name ".github/workflows/*.yml" -o -name ".gitlab-ci.yml" -o -name "azure-pipelines.yml"
|
||||
```
|
||||
|
||||
## Migration Strategy for Large Workspaces
|
||||
|
||||
1. **Migrate in phases**: Start with a small project, validate, then expand
|
||||
2. **Use feature branches**: Create separate branches for different migration aspects
|
||||
3. **Run tests frequently**: After each configuration change, run affected tests
|
||||
4. **Document issues**: Keep track of project-specific issues and solutions
|
||||
5. **Automate where possible**: Create codemods for repetitive changes
|
||||
|
||||
## Useful Commands During Migration
|
||||
|
||||
```bash
|
||||
# Find all vitest configurations
|
||||
nx show projects --with-target test
|
||||
|
||||
# Test specific project after changes
|
||||
nx test PROJECT_NAME
|
||||
|
||||
# Test all affected
|
||||
nx affected -t test
|
||||
|
||||
# View project details
|
||||
nx show project PROJECT_NAME --web
|
||||
|
||||
# Clear Nx cache if needed
|
||||
nx reset
|
||||
```
|
||||
|
||||
## Guard Rails
|
||||
|
||||
DO NOT
|
||||
|
||||
- Force tests to pass by removing test logic and replacing it with `expect(true).toBe(true)`
|
||||
- Remove assertions
|
||||
- Add additional mocks that force tests to pass
|
||||
|
||||
---
|
||||
|
||||
## Notes for LLM Execution
|
||||
|
||||
When executing this migration:
|
||||
|
||||
1. **Work systematically**: Complete one category before moving to the next
|
||||
2. **Test after each change**: Don't batch all changes without validation
|
||||
3. **Keep user informed**: Report progress through each section
|
||||
4. **Handle errors promptly**: If tests fail, fix immediately before proceeding
|
||||
5. **Update documentation**: Note any workspace-specific patterns or issues
|
||||
6. **Create meaningful commits**: Group related changes together with clear messages
|
||||
7. **Use TodoWrite tool**: Track migration progress for visibility
|
||||
Reference in New Issue
Block a user