1. 概要・目的

1.1 概要表

項目内容
案件 IDMAS-359
カテゴリ🔧 Infra (フロントエンドビルド最適化)
PhaseP2
優先度★★★ (MAS-356 Tremor 採用・MAS-357 Firebase 移行の前提条件)
所要時間Phase 1: ~2 日 / Phase 2: ~3 日 (MAS-357 と並行)
対象ファイル (変更)webapp_client/vite.config.ts
webapp_client/src/CockpitApp.tsx
webapp_client/src/cockpit-main.tsx
webapp_client/src/multiyear-main.tsx
webapp_client/scripts/sync-engines.mjs
前提案件MAS-356 Tremor + v0.dev 採用 (問題の直接的な契機)
後続案件・連携余地MAS-357 Firebase Hosting 移行 (Phase 2 の実施条件) / MAS-358 Cockpit 左サイドバー (バンドル圧迫を緩和してから実装)

1.2 背景・課題

発生経緯

MAS-356 で Tremor (@tremor/react) + Tailwind CSS v4 を導入した結果、financial_cockpit.html のバンドルサイズが 364 KB → 1,200 KB (+836 KB) に増大した。

ファイルMAS-356 前MAS-356 後増加
financial_cockpit.html364 KB1,200 KB+836 KB
multiyear_cockpit.html290 KB322 KB+32 KB
sidebar_spa_shell.html179 KB179 KB0 KB

根本原因

GAS HtmlService の CSP 制約により vite-plugin-singlefile で全 JS/CSS を 1 HTML にインライン化している。inlineDynamicImports: true の強制により Vite の標準コード分割 (manualChunks / dynamic import) が無効化されており、Tremor の依存ライブラリ (shadcn/ui + tailwind-merge) が全量バンドルに入る。

financial_cockpit.html の内訳 (推定)
├── React 18 + React DOM          ~130 KB
├── Tailwind CSS (purged)         ~30 KB
├── Tremor + shadcn/ui + deps     ~600 KB  ← 問題の主因
├── chart.js + d3-sankey          ~150 KB
├── GAS エンジン ×8              ~100 KB
└── アプリコード (13 panels)     ~190 KB

なぜ「ファイル分割」か

パネルが増えるほど cockpit は肥大化し続ける(MAS-358 サイドバー追加でさらに増加見込み)。Tremor を使い続けるためにはバンドルを複数ファイルに分割して総量を分散させる必要がある。

1.3 目標サイズ

フェーズcockpitmultiyearsidebar手段
現状1,200 KB322 KB179 KB
Phase 1 後600 KB 以下322 KB179 KBtree-shaking 最適化 + 未使用削除
Phase 2 後200 KB 以下150 KB 以下100 KB 以下Firebase + 本格コード分割

2. Phase 1 — GAS 上での最適化 (singlefile 継続)

2-A. Tremor の tree-shaking 効果測定

現在 Tremor は named import を使用しており、原理上 tree-shaking が効く。ただし Tremor v3 の内部依存 (Recharts など) が side-effect で引き込まれていないか確認する。

# ビルド前後のサイズ比較
npm run build:cockpit
ls -la ../templates/financial_cockpit.html

# Vite のバンドル解析
VITE_BUILD_TARGET=cockpit npx vite build --reportCompressedSize 2>&1 | grep KB

確認ポイント: Recharts (~300 KB) が含まれていないか。Badge / Callout / Card / Metric / ProgressBar は非チャートコンポーネントのため、Recharts が除外されればそれだけで ~300 KB 削減できる。

2-B. 未使用コンポーネントの削除

CockpitApp.tsx でインポートされているが現在の画面に表示されていないコンポーネントを確認・削除する。

コンポーネント状況対処
InsightPanel.tsxMAS-057 v2.5 で AiSuggestPanel に統合済み → 不使用の可能性import 削除対象確認
ThreeSectionTable.tsx同上、削除済みの可能性import 削除対象確認
FiveYearChart.tsxv2.5 で削除済みの可能性import 削除対象確認
# CockpitApp.tsx で import されているが JSX に登場しないコンポーネントを検出
grep -n "import {" src/CockpitApp.tsx
grep -n "<InsightPanel\|<ThreeSectionTable\|<FiveYearChart" src/CockpitApp.tsx

2-C. エンジンファイルのエントリ分離

cockpit-main.tsx に 8 つのエンジン副作用 import が集中しているが、一部は multiyear のみで必要なものがある。エントリ別に必要最小限のエンジンのみ import する。

