開発

FuseBoxでElectron開発環境をつくった話

注記:下記内容は古いバージョンを元に書かれています。結論は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などテスト環境を作っていきたいと思います。

管理人

Recent Posts

Serifのスプリングセール – アドオンが50%オフ

Affinity Photoなどレタッチ…

3週間 ago

音声がロボットのようになるときの対処

リモート会議などでたまに相手の音声がおか…

2か月 ago

Serifのブラックフライデー – 全品40%オフ V1ユーザは更にお得!

恒例のブラックフライデーセールが始まりま…

4か月 ago

[rust] rayonで書き直してみました

前回のコードを元にrayonを使った処理…

5か月 ago

[rust] async-stdで書き直してみました

前回のコードをasync-stdで書き直…

5か月 ago

[rust] mutexを使わないバージョン tokio版ディレクトリ再帰的取得

前回の続き。mutexを使わないバージョ…

5か月 ago