Преглед изворни кода

fix: update project structure and add build optimization guidelines

Yuki Takei пре 1 месец
родитељ
комит
c109c2a755

+ 10 - 2
.kiro/steering/structure.md

@@ -4,5 +4,13 @@ See: `.claude/skills/monorepo-overview/SKILL.md` (auto-loaded by Claude Code)
 
 ## cc-sdd Specific Notes
 
-Currently, there are no additional instructions specific to Kiro.
-If instructions specific to the cc-sdd workflow are needed in the future, add them to this section.
+### Server-Client Boundary Enforcement
+
+In full-stack packages (e.g., `apps/app`), server-side code (`src/server/`, models with mongoose) must NOT be imported from client components. This causes module leakage — server-only dependencies get pulled into the client bundle.
+
+- **Pattern**: If a client component needs functionality from a server module, extract the client-safe logic into a shared utility (`src/utils/` or `src/client/util/`)
+
+For apps/app-specific examples and build tooling details, see `apps/app/.claude/skills/build-optimization/SKILL.md`.
+
+---
+_Updated: 2026-03-03. apps/app details moved to `apps/app/.claude/skills/build-optimization/SKILL.md`._

+ 18 - 2
.kiro/steering/tech.md

@@ -4,5 +4,21 @@ See: `.claude/skills/tech-stack/SKILL.md` (auto-loaded by Claude Code)
 
 ## cc-sdd Specific Notes
 
-Currently, there are no additional instructions specific to Kiro.
-If instructions specific to the cc-sdd workflow are needed in the future, add them to this section.
+### Bundler Strategy (Project-Wide Decision)
+
+GROWI uses **Webpack** (not Turbopack) across all Next.js applications. Turbopack is the default in Next.js 16, but GROWI opts out via `--webpack` flag due to custom webpack configuration that Turbopack does not support.
+
+Turbopack migration is deferred as a separate initiative. See `apps/app/.claude/skills/build-optimization/SKILL.md` for details and blockers.
+
+### Import Optimization Principles
+
+To prevent module count regression across the monorepo:
+
+- **Subpath imports over barrel imports** — e.g., `import { format } from 'date-fns/format'` instead of `from 'date-fns'`
+- **Lightweight replacements** — prefer small single-purpose packages over large multi-feature libraries
+- **Server-client boundary** — never import server-only code from client modules; extract client-safe utilities if needed
+
+For apps/app-specific build optimization details (webpack config, null-loader rules, SuperJSON architecture, module count KPI), see `apps/app/.claude/skills/build-optimization/SKILL.md`.
+
+---
+_Updated: 2026-03-03. apps/app details moved to `apps/app/.claude/skills/build-optimization/SKILL.md`._

+ 12 - 0
apps/app/.claude/skills/app-commands/SKILL.md

@@ -151,6 +151,18 @@ pnpm run version:prerelease
 pnpm run version:preminor
 ```
 
+## Build Measurement
+
+```bash
+# Measure module count KPI (cleans .next, starts next dev, triggers compilation)
+./bin/measure-chunk-stats.sh           # default port 3099
+./bin/measure-chunk-stats.sh 3001      # custom port
+```
+
+Output: `[ChunkModuleStats] initial: N, async-only: N, total: N`
+
+For details on module optimization and baselines, see the `build-optimization` skill.
+
 ## Production
 
 ```bash

+ 81 - 0
apps/app/.claude/skills/build-optimization/SKILL.md

