Electronでサンプルアプリケーションを作ってみた(electron-prebuilt)

YAPC::Asia Tokyo 2015Electron: Building desktop apps with web technologies – YAPC::Asia Tokyo 2015を聴いて、Electronに興味が出てきたので、サンプルアプリケーションを作って起動するまでやってみました。

とりあえず、ウィンドウのあるアプリケーションと、メニューバーにアイコンをだしてクリックするとウィンドウが出るアプリケーションが、すごい簡単にできました。
アプリケーションといっても、ウィンドウが出ただけですが。

electron-starter -> electron-prebuilt

THIS REPO IS DEPRECATED

This repo represents the "old way" of creating and publishing Electron applications, based on Atom's build process – while this way still works and there are still some good ideas in this repo, it's actually far easier to use electron-prebuilt and electron-packager to get started.
https://github.com/atom-archive/electron-starter

発表ではelectron-starterを使ってデモをしていた様な気がするけど、すでにDEPRECATEDらしく、electron-prebuiltとelectron-packagerを使えとあるので、そっちでやってみました。

electron-packagerは名前の通りパッケージング(.exeや.appを作る)為のもので、とりあえず動かすだけならelectron-prebuiltだけで十分でした。

手順

とりあえずnpm init

$ mkdir try_electron
$ cd try_electron
$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (try_electron)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/myuser/dev/try_electron/package.json:

{
  "name": "try_electron",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes)

scriptsに幾つか書きたいだけなので、全部デフォルトでpackage.jsonを生成しておきます。

electron-prebuiltのインストール

$ npm install electron-prebuilt electron-packager standard --save-dev
npm WARN package.json try_electron@1.0.0 No description
npm WARN package.json try_electron@1.0.0 No repository field.
npm WARN package.json try_electron@1.0.0 No README data
npm WARN engine xmlbuilder@2.2.1: wanted: {"node":"0.8.x || 0.10.x"} (current: {"node":"0.12.7","npm":"2.11.3"})

> electron-prebuilt@0.31.0 postinstall /Users/myuser/dev/try_electron/node_modules/electron-prebuilt
> node install.js

electron-prebuilt@0.31.0 node_modules/electron-prebuilt
├── extract-zip@1.0.3 (debug@0.7.4, minimist@0.1.0, async@0.9.0, mkdirp@0.5.0, yauzl@2.3.1, through2@0.6.3, concat-stream@1.5.0)
└── electron-download@1.0.7 (path-exists@1.0.0, home-path@0.1.2, debug@2.2.0, mkdirp@0.5.1, mv@2.1.1, nugget@1.5.4)

electron-packager@5.0.2 node_modules/electron-packager
├── mv@2.1.1
├── rcedit@0.3.0
├── minimist@1.1.3
├── ncp@2.0.0
├── mkdirp@0.5.1 (minimist@0.0.8)
├── run-series@1.1.2 (dezalgo@1.0.3)
├── rimraf@2.4.2 (glob@5.0.14)
├── asar@0.6.1 (commander@2.3.0, chromium-pickle-js@0.1.0, glob@5.0.14, minimatch@2.0.4, cuint@0.1.5)
├── extract-zip@1.0.3 (debug@0.7.4, minimist@0.1.0, async@0.9.0, yauzl@2.3.1, mkdirp@0.5.0, through2@0.6.3, concat-stream@1.5.0)
├── electron-download@1.0.7 (path-exists@1.0.0, home-path@0.1.2, debug@2.2.0, nugget@1.5.4)
└── plist@1.1.0 (util-deprecate@1.0.0, base64-js@0.0.6, xmldom@0.1.19, xmlbuilder@2.2.1)

standard@5.1.0 node_modules/standard
├── eslint-config-standard-react@1.0.4
├── eslint-config-standard@4.1.0
├── eslint-plugin-react@3.2.2
├── eslint-plugin-standard@1.2.0 (requireindex@1.1.0)
├── standard-engine@2.0.6 (get-stdin@4.0.1, find-root@0.1.1, xtend@4.0.0, minimist@1.1.3, defaults@1.0.2, pkg-config@1.1.0, dezalgo@1.0.3, multiline@1.0.2, deglob@1.0.1)
├── standard-format@1.6.5 (esformatter-spaced-lined-comment@2.0.1, stdin@0.0.1, esformatter-quotes@1.0.3, minimist@1.1.3, esformatter-eol-last@1.0.0, deglob@1.0.1, esformatter-semicolon-first@1.1.0, esformatter-literal-notation@1.0.1, esformatter-jsx@1.3.0, esformatter@0.7.3)
└── eslint@1.1.0 (escape-string-regexp@1.0.3, path-is-absolute@1.0.0, object-assign@2.1.1, path-is-inside@1.0.1, xml-escape@1.0.0, user-home@1.1.1, strip-json-comments@1.0.4, estraverse-fb@1.3.1, globals@8.6.0, estraverse@4.1.0, text-table@0.2.0, chalk@1.1.1, debug@2.2.0, mkdirp@0.5.1, optionator@0.5.0, minimatch@2.0.10, lodash.merge@3.3.2, lodash.clonedeep@3.0.2, espree@2.2.4, lodash.omit@3.1.0, is-my-json-valid@2.12.1, concat-stream@1.5.0, doctrine@0.6.4, inquirer@0.9.0, js-yaml@3.3.1, escope@3.2.0)

electron-prebuiltと、今回はいらないと書いたけど参考にしたElectron製アプリに倣ってelectron-packagerとstandardをインストールします。
今回は試しなのでglobalには入れず、ローカルに入れました。

package.jsonを編集

