- Created stat-index.json to store metadata for various data files, including size, modification time, and hash. - Added graph.html and graph.json files to the graphify-out directory for enhanced graph representation.
235 lines
13 KiB
Markdown
235 lines
13 KiB
Markdown
# ارتقای Next.js از 15 به 16 (آخرین نسخه) + فعالسازی امکانات جدید
|
||
|
||
## پروژه
|
||
|
||
`nobat724_front`
|
||
|
||
## زمینه
|
||
|
||
پروژه روی `next@15.5.7` و `react@18.3.1` است (React با `overrides` در `package.json` روی 18 پین شده). آخرین نسخه پایدار Next.js نسخه **16.3.x** است (16.2.10 LTS هم موجود است — هدف این ارتقا `next@latest` یعنی 16.3.x). Next 16 چند breaking change دارد (React 19 اجباری، حذف `next lint`، deprecate شدن `middleware` به نفع `proxy`، Turbopack بهعنوان پیشفرض build/dev) و چند قابلیت جدید (Cache Components، React Compiler، Instant Navigations / Partial Prefetching در 16.3).
|
||
|
||
**تمام کار باید روی برنچ جدا انجام شود** — `main` دست نخورد.
|
||
|
||
## مشکل / هدف
|
||
|
||
1. ارتقای `next` به آخرین نسخه (16.3.x) و `react`/`react-dom` به 19
|
||
2. رفع همه breaking changeها تا `npm run build` و `npm run test` سبز شود
|
||
3. فعالسازی امکانات جدید نسخه 16 که با معماری multi-domain پروژه سازگارند
|
||
4. رفتار SEO فعلی (متادیتا داخل `<head>`، canonical، sitemap، robots) عیناً حفظ شود
|
||
|
||
## فایلهای مرتبط
|
||
|
||
| فایل | نقش |
|
||
|------|-----|
|
||
| `package.json` | نسخهها، `overrides` پین React 18، اسکریپت `next lint` |
|
||
| `next.config.js` | کانفیگ — شامل `htmlLimitedBots: /.*/` (فیکس SEO، **نباید حذف شود**)، `output: 'standalone'`، CSP headers |
|
||
| `middleware.js` | تزریق هدر `x-pathname` برای canonical — در 16 باید به `proxy.js` تبدیل شود |
|
||
| `app/layout.js` | `generateMetadata` مبتنی بر host (multi-domain) |
|
||
| `lib/getStateInfo.js`, `lib/getCanonicalUrl.js`, `lib/auth.js` | خواندن `headers()`/`cookies()` — ۱۰ فایل از `next/headers` استفاده میکنند |
|
||
| `app/doctor/[slug]/page.js`, `app/clinic/[slug]/page.js`, `app/blog/[slug]/page.js`, `app/sitemap.js` | fetch با `next: { revalidate: 3600, tags: [...] }` |
|
||
| `Dockerfile`, `nixpacks.toml`, `liara.json` | deploy — خروجی standalone |
|
||
| `vitest.config.mjs`, `test/` | تستها با vitest + @testing-library/react |
|
||
|
||
## وضعیت فعلی
|
||
|
||
```json
|
||
// package.json (بخشهای مرتبط)
|
||
"scripts": {
|
||
"dev": "cross-env HOST=yazd-nobat.localhost PORT=3000 NODE_TLS_REJECT_UNAUTHORIZED=0 next dev",
|
||
"build": "next build",
|
||
"lint": "next lint"
|
||
},
|
||
"dependencies": {
|
||
"next": "^15.5.7",
|
||
"@next/third-parties": "^15.5.7",
|
||
"react": "^18.3.1",
|
||
"react-dom": "^18.3.1",
|
||
"@mui/material": "^5.18.0",
|
||
"@mui/x-charts": "^7.29.1",
|
||
"@mui/x-date-pickers": "^7.29.4"
|
||
},
|
||
"overrides": {
|
||
"react": "^18.3.1",
|
||
"react-dom": "^18.3.1"
|
||
}
|
||
```
|
||
|
||
```js
|
||
// middleware.js — کل فایل
|
||
import { NextResponse } from 'next/server';
|
||
|
||
export function middleware(request) {
|
||
const response = NextResponse.next();
|
||
response.headers.set('x-pathname', request.nextUrl.pathname);
|
||
return response;
|
||
}
|
||
|
||
export const config = {
|
||
matcher: [
|
||
'/((?!api|_next/static|_next/image|favicon.ico|panel|dashboard|login|login-verify).*)',
|
||
],
|
||
};
|
||
```
|
||
|
||
نکات پیشبررسیشده (لازم نیست دوباره جستجو شوند):
|
||
|
||
- هیچ استفادهای از `legacyBehavior`، `useFormState`، `publicRuntimeConfig`/`serverRuntimeConfig`، `getInitialProps`، `next/legacy/image`، prop سفارشی `quality` روی `<Image>`، custom webpack config، یا `export const dynamic/revalidate` در پروژه **وجود ندارد**.
|
||
- `await params` از قبل در همه صفحات dynamic رعایت شده (الگوی Next 15).
|
||
- هیچ فایل کانفیگ ESLint وجود ندارد (`next lint` بدون config اجرا میشده) و `eslint` در devDependencies نیست.
|
||
- Node محلی v22 است (Next 16 حداقل 20.9 میخواهد — OK).
|
||
|
||
## وظایف
|
||
|
||
### ۱. ساخت برنچ
|
||
|
||
```bash
|
||
cd nobat724_front
|
||
git checkout -b upgrade/next-16
|
||
```
|
||
|
||
همه commitها روی این برنچ. به `main` هیچ چیزی push نشود.
|
||
|
||
### ۲. ارتقای پکیجها
|
||
|
||
```bash
|
||
npx @next/codemod@latest upgrade latest
|
||
```
|
||
|
||
اگر codemod به هر دلیل کامل اجرا نشد، دستی:
|
||
|
||
```bash
|
||
npm install next@latest react@latest react-dom@latest @next/third-parties@latest
|
||
```
|
||
|
||
- بخش `overrides` (پین React 18) از `package.json` **حذف شود** — دلیل پین، سازگاری قدیمی MUI بود؛ `@mui/material@5.18` و `@mui/x-*@7.29` هر دو peer-dep React 19 را پشتیبانی میکنند.
|
||
- اگر پکیجهای قدیمی (`react-google-map-picker`، `react-images-uploading`، `aos`) خطای peer-dep برای React 19 دادند: اول `npm install --legacy-peer-deps` را امتحان کن و در گزارش نهایی صریح ذکر کن؛ سپس در مرحله تست، صفحات استفادهکننده از این پکیجها را در مرورگر چک کن (map picker در پنل، آپلود عکس، انیمیشنهای AOS صفحه اصلی).
|
||
- `@testing-library/react@16` و `vitest@2` با React 19 سازگارند؛ اگر تستها خطای rendering دادند فقط نسخه `@testing-library/react` را به آخرین minor ارتقا بده.
|
||
|
||
### ۳. تبدیل `middleware.js` به `proxy.js`
|
||
|
||
در Next 16 نام `middleware` deprecated و جایگزینش `proxy` است. فایل `middleware.js` را به `proxy.js` تغییر نام بده و تابع را rename کن:
|
||
|
||
```js
|
||
// proxy.js
|
||
import { NextResponse } from 'next/server';
|
||
|
||
export function proxy(request) {
|
||
const response = NextResponse.next();
|
||
response.headers.set('x-pathname', request.nextUrl.pathname);
|
||
return response;
|
||
}
|
||
|
||
export const config = {
|
||
matcher: [
|
||
'/((?!api|_next/static|_next/image|favicon.ico|panel|dashboard|login|login-verify).*)',
|
||
],
|
||
};
|
||
```
|
||
|
||
**تست حیاتی بعد از این تغییر:** هدر `x-pathname` باید همچنان برسد — `curl -s http://yazd-nobat.localhost:3000/doctors | grep canonical` باید `https://nobat724.com/doctors` بدهد (اگر header نرسد، `getCanonicalUrl` مقدار `null` برمیگرداند و تگ canonical حذف میشود — این یعنی regression).
|
||
|
||
### ۴. جایگزینی `next lint`
|
||
|
||
`next lint` در Next 16 حذف شده. چون هیچ کانفیگ ESLint موجود نیست:
|
||
|
||
```bash
|
||
npm install -D eslint eslint-config-next
|
||
```
|
||
|
||
فایل `eslint.config.mjs` (flat config) بساز:
|
||
|
||
```js
|
||
import { FlatCompat } from '@eslint/eslintrc';
|
||
|
||
const compat = new FlatCompat({ baseDirectory: import.meta.dirname });
|
||
|
||
export default [
|
||
...compat.extends('next/core-web-vitals'),
|
||
];
|
||
```
|
||
|
||
و در `package.json`:
|
||
|
||
```json
|
||
"lint": "eslint app components lib services hooks context utils helper"
|
||
```
|
||
|
||
خطاهای lint موجود را فیکس نکن (خارج از scope) — فقط مطمئن شو دستور اجرا میشود؛ در صورت نیاز سطح خطاهای پرتکرار را در config به `warn` تنزل بده.
|
||
|
||
### ۵. Turbopack (پیشفرض جدید dev و build)
|
||
|
||
Next 16 هم `next dev` و هم `next build` را با Turbopack اجرا میکند. پروژه custom webpack config ندارد، پس انتظار سازگاری میرود. فقط verify کن:
|
||
|
||
- `npm run dev` بالا بیاید و صفحه اول، `/doctors`، `/doctor/[slug]` (یک uuid واقعی از خروجی `/doctors`) و `/panel` بدون خطای کنسول رندر شوند
|
||
- `npm run build` موفق شود و خروجی `standalone` تولید کند (برای Docker/liara لازم است — `ls .next/standalone`)
|
||
- استایلهای emotion RTL (`stylis-plugin-rtl` در `mui/index.js` یا `ThemeRegistry`) درست کار کنند — یک صفحه را چک کن که دکمهها/فرمها RTL باشند
|
||
|
||
اگر Turbopack با emotion/styled-components مشکل داشت، ابتدا `compiler.styledComponents: true` را در `next.config.js` امتحان کن؛ فقط بهعنوان آخرین راهحل build را با فلگ `--webpack` برگردان و دلیل را در گزارش بنویس.
|
||
|
||
### ۶. فعالسازی React Compiler
|
||
|
||
قابلیت پایدار نسخه 16 (و در 16.3 پشتیبانی Rust در Turbopack):
|
||
|
||
```bash
|
||
npm install -D babel-plugin-react-compiler
|
||
```
|
||
|
||
```js
|
||
// next.config.js
|
||
const nextConfig = {
|
||
reactCompiler: true,
|
||
// ...
|
||
};
|
||
```
|
||
|
||
بعد از فعالسازی، `npm run build` و تستهای vitest باید سبز بمانند. اگر کامپایلر روی کامپوننت خاصی خطا داد (کامپوننتهای کلاسی یا الگوهای غیراستاندارد)، همان کامپوننت را با `"use no memo"` مستثنا کن — کل قابلیت را خاموش نکن.
|
||
|
||
### ۷. Cache Components — ارزیابی محتاطانه (فعالسازی مشروط)
|
||
|
||
`cacheComponents: true` مدل کش جدید نسخه 16 است (`"use cache"`, `cacheLife`, `cacheTag`) و پیشنیاز Partial Prefetching در 16.3.
|
||
|
||
**هشدار معماری:** این پروژه multi-domain است — `generateMetadata` و layout و ده فایل دیگر به `headers()` (هدر `host`) وابستهاند؛ خروجی هر صفحه per-host متفاوت است. با `cacheComponents`، هر چیزی که `headers()` میخواند dynamic میماند و کش کردنش باعث نشت محتوای یک شهر به دامنه شهر دیگر میشود.
|
||
|
||
ترتیب کار:
|
||
|
||
1. ابتدا **بدون** فعالسازی، fetchهای موجود را دستنخورده بگذار — الگوی `next: { revalidate: 3600, tags: [...] }` در Next 16 همچنان پشتیبانی میشود
|
||
2. `cacheComponents: true` را روی یک commit جدا فعال کن و `npm run build` بگیر
|
||
3. اگر build خطای «uncached data access» برای مسیرهای متکی به `headers()` داد و رفعش نیاز به `Suspense`گذاری گسترده یا `"use cache"` روی توابع host-dependent داشت → **فعالسازی را revert کن** و در گزارش نهایی بنویس چرا (این قابلیت برای این معماری فعلاً امن نیست)
|
||
4. اگر build پاس شد → با دو host تست کن (`HOST=yazd-nobat.localhost` و یک شهر دیگر از `data/city.json` مثلاً با تغییر موقت HOST در اسکریپت dev) و مطمئن شو title/description/canonical هر دامنه مستقل است
|
||
|
||
### ۸. تمیزکاریهای کانفیگ نسخه 16
|
||
|
||
در `next.config.js`:
|
||
|
||
- `htmlLimitedBots: /.*/` **باید بماند** — فیکس عمدی برای رندر متادیتا داخل `<head>` است (streaming metadata خاموش). بعد از ارتقا با `curl -s -A "Mozilla/5.0" http://yazd-nobat.localhost:3000/ | python3 -c "import sys; h=sys.stdin.read(); e=h.find('</head>'); print('HEAD' if 0<=h.find('rel=\"canonical\"')<e else 'BODY')"` تأیید کن هنوز `HEAD` است
|
||
- `images.minimumCacheTTL` در 16 پیشفرض ۴ ساعت شده — نیازی به تغییر نیست، فقط بدان
|
||
- `env.DEV_MODE` و CSP headers و `output: 'standalone'` دست نخورند
|
||
|
||
### ۹. تست نهایی و commit
|
||
|
||
```bash
|
||
npm run test # همه تستهای vitest سبز
|
||
npm run build # build موفق با Turbopack
|
||
npm run lint # اجرای eslint بدون crash
|
||
```
|
||
|
||
بعد smoke test دستی با dev server:
|
||
|
||
- `/` — صفحه اصلی، جستجو، تصاویر next/image
|
||
- `/doctors` و `/doctor/[uuid]` — لیست و صفحه پزشک + JSON-LD
|
||
- `/blogs` و `/blog/[slug]`
|
||
- `/login` — فلوی OTP (فقط رندر فرم؛ ارسال واقعی لازم نیست)
|
||
- view-source صفحه اول: متادیتا (`keywords`, `canonical`, `og:*`, `twitter:*`) داخل `<head>`
|
||
|
||
Commitها را مرحلهای بزن (ارتقا پکیجها / proxy / eslint / react compiler / cache components هرکدام جدا) تا در صورت مشکل، revert تکی ممکن باشد.
|
||
|
||
## نکات مهم
|
||
|
||
- **برنچ:** همهچیز روی `upgrade/next-16`؛ merge به `main` با کاربر است، خودت merge نکن.
|
||
- **SEO regression خط قرمز است:** canonical به `https://nobat724.com/<path>`، متادیتا در `<head>`، `robots.js` و `sitemap.js` باید عیناً مثل قبل کار کنند.
|
||
- ده فایل از `next/headers` استفاده میکنند — همه `await headers()` هستند (الگوی 15)؛ در 16 هم همین درست است.
|
||
- `services/response.js` قرارداد API با backend (`clinicpro`) است — این ارتقا نباید هیچ تغییری در آن بدهد.
|
||
- اسکریپت dev به `NODE_TLS_REJECT_UNAUTHORIZED=0` و `HOST` سفارشی وابسته است — بدون تغییر بماند.
|
||
- اگر در حین کار مستندات لازم شد: راهنمای رسمی مهاجرت `https://nextjs.org/docs/app/guides/upgrading/version-16` و بلاگهای `nextjs.org/blog/next-16`, `next-16-1`, `next-16-2`, و 16.3.
|
||
- بعد از اتمام: `graphify update .` در روت workspace اجرا شود.
|