@@ -0,0 +1,81 @@
+---
+name: build-optimization
+description: GROWI apps/app webpack configuration, module optimization, and build measurement tooling. Auto-invoked when working in apps/app.
+user-invocable: false
+---
+
+# Build Optimization (apps/app)
+
+## Next.js Version & Bundler Strategy
+
+- **Next.js 16** (`^16.0.0`) with **Webpack** bundler (not Turbopack)
+- Turbopack is the default in v16, but GROWI opts out via `--webpack` flag due to custom webpack configuration
+- Build: `next build --webpack`; Dev: Express server calls `next({ dev })` which uses webpack when `webpack()` config exists
+- React stays at `^18.2.0` — Pages Router has full React 18 support in v16
+
+## Custom Webpack Configuration
+
+| Component | File | Purpose |
+|-----------|------|---------|
+| **superjson-ssr-loader** | `src/utils/superjson-ssr-loader.js` | Auto-wraps `getServerSideProps` with SuperJSON serialization |
+| **null-loader rules** (7) | `next.config.ts` | Exclude server-only packages from client bundle |
+| **I18NextHMRPlugin** | `next.config.ts` | i18n hot module replacement in dev mode |
+| **ChunkModuleStatsPlugin** | `src/utils/next.config.utils.js` | Dev-time module count analysis (initial/async-only/total) |
+| **source-map-loader** | `next.config.ts` | Source map extraction in dev builds |
+
+### null-loader Rules
+
+7 packages excluded from client bundle: `dtrace-provider`, `mongoose`, `mathjax-full`, `i18next-fs-backend`, `bunyan`, `bunyan-format`, `core-js`
+
+**Important**: Any changes to these loaders/plugins must be verified against the module count baseline.
+
+## SuperJSON Serialization Architecture
+
+The `next-superjson` SWC plugin was replaced by a custom webpack loader:
+
+- **Build time**: `superjson-ssr-loader.js` auto-wraps `getServerSideProps` in `.page.{ts,tsx}` files with `withSuperJSONProps()`
+- **Runtime (server)**: `withSuperJSONProps()` in `src/pages/utils/superjson-ssr.ts` serializes props via superjson
+- **Runtime (client)**: `_app.page.tsx` calls `deserializeSuperJSONProps()` for centralized deserialization
+- **No per-page changes needed** — new pages automatically get superjson serialization
+- Custom serializers registered in `_app.page.tsx` (ObjectId, PageRevisionWithMeta)
+
+## Module Optimization Configuration
+
+- `bundlePagesRouterDependencies: true` — bundles server-side dependencies for Pages Router
+- `serverExternalPackages: ['handsontable']` — packages excluded from server-side bundling
+- `optimizePackageImports` — 11 `@growi/*` packages configured (expansion to third-party packages was tested and reverted — it increased dev module count)
+
+## Module Count Measurement
+
+KPI: `[ChunkModuleStats] initial: N, async-only: N, total: N`
+
+- `initial` = modules in eager (initial) chunks — the primary reduction target
+- Measured via `bin/measure-chunk-stats.sh` (cleans `.next`, starts `next dev`, triggers compilation)
+- Any changes to webpack config or import patterns should be verified against the `initial` count
+
+## Effective Module Reduction Techniques
+
+Techniques that have proven effective for reducing module count, ordered by typical impact:
+
+| Technique | When to Use |
+|-----------|-------------|
+| `next/dynamic({ ssr: true })` | Heavy rendering pipelines (markdown, code highlighting) that can be deferred to async chunks while preserving SSR |
+| `next/dynamic({ ssr: false })` | Client-only heavy components (e.g., Mermaid diagrams, interactive editors) |
+| Subpath imports | Packages with large barrel exports (e.g., `date-fns/format` instead of `date-fns`) |
+| Deep ESM imports | Packages that re-export multiple engines via barrel (e.g., `react-syntax-highlighter/dist/esm/prism-async-light`) |
+| null-loader | Server-only packages leaking into client bundle via transitive imports |
+| Lightweight replacements | Replace large libraries used for a single feature (e.g., `tinykeys` instead of `react-hotkeys`, regex instead of `validator`) |
+
+### Techniques That Did NOT Work
+
+- **Expanding `optimizePackageImports` to third-party packages** — In dev mode, this resolves individual sub-module files instead of barrel, resulting in MORE module entries. Reverted.
+- **Refactoring internal barrel exports** — Internal barrels (`states/`, `features/`) are small and well-scoped; refactoring had no measurable impact.
+
+## Turbopack Migration Path (Future)
+
+Turbopack adoption is deferred. Key blockers:
+
+- `webpack()` config not supported — null-loader rules need `turbopack.resolveAlias` migration
+- Custom loaders (superjson-ssr-loader) need Turbopack rules testing
+- I18NextHMRPlugin has no Turbopack equivalent
+- Use `--webpack` flag in both dev and build until migration is complete