エンジンファイルcockpitmultiyear備考
442_social_insurance_tier_engine.js両方必要
443_corporate_housing_optimizer.js×cockpit のみ
444_per_diem_policy_engine.js×cockpit のみ
445_required_revenue_solver.js両方必要
447_capital_allocation_engine.js×cockpit のみ (MAS-355)
449_dividend_mix_optimizer.js×cockpit のみ
451_multiyear_planner.js両方必要
454_compensation_strategy_engine.js×cockpit のみ

multiyear-main.tsx から cockpit 専用エンジンを削除することで multiyear のサイズ削減が見込める。

2-D. Phase 1 判定

npm run build:cockpit
ls -la ../templates/financial_cockpit.html
結果判断
600 KB 以下✅ Phase 1 完了。Firebase 移行まで運用継続
600〜900 KB⚠️ 一定の改善。Phase 2 (Firebase) を優先スケジューリング
900 KB 超❌ tree-shaking が効いていない。Tremor の import 方法を再検討、または Tremor を CapitalAllocationPanel のみに限定

3. Phase 2 — Firebase Hosting + 本格コード分割

前提: MAS-357 Phase 1 (Firebase Hosting への移行) 完了後に実施。vite-plugin-singlefile を削除し、Vite 標準のコード分割に戻す。

3-A. vite.config.ts の変更

// Before (GAS singlefile)
plugins: [react(), tailwindcss(), viteSingleFile()],
build: {
  rollupOptions: {
    output: { inlineDynamicImports: true },
  },
  assetsInlineLimit: 100_000_000,
  cssCodeSplit: false,
}

// After (Firebase 標準ビルド)
plugins: [react(), tailwindcss()],
build: {
  rollupOptions: {
    output: {
      manualChunks: {
        'vendor-react':   ['react', 'react-dom'],
        'vendor-tremor':  ['@tremor/react'],
        'vendor-charts':  ['chart.js', 'd3-sankey'],
        'engines':        [
          './src/engines/442_social_insurance_tier_engine.js',
          './src/engines/443_corporate_housing_optimizer.js',
          // ... 他エンジン
        ],
      },
    },
  },
  cssCodeSplit: true,
}

3-B. React.lazy() によるパネル遅延ロード

// Before
import { CapitalAllocationPanel } from './cockpit/CapitalAllocationPanel';
import { SoloFinancialStatementsPanel } from './cockpit/SoloFinancialStatementsPanel';

// After — スクロールで見えるまでロードを遅延
const CapitalAllocationPanel = React.lazy(
  () => import('./cockpit/CapitalAllocationPanel')
);
const SoloFinancialStatementsPanel = React.lazy(
  () => import('./cockpit/SoloFinancialStatementsPanel')
);

// JSX 側
<Suspense fallback={<div className="panel-skeleton" />}>
  <CapitalAllocationPanel ... />
</Suspense>

優先度の高い遅延ロード候補: 重量パネルから適用する。

パネル推定サイズlazy 候補
CapitalAllocationPanel大 (Tremor 使用)✅ 最優先
CompensationStrategyPanel大 (Tremor 使用)
SoloFinancialStatementsPanel
ApproachComparisonPanel
ScenarioBar / GuardrailBanners小 (初期表示必須)

3-C. エントリポイント別ビルドの維持

Phase 2 以降も 3 エントリ構造は維持する。Firebase Hosting では各 HTML が CDN から配信され、共有 chunk (vendor-react.js など) はブラウザキャッシュに乗る。

Firebase Hosting
├── financial_cockpit.html      ~50 KB (HTML シェル + 初期 chunk)
├── multiyear_cockpit.html      ~50 KB
├── sidebar_spa_shell.html      ~50 KB
└── assets/
    ├── vendor-react.xxxx.js    ~130 KB  ← 3 ページ共通キャッシュ
    ├── vendor-tremor.xxxx.js   ~600 KB  ← cockpit 初回のみ、以降キャッシュ
    ├── vendor-charts.xxxx.js   ~150 KB
    ├── engines.xxxx.js         ~100 KB
    ├── cockpit.xxxx.js         ~100 KB
    └── cockpit.xxxx.css        ~30 KB

2 回目以降のアクセスでは vendor-react / vendor-tremor はキャッシュ済みになり、実効ダウンロードは ~100 KB 程度に収まる。


4. システムアーキテクチャ (ファイル構成・変更箇所)

4.1 Phase 1 変更ファイル

