Browse Source

update spec

Yuki Takei 1 month ago
parent
commit
880b60e5b0

+ 14 - 16
.kiro/specs/reduce-modules-loaded/analysis-ledger.md

@@ -29,32 +29,30 @@ Measured via `ChunkModuleStatsPlugin` in `next.config.utils.js`. The `initial` c
 
 ### Measurement Method
 
-The following method was used for all measurements on 2026-02-19:
+**Automated (Phase 2+)**:
 
 ```bash
-# 1. Clean .next cache
-rm -rf apps/app/.next
-
-# 2. Start Next.js dev server directly (bypassing Express/MongoDB)
-cd apps/app && node_modules/.bin/next dev -p 3000 &
+# One-command measurement — cleans .next, starts next dev, triggers compilation, outputs results
+./apps/app/bin/measure-chunk-stats.sh        # default port 3099
+./apps/app/bin/measure-chunk-stats.sh 3001   # custom port
+```
 
-# 3. Wait for "Ready" in log, then trigger on-demand compilation
-curl -s http://localhost:3000/
+Output: `[ChunkModuleStats] initial: N, async-only: N, total: N` + `Compiled /[[...path]] in Xs (N modules)`
 
-# 4. Read compilation result from terminal log
-#    e.g. "✓ Compiled /[[...path]] in 31s (10066 modules)"
+**Manual (Phase 1, legacy)**:
 
-# 5. Kill dev server
-pkill -f "next dev"
+```bash
+rm -rf apps/app/.next
+cd apps/app && node_modules/.bin/next dev -p 3000 &
+curl -s http://localhost:3000/
+# Read log output, then: pkill -f "next dev"
 ```
 
 **Key details**:
 - `next dev` can be started without MongoDB — it compiles pages on-demand via webpack regardless of database connectivity
 - Compilation is triggered by HTTP access (curl), not by server startup alone (Next.js uses on-demand compilation)
-- For A/B bisection, files were backed up and swapped between measurements using `cp` to isolate each change group
-- Single measurement per configuration (not 3x median) due to consistent results (~0.5s variance between runs)
-
-> **Measurement Protocol**: Clean `.next` → `next dev` → `curl localhost:3000` → read `Compiled /[[...path]] in Xs (N modules)` from log
+- `ChunkModuleStatsPlugin` (in `src/utils/next.config.utils.js`) separates modules into initial (eager) vs async-only (lazy) chunks
+- The `initial` count is the primary KPI — modules the browser must load on first page access
 
 ## Import Violations (Task 3)
 | # | File | Violation | Fix Strategy | Status |

+ 41 - 29
.kiro/specs/reduce-modules-loaded/tasks.md

@@ -137,44 +137,56 @@ Create `.kiro/specs/reduce-modules-loaded/analysis-ledger.md` during task 1.2 an
   - Production build: Succeeds
   - _Requirements: 6.2, 6.3_
 
-## Phase 2: Next.js Version Upgrade Evaluation
-
-- [ ] 8. Evaluate Phase 1 results and Next.js upgrade decision
-- [x] 8.1 Assess whether Phase 1 reduction is sufficient
-  - **Actual measurement results (A/B bisection):**
-    - Baseline (no changes): 10,066 modules / ~31s
-    - All Phase 1 changes: 10,281 modules / ~31.6s (optimizePackageImports caused +213 modules)
-    - Committed changes only (without optimizePackageImports): 10,068 modules / ~31s
-    - Each change group tested independently — none produced measurable compilation time improvement
+## Phase 2: Iterative Module Reduction (Dynamic Import & Import Optimization)
+
+### KPI
+
+- **Primary**: `[ChunkModuleStats] initial` — modules in eager (initial) chunks
+- **Baseline**: initial: 2,704 (before Phase 2 changes)
+- Measured via `bin/measure-chunk-stats.sh` (cleans `.next`, starts `next dev`, triggers compilation, outputs ChunkModuleStats)
+
+### Reduction Loop
+
+The following loop repeats until the user declares completion:
+
+1. **Measure** — Run `bin/measure-chunk-stats.sh`, record `initial` / `async-only` / `total` in `analysis-ledger.md`
+2. **Analyze & Propose** — Analyze the initial chunk module graph, identify the top contributors, and propose one or more reduction approaches (e.g., `next/dynamic`, import refactoring, dependency replacement). Alternatively, if further reduction is impractical, propose ending the loop.
+3. **User Decision** — The user approves the proposed approach, adjusts it, or declares the loop complete.
+4. **Implement & Verify** — Apply the approved changes, then run `turbo run lint:typecheck --filter @growi/app && turbo run lint:biome --filter @growi/app`. Fix any errors before returning to step 1.
+
+### Task Log
+
+- [x] 8.1 Phase 1 sufficiency assessment
   - **Assessment: Phase 1 is insufficient for compilation time reduction.** Changes are code quality improvements only.
-  - **optimizePackageImports rejected**: Adding reactstrap/react-hook-form/react-markdown increased module count by 213 with no time benefit — reverted
-  - Recommendation: Proceed with Next.js upgrade evaluation (Task 8.2) or Turbopack/route splitting
   - Full assessment documented in `analysis-ledger.md`
   - _Requirements: 5.1_
 
