electron-wrapper について
既存の Bun Web アプリを Electron デスクトップアプリとして構成するための実装ガイドです。メインプロセス、プリロードブリッジ、Web アプリの適応、ビルドとリリースを扱い、サーバーロジックを保ちながらデスクトップ機能を追加したい開発者向けです。
主な機能
- 01Electron と Bun の二つのランタイム構成
- 02Bun サーバー起動とローカルページ読み込み
- 03プリロードブリッジと IPC 管理
- 04Electron 環境向けの Web アプリ適応
- 05ビルド、署名、CI リリースの手順
活用シーン
- 01既存の Bun Web サーバーをデスクトップアプリ化
- 02ウィンドウ管理やネイティブメニューの追加
- 03Electron のビルドとリリース手順の設定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ brianlovin/ agent-config --skill electron-wrapperインストール後、Claude Code で/electron-wrapperを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/build-and-distribute.mdmain-process.mdpitfalls.mdproject-setup.mdweb-adaptation.md詳細
Bun Web アプリ向け Electron ラッパー
このスキルでは、既存の Bun ウェブサーバーを Electron を使ってネイティブデスクトップアプリにラップする方法を説明します。主要な統合上の課題をすべて解決した、実績のある実装に基づいています。
アーキテクチャ
「Electron はクローム、Bun はサーバー」 — 2 つのランタイムが連携します。
- Electron/Node.js はウィンドウ管理、ネイティブメニュー、自動更新、IPC を処理します
- Bun はすべてのアプリケーションロジックを含む実際のウェブサーバーを実行します
Electron のメインプロセスがバンドル済みの Bun バイナリを起動してサーバーを実行し、その後 http://localhost:{port} を BrowserWindow に読み込みます。ウェブアプリは Electron 内で動作していることを認識する必要も、気にする必要もありません。ネイティブ機能向けのオプションの window.electronAPI ブリッジを備えた、通常のウェブページとして動作します。
このアーキテクチャには次の利点があります。
- サーバーコードを一切変更する必要がありません(標準的な Bun HTTP サーバーのままです)
- ウェブアプリはブラウザーでも Electron でも同じように動作します
- Electron が処理するのは、ブラウザーでは対応できない機能だけです。ウィンドウのクローム、システムトレイ、自動更新、ファイルシステムへのアクセスなどです
node_modulesは 2 つに分かれています。Electron は npm を、ウェブアプリは Bun を使用します
フェーズ 1:プロジェクトのセットアップ
既存の Bun ウェブアプリと並ぶ場所に Electron サブプロジェクトを作成します。
作成するもの:
- 独自の
package.json(Bun ではなく npm を使用)、2 つの tsconfig(メインプロセス用の ESM、プリロード用の CJS)、electron-builder.yml、macOS のエンタイトルメントを含むelectron/ディレクトリ - サーバーをバンドルする
scripts/build-server.ts - プラットフォーム固有の Bun バイナリをダウンロードする
scripts/download-bun.ts - 親プロジェクトの変更:新しいスクリプト、tsconfig の除外設定、.gitignore のエントリー
リファレンス: project-setup.md
フェーズ 2:メインプロセス
エントリーポイント、サーバーの起動、ウィンドウ管理、自動更新、プリロードブリッジを担う Electron メインプロセスを構築します。
作成するファイル:
| ファイル | 目的 |
|---|---|
electron/src/main/index.ts | アプリのライフサイクル、開発/本番モード、単一インスタンスのロック、IPC ハンドラー |
electron/src/main/bun-server.ts | バンドル済み Bun の起動、ポート選択、ヘルスチェックのポーリング、環境変数の注入 |
electron/src/main/window.ts | BrowserWindow の設定、ウィンドウ境界の永続化、セキュリティ設定、ナビゲーションガード |
electron/src/main/updater.ts | electron-updater のセットアップ、レンダラーへのイベント転送 |
electron/src/preload/index.ts | invoke/on パターンと購読解除をサポートする contextBridge API |
主な判断事項:
- 開発モードでは
ELECTRON_DEV_URL環境変数を使って外部の開発サーバーに接続します(内部で Bun を起動しません) autoDownload: false— 更新をダウンロードするタイミングはユーザーが選べるようにします- プリロードでは特定のメソッドのみを公開し、生の
ipcRendererは公開しません electron-storeを使ってウィンドウ境界を永続化します
リファレンス: main-process.md
フェーズ 3:ウェブアプリの適応
既存のウェブアプリを、単独のウェブアプリとして完全に機能する状態を保ちながら、Electron 環境を検出して対応できるようにします。
ウェブアプリへの変更:
| 変更 | 詳細 |
|---|---|
| Electron 検出ユーティリティ | isElectron()、getElectronPlatform()、isMacElectron()、applyElectronDocumentAttributes() |
| 型宣言 | すべてのプロパティをオプションにした window.electronAPI |
| CSS のドラッグ領域 | .app-window-drag/.app-window-no-drag クラス。インタラクティブ要素は自動的に除外 |
| トラフィックライトの余白 | --electron-traffic-left CSS 変数(macOS では 72px、それ以外では 0px) |
| ストレージパス | データディレクトリ用の環境変数。設定されていない場合は CWD にフォールバック |
| 静的アセットの配信 | 本番モードで静的ディレクトリを指定する環境変数 |
| 自動更新フック | ダウンロード/インストール制御を備えた useElectronUpdater() React フック |
| 更新通知 | 利用可能 → ダウンロード中 → 準備完了の状態を表示するピルコンポーネント |
| 機能の制御 | Electron 内ではデモモードとホスト型機能を無効化 |
リファレンス: web-adaptation.md
フェーズ 4:ビルドと配布
すべてをバンドルし、CI/CD をセットアップして、コード署名に対応します。
ビルドパイプライン:
- Web アプリをビルドします(
bun run build) - サーバーを単一ファイルにバンドルします(
Bun.build()→resources/server/index.js) - プラットフォーム固有の Bun バイナリをダウンロードします →
resources/bun/{platform}-{arch}/ - Electron の TypeScript をコンパイルします(2 回に分けて実行:main は ESM、preload は CJS)
- electron-builder が
extraResourcesを使ってすべてをパッケージ化します
CI/CD:
- バージョンタグ(例:
v*、clippy-v*)によって GitHub Actions が起動します - マトリックスビルド:
macos-14で macOS arm64/x64、windows-latestで Windows x64 - ビルドステップでは
--publish neverを指定し、別の publish ジョブで GitHub のドラフトリリースを作成します - CI で Apple の証明書をインポートし、公証を行います
コード署名:
- macOS:Developer ID Application 証明書を base64 形式の .p12 としてエクスポートします
- Apple ID とアプリ固有のパスワードを使って公証します
- 必要な GitHub シークレットは 5 つです:
APPLE_CERTIFICATE、APPLE_CERTIFICATE_PASSWORD、APPLE_ID、APPLE_PASSWORD、APPLE_TEAM_ID
アイコン:
- macOS:ソース PNG から
sipsとiconutilを使って.icnsを作成します - Windows:
png-to-iconpm パッケージを使って.icoを作成します
参考: ビルドと配布
リリースの作成
リリース用の成果物をローカルでビルドしないでください。 署名証明書と公証用の認証情報は CI にあります。ローカルでビルドしたアプリには署名がなく、macOS Gatekeeper によってブロックされます。
リリースの手順:
electron/package.jsonの バージョンを更新し、コミットして main にマージします- CI ワークフローで指定されているタグのパターンを確認します:
grep -A2 'tags:' .github/workflows/*.yml - main にマージされたコミットにタグを付けます:
git tag <pattern><version> origin/main git push origin <pattern><version> - CI を監視します:
gh run list --workflow=<workflow>.yml --limit=1 - GitHub でドラフトリリースを確認して公開します
よくある間違い:
- ローカルで
electron-builder --publish alwaysを実行する — 公証されません - ローカルの成果物を使って
gh release createを実行する — 署名されていません - バージョン更新のマージ前にタグを付ける — ビルドのバージョンが誤ります
origin/mainではなく、機能ブランチにタグを付ける
詳細は pitfalls.md §13 を参照してください。
重大な落とし穴
クイックリファレンスです。症状とコード例を含む詳細は pitfalls.md を参照してください。
| # | 落とし穴 | 1 行での修正方法 |
|---|---|---|
| 1 | ESM/CJS の競合 | "type": "module" と、CJS パッケージ用のデフォルトインポートパターンを使用します |
| 2 | Preload は CJS である必要があります | "module": "CommonJS" を指定した別の tsconfig を用意します |
| 3 | __dirname が利用できません | fileURLToPath(import.meta.url) のポリフィルを使用します |
| 4 | 開発モードで MIME エラーが発生します | ELECTRON_DEV_URL を使って外部の開発サーバーに接続します |
| 5 | Bun のバージョンが一致しません | ダウンロードスクリプトでバージョンを固定し、開発環境のバージョンと一致させます |
| 6 | nvm の PATH に関する問題 | 起動するプロセスには bash -lc を使用します |
| 7 | ストレージのパスが誤っています | 環境変数と app.getPath("userData") を使用します |
| 8 | 開いたときに白く点滅します | show: false と ready-to-show を使い、backgroundColor を暗色に設定します |
| 11 | ${platform} と process.platform は異なります | Bun の extraResources を darwin-/win32- のプレフィックスとともに mac:/win: セクションに配置します |
| 12 | Bun のワークスペースによって依存関係がルートに集約されます | esbuild と createRequire バナーで main をバンドルするか、electron ディレクトリで npm を使用します |
| 13 | ローカルビルドは公証されません | リリースは必ず CI タグ経由で行い、ローカルで electron-builder --publish を実行しないでください |
開発ワークフロー
electron:dev コマンドで開発環境全体を起動します:
npm run dev
├── concurrently
│ ├── dev:web → cd .. && bun run dev (Bun dev server with HMR)
│ └── dev:electron
│ ├── wait-on http://localhost:3005 (wait for dev server)
│ ├── npm run build (compile TS)
│ └── ELECTRON_DEV_URL=... electron . (launch Electron)
- Web 開発サーバーは HMR で動作し、変更は即座に反映されます
- Electron は独自の Bun を起動せず、開発サーバーに接続します
- Preload と main プロセスの変更を反映するには、
electron:devを再起動する必要があります - Web アプリの変更は自動的にホットリロードされます
本番に近い動作をローカルでテストするには:
bun run electron:pack # builds everything, packages without installer
# Output in electron/release/
カスタマイズチェックリスト
新しいプロジェクトに合わせて変更する場合は、次のプロジェクト固有の値を更新してください。
-
electron-builder.ymlのアプリ名(productName、appId) -
window.tsのウィンドウタイトル -
dev:electronスクリプトとdevスクリプトの開発サーバーポート - 環境変数名(例:
APP_DATA_DIR、APP_STATIC_DIR) - アプリのテーマに合わせたウィンドウ設定の
backgroundColor -
electron-builder.ymlの mac セクションにあるcategory -
electron/package.jsonのリポジトリ URL -
download-bun.tsの Bun バージョン -
electron/assets/のアイコンアセット