ファイル変更内容
webapp_client/src/CockpitApp.tsx未使用 import の削除
webapp_client/src/cockpit-main.tsxcockpit 専用エンジンのみに整理
webapp_client/src/multiyear-main.tsxmultiyear 専用エンジンのみに整理
webapp_client/scripts/sync-engines.mjsエントリ別エンジンリストを管理できるよう整理

4.2 Phase 2 変更ファイル (MAS-357 完了後)

ファイル変更内容
webapp_client/vite.config.tsviteSingleFile() 削除・manualChunks 追加・cssCodeSplit: true
webapp_client/src/CockpitApp.tsxReact.lazy() + Suspense 適用
webapp_client/src/MultiyearApp.tsx同上
webapp_client/package.jsonvite-plugin-singlefile 削除
300_ui/302_spa_bridge.jsHtmlService.createHtmlOutputFromFile → Firebase URL 参照に変更 (MAS-357 と共同)

5. エッジケース・異常系

#条件検知方法期待される挙動
1Phase 1 後も 900 KB 超ls -la templates/financial_cockpit.htmlTremor を CapitalAllocationPanel のみに限定し CompensationStrategyPanel は既存 CSS に戻す
2React.lazy() 適用後に Suspense fallback が長く表示されるブラウザ DevTools の Network タブchunk サイズを確認し、Suspense の boundary 粒度を調整
3manualChunks で循環依存が発生Vite ビルドエラー Circular dependency依存グラフを確認し chunk 境界を調整
4Firebase 移行後に GAS 側の HtmlService 呼び出しが残存GAS 実行時エラーMAS-357 と連携してルーティングを Firebase URL に切り替え
5vendor-tremor.js が CDN キャッシュされず毎回 600 KB ダウンロードNetwork タブで Cache-Control ヘッダー確認Firebase Hosting の firebase.jsonCache-Control: max-age=31536000 を設定

6. テスト要件

Phase 1 検証

# 1. ビルド成功確認
npm run build:cockpit && npm run build:multiyear && npm run build:sidebar

# 2. サイズ確認
ls -la ../templates/*.html

# 3. 既存機能の動作確認 (dev サーバーで)
VITE_BUILD_TARGET=cockpit npm run dev
# → 全パネルが正常表示されること
# → CapitalAllocationPanel の 3 バケツ・ROE・Rule of 40 が表示されること

Phase 2 検証

# 1. チャンク分割確認
npm run build:cockpit 2>&1 | grep "dist/"
# → vendor-react / vendor-tremor / engines チャンクが生成されること

# 2. Firebase ローカルエミュレーター確認
firebase emulators:start --only hosting
# → 2 回目アクセス時に tremor chunk がキャッシュされること (Network: from cache)

7. 推奨実行モデル

ステップモデル理由
Phase 1-A tree-shaking 計測Haikuコマンド実行・ファイルサイズ確認のみ
Phase 1-B 未使用 import 削除SonnetJSX 内の実際の使用箇所を横断確認する判断が必要
Phase 1-C エンジン分離Sonnetcockpit / multiyear 両エントリの依存関係を追う必要あり
Phase 2 vite.config 変更SonnetmanualChunks の設計判断
Phase 2 React.lazy() 適用SonnetSuspense boundary の粒度判断

8. 受け入れ条件

Phase 1

  • financial_cockpit.html が 600 KB 以下
  • 既存パネルの表示が変化しない(CapitalAllocationPanel・CompensationStrategyPanel 含む)
  • multiyear_cockpit.html が 300 KB 以下

Phase 2

  • financial_cockpit.html (HTML シェル) が 100 KB 以下
  • vendor-react.js が cockpit / multiyear / sidebar 3 エントリで共有される
  • 2 回目アクセス時のネットワーク転送量が 200 KB 以下 (キャッシュ効果確認)
  • React.lazy() 適用パネルが Suspense fallback を経て正常表示される

9. 依存関係

  • MAS-356: Tremor 導入がバンドル増大の起点。MAS-359 Phase 1 が完了してから MAS-356 Phase 3 (v0.dev 活用) を進めると効率的
  • MAS-357: Phase 2 の前提条件。Firebase Hosting への移行が完了してから Phase 2 に着手する
  • MAS-358: Cockpit ナビサイドバー追加はさらにバンドルを圧迫するため、Phase 1 完了後に実装推奨

10. 変更履歴

日付バージョン変更内容
2026-05-03v1.0初版起票。MAS-356 Tremor 導入後の 1,200 KB 問題を受け、Phase 1 (GAS 上の最適化) / Phase 2 (Firebase + 本格コード分割) の 2 段階解決策を設計