Yuki Takei 1 месяц назад
Родитель
Сommit
51b5b0ea2e

+ 320 - 0
.kiro/specs/nextjs-v16-upgrade/design.md

@@ -0,0 +1,320 @@
+# Design Document: Next.js v16 Upgrade
+
+## Overview
+
+**Purpose**: This feature upgrades the GROWI main application from Next.js 15.5.12 to Next.js 16, preserving all custom webpack configurations and module reduction achievements from the `reduce-modules-loaded` feature.
+
+**Users**: GROWI developers benefit from continued framework support, security patches, and a clear path to Turbopack adoption. End users experience no visible changes.
+
+**Impact**: Changes the build toolchain configuration while maintaining identical runtime behavior. The `--webpack` flag opts out of Turbopack as the default bundler, preserving all 5 custom loaders/plugins and 7 null-loader rules.
+
+### Goals
+- Upgrade Next.js from v15 to v16 with zero functional regression
+- Preserve all module reduction achievements (895 initial modules, 67% reduction)
+- Update build scripts for v16 Turbopack-default behavior
+- Prepare codebase for future Turbopack migration (Sass tilde fix)
+
+### Non-Goals
+- Turbopack migration (separate initiative — requires null-loader rewrites, custom loader testing)
+- React 19 upgrade (no benefit for Pages Router; separate initiative)
+- App Router migration
+- Performance optimization beyond maintaining current metrics
+
+## Architecture
+
+### Existing Architecture Analysis
+
+GROWI uses a custom Express server that programmatically initializes Next.js via `next({ dev })`. The build pipeline consists of:
+
+- **Client build**: `next build` (webpack, Pages Router)
+- **Server build**: `tspc` (TypeScript Project Compiler, separate from Next.js)
+- **Dev mode**: Express server starts Next.js dev server internally
+
+Custom webpack configuration (established in `reduce-modules-loaded`):
+1. **superjson-ssr-loader**: Auto-wraps `getServerSideProps` with SuperJSON serialization
+2. **null-loader rules** (7): Exclude server-only packages from client bundle
+3. **I18NextHMRPlugin**: i18n hot module replacement in dev mode
+4. **ChunkModuleStatsPlugin**: Module count analysis in dev mode
+5. **source-map-loader**: Source map extraction in dev builds
+6. **resolve.fallback**: `{ fs: false }` for client-side
+
+### Architecture Pattern & Boundary Map
+
+```mermaid
+graph TB
+    subgraph BuildPipeline
+        PKG[package.json scripts]
+        NC[next.config.ts]
+        WP[webpack config fn]
+    end
+
+    subgraph WebpackPlugins
+        SSL[superjson-ssr-loader]
+        NL[null-loader x7]
+        I18N[I18NextHMRPlugin]
+        CMS[ChunkModuleStatsPlugin]
+        SML[source-map-loader]
+    end
+
+    subgraph V16Changes
+        FLAG[--webpack flag]
+        DEVDIR[.next-dev directory]
+        SASS[Sass tilde fix]
+        BA[bundle-analyzer bump]
+    end
+
+    PKG -->|adds --webpack| FLAG
+    FLAG --> NC
+    NC --> WP
+    WP --> SSL
+    WP --> NL
+    WP --> I18N
+    WP --> CMS
+    WP --> SML
+    DEVDIR -->|update| MCS[measure-chunk-stats.sh]
+    SASS -->|single file| TOASTR[toastr.scss]
+    BA -->|version bump| PKG
+```
+
+**Architecture Integration**:
+- Selected pattern: In-place upgrade with webpack opt-out (see `research.md` Decision: Webpack-Only Upgrade Strategy)
+- Domain boundaries: Build configuration only — no runtime logic changes
+- Existing patterns preserved: Phase-based config, custom webpack function, all loaders/plugins
+- New components: None
+- Steering compliance: Maintains existing monorepo build patterns
+
+### Technology Stack
+
+| Layer | Choice / Version | Role in Feature | Notes |
+|-------|------------------|-----------------|-------|
+| Framework | Next.js ^16.0.0 | Core upgrade target | From ^15.0.0 |
+| Bundler | webpack (via `--webpack`) | Client build | Turbopack opt-out |
+| Runtime | React ^18.2.0 | No change | Pages Router supports React 18 in v16 |
+| Analysis | @next/bundle-analyzer ^16.0.0 | Bundle analysis | Version bump from ^15.0.0 |
+| Node.js | ^24 | No change | Exceeds v16 minimum (20.9+) |
+| TypeScript | ~5.0.0 | No change | Exceeds v16 minimum (5.1+) |
+
+## System Flows
+
+### Upgrade Execution Flow
+
+```mermaid
+flowchart TD
+    A[Bump next to v16] --> B[Bump bundle-analyzer to v16]
+    B --> C[Add --webpack to build scripts]
+    C --> D[Add --webpack to dev and measurement scripts]
+    D --> E[Fix Sass tilde import]
+    E --> F[Update measure-chunk-stats.sh for .next-dev]
+    F --> G[Verify ts-node hook preservation]
+    G --> H{Run quality checks}
+    H -->|typecheck pass| I{Run tests}
+    H -->|typecheck fail| J[Fix type errors]
+    J --> H
+    I -->|tests pass| K{Production build}
+    I -->|tests fail| L[Fix test failures]
+    L --> I
+    K -->|build pass| M[Measure module counts]
+    K -->|build fail| N[Fix build errors]
+    N --> K
+    M --> O[Compare with baseline]
+    O --> P[Document results]
+```
+
+## Requirements Traceability
+
+| Requirement | Summary | Components | Interfaces | Flows |
+|-------------|---------|------------|------------|-------|
+| 1.1 | Upgrade next dependency | PackageUpgrade | — | Upgrade step A |
+| 1.2 | --webpack flag for build | BuildScriptUpdate | — | Upgrade step C |
+| 1.3 | --webpack flag for dev | BuildScriptUpdate | — | Upgrade step D |
+| 1.4 | Type compatibility | PackageUpgrade | — | Upgrade step H |
+| 1.5 | Quality checks pass | RegressionValidation | — | Upgrade steps H-K |
+| 2.1 | null-loader preservation | WebpackConfigPreservation | — | — |
+| 2.2 | superjson-ssr-loader preservation | WebpackConfigPreservation | — | — |
+| 2.3 | I18NextHMRPlugin preservation | WebpackConfigPreservation | — | — |
+| 2.4 | ChunkModuleStatsPlugin preservation | WebpackConfigPreservation | — | — |
+| 2.5 | source-map-loader preservation | WebpackConfigPreservation | — | — |
+| 2.6 | Module count baseline | MetricsValidation | ChunkModuleStats output | Upgrade step M |
+| 3.1 | build:client script update | BuildScriptUpdate | — | Upgrade step C |
+| 3.2 | dev script update | BuildScriptUpdate | — | Upgrade step D |
+| 3.3 | measure-chunk-stats.sh update | MeasurementToolUpdate | — | Upgrade step F |
+| 3.4 | Root commands functional | RegressionValidation | — | Upgrade step K |
+| 4.1 | Sass without tilde prefix | SassTildeFixup | — | Upgrade step E |
+| 4.2 | toastr.scss fix | SassTildeFixup | — | Upgrade step E |
+| 4.3 | No other tilde imports | SassTildeFixup | — | Upgrade step E |
+| 4.4 | Identical rendering | RegressionValidation | — | Upgrade step K |
+| 5.1 | bundlePagesRouterDependencies works | NextConfigValidation | — | Upgrade step K |
+| 5.2 | optimizePackageImports works | NextConfigValidation | — | Upgrade step K |
+| 5.3 | transpilePackages works | NextConfigValidation | — | Upgrade step K |
+| 5.4 | New v16 options evaluation | NextConfigValidation | — | — |
+| 5.5 | Deprecated options updated | NextConfigValidation | — | — |
+| 6.1 | ts-node hook preservation | TsNodeHookPreservation | — | Upgrade step G |
+| 6.2 | v16 transpiler behavior check | TsNodeHookPreservation | — | Upgrade step G |
+| 6.3 | Dev mode TypeScript loading | TsNodeHookPreservation | — | Upgrade step G |
+| 7.1 | Unit tests pass | RegressionValidation | — | Upgrade step I |
+| 7.2 | TypeScript typecheck passes | RegressionValidation | — | Upgrade step H |
+| 7.3 | Biome lint passes | RegressionValidation | — | Upgrade step H |
+| 7.4 | Production build succeeds | RegressionValidation | — | Upgrade step K |
+| 7.5 | SuperJSON round-trip works | RegressionValidation | — | Upgrade step I |
+| 7.6 | Catch-all page compiles | RegressionValidation | — | Upgrade step M |
+| 7.7 | Before/after metrics documented | MetricsValidation | — | Upgrade steps M-P |
+
+## Components and Interfaces
+
+| Component | Domain/Layer | Intent | Req Coverage | Key Dependencies | Contracts |
+|-----------|-------------|--------|--------------|-----------------|-----------|
+| PackageUpgrade | Build | Bump next and bundle-analyzer versions | 1.1, 1.4 | package.json (P0) | — |
+| BuildScriptUpdate | Build | Add --webpack flag to scripts | 1.2, 1.3, 3.1, 3.2, 3.4 | package.json (P0) | — |
+| MeasurementToolUpdate | Tooling | Update shell script for v16 | 3.3 | measure-chunk-stats.sh (P0) | — |
+| SassTildeFixup | Styles | Remove node_modules tilde prefix | 4.1, 4.2, 4.3, 4.4 | toastr.scss (P0) | — |
+| WebpackConfigPreservation | Build | Verify webpack config unchanged | 2.1–2.5 | next.config.ts (P0) | — |
+| NextConfigValidation | Build | Verify v16 config compatibility | 5.1–5.5 | next.config.ts (P0) | — |
+| TsNodeHookPreservation | Server | Verify ts-node hook survives v16 | 6.1–6.3 | crowi/index.ts (P0) | — |
+| MetricsValidation | Tooling | Measure and compare module counts | 2.6, 7.7 | ChunkModuleStatsPlugin (P0) | — |
+| RegressionValidation | QA | Full quality check suite | 1.5, 7.1–7.6 | turbo CLI (P0) | — |
+
+### Build Layer
+
+#### PackageUpgrade
+
+| Field | Detail |
+|-------|--------|
+| Intent | Bump `next` and `@next/bundle-analyzer` to v16 in `apps/app/package.json` |
+| Requirements | 1.1, 1.4 |
+
+**Responsibilities & Constraints**
+- Update `next` from `^15.0.0` to `^16.0.0`
+- Update `@next/bundle-analyzer` from `^15.0.0` to `^16.0.0`
+- Verify `@types/react` and `@types/react-dom` remain compatible (React 18 types work with v16)
+- Run `pnpm install` to resolve dependency tree
+
+**Dependencies**
+- External: next@^16.0.0 — core framework (P0)
+- External: @next/bundle-analyzer@^16.0.0 — analysis tool (P1)
+
+**Implementation Notes**
+- React stays at ^18.2.0 — v16 peer dependency range includes `^18.2.0`
+- `babel-plugin-superjson-next` stays as-is — not used by custom loader approach
+- The `next-i18next` package compatibility should be verified against Next.js 16
+
+#### BuildScriptUpdate
+
+| Field | Detail |
+|-------|--------|
+| Intent | Add `--webpack` flag to client build and dev scripts |
+| Requirements | 1.2, 1.3, 3.1, 3.2, 3.4 |
+
+**Responsibilities & Constraints**
+- Update `build:client` script: `next build` → `next build --webpack`
+- The `dev` script runs the Express server (not `next dev` directly), so `--webpack` cannot be added to the npm script
+- The Express server calls `next({ dev })` programmatically — webpack usage is determined by the absence of Turbopack config, not a CLI flag
+- Verify root-level commands: `pnpm run app:build`, `turbo run build`
+
+**Implementation Notes**
+- Integration: The `next({ dev })` API may need investigation to determine if v16 defaults to Turbopack when called programmatically. If so, a config option may be needed.
+- Risk: Programmatic `next()` call may behave differently from CLI `next dev --webpack`. Must test thoroughly.
+
+#### MeasurementToolUpdate
+
+| Field | Detail |
+|-------|--------|
+| Intent | Update `bin/measure-chunk-stats.sh` for v16 directory changes and webpack flag |
+| Requirements | 3.3 |
+
+**Responsibilities & Constraints**
+- Update line 27: `rm -rf .next` → `rm -rf .next/dev` (or both `.next` and `.next/dev`)
+- Update line 31: `npx next dev` → `npx next dev --webpack`
+- Verify log output path still works with `.next/dev` directory
+
+**Dependencies**
+- Inbound: ChunkModuleStatsPlugin — provides log output (P0)
+
+### Styles Layer
+
+#### SassTildeFixup
+
+| Field | Detail |
+|-------|--------|
+| Intent | Remove Sass tilde prefix from node_modules imports |
+| Requirements | 4.1, 4.2, 4.3, 4.4 |
+
+**Responsibilities & Constraints**
+- Change `@import '~react-toastify/scss/main'` → `@import 'react-toastify/scss/main'` in `src/styles/molecules/toastr.scss`
+- The `~/...` pattern (path alias) used throughout other SCSS files is NOT the node_modules tilde prefix and requires no changes
+- Verify style compilation produces identical output
+
+**Implementation Notes**
+- Scope is minimal: only 1 file affected
+- webpack Sass resolution already supports non-tilde imports for node_modules
+
+### Server Layer
+
+#### TsNodeHookPreservation
+
+| Field | Detail |
+|-------|--------|
+| Intent | Verify the ts-node `.ts` extension hook survives Next.js v16 initialization |
+| Requirements | 6.1, 6.2, 6.3 |
+
+**Responsibilities & Constraints**
+- Existing defensive pattern in `src/server/crowi/index.ts` (lines 557-566) saves and restores `require.extensions['.ts']`
+- v16 may change its config transpiler behavior — the save/restore pattern handles this generically
+- Must verify by starting dev server and confirming TypeScript files load correctly
+
+**Dependencies**
+- External: ts-node — TypeScript execution (P0)
+- Inbound: Next.js config transpiler — may deregister hooks (P0)
+
+**Implementation Notes**
+- The comment already references "Next.js 15's next.config.ts transpiler" — update comment to note v16 if behavior changes
+- If v16 no longer deregisters the hook, the defensive code becomes a no-op (safe to keep)
+
+## Error Handling
+
+### Error Strategy
+
+| Error Type | Scenario | Response |
+|------------|----------|----------|
+| Build failure | `next build` fails without `--webpack` | Hard error from Next.js — add `--webpack` flag |
+| Type errors | New v16 types break existing code | Fix type annotations, check `@types/react` compatibility |
+| Module count regression | Initial modules exceed 940 (895 + 5%) | Investigate webpack config changes, check null-loader rules |
+| ts-node hook loss | Next.js v16 removes `.ts` extension hook | Existing save/restore pattern handles this |
+| Dev server failure | `.next/dev` directory causes issues | Fall back to `isolatedDevBuild: false` |
+
+## Testing Strategy
+
+### Unit Tests
+- Run existing superjson-ssr.spec.ts (10 tests) — verifies serialization round-trip
+- Run existing mongo-id.spec.ts (8 tests) — verifies client utility
+- Run existing locale-utils.spec.ts (18 tests) — verifies date-fns imports
+- Run existing HotkeysManager.spec.tsx (3 tests) — verifies tinykeys integration
+- Run existing PageContentRenderer.spec.tsx (3 tests) — verifies dynamic import
+
+### Integration Tests
+- Full test suite: `turbo run test --filter @growi/app` (1,375+ tests, 127+ files)
+- TypeScript check: `turbo run lint:typecheck --filter @growi/app`
+- Biome lint: `turbo run lint:biome --filter @growi/app`
+
+### Build Tests
+- Production build: `turbo run build --filter @growi/app`
+- Bundle analysis: `ANALYZE=1 next build --webpack` (verify analyzer works)
+
+### Manual Verification
+- Start dev server, compile `[[...path]]`, record ChunkModuleStats output
+- Compare initial/async-only/total module counts with baseline (895/4,775/5,670)
+- Verify page renders correctly in browser
+
+## Performance & Scalability
+
+### Target Metrics
+
+| Metric | Baseline (v15) | Target (v16) | Tolerance |
+|--------|---------------|--------------|-----------|
+| Initial modules | 895 | 895 | ±5% (850–940) |
+| Async-only modules | 4,775 | 4,775 | ±10% |
+| Total modules | 5,670 | 5,670 | ±10% |
+| Compilation time | ~30s | ~30s | ±20% |
+
+### Measurement Approach
+- Use `bin/measure-chunk-stats.sh` (after v16 updates) for standardized measurement
+- Record before/after in `analysis-ledger.md` or commit message

