Yuki Takei 3 недель назад
Родитель
Сommit
df39ad644a

+ 1 - 1
.kiro/specs/optimise-deps-for-prod/design.md

@@ -340,7 +340,7 @@ bash apps/app/bin/assemble-prod.sh
 
 > **注意**:
 > - `assemble-prod.sh` は `apps/app/next.config.ts` を削除する。**`next.config.ts` はサーバーテスト完了後(Step 6)に復元すること。** サーバー起動前に復元すると、Next.js が起動時に TypeScript インストールを試みて pnpm install が走り、`apps/app/node_modules` の symlink が上書きされ HTTP 500 となる。
-> - `pnpm deploy` はサイドエフェクトとしてワークスペースルートの `node_modules` を再作成する。ワークスペースルートの `node_modules` は削除・リネームする必要はない。Docker Dockerfile を確認すると release stage では `apps/app/node_modules/` のみ COPY し root `node_modules` は含まれないが、`pnpm deploy --prod --legacy` が workspace パッケージ(`@growi/core` 等)を `.pnpm/` ローカルストアの実体ディレクトリとしてデプロイするため、production 環境でも依存関係が正しく解決される
+> - `pnpm deploy` はサイドエフェクトとしてワークスペースルートの `node_modules` を再作成する。Docker の release stage では `apps/app/node_modules/` のみ COPY され、ワークスペースルートの `node_modules` は含まれない。`pnpm deploy --prod --legacy` が workspace パッケージ(`@growi/core` 等)を `.pnpm/` ローカルストアの実体ディレクトリとしてデプロイするため、`apps/app/node_modules/` は自己完結している。より正確な production 再現テストを行う場合は `mv node_modules node_modules.bak` でワークスペースルートを退避してからサーバーを起動し、テスト後に `mv node_modules.bak node_modules` で復元すること
 
 **Step 3 — プロダクションサーバー起動**(`apps/app/` から実行)
 

+ 1 - 1
.kiro/specs/optimise-deps-for-prod/tasks.md

@@ -141,7 +141,7 @@
     - Assert HTTP 200, response body contains `内部仕様や仕様策定中の議論の内容をメモしていく Wiki です。`, and zero `ERR_MODULE_NOT_FOUND` lines in `/tmp/server.log`
   - Kill the server after verification: `kill $(lsof -ti:3000)`
   - Restore `next.config.ts`: `git show HEAD:apps/app/next.config.ts > apps/app/next.config.ts`
-  - **Result**: HTTP 200 on `GET /`. Response body contains `内部仕様や仕様策定中の議論の内容をメモしていく Wiki です。` (2 matches). Zero `ERR_MODULE_NOT_FOUND` in server log. Task 3.1 `fslightbox-react` broken symlink in `.next/node_modules/` confirmed as harmless (SSR never accesses it).
+  - **Result**: HTTP 200 on `GET /`. Response body contains `内部仕様や仕様策定中の議論の内容をメモしていく Wiki です。` (2 matches). Zero `ERR_MODULE_NOT_FOUND` in server log. Task 3.1 `fslightbox-react` broken symlink in `.next/node_modules/` confirmed as harmless (SSR never accesses it). Re-verified after `RevisionDiff.tsx` fix with workspace-root `node_modules` renamed (`mv node_modules node_modules.bak`): still HTTP 200, zero `ERR_MODULE_NOT_FOUND`, confirming `apps/app/node_modules/` is fully self-contained via `pnpm deploy --prod --legacy`.
   - **Root-cause summary**: The spec's Phase 2–4 assumption that `ssr: false` wrapping removes packages from `.next/node_modules/` was incorrect — Turbopack still externalises them. Additionally, the initial survey of 23 packages was incomplete; 19 further transitive packages (all `@codemirror/*`, `codemirror`, `codemirror-emacs/vim/vscode-keymap`, `@lezer/highlight`, `@marp-team/*`, `@emoji-mart/react`, `reveal.js`, `pako`, `cm6-theme-basic-light`, `y-codemirror.next`) also appear in `.next/node_modules/`. All 29 missing packages were added/moved to `dependencies`. Two `assemble-prod.sh` bugs were fixed: (1) `[ ... ] && ...` under `set -e`; (2) missing rewrite of `apps/app/node_modules/` symlinks from workspace-root paths to local `.pnpm/` paths.
   - _Requirements: 5.1, 5.2, 5.3, 5.4_
 

+ 96 - 0
apps/app/.claude/rules/package-dependencies.md

