アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

Electron を試す 8 - electron-prebuilt のパッケージ名変更と Browserify

electron-packager の更新履歴をみていたら v7.5.0Add 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)

この問題を回避する方法はふたつ。

  1. electron-prebuilt を旧名称で npm install する
  2. 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 を削除すれば今回の問題と壊れたビルド結果を確認できる。

Copyright © 2009 - 2023 akabeko.me All Rights Reserved.