chore(deps): upgrade Next.js to 16.3.4 for Payload CMS compatibility

Payload 3.88.0's peer range for `next` is >=15.2.9 <15.3.0 ||
>=15.3.9 <15.4.0 || >=15.4.11 <15.5.0 || >=16.2.6 <17.0.0 — the
project's previous 15.5.23 fell in the unsupported gap between the
15.4.x and 16.2.x ranges. Upgrading to 16.3.4 (React 19.1.0 stays
compatible) unblocks MITHOME-86.

- next.config.ts: drop the removed `eslint.ignoreDuringBuilds` option
  (Next 16 no longer runs ESLint during `next build`)
- eslint.config.mjs: import eslint-config-next's native flat-config
  arrays directly instead of bridging through FlatCompat, which threw
  "Converting circular structure to JSON" under ESLint 9 with the
  upgraded config
- tsconfig.json: Next 16's own migration set `jsx: react-jsx` and added
  `.next/dev/types/**/*.ts` to `include`
- ThemeProvider.tsx: suppress two react-hooks/set-state-in-effect
  false positives (new rule shipped with eslint-config-next 16) —
  these effects intentionally sync state from localStorage/DOM on
  mount, there is no subscription to move the setState into

Verified: npm run build, npm run lint, tsc --noEmit, npm test (58
passed) all clean on this change alone, before installing Payload.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Do Siki
2026-09-10 03:04:54 +02:00
co-authored by Claude Sonnet 5
parent e9cb17ed67
commit 6cb99f603a
6 changed files with 508 additions and 407 deletions
+9 -11
View File
@@ -1,16 +1,14 @@
import { dirname } from "path"; // NOTE (Next.js 16 / eslint-config-next 16): the FlatCompat("next/core-web-vitals")
import { fileURLToPath } from "url"; // bridge to the legacy .eslintrc-style config caused a circular-JSON crash in
import { FlatCompat } from "@eslint/eslintrc"; // ESLint 9 (the "react" plugin object closes a cycle when re-validated through
// FlatCompat). eslint-config-next now ships native flat-config arrays, so
const __filename = fileURLToPath(import.meta.url); // import those directly instead of going through the compat layer.
const __dirname = dirname(__filename); import nextCoreWebVitals from "eslint-config-next/core-web-vitals";
import nextTypescript from "eslint-config-next/typescript";
const compat = new FlatCompat({
baseDirectory: __dirname,
});
const eslintConfig = [ const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"), ...nextCoreWebVitals,
...nextTypescript,
{ {
ignores: [ ignores: [
"node_modules/**", "node_modules/**",
+3 -4
View File
@@ -4,10 +4,9 @@ const nextConfig: NextConfig = {
// Enable standalone output for Docker deployment // Enable standalone output for Docker deployment
output: 'standalone', output: 'standalone',
// Skip linting during build for faster Docker builds // NOTE (Next.js 16): `eslint.ignoreDuringBuilds` was removed — `next build`
eslint: { // no longer runs ESLint itself (lint is now only `next lint` / `npm run lint`),
ignoreDuringBuilds: true, // so there is nothing left to ignore here.
},
// Skip TypeScript checking during build (for faster Docker builds) // Skip TypeScript checking during build (for faster Docker builds)
typescript: { typescript: {
+464 -383
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -32,7 +32,7 @@
"dependencies": { "dependencies": {
"mongodb": "^6.5", "mongodb": "^6.5",
"mongoose": "^8.2", "mongoose": "^8.2",
"next": "^15.5.23", "next": "^16.3.4",
"react": "19.1.0", "react": "19.1.0",
"react-dom": "19.1.0" "react-dom": "19.1.0"
}, },
@@ -49,7 +49,7 @@
"@types/react-dom": "^19", "@types/react-dom": "^19",
"@types/winston": "^2.4", "@types/winston": "^2.4",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "15.5.2", "eslint-config-next": "^16.3.4",
"jest": "^29.7", "jest": "^29.7",
"jest-environment-jsdom": "^29.7", "jest-environment-jsdom": "^29.7",
"tailwindcss": "^4", "tailwindcss": "^4",
+9
View File
@@ -38,6 +38,13 @@ export function ThemeProvider({ children, defaultTheme = 'system' }: ThemeProvid
const [mounted, setMounted] = useState(false) const [mounted, setMounted] = useState(false)
useEffect(() => { useEffect(() => {
// WHY: this effect synchronizes React state with two external systems —
// the DOM (hydration-safe mount flag) and localStorage (persisted theme
// preference) — read once on mount. There is no subscription to wrap the
// setState calls in, so the new react-hooks/set-state-in-effect rule
// (added with the Next.js 16 / eslint-config-next upgrade) is a false
// positive here; suppress rather than restructure a working component.
// eslint-disable-next-line react-hooks/set-state-in-effect
setMounted(true) setMounted(true)
const savedTheme = localStorage.getItem('theme') as Theme | null const savedTheme = localStorage.getItem('theme') as Theme | null
if (savedTheme) { if (savedTheme) {
@@ -59,6 +66,8 @@ export function ThemeProvider({ children, defaultTheme = 'system' }: ThemeProvid
root.setAttribute('data-theme', theme) root.setAttribute('data-theme', theme)
} }
// WHY: syncs React state with the resolved DOM/OS theme — see note above.
// eslint-disable-next-line react-hooks/set-state-in-effect
setResolvedTheme(resolved) setResolvedTheme(resolved)
}, [theme, mounted]) }, [theme, mounted])
+19 -5
View File
@@ -1,7 +1,11 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2017", "target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
@@ -11,7 +15,7 @@
"module": "esnext", "module": "esnext",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "react-jsx",
"incremental": true, "incremental": true,
"plugins": [ "plugins": [
{ {
@@ -19,9 +23,19 @@
} }
], ],
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": [
"./src/*"
]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": [
"exclude": ["node_modules"] "next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
]
} }