@@ -0,0 +1,96 @@
+# Package Dependency Classification (Turbopack)
+
+## The Rule
+
+> Any package that appears in `apps/app/.next/node_modules/` after a production build MUST be listed under `dependencies`, not `devDependencies`.
+
+Turbopack externalises packages by generating runtime symlinks in `.next/node_modules/`. `pnpm deploy --prod` excludes `devDependencies`, so any externalised package missing from `dependencies` causes `ERR_MODULE_NOT_FOUND` in production.
+
+## How to Classify a New Package
+
+**Step 1 — Build and check:**
+
+```bash
+turbo run build --filter @growi/app
+ls apps/app/.next/node_modules/ | grep <package-name>
+```
+
+- **Found** → `dependencies`
+- **Not found** → `devDependencies` (if runtime code) or `devDependencies` (if build/test only)
+
+**Step 2 — If unsure, check the import site:**
+
+| Import pattern | Classification |
+|---|---|
+| `import foo from 'pkg'` at module level in SSR-executed code | `dependencies` |
+| `import type { Foo } from 'pkg'` only | `devDependencies` (type-erased at build) |
+| `await import('pkg')` inside `useEffect` / event handler | Check `.next/node_modules/` — may still be externalised |
+| Used only in `*.spec.ts`, build scripts, or CI | `devDependencies` |
+
+## Common Misconceptions
+
+**`dynamic({ ssr: false })` does NOT prevent Turbopack externalisation.**
+It skips HTML rendering for that component but Turbopack still externalises packages found via static import analysis inside the dynamically-loaded file.
+
+**`useEffect`-guarded `import()` does NOT guarantee devDependencies.**
+Bootstrap and i18next backends are loaded this way yet still appear in `.next/node_modules/` due to transitive imports.
+
+## Packages Confirmed as devDependencies (Verified)
+
+These were successfully removed from production artifact by eliminating their SSR import path:
+
+| Package | Technique |
+|---|---|
+| `fslightbox-react` | Replaced static import with `import()` inside `useEffect` in `LightBox.tsx` |
+| `socket.io-client` | Replaced static import with `await import()` inside `useEffect` in `admin/states/socket-io.ts` |
+| `@emoji-mart/data` | Replaced runtime import with bundled static JSON (`emoji-native-lookup.json`) |
+
+## Verifying the Production Artifact
+
+Use the lightest check that fits the situation.
+
+### Level 1 — Externalisation check (30–60 s, incremental)
+
+Just want to know if a package gets externalised by Turbopack? Only a build is needed.
+`assemble-prod.sh` and server startup are not required.
+
+```bash
+turbo run build --filter @growi/app
+ls apps/app/.next/node_modules/ | grep <package-name>
+# Found → dependencies required
+# Not found → devDependencies is safe
+```
+
+Turbopack build is incremental via cache, so subsequent runs after the first are fast.
+
+### Level 2 — Symlink integrity check (adds ~30 s)
+
+Want to confirm all `.next/node_modules/` symlinks resolve (no broken links)?
+
+```bash
+bash apps/app/bin/assemble-prod.sh
+
+cd apps/app && find .next/node_modules -maxdepth 2 -type l | while read link; do
+  linkdir=$(dirname "$link"); target=$(readlink "$link")
+  resolved=$(cd "$linkdir" 2>/dev/null && realpath -m "$target" 2>/dev/null || echo "UNRESOLVABLE")
+  { [ "$resolved" = "UNRESOLVABLE" ] || [ ! -e "$resolved" ]; } && echo "BROKEN: $link"
+done
+# Zero output (except fslightbox-react which is intentionally broken but harmless)
+
+git show HEAD:apps/app/next.config.ts > apps/app/next.config.ts
+```
+
+### Level 3 — Full server smoke test (adds ~60 s, for release gate only)
+
+```bash
+# assemble-prod.sh already run in Level 2; do NOT restore next.config.ts yet
+cd apps/app && pnpm run server > /tmp/server.log 2>&1 &
+timeout 90 bash -c 'until grep -q "Express server is listening" /tmp/server.log; do sleep 2; done'
+
+# Use / not /login — /login returns 200 even when SSR is broken
+curl -s -o /tmp/res.html -w "%{http_code}" http://localhost:3000/
+grep -c "ERR_MODULE_NOT_FOUND" /tmp/server.log  # must be 0
+
+kill $(lsof -ti:3000)
+git show HEAD:apps/app/next.config.ts > apps/app/next.config.ts
+```

+ 8 - 0
apps/app/AGENTS.md

@@ -159,3 +159,11 @@ Plus all global skills (monorepo-overview, tech-stack).
 ---
 
 For detailed patterns and examples, refer to the Skills in `.claude/skills/`.
+
+## Rules (Always Applied)
+
+The following rules in `.claude/rules/` are always applied when working in this directory:
+
+| Rule | Description |
+|------|-------------|
+| **package-dependencies** | Turbopack dependency classification — when to use `dependencies` vs `devDependencies`, verification procedure |