|
@@ -1,525 +0,0 @@
|
|
|
-# Design Document: reduce-modules-loaded
|
|
|
|
|
-
|
|
|
|
|
-## Overview
|
|
|
|
|
-
|
|
|
|
|
-**Purpose**: This feature reduces the excessive module count (10,066 modules) compiled for the `[[...path]]` catch-all page in `apps/app`, improving developer experience through faster compilation times and a tighter development feedback loop.
|
|
|
|
|
-
|
|
|
|
|
-**Users**: GROWI developers working on `apps/app` will benefit from significantly reduced `turbo run dev` compilation times when accessing pages during local development.
|
|
|
|
|
-
|
|
|
|
|
-**Impact**: Changes the current build configuration, import patterns, and potentially the Next.js version to eliminate unnecessary module loading — particularly server-side modules leaking into the client compilation graph.
|
|
|
|
|
-
|
|
|
|
|
-### Goals
|
|
|
|
|
-- Reduce the `[[...path]]` page module count from 10,066 to a significantly lower number (target: measurable reduction with before/after metrics)
|
|
|
|
|
-- Identify and fix server-side module leakage into client bundle
|
|
|
|
|
-- Optimize barrel export patterns to prevent full module tree traversal
|
|
|
|
|
-- Evaluate and apply Next.js official configuration options for module reduction
|
|
|
|
|
-- If beneficial, upgrade Next.js to unlock `bundlePagesRouterDependencies` and `serverExternalPackages`
|
|
|
|
|
-
|
|
|
|
|
-### Non-Goals
|
|
|
|
|
-- Migration from Pages Router to App Router
|
|
|
|
|
-- Complete elimination of barrel exports across the entire codebase
|
|
|
|
|
-- Turbopack adoption in Phase 1 (deferred to Phase 2b due to webpack config incompatibility; see `research.md` — Turbopack Compatibility section)
|
|
|
|
|
-- Performance optimization beyond module count reduction (runtime perf, SSR latency, etc.)
|
|
|
|
|
-
|
|
|
|
|
-## Architecture
|
|
|
|
|
-
|
|
|
|
|
-### Existing Architecture Analysis
|
|
|
|
|
-
|
|
|
|
|
-GROWI `apps/app` uses Next.js 14 with Pages Router, Webpack, and the following relevant configuration:
|
|
|
|
|
-
|
|
|
|
|
-| Mechanism | Current State | Gap |
|
|
|
|
|
-|-----------|--------------|-----|
|
|
|
|
|
-| `optimizePackageImports` | 11 `@growi/*` packages | Not expanded to third-party or internal barrel-heavy modules |
|
|
|
|
|
-| null-loader (client exclusion) | `mongoose`, `dtrace-provider`, `mathjax-full` | 30+ server-only packages not covered |
|
|
|
|
|
-| `next/dynamic` + LazyLoaded pattern | Well-implemented for modal components | Already correct — not a primary contributor |
|
|
|
|
|
-| `@next/bundle-analyzer` | Installed, not routinely used | Useful for investigating module composition, but NOT for measuring dev compilation module count |
|
|
|
|
|
-| `bundlePagesRouterDependencies` | Not configured | Requires Next.js 15+ |
|
|
|
|
|
-| `serverExternalPackages` | Not configured | Requires Next.js 15+ |
|
|
|
|
|
-
|
|
|
|
|
-**Confirmed Import Violations**:
|
|
|
|
|
-1. `src/client/components/RecentActivity/ActivityListItem.tsx` → `~/server/util/locale-utils` (server boundary violation)
|
|
|
|
|
-2. `src/client/components/InAppNotification/.../PageBulkExportJobModelNotification.tsx` → `~/models/serializers/.../page-bulk-export-job.ts` → `import mongoose from 'mongoose'` (server module via serializer)
|
|
|
|
|
-
|
|
|
|
|
-**High-Impact Barrel Exports**:
|
|
|
|
|
-- `src/states/ui/editor/index.ts` — 7 wildcard re-exports
|
|
|
|
|
-- `src/features/page-tree/index.ts` — 3-level cascading barrels (15+ modules)
|
|
|
|
|
-- `src/utils/axios/index.ts` — re-exports entire axios library
|
|
|
|
|
-
|
|
|
|
|
-### Architecture Pattern & Boundary Map
|
|
|
|
|
-
|
|
|
|
|
-**Selected pattern**: Phased configuration-driven optimization with incremental structural fixes
|
|
|
|
|
-
|
|
|
|
|
-The optimization is divided into two phases. Phase 1 operates within the current Next.js 14 + Webpack architecture. Phase 2 evaluates and optionally executes a Next.js version upgrade based on Phase 1 results.
|
|
|
|
|
-
|
|
|
|
|
-```mermaid
|
|
|
|
|
-graph TB
|
|
|
|
|
- subgraph Phase1[Phase 1 - v14 Optimizations]
|
|
|
|
|
- A1[Bundle Analysis Baseline] --> A2[Expand optimizePackageImports]
|
|
|
|
|
- A1 --> A3[Fix Import Violations]
|
|
|
|
|
- A1 --> A4[Expand null-loader Rules]
|
|
|
|
|
- A2 --> A5[Measure Module Count]
|
|
|
|
|
- A3 --> A5
|
|
|
|
|
- A4 --> A5
|
|
|
|
|
- A5 --> A6[Refactor High-Impact Barrels]
|
|
|
|
|
- A6 --> A7[Final Measurement]
|
|
|
|
|
- end
|
|
|
|
|
-
|
|
|
|
|
- subgraph Phase2[Phase 2 - Version Upgrade Evaluation]
|
|
|
|
|
- B1[Evaluate Phase 1 Results]
|
|
|
|
|
- B1 --> B2{Sufficient Reduction?}
|
|
|
|
|
- B2 -->|Yes| B3[Document Results]
|
|
|
|
|
- B2 -->|No| B4[Next.js 15 Upgrade]
|
|
|
|
|
- B4 --> B5[Enable bundlePagesRouterDependencies]
|
|
|
|
|
- B4 --> B6[Configure serverExternalPackages]
|
|
|
|
|
- B4 --> B7[Resolve next-superjson Blocker]
|
|
|
|
|
- end
|
|
|
|
|
-
|
|
|
|
|
- A7 --> B1
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Domain boundaries**:
|
|
|
|
|
-- Build Configuration (`next.config.js`) — config-only changes, zero code risk
|
|
|
|
|
-- Import Graph (source files) — import path fixes, moderate risk
|
|
|
|
|
-- Framework Version (Next.js/React) — major upgrade, high risk
|
|
|
|
|
-
|
|
|
|
|
-**Existing patterns preserved**: Pages Router, `getServerSideProps`, Jotai/SWR state management, feature-based directory structure
|
|
|
|
|
-
|
|
|
|
|
-### Technology Stack
|
|
|
|
|
-
|
|
|
|
|
-| Layer | Choice / Version | Role in Feature | Notes |
|
|
|
|
|
-|-------|------------------|-----------------|-------|
|
|
|
|
|
-| Build System | Next.js ^14.2.35 (Phase 1) / ^15.x (Phase 2) | Module bundling, compilation | Webpack bundler |
|
|
|
|
|
-| Bundler | Webpack 5 (via Next.js) | Module resolution, tree-shaking | Turbopack deferred to Phase 2b |
|
|
|
|
|
-| Analysis | `@next/bundle-analyzer` | Baseline and verification measurement | Already installed |
|
|
|
|
|
-| Linting | Biome (existing) | Import boundary enforcement | Optional ESLint rule for server/client boundary |
|
|
|
|
|
-
|
|
|
|
|
-## System Flows
|
|
|
|
|
-
|
|
|
|
|
-### Phase 1: Optimization Flow
|
|
|
|
|
-
|
|
|
|
|
-```mermaid
|
|
|
|
|
-sequenceDiagram
|
|
|
|
|
- participant Dev as Developer
|
|
|
|
|
- participant DevServer as next dev
|
|
|
|
|
- participant BA as Bundle Analyzer
|
|
|
|
|
- participant Config as next.config.js
|
|
|
|
|
- participant Src as Source Files
|
|
|
|
|
-
|
|
|
|
|
- Note over Dev,DevServer: Baseline Measurement
|
|
|
|
|
- Dev->>DevServer: turbo run dev + access page
|
|
|
|
|
- DevServer-->>Dev: Compiled in 51.5s (10066 modules)
|
|
|
|
|
-
|
|
|
|
|
- Note over Dev,BA: Investigation (optional)
|
|
|
|
|
- Dev->>BA: ANALYZE=true pnpm run build
|
|
|
|
|
- BA-->>Dev: Module composition treemap
|
|
|
|
|
-
|
|
|
|
|
- Note over Dev,Src: Apply Optimizations
|
|
|
|
|
- Dev->>Config: Expand optimizePackageImports
|
|
|
|
|
- Dev->>Config: Add null-loader rules for server packages
|
|
|
|
|
- Dev->>Src: Fix client to server import violations
|
|
|
|
|
- Dev->>Src: Refactor high-impact barrel exports
|
|
|
|
|
-
|
|
|
|
|
- Note over Dev,DevServer: Verification Measurement
|
|
|
|
|
- Dev->>DevServer: turbo run dev + access page
|
|
|
|
|
- DevServer-->>Dev: Compiled in Ys (M modules)
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-## Requirements Traceability
|
|
|
|
|
-
|
|
|
|
|
-| Requirement | Summary | Components | Interfaces | Flows |
|
|
|
|
|
-|-------------|---------|------------|------------|-------|
|
|
|
|
|
-| 1.1-1.4 | Next.js config research | ConfigResearch | — | Phase 1 |
|
|
|
|
|
-| 2.1-2.3 | Module count root cause analysis | DevCompilationMeasurement | — | Phase 1 |
|
|
|
|
|
-| 3.1-3.3 | Server-side leakage prevention | ImportViolationFix, NullLoaderExpansion | — | Phase 1 |
|
|
|
|
|
-| 3.4 | serverExternalPackages | NextjsUpgrade | next.config.js | Phase 2 |
|
|
|
|
|
-| 4.1-4.4 | Barrel export and package import optimization | OptimizePackageImportsExpansion, BarrelExportRefactor | — | Phase 1 |
|
|
|
|
|
-| 5.1-5.4 | Next.js version evaluation and upgrade | NextjsUpgrade | next.config.js | Phase 2 |
|
|
|
|
|
-| 6.1-6.3 | Compilation time and module count reduction | — (outcome) | — | Both |
|
|
|
|
|
-| 7.1-7.3 | Lazy loading verification | LazyLoadVerification | — | Phase 1 |
|
|
|
|
|
-
|
|
|
|
|
-## Components and Interfaces
|
|
|
|
|
-
|
|
|
|
|
-| Component | Domain | Intent | Req Coverage | Key Dependencies | Contracts |
|
|
|
|
|
-|-----------|--------|--------|--------------|-----------------|-----------|
|
|
|
|
|
-| DevCompilationMeasurement | Build | Measure dev module count as primary metric; bundle analyzer for investigation | 1.4, 2.1-2.3, 6.1 | Dev server log (P0), `@next/bundle-analyzer` (P1) | — |
|
|
|
|
|
-| OptimizePackageImportsExpansion | Build Config | Expand barrel file optimization coverage | 1.1, 4.3, 4.4 | `next.config.js` (P0) | Config |
|
|
|
|
|
-| NullLoaderExpansion | Build Config | Exclude additional server packages from client bundle | 3.1, 3.2 | `next.config.js` (P0) | Config |
|
|
|
|
|
-| ImportViolationFix | Source | Fix confirmed client-to-server import violations | 3.1, 3.2, 3.3 | Source files (P0) | — |
|
|
|
|
|
-| BarrelExportRefactor | Source | Refactor high-impact barrel exports to direct exports | 4.1, 4.2 | State/feature barrel files (P1) | — |
|
|
|
|
|
-| LazyLoadVerification | Build | Verify lazy-loaded components excluded from initial compilation | 7.1-7.3 | Bundle analysis output (P1) | — |
|
|
|
|
|
-| NextjsUpgrade | Framework | Evaluate and execute Next.js 15 upgrade | 5.1-5.4, 3.4 | next-superjson (P0 blocker), React 19 (P0) | Config |
|
|
|
|
|
-| ConfigResearch | Documentation | Document Next.js config options and applicability | 1.1-1.3 | — | — |
|
|
|
|
|
-
|
|
|
|
|
-### Build Configuration Domain
|
|
|
|
|
-
|
|
|
|
|
-#### DevCompilationMeasurement
|
|
|
|
|
-
|
|
|
|
|
-| Field | Detail |
|
|
|
|
|
-|-------|--------|
|
|
|
|
|
-| Intent | Measure dev compilation module count and time as the primary DX metric; use bundle analyzer as a supplementary investigation tool |
|
|
|
|
|
-| Requirements | 1.4, 2.1, 2.2, 2.3, 6.1 |
|
|
|
|
|
-
|
|
|
|
|
-**Responsibilities & Constraints**
|
|
|
|
|
-- Record dev compilation output (`Compiled /[[...path]] in Xs (N modules)`) as the **primary success metric**
|
|
|
|
|
-- Use `@next/bundle-analyzer` (`ANALYZE=true`) only as a **supplementary investigation tool** to understand which modules are included and trace import chains — NOT as the success metric
|
|
|
|
|
-- Establish baseline before any optimization, then measure after each step
|
|
|
|
|
-- Note: dev compilation does NOT tree-shake, so module count reflects the full dependency graph — this is exactly the metric we want to reduce
|
|
|
|
|
-
|
|
|
|
|
-**Important Distinction**:
|
|
|
|
|
-- `next dev` module count = modules webpack processes during on-demand compilation (no tree-shaking) → **this is what makes dev slow**
|
|
|
|
|
-- `next build` + ANALYZE = production bundle after tree-shaking → useful for investigation but does NOT reflect dev DX
|
|
|
|
|
-
|
|
|
|
|
-**Dependencies**
|
|
|
|
|
-- External: `@next/bundle-analyzer` — supplementary investigation tool (P1)
|
|
|
|
|
-- Inbound: Dev server compilation log — primary metric source (P0)
|
|
|
|
|
-
|
|
|
|
|
-**Contracts**: —
|
|
|
|
|
-
|
|
|
|
|
-**Implementation Notes**
|
|
|
|
|
-- Primary measurement: `turbo run dev` → access page → read `Compiled /[[...path]] in Xs (N modules)` from log
|
|
|
|
|
-- Clean `.next` directory before each measurement for consistent results
|
|
|
|
|
-- Supplementary: `ANALYZE=true pnpm run app:build` to inspect module composition when investigating specific leakage paths
|
|
|
|
|
-- Repeat measurement 3 times and take median to account for system variability
|
|
|
|
|
-
|
|
|
|
|
-#### OptimizePackageImportsExpansion
|
|
|
|
|
-
|
|
|
|
|
-| Field | Detail |
|
|
|
|
|
-|-------|--------|
|
|
|
|
|
-| Intent | Expand `optimizePackageImports` in `next.config.js` to cover barrel-heavy internal and third-party packages |
|
|
|
|
|
-| Requirements | 1.1, 4.3, 4.4 |
|
|
|
|
|
-
|
|
|
|
|
-**Responsibilities & Constraints**
|
|
|
|
|
-- Add packages identified by bundle analysis as barrel-heavy contributors
|
|
|
|
|
-- Maintain the existing 11 `@growi/*` entries
|
|
|
|
|
-- Identify third-party packages with barrel exports not in the auto-optimized list
|
|
|
|
|
-
|
|
|
|
|
-**Dependencies**
|
|
|
|
|
-- Outbound: `next.config.js` `experimental.optimizePackageImports` — config array (P0)
|
|
|
|
|
-- Inbound: BundleAnalysis — identifies which packages need optimization
|
|
|
|
|
-
|
|
|
|
|
-**Contracts**: Config [x]
|
|
|
|
|
-
|
|
|
|
|
-##### Configuration Interface
|
|
|
|
|
-
|
|
|
|
|
-Current configuration to extend:
|
|
|
|
|
-
|
|
|
|
|
-```typescript
|
|
|
|
|
-// next.config.js — experimental.optimizePackageImports
|
|
|
|
|
-// Existing entries preserved; new entries added based on bundle analysis
|
|
|
|
|
-const optimizePackageImports: string[] = [
|
|
|
|
|
- // Existing @growi/* packages (11)
|
|
|
|
|
- '@growi/core',
|
|
|
|
|
- '@growi/editor',
|
|
|
|
|
- '@growi/pluginkit',
|
|
|
|
|
- '@growi/presentation',
|
|
|
|
|
- '@growi/preset-themes',
|
|
|
|
|
- '@growi/remark-attachment-refs',
|
|
|
|
|
- '@growi/remark-drawio',
|
|
|
|
|
- '@growi/remark-growi-directive',
|
|
|
|
|
- '@growi/remark-lsx',
|
|
|
|
|
- '@growi/slack',
|
|
|
|
|
- '@growi/ui',
|
|
|
|
|
- // Candidates for addition (validate with bundle analysis):
|
|
|
|
|
- // - Third-party packages with barrel exports not in auto-list
|
|
|
|
|
- // - Internal directories if supported by config
|
|
|
|
|
-];
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Implementation Notes**
|
|
|
|
|
-- Zero-risk config change — does not affect runtime behavior
|
|
|
|
|
-- Validate each addition with before/after module count measurement
|
|
|
|
|
-- Some packages may already be auto-optimized by Next.js (check against the auto-list in docs)
|
|
|
|
|
-
|
|
|
|
|
-#### NullLoaderExpansion
|
|
|
|
|
-
|
|
|
|
|
-| Field | Detail |
|
|
|
|
|
-|-------|--------|
|
|
|
|
|
-| Intent | Expand null-loader rules in webpack config to exclude additional server-only packages from client bundle |
|
|
|
|
|
-| Requirements | 3.1, 3.2 |
|
|
|
|
|
-
|
|
|
|
|
-**Responsibilities & Constraints**
|
|
|
|
|
-- Add null-loader rules for server-only packages confirmed to appear in client bundle by bundle analysis
|
|
|
|
|
-- Maintain existing rules for `dtrace-provider`, `mongoose`, `mathjax-full`
|
|
|
|
|
-- Only add packages that are actually present in the client bundle (verify with bundle analysis first)
|
|
|
|
|
-
|
|
|
|
|
-**Dependencies**
|
|
|
|
|
-- Outbound: `next.config.js` `webpack()` config — null-loader rules (P0)
|
|
|
|
|
-- Inbound: BundleAnalysis — confirms which server packages are in client bundle
|
|
|
|
|
-
|
|
|
|
|
-**Contracts**: Config [x]
|
|
|
|
|
-
|
|
|
|
|
-##### Configuration Interface
|
|
|
|
|
-
|
|
|
|
|
-```typescript
|
|
|
|
|
-// next.config.js — webpack config, client-side only (!options.isServer)
|
|
|
|
|
-// Existing patterns preserved; candidates added after bundle analysis verification
|
|
|
|
|
-const serverPackageExclusions: RegExp[] = [
|
|
|
|
|
- /dtrace-provider/, // existing
|
|
|
|
|
- /mongoose/, // existing
|
|
|
|
|
- /mathjax-full/, // existing
|
|
|
|
|
- // Candidates (add only if confirmed in client bundle):
|
|
|
|
|
- // /@elastic\/elasticsearch/,
|
|
|
|
|
- // /passport/,
|
|
|
|
|
- // /@aws-sdk\//,
|
|
|
|
|
- // /@azure\//,
|
|
|
|
|
- // /@google-cloud\//,
|
|
|
|
|
- // /openai/,
|
|
|
|
|
- // /@opentelemetry\//,
|
|
|
|
|
- // /ldapjs/,
|
|
|
|
|
- // /nodemailer/,
|
|
|
|
|
- // /multer/,
|
|
|
|
|
- // /socket\.io/,
|
|
|
|
|
-];
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Implementation Notes**
|
|
|
|
|
-- Must verify each package appears in client bundle before adding rule (avoid unnecessary config)
|
|
|
|
|
-- null-loader replaces module content with empty module — no runtime impact for correctly excluded packages
|
|
|
|
|
-- If a package is accidentally excluded that IS needed on client, it will cause runtime errors — test thoroughly
|
|
|
|
|
-
|
|
|
|
|
-### Source Code Domain
|
|
|
|
|
-
|
|
|
|
|
-#### ImportViolationFix
|
|
|
|
|
-
|
|
|
|
|
-| Field | Detail |
|
|
|
|
|
-|-------|--------|
|
|
|
|
|
-| Intent | Fix confirmed client-to-server import violations that cause server modules to leak into client bundle |
|
|
|
|
|
-| Requirements | 3.1, 3.2, 3.3 |
|
|
|
|
|
-
|
|
|
|
|
-**Responsibilities & Constraints**
|
|
|
|
|
-- Fix the confirmed import violation in `ActivityListItem.tsx` (`~/server/util/locale-utils`)
|
|
|
|
|
-- Fix the serializer import in `PageBulkExportJobModelNotification.tsx` (pulls in mongoose)
|
|
|
|
|
-- Ensure fixed modules maintain identical functionality
|
|
|
|
|
-- Establish a pattern for preventing future violations
|
|
|
|
|
-
|
|
|
|
|
-**Dependencies**
|
|
|
|
|
-- Inbound: BundleAnalysis — identifies import chains causing leakage (P0)
|
|
|
|
|
-
|
|
|
|
|
-**Contracts**: —
|
|
|
|
|
-
|
|
|
|
|
-##### Confirmed Violations to Fix
|
|
|
|
|
-
|
|
|
|
|
-| File | Violation | Fix Strategy |
|
|
|
|
|
-|------|-----------|-------------|
|
|
|
|
|
-| `src/client/components/RecentActivity/ActivityListItem.tsx` | Imports `getLocale` from `~/server/util/locale-utils` | Extract `getLocale` to a client-safe utility module (the function only needs `date-fns/locale`, no server deps) |
|
|
|
|
|
-| `src/client/components/InAppNotification/.../PageBulkExportJobModelNotification.tsx` | Imports serializer that has `import mongoose from 'mongoose'` | Split serializer: server-side `stringifySnapshot` stays in `~/models/`; client-side `parseSnapshot` moves to client-accessible module |
|
|
|
|
|
-| `src/stores/in-app-notification.ts` | Imports `~/models/serializers/.../user` | Verify this serializer is clean (confirmed: no mongoose import). Low priority. |
|
|
|
|
|
-
|
|
|
|
|
-**Implementation Notes**
|
|
|
|
|
-- The `getLocale` function itself has no server dependencies — only `date-fns/locale` and `@growi/core/dist/interfaces`. The file's location in `~/server/util/` is misleading; extracting it to `~/utils/` or `~/client/util/` resolves the violation.
|
|
|
|
|
-- For the serializer split: `parseSnapshot` is a pure JSON parsing function; `stringifySnapshot` uses mongoose and should remain server-only.
|
|
|
|
|
-- Consider adding a lint rule to prevent `src/client/**` or `src/components/**` from importing `src/server/**`.
|
|
|
|
|
-
|
|
|
|
|
-#### BarrelExportRefactor
|
|
|
|
|
-
|
|
|
|
|
-| Field | Detail |
|
|
|
|
|
-|-------|--------|
|
|
|
|
|
-| Intent | Refactor high-impact barrel export files to reduce unnecessary module tree traversal |
|
|
|
|
|
-| Requirements | 4.1, 4.2 |
|
|
|
|
|
-
|
|
|
|
|
-**Responsibilities & Constraints**
|
|
|
|
|
-- Refactor after verifying that `optimizePackageImports` expansion does not already resolve the issue
|
|
|
|
|
-- Prioritize files with highest module count impact (determined by bundle analysis)
|
|
|
|
|
-- Maintain backward compatibility — consumers should not need to change their import paths unless necessary
|
|
|
|
|
-
|
|
|
|
|
-**Dependencies**
|
|
|
|
|
-- Inbound: OptimizePackageImportsExpansion — determines which barrels are already optimized (P1)
|
|
|
|
|
-- Inbound: BundleAnalysis — quantifies barrel impact (P1)
|
|
|
|
|
-
|
|
|
|
|
-**Contracts**: —
|
|
|
|
|
-
|
|
|
|
|
-##### Target Barrel Files (Priority Order)
|
|
|
|
|
-
|
|
|
|
|
-| File | Issue | Refactor Strategy |
|
|
|
|
|
-|------|-------|-------------------|
|
|
|
|
|
-| `src/utils/axios/index.ts` | `export * from 'axios'` re-exports entire library | Replace with specific named exports used by consumers |
|
|
|
|
|
-| `src/states/ui/editor/index.ts` | 7 wildcard `export *` | Convert to named re-exports; or verify `optimizePackageImports` handles it |
|
|
|
|
|
-| `src/features/page-tree/index.ts` | 3-level cascading barrel (15+ modules) | Flatten to single-level named exports; or consumers import directly from submodules |
|
|
|
|
|
-| `src/states/page/index.ts` | 2 wildcard + named exports | Convert to named re-exports if still problematic after config optimization |
|
|
|
|
|
-
|
|
|
|
|
-**Implementation Notes**
|
|
|
|
|
-- Attempt `optimizePackageImports` expansion first — if it handles barrel files for `@growi/*` packages effectively, many of these refactors become unnecessary
|
|
|
|
|
-- For `utils/axios/index.ts`, the `export * from 'axios'` pattern is universally problematic; this should be fixed regardless of other optimizations
|
|
|
|
|
-- Barrel refactoring may require updating import paths across many files — use IDE refactoring tools and verify with `turbo run lint:typecheck`
|
|
|
|
|
-
|
|
|
|
|
-### Build Verification Domain
|
|
|
|
|
-
|
|
|
|
|
-#### LazyLoadVerification
|
|
|
|
|
-
|
|
|
|
|
-| Field | Detail |
|
|
|
|
|
-|-------|--------|
|
|
|
|
|
-| Intent | Verify that lazy-loaded components are correctly excluded from initial page compilation |
|
|
|
|
|
-| Requirements | 7.1, 7.2, 7.3 |
|
|
|
|
|
-
|
|
|
|
|
-**Responsibilities & Constraints**
|
|
|
|
|
-- Verify the existing `*LazyLoaded` pattern (dynamic.tsx + useLazyLoader) does not contribute to initial module count
|
|
|
|
|
-- Confirm `index.ts` files in lazy-loaded component directories only re-export from `dynamic.tsx`
|
|
|
|
|
-- Check bundle analysis output for any lazy-loaded component modules in the initial bundle
|
|
|
|
|
-
|
|
|
|
|
-**Dependencies**
|
|
|
|
|
-- Inbound: BundleAnalysis — verifies exclusion from initial bundle (P1)
|
|
|
|
|
-
|
|
|
|
|
-**Contracts**: —
|
|
|
|
|
-
|
|
|
|
|
-**Implementation Notes**
|
|
|
|
|
-- Gap analysis confirms the LazyLoaded pattern is already well-implemented
|
|
|
|
|
-- This component is primarily a verification step, not a fix
|
|
|
|
|
-- If any lazy-loaded components are found in the initial bundle, the fix follows the existing `dynamic.tsx` pattern
|
|
|
|
|
-
|
|
|
|
|
-### Framework Upgrade Domain (Phase 2)
|
|
|
|
|
-
|
|
|
|
|
-#### NextjsUpgrade
|
|
|
|
|
-
|
|
|
|
|
-| Field | Detail |
|
|
|
|
|
-|-------|--------|
|
|
|
|
|
-| Intent | Evaluate and optionally execute Next.js 15 upgrade to unlock `bundlePagesRouterDependencies` and `serverExternalPackages` |
|
|
|
|
|
-| Requirements | 5.1, 5.2, 5.3, 5.4, 3.4 |
|
|
|
|
|
-
|
|
|
|
|
-**Responsibilities & Constraints**
|
|
|
|
|
-- Only proceed if Phase 1 results indicate insufficient module reduction
|
|
|
|
|
-- Address the `next-superjson` compatibility blocker before upgrading
|
|
|
|
|
-- Use the official `@next/codemod` for automated migration
|
|
|
|
|
-- Maintain React 18 compatibility with Pages Router (backward compat available in v15)
|
|
|
|
|
-
|
|
|
|
|
-**Dependencies**
|
|
|
|
|
-- External: `next-superjson` — SWC plugin compatibility (P0 blocker)
|
|
|
|
|
-- External: React 19 — peer dependency (P0, but backward compat available)
|
|
|
|
|
-- External: `@next/codemod` — migration automation (P1)
|
|
|
|
|
-
|
|
|
|
|
-**Contracts**: Config [x]
|
|
|
|
|
-
|
|
|
|
|
-##### Configuration Interface (Post-Upgrade)
|
|
|
|
|
-
|
|
|
|
|
-```typescript
|
|
|
|
|
-// next.config.js — New v15 options
|
|
|
|
|
-const nextConfig = {
|
|
|
|
|
- // Enable automatic server-side dependency bundling for Pages Router
|
|
|
|
|
- bundlePagesRouterDependencies: true,
|
|
|
|
|
- // Exclude heavy server-only packages from bundling
|
|
|
|
|
- serverExternalPackages: [
|
|
|
|
|
- 'mongoose',
|
|
|
|
|
- // Additional packages based on bundle analysis
|
|
|
|
|
- ],
|
|
|
|
|
-};
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-##### Known Blockers
|
|
|
|
|
-
|
|
|
|
|
-| Blocker | Severity | Mitigation |
|
|
|
|
|
-|---------|----------|------------|
|
|
|
|
|
-| `next-superjson` SWC plugin broken in v15 | Critical | **Resolved** — custom webpack loader (`superjson-ssr-loader.js`) replaces the SWC plugin with a simple regex-based source transform; see SuperJSON Migration section below |
|
|
|
|
|
-| `I18NextHMRPlugin` (webpack plugin) | Medium | Only affects dev HMR for i18n; can use `--webpack` flag for dev |
|
|
|
|
|
-| React 19 peer dependency | Low | Pages Router has React 18 backward compat in v15 |
|
|
|
|
|
-| `@next/font` removal | Low | Codemod available; switch to `next/font` |
|
|
|
|
|
-
|
|
|
|
|
-##### SuperJSON Migration: Custom Webpack Loader Approach
|
|
|
|
|
-
|
|
|
|
|
-The `next-superjson` SWC plugin is replaced by a custom webpack loader that achieves the same transparent auto-wrapping of `getServerSideProps` without any SWC/Babel dependency.
|
|
|
|
|
-
|
|
|
|
|
-**Architecture**:
|
|
|
|
|
-
|
|
|
|
|
-```
|
|
|
|
|
-┌─────────────────────────────────────────────────────────────────┐
|
|
|
|
|
-│ Build Time (webpack) │
|
|
|
|
|
-│ │
|
|
|
|
|
-│ .page.tsx files ──► superjson-ssr-loader.js ──► bundled output │
|
|
|
|
|
-│ (auto-wraps getServerSideProps │
|
|
|
|
|
-│ with withSuperJSONProps) │
|
|
|
|
|
-└─────────────────────────────────────────────────────────────────┘
|
|
|
|
|
-
|
|
|
|
|
-┌─────────────────────────────────────────────────────────────────┐
|
|
|
|
|
-│ Runtime │
|
|
|
|
|
-│ │
|
|
|
|
|
-│ Server: getServerSideProps → superjson.serialize(props) │
|
|
|
|
|
-│ Client: _app.page.tsx → deserializeSuperJSONProps(pageProps) │
|
|
|
|
|
-└─────────────────────────────────────────────────────────────────┘
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Components**:
|
|
|
|
|
-
|
|
|
|
|
-| File | Role |
|
|
|
|
|
-|------|------|
|
|
|
|
|
-| `src/utils/superjson-ssr-loader.js` | Webpack loader — regex-based source transform that auto-wraps `getServerSideProps` exports |
|
|
|
|
|
-| `src/pages/utils/superjson-ssr.ts` | Runtime helpers — `withSuperJSONProps()` (server) and `deserializeSuperJSONProps()` (client) |
|
|
|
|
|
-| `_app.page.tsx` | Centralized client-side deserialization via `deserializeSuperJSONProps()` |
|
|
|
|
|
-| `next.config.js` | Loader registration targeting `.page.{ts,tsx}` files |
|
|
|
|
|
-
|
|
|
|
|
-**Why this approach over per-page wrapping**:
|
|
|
|
|
-- Zero per-page file changes (38 fewer files modified vs manual wrapping)
|
|
|
|
|
-- New pages automatically get superjson serialization without manual wrapping
|
|
|
|
|
-- Closer to the original `next-superjson` DX (config-only, transparent to page authors)
|
|
|
|
|
-- Webpack loader API is stable across Next.js versions — no SWC ABI fragility
|
|
|
|
|
-
|
|
|
|
|
-**Why not `next-superjson` v1.0.8**:
|
|
|
|
|
-- v1.0.8 achieves "v15 support" by pinning `@swc/core@1.4.17` (March 2024) and running a separate SWC compilation pass — fragile binary pinning with double compilation
|
|
|
|
|
-- Depends on unmaintained `next-superjson-plugin` v0.6.3 WASM binary
|
|
|
|
|
-- See `research.md` for detailed assessment
|
|
|
|
|
-
|
|
|
|
|
-**Implementation Notes**
|
|
|
|
|
-- Run codemod first: `npx @next/codemod@canary upgrade latest`
|
|
|
|
|
-- Test with `--webpack` flag to isolate bundler-related issues from framework issues
|
|
|
|
|
-- The `bundlePagesRouterDependencies: true` setting is the highest-value v15 feature for this spec — it automatically bundles server-side deps, which combined with `serverExternalPackages` provides fine-grained control
|
|
|
|
|
-
|
|
|
|
|
-## Testing Strategy
|
|
|
|
|
-
|
|
|
|
|
-### Verification Tests (Module Count — Primary DX Metric)
|
|
|
|
|
-- **Primary**: Run `turbo run dev`, access page, record `Compiled /[[...path]] in Xs (N modules)` from log before and after each optimization step
|
|
|
|
|
-- **Supplementary**: Run `ANALYZE=true pnpm run app:build` only when investigating specific module composition (e.g., tracing which server modules appear in client bundle)
|
|
|
|
|
-- Clean `.next` directory before each measurement; repeat 3 times, take median
|
|
|
|
|
-
|
|
|
|
|
-### Regression Tests
|
|
|
|
|
-- `turbo run lint:typecheck --filter @growi/app` — verify no type errors from import changes
|
|
|
|
|
-- `turbo run lint:biome --filter @growi/app` — verify no lint violations
|
|
|
|
|
-- `turbo run test --filter @growi/app` — verify all existing tests pass
|
|
|
|
|
-- `turbo run build --filter @growi/app` — verify production build succeeds
|
|
|
|
|
-- Manual smoke test: access `[[...path]]` page and verify all functionality works (page rendering, editing, navigation, modals)
|
|
|
|
|
-
|
|
|
|
|
-### Phase 2 Additional Tests
|
|
|
|
|
-- All Phase 1 tests
|
|
|
|
|
-- `npx @next/codemod@canary upgrade latest --dry` — preview upgrade changes
|
|
|
|
|
-- Test superjson serialization: verify `getServerSideProps` data correctly serialized/deserialized for all page routes
|
|
|
|
|
-- Test i18n HMR: verify locale changes reflect in dev mode (may degrade if I18NextHMRPlugin is removed)
|
|
|
|
|
-
|
|
|
|
|
-## Performance & Scalability
|
|
|
|
|
-
|
|
|
|
|
-**Target Metrics**:
|
|
|
|
|
-- **Primary (DX metric)**: Dev compilation module count for `[[...path]]` page (baseline: 10,066 modules)
|
|
|
|
|
-- **Secondary (DX metric)**: Dev compilation time for `[[...path]]` page (baseline: 51.5s)
|
|
|
|
|
-- **Supplementary (investigation only)**: Production bundle composition via `@next/bundle-analyzer`
|
|
|
|
|
-
|
|
|
|
|
-> **Important**: The primary metric is the dev compilation log, NOT the production bundle analyzer. Dev compilation does not tree-shake, so the module count directly reflects what makes development slow. Production bundle analysis is useful for tracing import chains but does not represent the dev experience.
|
|
|
|
|
-
|
|
|
|
|
-**Measurement Protocol**:
|
|
|
|
|
-1. Clean `.next` directory (`rm -rf apps/app/.next`)
|
|
|
|
|
-2. Run `turbo run dev`
|
|
|
|
|
-3. Navigate to `/` or any wiki page path in the browser
|
|
|
|
|
-4. Record `Compiled /[[...path]] in Xs (N modules)` from the terminal log
|
|
|
|
|
-5. Repeat 3 times, take median value
|
|
|
|
|
-6. Record results in a comparison table for each optimization step
|
|
|
|
|
-
|
|
|
|
|
-## Supporting References
|
|
|
|
|
-
|
|
|
|
|
-### Server-Only Package Candidates for null-loader
|
|
|
|
|
-
|
|
|
|
|
-From `apps/app/package.json`, the following packages are server-only and should be excluded from client bundle if they appear there:
|
|
|
|
|
-
|
|
|
|
|
-| Category | Packages |
|
|
|
|
|
-|----------|----------|
|
|
|
|
|
-| Database | `mongoose`, `mongodb`, `mongoose-gridfs`, `mongoose-paginate-v2`, `mongoose-unique-validator` |
|
|
|
|
|
-| Search | `@elastic/elasticsearch7`, `@elastic/elasticsearch8`, `@elastic/elasticsearch9` |
|
|
|
|
|
-| Auth | `passport`, `passport-github2`, `passport-google-oauth20`, `passport-ldapauth`, `passport-saml` |
|
|
|
|
|
-| Cloud Storage | `@aws-sdk/client-s3`, `@aws-sdk/s3-request-presigner`, `@azure/storage-blob`, `@google-cloud/storage` |
|
|
|
|
|
-| AI | `openai`, `@azure/openai` |
|
|
|
|
|
-| Identity | `@azure/identity`, `ldapjs` |
|
|
|
|
|
-| File Upload | `multer`, `multer-autoreap` |
|
|
|
|
|
-| Email | `nodemailer`, `nodemailer-ses-transport` |
|
|
|
|
|
-| Real-time | `socket.io`, `y-socket.io`, `y-mongodb-provider` |
|
|
|
|
|
-| Session/Cache | `connect-redis`, `redis` |
|
|
|
|
|
-| Observability | `@opentelemetry/*` (8 packages) |
|
|
|
|
|
-
|
|
|
|
|
-> Only add null-loader rules for packages confirmed present in the client bundle by bundle analysis.
|
|
|
|
|
-
|
|
|
|
|
-### Auto-Optimized Packages (No Need to Add to optimizePackageImports)
|
|
|
|
|
-
|
|
|
|
|
-The following packages are automatically optimized by Next.js and should NOT be added to the config:
|
|
|
|
|
-`lucide-react`, `date-fns`, `lodash-es`, `ramda`, `antd`, `react-bootstrap`, `ahooks`, `@ant-design/icons`, `@headlessui/react`, `@headlessui-float/react`, `@heroicons/react/*`, `@visx/visx`, `@tremor/react`, `rxjs`, `@mui/material`, `@mui/icons-material`, `recharts`, `react-use`, `@material-ui/*`, `@tabler/icons-react`, `mui-core`, `react-icons/*`, `effect`, `@effect/*`
|
|
|