注記:下記内容は古いバージョンを元に書かれています。結論はgithub.com/fuse-box/fuse-box-electron-seed を真似ればよい、で変わらないですが最近のバージョンではnodeman等使わずにfuse-box単体でHMRできるようです。試してからまとめてみたいと思います。
前回アップデート時にgulpからfuse-boxに移行してホットリロードとかやりたかったのですがうまくいかなかった。今回、色々と調べてやり方がわかったのでメモしておきます。
結論から書くとgithub.com/fuse-box/fuse-box-electron-seed を真似ればよいです。
スクラッチからはじめるならコレをベースにしたほうが速いです。

考え方としてはElectronはMainプロセスとRendererプロセスに分かれているのでバンドルファイルや開発環境設定を2つに分けて考えていきます。
fuse-boxのHMR(Hot Module Reload)を利用するためにfuse-boxの開発サーバを使う必要がります。development環境の時はこの開発サーバをつかって、production環境ではHTMLを呼び出すようにします。
そのためには、まずMainプロセス側のコードを修正します。Mainが起動してその後、Rendererを呼び出す流れになっているのでdevelopmentはfuse-boxの開発サーバを経由してRendererコードを呼び出す様に修正します。productionは修正前と同じ処理をするようにします。Rendererプロセス側のコードは開発サーバを通るか通らないかだけなので基本的に変更はないです。
Mainプロセス側
const createWindow = () => {
mainWindow = new BrowserWindow({width: 900, height: 600})
if(isProduction) {
mainWindow.loadURL(url.format({
pathname: path.join(electron.app.getAppPath(), 'released', 'index.html'),
protocol: 'file:',
slashes: true
}))
} else {
mainWindow.loadURL('http://localhost:4444/released');
// Open the DevTools
mainWindow.webContents.openDevTools()
}
}
fuse-boxの設定ファイルはfuse.jsに入れておくのがデフォルトですがこれもそれぞれMain、Renderer設定を分ける必要があります。
Renderer設定のdevelopment環境では開発サーバ経由でコードを読み込ませる設定が必要です。この場合だと、http://localhost:4444/released にアクセスしにくるのでproductionと同じレスポンスを返すように設定を追加すればよいです。fuse-boxの開発サーバはexpress.jsを使っているようなのでfuse.jsでレスポンス設定を追加します。
fuse.js 開発サーバ設定
if (!isProduction) {
// Configure development server
fuse.dev({ root: false, port: 4444 }, server => {
const dist = path.join(__dirname, "released");
const app = server.httpServer.app;
app.use("/released/", express.static(dist));
app.get("*", function(req, res) {
res.sendFile(path.join(dist, "index.html"));
});
});
}
fuse.jsのMainプロセス側設定はトランスパイルした後、Electronが立ち上がるように設定を入れてあげます。こちらはHMRはできないのでnodemonでファイル監視して変更があった場合はElectron再起動する設定となっています。
fuse.js Mainプロセス側設定
return fuse.run().then(() => {
const nodemoncli = path.join(__dirname, 'node_modules', 'nodemon', 'bin', 'nodemon.js')
const elecli = path.join(__dirname, 'node_modules', 'electron', 'cli.js')
const child = spawn(nodemoncli, ['--watch', path.join(__dirname, 'released', 'main.js'), '--exec', elecli, __dirname ], {
// env: Object.assign(process.env, { NODE_ENV: "development" }),
});
child.stdout.on('data', function(data) {
console.log(data.toString());
});
child.stderr.on('data', function(data) {
console.error(data.toString());
});
});
詳しくはgithubのコードを参照してください。
その他、fuse-boxでハマった点としてはfuse.jsでtarget指定する際に
Rendererプロセス側はtarget : “electron”, Mainプロセス側はtarget : “server”にする必要があります。Mainプロセス側は仕組みを見ていくとわかりますがRenderer側はbrowserでも良さそうですがなにか特殊な処理がはいっているのかもしれません。
また、bundleの設定ですが通常のfuse-boxだと.instructions(`> ts/main.ts`);と書いちゃいますがコレだとエラーが出るので.instructions(`> [ts/main.ts]`);と自分のコードだけバンドルするようにします。
Bundle設定
const app = fuse
.bundle('main')
// NG .instructions(`> ts/main.ts`);
.instructions(`> [ts/main.ts]`);
環境変数NODE_ENVを渡してもfuse.jsは受け取ってくれないのでEnvPluginでfuse.jsの中で設定して上げる必要があります。この辺り、Windows環境でちゃんと動作するのかよくわかりませんが動くようであればクロスプラットフォームを意識した作りになっているのでcross-envとかいらなくなるかもしれません。
また、挙動を正確に確認していないですがtsconfig.jsonが各バンドルファイルのフォルダにない場合、自動的に作成されるようです。useTypescriptCompilerをtrueにしておくとプロジェクトフォルダのtsconfig.jsonを使うようです。
次はspectronなどテスト環境を作っていきたいと思います。