+ 99 - 0
.kiro/specs/nextjs-v16-upgrade/requirements.md

@@ -0,0 +1,99 @@
+# Requirements Document
+
+## Introduction
+
+Upgrade the GROWI main application (`apps/app`) from Next.js 15.5.12 to Next.js 16. Next.js 16 makes Turbopack the default bundler and introduces several breaking changes. GROWI has extensive custom webpack configuration (null-loader rules, superjson-ssr-loader, ChunkModuleStatsPlugin, I18NextHMRPlugin) established during the `reduce-modules-loaded` feature. The upgrade must preserve all existing optimizations while adapting to v16 changes.
+
+**Current State**:
+- Next.js: ^15.0.0 (installed 15.5.12)
+- React: ^18.2.0
+- Node.js: ^24
+- TypeScript: ~5.0.0
+- Custom webpack config: 5 loaders/plugins, 7 null-loader rules, superjson-ssr-loader, ChunkModuleStatsPlugin
+
+**Key v16 Changes Impacting GROWI**:
+- Turbopack is the default bundler — `next build` fails if custom `webpack()` config exists unless `--webpack` flag is used
+- `middleware` filename deprecated → renamed to `proxy`
+- `next lint` command removed (GROWI uses Biome — no impact)
+- Dev output directory changed to `.next/dev`
+- Sass tilde (`~`) imports not supported by Turbopack
+- AMP support removed (GROWI doesn't use — no impact)
+- `serverRuntimeConfig`/`publicRuntimeConfig` removed (GROWI doesn't use — no impact)
+
+## Requirements
+
+### Requirement 1: Next.js Version Upgrade
+
+**Objective:** As a developer, I want to upgrade Next.js from v15 to v16, so that the application benefits from improved performance, Turbopack stability, and continued framework support.
+
+#### Acceptance Criteria
+1. The build system shall upgrade the `next` dependency from `^15.0.0` to `^16.0.0` in `apps/app/package.json`.
+2. When `next build` is executed, the build system shall use the `--webpack` flag to opt out of Turbopack as the default bundler, preserving all existing custom webpack configuration.
+3. When `next dev` is executed, the build system shall continue to use webpack for development, maintaining compatibility with all custom loaders and plugins.
+4. The build system shall verify that `@types/react` and `@types/react-dom` are compatible with the upgraded Next.js version.
+5. When the upgrade is complete, the build system shall pass all existing quality checks: `lint:typecheck`, `lint:biome`, `test`, and `build`.
+
+### Requirement 2: Webpack Configuration Preservation
+
+**Objective:** As a developer, I want all custom webpack configurations from the `reduce-modules-loaded` feature to remain functional after the upgrade, so that the module reduction achievements (67% initial chunk reduction) are not lost.
+
+#### Acceptance Criteria
+1. The build system shall preserve all 7 null-loader rules (dtrace-provider, mongoose, mathjax-full, i18next-fs-backend, bunyan, bunyan-format, core-js) after the upgrade.
+2. The build system shall preserve the custom superjson-ssr-loader for automatic `getServerSideProps` wrapping on `.page.(tsx|ts)` files.
+3. The build system shall preserve the I18NextHMRPlugin for i18next hot module replacement in development mode.
+4. The build system shall preserve the ChunkModuleStatsPlugin for development-time module analysis.
+5. The build system shall preserve the source-map-loader configuration for development builds.
+6. When `next dev` is started after the upgrade, the ChunkModuleStatsPlugin shall report initial module counts within ±5% of pre-upgrade baseline (approximately 895 initial modules).
+
+### Requirement 3: Build Script Updates
+
+**Objective:** As a developer, I want the build scripts to be updated for v16 compatibility, so that the build pipeline works correctly with the new default Turbopack behavior.
+
+#### Acceptance Criteria
+1. The build system shall update the `build:client` script in `apps/app/package.json` to include the `--webpack` flag (e.g., `next build --webpack`).
+2. If `next dev` defaults to Turbopack in v16, the build system shall add the `--webpack` flag to the dev script to maintain webpack compatibility.
+3. The build system shall update `bin/measure-chunk-stats.sh` to account for the new `.next/dev` output directory if the dev output path changes.
+4. The root-level build commands (`pnpm run app:build`, `turbo run build`) shall continue to function correctly after the upgrade.
+
+### Requirement 4: Sass Tilde Import Migration
+
+**Objective:** As a developer, I want Sass tilde (`~`) imports to be removed, so that the codebase is compatible with Turbopack's import resolution (enabling a future Turbopack migration path).
+
+#### Acceptance Criteria
+1. When Sass files are processed, the build system shall resolve node_modules imports without the tilde (`~`) prefix.
+2. The build system shall replace `@import '~react-toastify/scss/main'` with `@import 'react-toastify/scss/main'` in `src/styles/molecules/toastr.scss`.
+3. The build system shall verify no other Sass files contain tilde-prefixed imports.
+4. When the styles are compiled, the application shall render identically to the pre-upgrade state.
+
+### Requirement 5: Next.js Configuration Compatibility
+
+**Objective:** As a developer, I want the `next.config.ts` to be compatible with v16 configuration changes, so that all settings are recognized and properly applied.
+
+#### Acceptance Criteria
+1. The build system shall verify that `bundlePagesRouterDependencies` and `serverExternalPackages` continue to function correctly in v16.
+2. The build system shall verify that `optimizePackageImports` settings remain effective in v16.
+3. The build system shall verify that `transpilePackages` configuration (ESM packages: remark-*, rehype-*, unified, etc.) works correctly in v16.
+4. If v16 introduces new configuration options relevant to Pages Router optimization, the build system shall evaluate and document them.
+5. If any existing `next.config.ts` options are deprecated or renamed in v16, the build system shall update them to v16 equivalents.
+
+### Requirement 6: ts-node Hook Preservation
+
+**Objective:** As a developer, I want the ts-node require hook workaround to remain functional, so that the Express server's TypeScript module loading is not disrupted by Next.js initialization.
+
+#### Acceptance Criteria
+1. While the GROWI server initializes Next.js, the server startup process shall preserve the ts-node `.ts` extension hook as implemented in `src/server/crowi/index.ts`.
+2. If Next.js v16 changes its config transpiler behavior for `.ts` hooks, the build system shall update the preservation logic accordingly.
+3. When the server starts in development mode, TypeScript files shall be loaded via ts-node without errors after Next.js initialization.
+
+### Requirement 7: Regression Testing and Validation
+
+**Objective:** As a developer, I want comprehensive regression testing after the upgrade, so that no existing functionality is broken.
+
+#### Acceptance Criteria
+1. When the upgrade is complete, all existing unit tests shall pass (currently 1,375+ tests across 127+ test files).
+2. When the upgrade is complete, TypeScript type checking shall produce zero errors.
+3. When the upgrade is complete, Biome linting shall produce zero errors.
+4. When the upgrade is complete, the production build shall succeed.
+5. The build system shall verify that the superjson serialization round-trip works correctly for all page types (Date, Map, Set objects).
+6. When `next dev` is started, the `[[...path]]` catch-all page shall compile and serve correctly.
+7. The build system shall document before/after metrics: module counts (initial, async-only, total) and compilation time.

+ 146 - 0
.kiro/specs/nextjs-v16-upgrade/research.md

@@ -0,0 +1,146 @@
+# Research & Design Decisions
+
+---
+**Purpose**: Capture discovery findings, architectural investigations, and rationale for the Next.js v16 upgrade.
+---
+
+## Summary
+- **Feature**: `nextjs-v16-upgrade`
+- **Discovery Scope**: Complex Integration (framework major version upgrade)
+- **Key Findings**:
+  - `--webpack` flag works for both `next dev` and `next build`, preserving all custom webpack config unchanged
+  - React 18 remains fully supported for Pages Router in Next.js 16 — no React upgrade required
+  - GROWI has no middleware file, no runtime config, no AMP, no legacy image imports — most v16 removals don't apply
+
+## Research Log
+
+### Next.js 16 Webpack Flag Behavior
+- **Context**: GROWI has extensive custom webpack config (5 loaders/plugins, 7 null-loader rules). Need to understand v16 webpack compatibility.
+- **Sources Consulted**: nextjs.org/docs/app/api-reference/cli/next, nextjs.org/docs/app/api-reference/config/next-config-js/webpack, nextjs.org/blog/next-16
+- **Findings**:
+  - `next dev --webpack` and `next build --webpack` both use webpack exactly as before
+  - The `webpack()` function in next.config.ts is invoked identically when `--webpack` flag is used
+  - If `next build` runs without `--webpack` and a `webpack()` function exists, the build **fails** (hard error, not warning)
+  - The webpack API (function signature, arguments, hooks) is unchanged between v15 and v16
+- **Implications**: GROWI must add `--webpack` flag to build and dev scripts. All existing webpack config works without modification.
+
+### Turbopack resolveAlias (Future Reference)
+- **Context**: Documenting Turbopack equivalents for potential future migration.
+- **Sources Consulted**: nextjs.org/docs/app/api-reference/config/next-config-js/turbopack
+- **Findings**:
+  - null-loader → `turbopack.resolveAlias` with `{ browser: './empty.ts' }` per package
+  - `resolve.fallback: { fs: false }` → `turbopack.resolveAlias: { fs: { browser: './empty.ts' } }`
+  - Custom loaders partially supported via `turbopack.rules` (babel-loader, @svgr/webpack, etc.)
+  - `null-loader` is NOT in the supported loaders list
+  - `superjson-ssr-loader.ts` would need testing with Turbopack
+- **Implications**: Full Turbopack migration is a separate, substantial effort. Not in scope for this upgrade.
+
+### `.next/dev` Isolated Build Directory
+- **Context**: v16 introduces `isolatedDevBuild` (enabled by default), changing dev output to `.next/dev`.
+- **Sources Consulted**: nextjs.org/docs/app/api-reference/config/next-config-js/isolatedDevBuild
+- **Findings**:
+  - `next dev` outputs to `.next/dev` (regardless of `--webpack` or Turbopack)
+  - `next build` outputs to `.next` (unchanged)
+  - Can disable with `experimental: { isolatedDevBuild: false }`
+  - A lockfile mechanism prevents multiple `next dev` instances on same project
+- **Implications**: `bin/measure-chunk-stats.sh` cleans `.next` (line 27) and runs `npx next dev` (line 31). Both need updating: clean `.next/dev` and add `--webpack` flag.
+
+### React 18 Compatibility with Next.js 16
+- **Context**: GROWI uses React 18.2.0 with Pages Router.
+- **Sources Consulted**: nextjs.org/blog/next-16, github.com/vercel/next.js/pull/69484, next.js package.json
+- **Findings**:
+  - Next.js 16 peer dependencies: `"react": "^18.2.0 || ^19.0.0"`
+  - App Router requires React 19 for new features (View Transitions, useEffectEvent, etc.)
+  - Pages Router continues full React 18 support
+- **Implications**: No React version change required. GROWI can stay on React 18.2.0.
+
+### Middleware to Proxy Rename
+- **Context**: v16 deprecates `middleware.ts` → `proxy.ts`.
+- **Sources Consulted**: nextjs.org/docs/messages/middleware-to-proxy
+- **Findings**:
+  - GROWI has **no** `middleware.ts` or `middleware.js` file
+  - The rename is a deprecation (old filename still works with warning)
+- **Implications**: No action required.
+
+### Phase Config and process.argv
+- **Context**: v16 changes when next.config is loaded during `next dev`, affecting `process.argv` checks.
+- **Sources Consulted**: nextjs.org/docs/app/guides/upgrading/version-16
+- **Findings**:
+  - `process.argv.includes('dev')` returns `false` in v16 during `next dev`
+  - GROWI uses `phase` parameter (PHASE_PRODUCTION_BUILD, PHASE_PRODUCTION_SERVER) — NOT process.argv
+- **Implications**: No impact on GROWI's phase-based config.
+
+### Sass Tilde Imports
+- **Context**: Turbopack doesn't support tilde (`~`) prefix for node_modules imports in Sass.
+- **Findings**:
+  - Only 1 node_modules tilde import: `@import '~react-toastify/scss/main'` in `toastr.scss`
+  - All other `~/...` patterns are Next.js path aliases (resolve to source root) — NOT affected
+- **Implications**: Single file change required. Other SCSS files are unaffected.
+
+### @next/bundle-analyzer Version
+- **Context**: Currently `^15.0.0`, needs verification for v16 compatibility.
+- **Findings**:
+  - `@next/bundle-analyzer` follows Next.js versioning
+  - Must be upgraded to `^16.0.0` alongside Next.js
+- **Implications**: Version bump required in package.json.
+
+### Deprecated/Removed Features Audit
+- **Context**: v16 removes several features. Need to verify GROWI doesn't use any.
+- **Findings**:
+  - `serverRuntimeConfig`/`publicRuntimeConfig`: NOT used
+  - `next/config` getConfig: NOT used
+  - `next/legacy/image`: NOT used
+  - AMP (`useAmp`, `next/amp`): NOT used
+  - `next lint`: NOT used (GROWI uses Biome)
+  - `images.domains`: NOT used
+  - `devIndicators` removed options: NOT used
+- **Implications**: All v16 removals are non-impacting for GROWI.
+
+## Architecture Pattern Evaluation
+
+| Option | Description | Strengths | Risks / Limitations | Notes |
+|--------|-------------|-----------|---------------------|-------|
+| A: Webpack-only | Keep `--webpack` everywhere | Zero migration risk, all config preserved | No Turbopack benefits | **Selected** |
+| B: Hybrid | Turbopack for dev, webpack for build | Faster dev restarts | Config duplication, potential behavior drift | Not recommended now |
+| C: Full Turbopack | Migrate all webpack config | Future-proof, best perf | High effort, null-loader unsupported, custom loaders risk | Separate initiative |
+
+## Design Decisions
+
+### Decision: Webpack-Only Upgrade Strategy
+- **Context**: GROWI has 5 webpack loaders/plugins and 7 null-loader rules. Turbopack doesn't support null-loader.
+- **Alternatives Considered**:
+  1. Option A: Upgrade with `--webpack` flag (preserve all config)
+  2. Option B: Partial Turbopack migration (dev only)
+  3. Option C: Full Turbopack migration
+- **Selected Approach**: Option A — use `--webpack` flag for both dev and build
+- **Rationale**: Zero risk to existing module reduction achievements. Turbopack migration requires resolveAlias rewrites, custom loader testing, and I18NextHMRPlugin replacement — substantial effort with unclear benefit for Pages Router.
+- **Trade-offs**: No Turbopack performance benefits vs. guaranteed stability
+- **Follow-up**: Evaluate Turbopack migration as separate initiative after v16 stabilization
+
+### Decision: Keep React 18
+- **Context**: Next.js 16 supports React 18.2.0+ for Pages Router via peer dependency range.
+- **Selected Approach**: Stay on React ^18.2.0
+- **Rationale**: React 19 features (View Transitions, useEffectEvent) are App Router only. Pages Router gets no benefit.
+- **Follow-up**: React 19 upgrade can be paired with future App Router migration
+
+### Decision: Disable isolatedDevBuild
+- **Context**: GROWI runs a custom Express server that initializes Next.js programmatically via `next({ dev })`. The `.next/dev` directory split may complicate the custom server setup.
+- **Alternatives Considered**:
+  1. Accept `.next/dev` and update all tooling
+  2. Disable `isolatedDevBuild` to maintain `.next` as output
+- **Selected Approach**: Accept `.next/dev` (Option 1) — align with v16 defaults
+- **Rationale**: Only `measure-chunk-stats.sh` needs updating. The custom server calls `next({ dev })` which handles directory resolution internally.
+- **Follow-up**: Verify custom server works correctly with `.next/dev`
+
+## Risks & Mitigations
+- **Risk 1**: `@next/bundle-analyzer@^15` incompatible with Next.js 16 — Mitigation: bump to `^16.0.0`
+- **Risk 2**: Custom superjson-ssr-loader may have subtle behavior changes with webpack version bundled in Next.js 16 — Mitigation: existing superjson-ssr.spec.ts tests cover round-trip serialization
+- **Risk 3**: `ts-node` hook preservation may behave differently if v16 changes config transpiler — Mitigation: existing workaround logic is defensive (check-and-restore pattern)
+- **Risk 4**: `next-i18next` or `i18next-hmr` may have compatibility issues with Next.js 16 — Mitigation: test in dev mode, fallback to disabling HMR plugin
+
+## References
+- [Next.js 16 Upgrade Guide](https://nextjs.org/docs/app/guides/upgrading/version-16) — primary migration reference
+- [Next.js 16 Blog Post](https://nextjs.org/blog/next-16) — feature overview
+- [Next.js CLI Reference](https://nextjs.org/docs/app/api-reference/cli/next) — `--webpack` flag docs
+- [Turbopack Config](https://nextjs.org/docs/app/api-reference/config/next-config-js/turbopack) — future migration reference
+- [isolatedDevBuild](https://nextjs.org/docs/app/api-reference/config/next-config-js/isolatedDevBuild) — `.next/dev` behavior

+ 22 - 0
.kiro/specs/nextjs-v16-upgrade/spec.json

@@ -0,0 +1,22 @@
+{
+  "feature_name": "nextjs-v16-upgrade",
+  "created_at": "2026-03-02T14:20:00Z",
+  "updated_at": "2026-03-02T14:50:00Z",
+  "language": "en",
+  "phase": "tasks-generated",
+  "approvals": {
+    "requirements": {
+      "generated": true,
+      "approved": true
+    },
+    "design": {
+      "generated": true,
+      "approved": true
+    },
+    "tasks": {
+      "generated": true,
+      "approved": false
+    }
+  },
+  "ready_for_implementation": false
+}

+ 83 - 0
.kiro/specs/nextjs-v16-upgrade/tasks.md

@@ -0,0 +1,83 @@
+# Implementation Plan
+
+## Tasks
+
+- [ ] 1. Record pre-upgrade baseline metrics
+- [ ] 1.1 Capture current module counts and compilation time using the measurement shell script
+  - Run the existing measurement tool against the current Next.js 15 build
+  - Record initial, async-only, and total module counts as the pre-upgrade baseline
+  - Save the `Compiled /[[...path]]` compilation time
+  - Document baseline values in the analysis ledger or commit message for later comparison
+  - _Requirements: 2.6, 7.7_
+
+- [ ] 2. Upgrade Next.js and related dependencies to v16
+- [ ] 2.1 Bump the `next` package from v15 to v16 in the main application
+  - Update the `next` dependency version specifier in `apps/app/package.json`
+  - Update the `@next/bundle-analyzer` dependency to match the new Next.js major version
+  - Verify that `@types/react` and `@types/react-dom` remain compatible with React 18 under v16 peer dependencies
+  - Run `pnpm install` to resolve the dependency tree and update the lockfile
+  - _Requirements: 1.1, 1.4_
+
+- [ ] 2.2 Add the `--webpack` flag to the client build script
+  - Update the `build:client` script to pass `--webpack` so the production build continues using webpack instead of the v16 Turbopack default
+  - Verify the build completes successfully with the flag by running the build command
+  - _Requirements: 1.2, 3.1_
+
+- [ ] 2.3 Ensure the development server continues using webpack
+  - Investigate whether the programmatic `next({ dev })` API defaults to Turbopack in v16 or respects the presence of a `webpack()` function in the config
+  - If Turbopack is used by default even programmatically, add a configuration option or environment variable to force webpack mode
+  - Verify the dev server starts correctly and all custom webpack loaders and plugins are active
+  - _Requirements: 1.3, 3.2_
+
+- [ ] 3. (P) Fix Sass tilde import for node_modules
+  - Remove the tilde (`~`) prefix from the react-toastify import in `src/styles/molecules/toastr.scss`
+  - Confirm that no other Sass files use the tilde prefix for node_modules resolution (the `~/` path alias pattern is unaffected)
+  - Verify the styles compile correctly after the change
+  - _Requirements: 4.1, 4.2, 4.3, 4.4_
+
+- [ ] 4. Update the measurement shell script for v16 changes
+  - Update the cache cleanup command to clear the new `.next/dev` directory used by v16's isolated dev build feature
+  - Add the `--webpack` flag to the `next dev` command within the script
+  - Verify the script still captures ChunkModuleStats output correctly after the directory change
+  - _Requirements: 3.3_
+
+- [ ] 5. Verify Next.js configuration compatibility with v16
+- [ ] 5.1 (P) Confirm existing configuration options work in v16
+  - Verify `bundlePagesRouterDependencies` and `serverExternalPackages` are recognized and functional
+  - Verify `optimizePackageImports` continues to apply to the configured packages
+  - Verify `transpilePackages` correctly handles all ESM packages (remark-*, rehype-*, unified, etc.)
+  - Check if any existing options have been deprecated or renamed in v16 and update accordingly
+  - _Requirements: 5.1, 5.2, 5.3, 5.5_
+
+- [ ] 5.2 (P) Evaluate new v16 configuration options relevant to Pages Router
+  - Review the v16 release notes and configuration reference for any new options that benefit Pages Router applications
+  - Document findings and apply any beneficial options
+  - _Requirements: 5.4_
+
+- [ ] 5.3 Verify the ts-node hook preservation works with v16
+  - Start the development server and confirm the ts-node `.ts` extension hook is saved and restored correctly after Next.js initialization
+  - If v16 changes the config transpiler behavior, update the hook preservation logic in the server startup code
+  - Update the comment referencing "Next.js 15" to reflect v16 if the behavior changes
+  - _Requirements: 6.1, 6.2, 6.3_
+
+- [ ] 6. Run full regression testing and validate the upgrade
+- [ ] 6.1 Run the quality check suite
+  - Execute TypeScript type checking and confirm zero errors
+  - Execute Biome linting and confirm zero errors
+  - Run the full unit test suite and confirm all tests pass
+  - Run the production build and confirm it succeeds
+  - Verify root-level build commands (`pnpm run app:build`, `turbo run build`) work correctly
+  - _Requirements: 1.5, 3.4, 7.1, 7.2, 7.3, 7.4_
+
+- [ ] 6.2 Verify SuperJSON serialization and page compilation
+  - Run the existing SuperJSON round-trip tests to confirm Date, Map, and Set objects serialize correctly
+  - Start the dev server and compile the `[[...path]]` catch-all page to verify it works end-to-end
+  - Confirm all 7 null-loader rules, superjson-ssr-loader, I18NextHMRPlugin, ChunkModuleStatsPlugin, and source-map-loader are active
+  - _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 7.5, 7.6_
+
+- [ ] 6.3 Measure post-upgrade module counts and document results
+  - Run the updated measurement shell script to capture post-upgrade module counts
+  - Compare initial, async-only, and total module counts with the pre-upgrade baseline
+  - Verify initial modules are within ±5% of 895 (target range: 850–940)
+  - Document the before/after comparison in the analysis ledger or commit message
+  - _Requirements: 2.6, 7.7_