アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

Electron を試す 7 - Electron v1.0 対応

ねんがんの Electron 1.0 をてにいれたぞ!というわけで akabekobeko/examples-electron のプロジェクトを対応させてみた。その過程で得られた知見を記録しておく。

  • 2016/5/16 補足 v1.0 からわずか数日で v1.1 がリリースされた。この記事に関係する変更点は Devtron の読み込み改善ぐらいである。examples-electron のプロジェクトは既に v1.1 を参照するように修正済み。

モジュール参照の変更

従来の Electron ではアプリケーション情報となる app やシェル操作に必要な shell などは直に参照していた。

import App from 'app';

console.log( App.getName() );

これらは非推奨モジュールとなっており v1.0 でまとめて削除された。詳しくは Remove deprecated apis by kevinsawicki - Pull Request #5373 - electron/electron を参照のこと。代替 API は electron モジュール配下へ移動されている。ipcMainipcRenderer と同じ構成へ統一された。

よって

import Electron from 'electron';

console.log( Electron.app.getName() );

のように修正する必要あり。Main プロセスで非推奨モジュールを import/require した場合は Electron 起動時にエラーとなるので、すぐに修正すること。この変更により Electron の提供するモジュールと Node や外部 npm 由来のものが区別しやすくなった。今後 Electron 関連モジュールが増えたとしても名前競合を気にしなくて済むのはよいことだ。

メニュー対応における混乱

モジュール参照の変更は単純な置換でよいのだけどメニュー関連で混乱した。既存コードでは

import Menu from 'menu';

const menu = Menu.buildFromTemplate( templates );
Menu.setApplicationMenu( menu );

のような感じで処理していたので appshell のように

import Electron from 'electron';

const menu = Electron.menu.buildFromTemplate( templates );
Electron.menu.setApplicationMenu( menu );

と修正したら menu プロパティの参照が実行時エラー。そこで electron/menu.md を読んでみたら掲載されている 2 種類のサンプルは共に electron.remote 経由で処理していた。ここで

  • Main プロセスだと実行時エラーになること
  • サンプルの Menu 参照は electron.remote 経由のものだけ紹介されていること

から Main プロセス版が廃止されたものと勘違い。そんな感じで困惑していることを Twitter でつぶやいていたら以下のような指摘が。

そして electron-api-demos/application-menu.js をみると Menu は electron.menu ではなく electron.Menu だった。

const electron = require('electron')
const BrowserWindow = electron.BrowserWindow
const Menu = electron.Menu
const app = electron.app

// ... 中略

app.on('ready', function () {
  const menu = Menu.buildFromTemplate(template)
  Menu.setApplicationMenu(menu)
})

electron/docs にもちゃんと Modules for the Main Process 欄に MenuMenuItem がある。これらや BrowserWindow のように new でインスタンス生成可能なものは camelCase でなく PascalCase で命名されているようだ。buildFromTemplate などは Menu の static method として提供されている。よって問題の処理は

import Electron from 'electron';

const menu = Electron.Menu.buildFromTemplate( templates );
Electron.Menu.setApplicationMenu( menu );

というように menu ではなく Menu で参照すればよかった。ドキュメントのサンプルについては 2016/5/11 夜の話であり、いずれ Main プロセス版が追加されるなどの訂正がおこなわれると思う。

この件における私の反省点。ドキュメントを調べることも大事だが、実行可能なデモがあるならそちらも当たるべきだった。それと自分が API リファレンスを書くときはサンプルとなるコードが単体完結しているか、それが難しければ文章で前提環境について言及するように心がけたい。

Devtron

Electron v1.0 リリースにあわせてアプリ開発の支援ツール Devtron も公開されていたので試す。提供形式は npm。サイズもさほど大きくないため Electron アプリのプロジェクト ローカルにインストールするとよいだろう。

$ npm i -D devtron

npm を追加した後にアプリを起動して Developer Tools を開く。そして Console タブのプロンプトでインストール用コマンドを入力してから Enter キーを押すと

> require('devtron').install()
Installing Devtron from ./node_modules/devtron  

というようになる。これで Devtron が有効になるはずなのだけどタブがない。バグか?と思って Electron のリポジトリをチェックしたら Release electron v1.0.1 があった。そして Changelog には

Fix devtools extension not loading.

とある。なるほどね。というわけで v1.0.0 で再試行してみたのだけど、それでもダメだ。devtron/README.md には

You can reload the extension by closing and reopening the dev tools.

