Electron を試す 8 - electron-prebuilt のパッケージ名変更と Browserify
electron-packager の更新履歴をみていたら v7.5.0 で Add support for the new electron
package name by zeke という PR に対応していた。内容を読むと electron-prebuilt のパッケージ名が electron に変更されたようだ。electron-prebuilt の README にも注記されている。
というわけで、このシリーズで作成したサンプルも名所変更に対応することにしたのだが Browserify 絡みで問題が起きたため、その内容と対策を記録しておく。
electron-prebuilt のパッケージ名変更
electron-prebuilt は Electron アプリの実行環境になる。従来、これをインストールするには
$ npm i -D electron-prebuilt
としていたのだが v1.3.1 からパッケージ名が electron
に変更された。以降は
$ npm i -D electron
とする。と、ここまでなら名前が短く分かりやすいので歓迎したいのだが...
Browserify のビルド問題と対策
Electron 本体に依存する機能は v1.0 から electron
というパッケージ名で提供される。この辺の話は Electron を試す 7 - Electron v1.0 対応でも触れた。v1.0 より前は機能単位でパッケージ名を分けていたのだけど electron
配下へ統合。これらを参照する場合、
const Electron = require( 'electron' );
とするか ES2015 Modules であれば
import Electron from 'electron';
のようになるだろう。
通常はこれでよいのだが Browserify を利用して require/import
を解決している場合は問題が起きる。electron-prebuilt のパッケージ名が electron
に変更されたことで electron
に対する require/import
が electron-prebuilt の実体を参照しようとしておかしくなるのだ。Browserify によるビルド結果は
module.exports = path.join(__dirname, fs.readFileSync(path.join(__dirname, 'path.txt'), 'utf-8'))
のようになる。そして electron-prebuilt からアプリを起動すると以下の実行時エラーが発生。
Error: ENOENT: no such file or directory, open '.../electron-starter/src/path.txt'
at Error (native)
at Object.fs.openSync (fs.js:640:18)
at Object.module.(anonymous function) [as openSync] (ELECTRON_ASAR.js:167:20)
at Object.fs.readFileSync (fs.js:508:33)
at Object.fs.readFileSync (ELECTRON_ASAR.js:500:29)
at Object.global.1.fs (.../electron-starter/src/main.js:5:42)
at s (.../electron-starter/src/main.js:1:333)
at .../electron-starter/src/main.js:1:384
at Object.global.5.../common/Constants.js (.../electron-starter/src/main.js:231:17)
at s (.../electron-starter/src/main.js:1:333)
この問題を回避する方法はふたつ。
- electron-prebuilt を旧名称で
npm install
する - electron-prebuilt に対する
require/import
参照を Browserify の対象外とする
方法 1 は electron-prebuilt 的に deprecated とされている。将来、旧名称が廃止される可能性もあって危険だ。よって正攻法の 2 を採用。substack/node-browserify の Usage を読むと --exclude
オプションにパッケージ名を指定することで bundle (参照解決) の対象外となるようだ。
--exclude, -u Omit a file from the output bundle. Files can be globs.
というわけで npm-scripts の
{
"scripts": {
"build:js-main": "browserify -t [ babelify ] ./src/js/main/Main.js --im --no-detect-globals --node -d | exorcist ./src/main.js.map > ./src/main.js"
}
}
となっていたものを
{
"scripts": {
"build:js-main": "browserify -t [ babelify ] ./src/js/main/Main.js --exclude electron --im --no-detect-globals --node -d | exorcist ./src/main.js.map > ./src/main.js"
}
}
に修正。ビルドしたところアプリが正常に起動 & 動作することを確認できた。
Renderer プロセスについて
Renderer プロセスについて。こちらで electron
を参照する場合、
const Electron = window.require( 'electron' );
のように window
経由で require
を利用しているため Browserify の対象外なのだが、念のためこちらをビルドするときも --exclude electron
している。なぜ Main/Renderer で参照方法を分けているのかは、
- Main
electron
の他にもfs
など dependency にないパッケージを参照する可能性が高い- そのため
--im
オプションで dependency に見つからない参照を無視している - 今回の問題は
electron
が見つかるようになってしまったことで発生した
- Renderer
- dependency に存在するパッケージのみで構成
- Web ブラウザ用の JavaScript ビルドと同じ思想で参照解決
--im
オプションを利用しないのでelectron
を直に require できない- よって window.require 経由で参照して Browserify の介在を回避
という理由から。この辺の話は Electron を試す - 開発環境の構築でも触れたが、今回の問題にも関わっているので改めて書き出してみた。ちなみに Renderer が利用する electron
由来の機能も ipRenderer に限定している。プロセス間通信は Web アプリにおける Client-Server Model を踏襲し、
- Clinet (Renderer) が必要に応じて Server (Main) へ Request
ipcRenderer
- Server は Request** 結果を Client に Responce
sender.send
- Server は必要に応じて Client に Push Notification
ipcMain
という感じで設計している。akabekobeko/examples-electron には対応を反映済み。各プロジェクトの npm-scripts で --exclude electron
を削除すれば今回の問題と壊れたビルド結果を確認できる。