Skip to content

Commit b8c0b5a

Browse files
committed
Add migration plan for dependency updates, App Router, and Sentry CLI
Three-phase incremental plan: - Phase 1: Update all deps to latest (Next.js 16.2.0, React 19, etc.) - Phase 2: Full Pages Router → App Router migration (37 pages) - Phase 3: Reconfigure Sentry via sentry-cli with full setup https://claude.ai/code/session_011UqxX4GJ2sUwDNcQtwzUpk
1 parent 9f16962 commit b8c0b5a

2 files changed

Lines changed: 351 additions & 1 deletion

File tree

PLAN.md

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
# Migration Plan: Update Deps, App Router, Sentry CLI
2+
3+
## Overview
4+
Incremental migration in 3 phases. Each phase verified with tests before proceeding.
5+
6+
**Success criteria:** All Playwright e2e tests and Vitest unit tests pass.
7+
8+
---
9+
10+
## Phase 1: Update All Dependencies to Latest
11+
12+
### 1.1 Update Next.js 12 → 16.2.0 (and React 18 → 19)
13+
Next.js 16 requires React 19. Key breaking changes across versions:
14+
15+
- **Next 13**: `<Link>` no longer needs `<a>` child, `next/image` removes `layout` prop, App Router introduced
16+
- **Next 14**: Turbopack stabilized, Server Actions stable
17+
- **Next 15**: `fetch` no longer cached by default, `params`/`searchParams` are async, React 19
18+
- **Next 16**: React 19 required, continued App Router improvements
19+
20+
**Changes needed before update:**
21+
1. **`next/link`** — Remove nested `<a>` tags in 8 files (about, contact, faq, get_involved, join/index, press, scholarship/index, terms, Nav component)
22+
2. **`next/image`** — Remove deprecated `layout` prop in 4 files (project_rebuild, scholarship/index, get_involved). Replace with CSS equivalents:
23+
- `layout="fill"``fill` boolean prop + parent `position: relative`
24+
- `layout="fixed"` → explicit `width`/`height` (already present)
25+
- `layout="intrinsic"` → explicit `width`/`height`
26+
3. **`images.domains`**`images.remotePatterns` in next.config.js
27+
4. **`@next/bundle-analyzer`** — Update to match Next.js version
28+
5. **`next.config.js`** — Convert to `next.config.mjs` (ESM), remove `excludeDefaultMomentLocales` (removed), update `devIndicators`
29+
30+
### 1.2 Update React 18 → 19
31+
- `react`, `react-dom` → ^19.x
32+
- `@types/react`, `@types/react-dom` → ^19.x
33+
- `react-test-renderer` → removed (deprecated in React 19, use `@testing-library/react` instead)
34+
- `react-is` → ^19.x
35+
- Check for removed APIs: `ReactDOM.render`, `findDOMNode` etc.
36+
37+
### 1.3 Update @sentry/nextjs 7 → 9 (latest)
38+
- Massive API changes — handled in Phase 3 detail
39+
- For now, just bump version
40+
41+
### 1.4 Update All Other Dependencies
42+
| Package | Current | Action |
43+
|---------|---------|--------|
44+
| `@radix-ui/react-dialog` | 1.1.15 | Update to latest |
45+
| `@radix-ui/react-tabs` | 1.1.13 | Update to latest |
46+
| `axios` | ^1.12.2 | Update to latest |
47+
| `cva` | 1.0.0-beta.4 | Update to latest stable |
48+
| `fast-xml-parser` | ^3.21.1 | Update to v5 (breaking: API changes) |
49+
| `formik` | ^2.4.6 | Update to latest |
50+
| `logrocket` | ^10.1.0 | Update to latest |
51+
| `react-select` | ^4.0.2 | Update to v5 (breaking: React 18+ only, type changes) |
52+
| `react-player` | ^2.16.0 | Update to latest |
53+
| `tailwind-merge` | ^3.3.1 | Update to latest |
54+
| `yup` | ^1.7.1 | Update to latest |
55+
| `next-sitemap` | ^4.2.3 | Update to latest |
56+
| `@svgr/webpack` | ^6.3.1 | Update to latest (v8) |
57+
| `eslint` | ^8.56.0 | Update to v9 (flat config) |
58+
| `@typescript-eslint/*` | ^6.21.0 | Update to latest (v8) |
59+
| `storybook` | ^7.4.1 | Update to v8 |
60+
| `@testing-library/react` | ^12.1.5 | Update to v16 |
61+
| `@testing-library/jest-dom` | ^6.9.1 | Update to latest |
62+
| `chromatic` | ^6.8.0 | Update to latest |
63+
| `playwright` | ^1.56.1 | Update to latest |
64+
| `lint-staged` | 13.0.3 | Update to latest |
65+
| `postcss` | ^8.5.6 | Update to latest |
66+
| `typescript` | ^5.9.3 | Update to latest |
67+
| `vitest` / `@vitest/*` | ^3.1.2 | Update to latest |
68+
69+
### 1.5 Remove Obsolete Dependencies
70+
- `fingerprintjs2` — discontinued, replace or remove
71+
- `next-cookies` — not needed with App Router (use `cookies()` from `next/headers`)
72+
- `path` — Node.js built-in, no need for polyfill
73+
- `intersection-observer` — polyfill no longer needed (widely supported)
74+
- `css-loader`, `style-loader`, `file-loader`, `url-loader` — webpack loaders not needed with Next.js
75+
- `require-context.macro` — CRA-specific, not used
76+
- `identity-obj-proxy` — replaced by vitest CSS handling
77+
- `process` — polyfill not needed
78+
- `express` — check if actually used, likely removable
79+
- `react-test-renderer` — deprecated in React 19
80+
81+
### 1.6 Fix Breaking Changes in Dependencies
82+
- **`fast-xml-parser` v3→v5**: API changed. Update `pages/podcast.tsx` parser usage
83+
- **`react-select` v4→v5**: Type imports changed, `styles` API slightly different
84+
- **`@testing-library/react` v12→v16**: `render` import stable, but `renderHook` moved
85+
- **ESLint v8→v9**: Requires flat config migration (`.eslintrc.js``eslint.config.mjs`)
86+
- **Storybook v7→v8**: Config format may change slightly
87+
88+
### 1.7 Verify Phase 1
89+
- Run `pnpm test` (vitest unit tests)
90+
- Run `pnpm test:e2e:headless` (playwright)
91+
- Fix any snapshot mismatches with `pnpm test:update-snaps`
92+
93+
---
94+
95+
## Phase 2: Migrate Pages Router → App Router (Full Migration)
96+
97+
### 2.1 Create App Directory Structure
98+
```
99+
app/
100+
├── layout.tsx ← from _app.tsx + _document.tsx
101+
├── page.tsx ← from pages/index.tsx
102+
├── not-found.tsx ← from pages/404.tsx
103+
├── error.tsx ← from pages/_error.tsx
104+
├── about/page.tsx
105+
├── blog/page.tsx
106+
├── branding/page.tsx
107+
├── challenge/page.tsx
108+
├── chapters/page.tsx
109+
├── chapter_leader/page.tsx
110+
├── contact/page.tsx
111+
├── corporate-training/page.tsx
112+
├── donate/page.tsx
113+
├── faq/page.tsx
114+
├── get_involved/page.tsx
115+
├── history/page.tsx
116+
├── jobs/page.tsx
117+
├── podcast/page.tsx
118+
├── policy/page.tsx
119+
├── press/page.tsx
120+
├── project_rebuild/page.tsx
121+
├── scholarship/
122+
│ ├── page.tsx
123+
│ └── code_platoon/page.tsx
124+
├── services/page.tsx
125+
├── slack_guide/page.tsx
126+
├── sponsorship/page.tsx
127+
├── team/page.tsx
128+
├── terms/page.tsx
129+
├── thank_you/page.tsx
130+
├── join/
131+
│ ├── page.tsx
132+
│ ├── form/page.tsx
133+
│ └── success/page.tsx
134+
└── api/
135+
└── registration/
136+
├── new/route.ts
137+
└── update/route.ts
138+
```
139+
140+
### 2.2 Convert _app.tsx + _document.tsx → app/layout.tsx
141+
The root layout combines both:
142+
- **From _document.tsx**: `<html lang="en">`, meta tags, GA scripts, favicon
143+
- **From _app.tsx**: Layout wrapper (Nav, Footer, ScrollToTopButton), analytics initialization, font loading
144+
- Metadata API replaces `<Head>` for static meta (title, OG, Twitter card)
145+
- Google Analytics moves to `<Script>` from `next/script`
146+
- Font loading moves to `next/font/google`
147+
- CSS import stays in layout
148+
149+
### 2.3 Convert Custom Head Component → Metadata API
150+
- `components/head.tsx` uses `next/head` — replace with:
151+
- Per-route `export const metadata` for static pages
152+
- `generateMetadata()` function for dynamic pages
153+
- Remove `next/head` import from all files
154+
155+
### 2.4 Convert Page Components
156+
For each page in `pages/*.tsx`:
157+
1. Move to `app/*/page.tsx`
158+
2. Remove `<Head>` usage, add `metadata` export
159+
3. Mark as `'use client'` if it uses hooks, state, or browser APIs
160+
4. Pages that are purely presentational can be Server Components (no directive needed)
161+
162+
**Pages requiring `'use client'`** (use hooks/state/effects):
163+
- join/index (useRouter, useEffect, useState)
164+
- join/form (useEffect, formik)
165+
- blog/index (useRouter)
166+
- podcast (if using client-side player)
167+
- faq (accordion state)
168+
169+
**Pages that can be Server Components:**
170+
- about, branding, challenge, chapters, chapter_leader, contact, corporate-training, donate, get_involved, history, jobs, policy, press, project_rebuild, services, slack_guide, sponsorship, team, terms, thank_you, scholarship/*, join/success
171+
172+
### 2.5 Convert Data Fetching
173+
- **`pages/podcast.tsx` `getStaticProps`** → async Server Component with `fetch()` + `revalidate`
174+
- **`pages/join/form.tsx` `getInitialProps`** (cookie check + redirect) → `middleware.ts` or Server Component with `cookies()` from `next/headers`
175+
176+
### 2.6 Convert API Routes
177+
- `pages/api/registration/new.ts``app/api/registration/new/route.ts`
178+
- `export default handler``export async function POST(request: Request)`
179+
- `req.body``await request.json()`
180+
- `res.status().json()``return NextResponse.json()`
181+
- Cookie setting via `NextResponse` headers
182+
- Same for `update.ts``route.ts` with `PATCH` export
183+
184+
### 2.7 Convert Router Usage
185+
5 files use `next/router`:
186+
- `useRouter()``useRouter()` from `next/navigation` (different API: no `query`, use `useSearchParams()`)
187+
- `Router.events` → removed in App Router. Use `usePathname()` + `useEffect` for route change detection
188+
- `router.push()``router.push()` (compatible)
189+
- `router.prefetch()``router.prefetch()` (compatible)
190+
- `router.query``useSearchParams()` or page `params` prop
191+
192+
### 2.8 Update next.config
193+
- Remove Pages Router-specific config
194+
- Ensure App Router compatibility
195+
- Update cache headers source patterns for app router static paths
196+
197+
### 2.9 Delete pages/ Directory
198+
After all pages are migrated and verified, delete `pages/` entirely.
199+
200+
### 2.10 Update tsconfig.json Path Aliases
201+
- Remove `pages/*` alias (no longer needed)
202+
- Ensure `app/*` works if needed
203+
204+
### 2.11 Update Tests
205+
- **Vitest**: Update `vitest.setup.tsx` if needed for App Router mocking
206+
- **Playwright**: Tests should mostly work as-is since they test the running app via URL
207+
- **Snapshots**: Regenerate all snapshots after migration
208+
- **next-router-mock**: May need updates for `next/navigation` mocking
209+
210+
### 2.12 Verify Phase 2
211+
- `pnpm build` (ensure production build succeeds)
212+
- `pnpm test` (unit tests)
213+
- `pnpm test:e2e:headless` (e2e tests)
214+
- Fix any failures
215+
216+
---
217+
218+
## Phase 3: Reconfigure Sentry via sentry-cli (Full Setup)
219+
220+
### 3.1 Update @sentry/nextjs to v9 (Latest)
221+
Already bumped in Phase 1, but now configure properly:
222+
223+
**New initialization pattern (v8+):**
224+
- `sentry.client.config.js``sentry.client.config.ts` (updated API)
225+
- `sentry.server.config.js``sentry.server.config.ts` (updated API)
226+
- Add `sentry.edge.config.ts` (new in v8)
227+
- Add `instrumentation.ts` in project root (Next.js instrumentation hook):
228+
```ts
229+
export async function register() {
230+
if (process.env.NEXT_RUNTIME === 'nodejs') {
231+
await import('./sentry.server.config');
232+
}
233+
if (process.env.NEXT_RUNTIME === 'edge') {
234+
await import('./sentry.edge.config');
235+
}
236+
}
237+
```
238+
239+
**Config changes:**
240+
- `Sentry.configureScope()` → removed, use `Sentry.setExtra()`, `Sentry.setTag()`
241+
- `Sentry.flush()` → still available
242+
- `withSentryConfig` API updated — new options format
243+
244+
### 3.2 Install and Configure sentry-cli
245+
```bash
246+
pnpm add -D @sentry/cli
247+
```
248+
249+
**Update `sentry.properties`:**
250+
```
251+
defaults.url=https://sentry.io/
252+
defaults.org=operation-code
253+
defaults.project=front-end
254+
cli.executable=node_modules/.bin/sentry-cli
255+
```
256+
257+
### 3.3 Configure Source Map Uploads
258+
In `next.config.mjs`, update `withSentryConfig` options:
259+
```js
260+
withSentryConfig(nextConfig, {
261+
org: "operation-code",
262+
project: "front-end",
263+
silent: !process.env.CI,
264+
widenClientFileUpload: true,
265+
sourcemaps: {
266+
deleteSourcemapsAfterUpload: true,
267+
},
268+
// Release management
269+
release: {
270+
name: process.env.SENTRY_RELEASE || `front-end@${pkg.version}`,
271+
create: true,
272+
finalize: true,
273+
// Commit integration
274+
setCommits: {
275+
auto: true,
276+
},
277+
// Deploy tracking
278+
deploy: {
279+
env: process.env.VERCEL_ENV || 'development',
280+
},
281+
},
282+
});
283+
```
284+
285+
### 3.4 Update Sentry Client Config
286+
```ts
287+
import * as Sentry from '@sentry/nextjs';
288+
289+
Sentry.init({
290+
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN || '...',
291+
tracesSampleRate: 1.0,
292+
replaysSessionSampleRate: 0.1,
293+
replaysOnErrorSampleRate: 1.0,
294+
integrations: [
295+
Sentry.replayIntegration(),
296+
Sentry.browserTracingIntegration(),
297+
],
298+
enabled: !!process.env.NEXT_PUBLIC_SENTRY_DSN,
299+
});
300+
```
301+
302+
### 3.5 Update Sentry Server Config
303+
```ts
304+
import * as Sentry from '@sentry/nextjs';
305+
306+
Sentry.init({
307+
dsn: process.env.SENTRY_DSN || '...',
308+
tracesSampleRate: 1.0,
309+
enabled: !!process.env.SENTRY_DSN,
310+
});
311+
```
312+
313+
### 3.6 Update Error Handling
314+
- `pages/_error.tsx``app/error.tsx` (already done in Phase 2)
315+
- App Router error boundary uses `'use client'` directive
316+
- `app/global-error.tsx` for root-level errors
317+
- Sentry auto-captures via `withSentryConfig`
318+
319+
### 3.7 Update Code Using Sentry APIs
320+
- `_app.tsx` (now `layout.tsx`): Replace `Sentry.configureScope()` with `Sentry.setExtra()`
321+
- `_error.tsx` (now `error.tsx`): Update error capture pattern
322+
323+
### 3.8 Add sentry-cli to CI/CD
324+
Update `.github/workflows/ci.yml`:
325+
- Add `SENTRY_AUTH_TOKEN` secret
326+
- Add `SENTRY_ORG` and `SENTRY_PROJECT` env vars
327+
- sentry-cli runs automatically via `withSentryConfig` during `next build`
328+
329+
### 3.9 Verify Phase 3
330+
- `pnpm build` (verify source maps upload)
331+
- `pnpm test` (unit tests)
332+
- `pnpm test:e2e:headless` (e2e tests)
333+
334+
---
335+
336+
## Risk Mitigation
337+
338+
1. **Commit after each sub-step** so we can bisect failures
339+
2. **Update snapshots** after visual changes (`pnpm test:update-snaps`)
340+
3. **ESLint flat config** migration is complex — may need to keep v8 compatibility if v9 causes issues with plugins
341+
4. **Storybook v8** may need separate verification (not in test criteria)
342+
5. **`fingerprintjs2`** removal may break LogRocket fingerprinting — assess if still needed
343+
6. **React 19** removes `react-test-renderer` — must migrate snapshot tests to `@testing-library/react`
344+
345+
## Estimated File Changes
346+
- ~50 page/component files modified
347+
- ~10 config files modified
348+
- ~5 new files created (instrumentation.ts, error.tsx, global-error.tsx, etc.)
349+
- ~40 files moved (pages/ → app/)
350+
- ~77 test files may need snapshot updates

0 commit comments

Comments
 (0)