-- [ ] 8.2 Document Next.js 15+ feature evaluation
+- [x] 8.2 Establish ChunkModuleStats KPI and measurement tooling
+  - Created `ChunkModuleStatsPlugin` in `src/utils/next.config.utils.js`
+  - Created `bin/measure-chunk-stats.sh` for one-command measurement
+  - Baseline recorded: initial: 2,704 / async-only: 4,146 / total: 6,850
+  - _Requirements: 2.1, 6.1_
+
+- [x] 8.3 Loop iteration 1: MermaidViewer dynamic import + date-fns subpath imports
+  - MermaidViewer → `next/dynamic({ ssr: false })` in client renderer
+  - date-fns barrel → subpath imports (12 files)
+  - Result: initial: 2,128 (-576, -21.3%) / async-only: 4,717 / total: 6,845
+  - _Requirements: 7.2, 4.1, 6.1_
+
+- [ ] 8.N Loop iteration N: (next iteration — measure, analyze, propose, implement)
+
+## Phase 3: Next.js Version Upgrade Evaluation (Deferred)
+
+- [ ] 9.1 Document Next.js 15+ feature evaluation
   - Document which Next.js 15+ features (`bundlePagesRouterDependencies`, `serverExternalPackages`, Turbopack, improved tree-shaking) are relevant to further module reduction
-  - Document which features are applicable to the current GROWI Pages Router architecture vs. those that require additional migration
-  - Assess the `next-superjson` compatibility blocker and identify mitigation options (manual superjson, direct usage without SWC plugin, or alternative serialization)
-  - If the upgrade is not beneficial or too risky, document the reasoning and confirm that Phase 1 optimizations are the final solution
+  - Assess the `next-superjson` compatibility blocker and identify mitigation options
   - _Requirements: 1.1, 1.2, 1.3, 5.1, 5.4_
 
-- [ ] 9. Execute Next.js 15 upgrade (conditional on task 8 decision)
-- [ ] 9.1 Run upgrade codemod and address breaking changes
-  - Run the official `@next/codemod` upgrade tool to apply automated migrations
-  - Address any breaking changes specific to the Pages Router (e.g., `@next/font` → `next/font`, renamed config options)
-  - Resolve the `next-superjson` compatibility issue using the mitigation strategy selected in task 8.2
+- [ ] 9.2 Execute Next.js 15 upgrade (conditional on 9.1 decision)
   - _Requirements: 5.2, 5.3_
 
-- [ ] 9.2 Enable v15-specific module optimization features
-  - Enable `bundlePagesRouterDependencies: true` in `next.config.js` for automatic server-side dependency bundling
-  - Configure `serverExternalPackages` to exclude heavy server-only packages from bundling
-  - Measure the dev compilation module count after enabling these features
+- [ ] 9.3 Enable v15-specific module optimization features
   - _Requirements: 3.4, 5.2_
 
-- [ ] 9.3 Run full regression test suite after upgrade
-  - Execute type checking, linting, unit tests, and production build
-  - Verify `getServerSideProps` superjson serialization works correctly across all page routes
-  - Verify i18n HMR still functions in development mode (may degrade if I18NextHMRPlugin is affected)
-  - Perform a manual smoke test for full functionality
+- [ ] 9.4 Run full regression test suite after upgrade
   - _Requirements: 5.3, 6.2, 6.3_

+ 60 - 0
apps/app/bin/measure-chunk-stats.sh

@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+# Measure ChunkModuleStats (initial / async-only / total) for [[...path]] page.
+# Usage: ./bin/measure-chunk-stats.sh [port]
+set -euo pipefail
+
+PORT="${1:-3099}"
+LOG=$(mktemp /tmp/chunk-stats-XXXXXX.log)
+
+cleanup() {
+  local pids
+  pids=$(lsof -ti :"$PORT" 2>/dev/null || true)
+  if [ -n "$pids" ]; then
+    kill -9 $pids 2>/dev/null || true
+  fi
+  rm -f "$LOG"
+}
+trap cleanup EXIT
+
+# 1. Ensure port is free
+cleanup_pids=$(lsof -ti :"$PORT" 2>/dev/null || true)
+if [ -n "$cleanup_pids" ]; then
+  kill -9 $cleanup_pids 2>/dev/null || true
+  sleep 1
+fi
+
+# 2. Clean .next cache
+rm -rf "$(dirname "$0")/../.next"
+
+# 3. Start Next.js dev server
+cd "$(dirname "$0")/.."
+npx next dev -p "$PORT" > "$LOG" 2>&1 &
+NEXT_PID=$!
+
+# 4. Wait for server ready
+echo "Waiting for Next.js to start on port $PORT ..."
+for i in $(seq 1 30); do
+  if grep -q "Local:" "$LOG" 2>/dev/null; then
+    break
+  fi
+  sleep 1
+done
+
+# 5. Trigger compilation
+echo "Triggering compilation ..."
+curl -s -o /dev/null http://localhost:"$PORT"/
+
+# 6. Wait for ChunkModuleStats output (non-zero initial)
+echo "Waiting for compilation ..."
+for i in $(seq 1 120); do
+  if grep -qP 'ChunkModuleStats\] initial: [1-9]' "$LOG" 2>/dev/null; then
+    break
+  fi
+  sleep 2
+done
+
+# 7. Print results
+echo ""
+echo "=== Results ==="
+grep -E 'ChunkModuleStats|Compiled.*modules' "$LOG" | grep -v 'initial: 0,' | head -5
+echo ""