$ vi package.json
$ git diff
diff --git a/package.json b/package.json
index cf28356..31c5780 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,9 @@
   "description": "",
   "main": "index.js",
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "test": "standard",
+    "start": "electron .",
+    "build": "electron-packager . TryElectron --platform=darwin --arch=x64 --version=0.31.0 --ignore='node_modules/(electron-packager|electron-prebuilt)'"
   },
   "author": "",
   "license": "ISC",

とりあえず試すだけならstartだけで十分だけど、これも参考にしたアプリに倣ってtestbuildも書いておきました。

ローカルにパッケージを入れたらnode_modules/.bin/electronにコマンドが置かれて、実行がちょっと面倒です。
"start": "electron ."を書いておくと、npm startで実行できるので便利。

Quick startからコードをコピペ

$ vi index.js
var app = require('app');  // Module to control application life.
var BrowserWindow = require('browser-window');  // Module to create native browser window.

// Report crashes to our server.
require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is GCed.
var mainWindow = null;

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform != 'darwin') {
    app.quit();
  }
});

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600});

  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  // Open the devtools.
  mainWindow.openDevTools();

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });
});

https://github.com/atom/electron/blob/master/docs/tutorial/quick-start.md#write-your-first-electron-app

$ vi index.html
<!DOCTYPE html>
<html>
  <head>
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using io.js <script>document.write(process.version)</script>
    and Electron <script>document.write(process.versions['electron'])</script>.
  </body>
</html>

https://github.com/atom/electron/blob/master/docs/tutorial/quick-start.md#write-your-first-electron-app

とりあえず今回は、typical exampleをコピペすれば動くだろうという事で、あまり内容は把握してないです。

実行

$ npm start

> try_electron@1.0.0 start /Users/myuser/dev/try_electron
> electron .

[94460:0822/042937:INFO:renderer_main.cc(200)] Renderer process started
[94461:0822/042937:INFO:renderer_main.cc(200)] Renderer process started

スクリーンショット 2015-08-22 05.51.10

こんな感じで実行できました。
なぜか開発者ツールが起動した状態で起動してます。

メニューバーアプリケーションにしてみる

$ npm install menubar --save
npm WARN package.json try_electron@1.0.0 No description
npm WARN package.json try_electron@1.0.0 No repository field.
npm WARN package.json try_electron@1.0.0 No README data
menubar@2.1.2 node_modules/menubar
└── extend@2.0.1

Electron用らしいmenubarというモジュールを入れます。

$ mkdir menu-app
$ cp index.html menu-app/

メニューバーのアイコンをクリックしたら表示するHTMLとして、とりあえずindex.htmlをコピーしておきます。

var Menubar = require('menubar');
var menubar = Menubar({ dir: __dirname + '/menu-app', width: 400, height: 175, x: 0, y: 0});

index.jsにこれを追記するだけで、とりあえずメニューバーにアイコンが出ました。

スクリーンショット 2015-08-22 05.51.21

右側にあるアイコン群の、一番左の猫の様なシルエットのアイコンが、このアプリのアイコンです。
特にアイコンをしなかったので、これがデフォルトのアイコンらしい。
Menubar()に渡すオブジェクトにiconキー追加して、画像ファイルパスを指定すると、アイコン変えられるみたい。
x: 0, y: 0なので、ウィンドウは左によってしまいました。アイコンのちょうど下に出て欲しかったけど、時間切れです。

あと、なぜかElectron製のアプリにフォーカスがあるのに、iTermがアクティブなアプリになってます。ここにElectron製のアプリの名前やメニュー載せるのも、何か記述が要るって事なのかな?

折角入れたのでstandardモジュールを動かしてみた

$ npm test

> try_electron@1.0.0 test /Users/myuser/dev/try_electron
> standard

standard: Use Use JavaScript Standard Style (https://github.com/feross/standard)
  /Users/myuser/dev/try_electron/index.js:1:26: Extra semicolon.
  /Users/myuser/dev/try_electron/index.js:2:47: Extra semicolon.
  /Users/myuser/dev/try_electron/index.js:5:35: Extra semicolon.
  /Users/myuser/dev/try_electron/index.js:9:23: Extra semicolon.
  /Users/myuser/dev/try_electron/index.js:12:37: Missing space before function parentheses.
  /Users/myuser/dev/try_electron/index.js:15:24: Expected '!==' and instead saw '!='.
  /Users/myuser/dev/try_electron/index.js:16:16: Extra semicolon.
  /Users/myuser/dev/try_electron/index.js:18:4: Extra semicolon.
  /Users/myuser/dev/try_electron/index.js:22:25: Missing space before function parentheses.
  /Users/myuser/dev/try_electron/index.js:24:61: Extra semicolon.
  /Users/myuser/dev/try_electron/index.js:27:61: Extra semicolon.
  /Users/myuser/dev/try_electron/index.js:30:29: Extra semicolon.
  /Users/myuser/dev/try_electron/index.js:33:35: Missing space before function parentheses.
  /Users/myuser/dev/try_electron/index.js:37:23: Extra semicolon.
  /Users/myuser/dev/try_electron/index.js:38:6: Extra semicolon.
  /Users/myuser/dev/try_electron/index.js:39:4: Extra semicolon.
npm ERR! Test failed.  See above for more details.

npm testで動かす様にしたけど、テストというかJavaScript Standard Styleに沿ってるかどうかをチェックするものっぽい。
なんか色々出てるけど、今回は動けばいいので放置です。

参考にしたElectron製アプリ

環境

環境 バージョン
Node v0.12.7
electron-prebuilt 0.31.0

書いた日

2015年8月21日頃

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>