とあるのに Developer Tools を開き直しても反映されない。もしかすると BrowserWindow.toggleDevTools だからなのかも。これはトグル形式の切り替えであり再起動ではなさそうなので。

最後の手段として Developer Tools の General にある Restore defaults and reload ボタンを押してみた。その後に Developr Tools を再表示すると Devtron タブが追加されている!よかった!

実際にこのツールが有効なことを確認するため IPC Monitor を利用してみる。Devtron タブを開いて左のメニューから IPC を選択してパネルを表示。パネルの右上にある Record ボタンが押されている状態で IPC の発生する操作をすると通信内容が出力された。

Devtron

従来 Main プロセスで IPC の絡む問題調査には console.log を利用していたけれど Devtron ならばコードに影響を与えず通信を監視可能。なので今後はこちらを利用することにした。

  • 2016/5/16 追記 v1.0.2 の更新をみると Developer Tools の Extension 読み込みが改善されたらしく続けて v1.1.0 もリリースされていた。そこで改めて動作確認したところ Console から Devtron を有効化するとタブに即時反映され、かつアプリを再起動してから Developrt Tools を表示しても Devtron タブは維持されていた。つまり以下の問題は修正済みといえる。

問題点がひとつ。

Devtron を有効にした後にアプリを終了して再起動後、改めて Developer Tools を表示すると Devtron タブが消えていた。え?なんで?と困惑しつつ require('devtron').install() し直したのだが表示されない。もう一度 Restore defaults and reload を押して Developrt Tools を再起動したら Devtron タブが表示された。いろいろ調査してみたところ

  • Console からの require('devtron').install() は初回だけ実行すればよい
  • アプリを起動するたびに Developer Tools の Settings - General で Restore defaults and reload ボタンを押す

という条件を満たさなと Devtron タブは有効にならないらしい。他に試した方法は以下。

  • Main プロセスの処理冒頭で require('devtron').install(); を実行する

    • 実行時エラーになる
    • Developer Tools の Console から実行しないとダメらしい
  • BrowserWindow.addDevToolsExtension で Devtron を登録

    • 効果なし
    • この方法は node_modules 配下のパス指定が面倒なので、成功したとしても採用したくない
  • Developer Tools 表示を BrowserWindow.toggleDevTools から openDevTools に変更

    • openDevTools なら Restore defaults and reload 相当になるかも?と考えて試した
    • ふつうに表示されるが Devtron タブはないのでダメ
    • この方法は自前でトグル管理することになり面倒なので、成功したとしても採用したくない

この動作がバグなのか仕様なのかは不明。Main プロセスを修正した場合は反映にアプリの再起動が必要なので仕様だとしたら毎回この操作が必要になって辛い。

タブを常駐させる方法については継続調査する。

パッケージ化におけるバージョン指定の簡略化

Electron v1.0 とは関係ないのだが、私はアプリのパッケージ化を npm-scripts で組んでおり electron-packager CLI を利用している。以下は OS X の定義。

{
  "scripts": {
    "release:pack-osx": "electron-packager ./dist/src AudioPlayer --out=dist/bin --cache=dist/cache --platform=darwin --arch=x64 --version=1.0.1 --overwrite --asar --icon=res/app.icns"
  }
}

Electron のバージョンは --version に指定。OS X 以外に Windows や Linux 版も対応する場合、変更箇所が増えて面倒でミスの危険も高まる。そこでこれを変数にできないか調べたら package.jsonconfig を利用できるとのこと。

{
  "config": {
    "app": "AudioPlayer",
    "electron": "1.0.1"
  },
  "scripts": {
    "release:pack-osx": "electron-packager ./dist/src $npm_package_config_app --out=dist/bin --cache=dist/cache --platform=darwin --arch=x64 --version=$npm_package_config_electron --overwrite --asar --icon=res/app.icns"
  }
}

こんな感じで config にプロパティ定義すれば npm-scripts から $npm_package_config_PROPERTYNAME のように参照できる。接頭語が長すぎて読みにくいけど npm-scripts の定義はそうそう変更するものではないからよしとする。gulp から npm-scripts へ移行したとき、共通設定を変数化できないのが不満だったけど、これで解消された。調べてみるものだ。

config をうまく利用すると npm-scripts の汎用性が高まる。これまで examples-electron ではプロジェクトごとにアプリ名が異なるためパッケージ指定も個別になっていたのだが、この部分を config で変数化することにより定義を統一できた。異なるのは config だけである。