Electronで1からデスクトップアプリを作り、electron-builderを使ってビルド・リリースするまで

# この記事について
この記事では、Electronを使ってデスクトップアプリを作成し、それを配布可能な状態にビルドするまでの過程を紹介します。
また、Electronアプリを作る際に、知っておくと便利な知識・ライブラリもあわせて紹介します。

## 使用する環境・バージョン
- OS : MacOS Mojave ver 10.14.5
- node.js v12.13.0
- npm 6.13.4
- electron 8.0.1
- electron-builder 22.3.2

## 前提条件
- node.jsとnpmは既にインストール済みで使用可能な状態とします。

## 読者に要求する前提知識
- 基本的なUNIXコマンドの意味がわかり、ターミナルで実行できること。
- JavaScriptの基本的な文法がわかること。

#Electronとは？
GitHubによって開発された、クロスプラットフォームデスクトップアプリのフレームワークです。
クロスプラットフォームなので、Electronで作成したアプリは、MacでもWindowsでも動きます。
また、アプリの画面を作るにあたってHTMLとCSS, JavaScriptといったWebフロントエンドの技術を使うので、Web系の知識がある人にとっては敷居が低いツールです。
参考：[Electron公式ドキュメント](https://www.electronjs.org)

#Electronのアプリケーションアーキテクチャ
Electronの仕組みは以下のようになっています。
![electron-security-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520877/5563fe10-9a9a-d9cd-01ba-43912f2beae4.png)
引用：[DeNA Engineers' Blog 「Electronのセキュリティは難しい？」](https://engineer.dena.com/posts/2016.04/electron-security/)

##メインプロセス
アプリの画面ウィンドウを生成して、起動・終了などのアプリ本体の制御を行います。1つのアプリに対してメインプロセスは1つだけです。
アプリのウィンドウ生成を`BrowserWindow`インスタンスの作成で行います。
Node.jsで動いています。つまり、npmモジュールや、ファイルの読み書きやネットワークなどのOS機能をAPI経由で使うことができます。

##レンダラープロセス
メインプロセスで作成されたアプリ画面をChromiumでレンダリングして表示します。1つのアプリに対して複数個(=画面の数だけ)用意することができます。
アプリの画面レイアウト・装飾をHTML/CSS・JavaScriptで行います。
レンダラープロセスで使える機能は基本的にブラウザ上で動くJavaScript(+α)です。

##プロセス間通信(IPC通信)
メインプロセスとレンダラープロセス間でやりとりをする&レンダラープロセスから機能を呼び出すためには、IPC通信というものを使います。
IPC通信をするためには、メインプロセス側では`ipcMain`モジュールを、レンダラープロセス側では`ipcRenderer`モジュールをインポートする必要があります。

###レンダラープロセス→メインプロセス
レンダラープロセス側からデータを送る場合は、`ipcRenderer`モジュールのAPIを呼び出す形になります。

```(レンダラープロセス)index.js
//ipcRendererモジュールをインポート
const { ipcRenderer } = require("electron");

//メインプロセスのipcMain.on("test-send")に変数dataを送る
ipcRenderer.send("test-send", data); 
```
メインプロセス側では、`ipcMain`モジュールのAPIでそれを受け取ります。

```(メインプロセス)main.js
//ipcMainモジュールをインポート
const { ipcMain } = require("electron");

//レンダラープロセスから送られたdataの内容がargに格納されている
ipcMain.on("test-send", (event, arg) => {
  //処理
});
```

###メインプロセス→レンダラープロセス
メインプロセス側からデータを送る場合は、基本的にレンダラープロセス側からのイベントに返信という形になります。
レンダラープロセスから送られたtest-sendイベントに、test-replyというチャネル名でdataを送ります。

```(メインプロセス)main.js
const { ipcMain } = require("electron");

ipcMain.on('test-send', (event, arg) => {
  event.reply('test-reply', data)
})
```
レンダラープロセス側では、`ipcRenderer`モジュールのAPIでそれを受け取ります。

```(レンダラープロセス)index.js
const { ipcRenderer } = require("electron");

//メインプロセスから送られたdataの内容がargに格納されている
ipcRenderer.on('test-reply', (event, arg) => {
    //処理
})
```
参考：[Electron アプリケーションアーキテクチャ](https://www.electronjs.org/docs/tutorial/application-architecture)
参考：[ようこそ！Electron入門](https://qiita.com/umamichi/items/6ce4f46c1458e89c4cfc)
参考：[今日から始める Electron](https://happo31sprogblog.hatenablog.jp/entry/2018/12/09/012357)

###`ipcMain/Renderer`の関数
IPC通信をするためのAPIは`ipcMain/Renderer`モジュール内に他にも存在します。
詳しくは公式ドキュメントを参照してください。

- `ipcMain`：[Electron公式ドキュメント ipcMain](https://www.electronjs.org/docs/api/ipc-main)
- `ipcRenderer`：[Electron公式ドキュメント ipcRenderer](https://www.electronjs.org/docs/api/ipc-renderer)

#アプリの作成
##0.ディレクトリ・ファイル構造
###一覧
アプリのソースを入れるフォルダを一つ作成してください(ここでは`app`フォルダとします)。
今後ここがアプリのルートディレクトリになります。

```bash
$ mkdir app
```
この`app`フォルダの中に、最終的に以下のような構造になるようにファイルを配置します。

```
/app #アプリのルートディレクトリ
  ├─assets #アプリのアイコンを格納
  │  ├─mac
  │  │  └─icon_mac.icns #Mac用のアプリアイコン
  │  └─win
  │     └─icon_win.ico #windows用のアプリアイコン
  ├─dist #ビルドされたアプリの格納場所
  ├─node_modules
  ├─package.json
  ├─package-lock.json
  └─src
     ├─main.js    #メインプロセス
     ├─preload.js
     ├─index.html #レンダラープロセス
     ├─index.css  #レンダラープロセス
     └─index.js   #レンダラープロセス
```

###git管理に含めるファイル・含めないファイル
開発にあたり、ソースコードをgit管理したいという人もいるでしょう。
基本的には問題ないのですが、それをGitHubにpushしたいと考えると、一部のファイルは管理対象外にした方が無難です。
というのも、GitHubは大容量ファイルのpushを拒否するようになっているからです。(50MB超で警告、100MB超で拒否)
参考：[GitHub公式ドキュメント 大容量ファイルの制限](https://help.github.com/ja/github/managing-large-files/conditions-for-large-files)

そのため、以下のディレクトリは`.gitignore`に追加しておきましょう。

- `dist`<br>
ビルドされたアプリ(数10MBになります)がこのディレクトリに入ります。当然重いです。
- `node_modules`<br>
npmモジュールのソースがそのままここに入るので当然重くなります。

##1.プロジェクトの作成
アプリルートディレクトリの中に、node.jsのプロジェクトを作成します。

```bash
$ cd app
$ npm init -y
```
このコマンドを実行すると、ルートディレクトリ中に`package.json`ファイルが作成されます。
その`package.json`を以下のように編集します。

```package.json
{
  "name": "your-app-name",
  "version": "1.0.0",
  "description": "your app's description",
  "main": "src/main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "your-name",
  "license": "ISC"
}
```
`package.json`の項目の意味は以下の通りです。

- name : アプリの名前。デフォルトだと`npm init`をしたディレクトリの名前
- version : アプリのバージョン。デフォルトは1.0.0
- description : アプリの説明。
- main : メインプロセスの相対パス。<br>
デフォルトは`index.js`だが、Electronアプリではメインプロセスのファイル名は`main.js`とするのが一般的。
- scripts : 後述(アプリ起動の項で解説)。
- keywords : 今回はさして重要ではないので放置。(本来は`npm search`されたときの検索キーワード)
- author : アプリの作者名。
- license : 配布時のライセンス。デフォルトはISC

参考：[npm公式ドキュメント npm-package.json](https://docs.npmjs.com/files/package.json)
参考：[package.jsonの内容をまとめてみました](https://qiita.com/gght/items/f74788c728b33f071e50)

##2.Electronのインストール
ルートディレクトリ直下で以下のコマンドを実行して、Electronをインストールします。

```bash
$ npm install -D electron
```
注意：インストールに時間がかかる場合がありますが、気長に待ちましょう。
注意：上記のようにdevDependenciesとして追加しないと、ビルド時にエラーが出ます。

この時点で、`package-lock.json`と、`node_modules`が作成されます。

##3.メインプロセスの作成
###スクリプト作成
`src`フォルダの中に、メインプロセスのコードを記述する`main.js`を作成します。

```bash
$ mkdir src
$ cd src
$ touch main.js
```
そうして作成した`main.js`に以下のように書き込みます。

```src/main.js
'use strict';

//モジュールを使えるようにする
const {app, BrowserWindow} = require("electron");

// メインウィンドウはGCされないようにグローバル宣言
let mainWindow;

//アプリの画面を作成
function createWindow (){
  mainWindow = new BrowserWindow({width: 800, height: 600, webPreferences: {
    nodeIntegration: false,
    contextIsolation: false,
    preload: __dirname + '/preload.js'
  }});
  mainWindow.loadURL('file://' + __dirname + '/index.html');
}

// Electronの初期化完了後に実行
app.on('ready', function() {
  createWindow();
});

//アプリの画面が閉じられたら実行
app.on('window-all-closed', () => {
  // macOSでは、ユーザが Cmd + Q で明示的に終了するまで、
  // アプリケーションとそのメニューバーは有効なままにするのが一般的です。
  if (process.platform !== 'darwin') {
    mainWindow = null;
    app.quit()
  }
});

app.on('activate', () => {
  // macOSでは、ユーザがドックアイコンをクリックしたとき、
  // そのアプリのウインドウが無かったら再作成するのが一般的です。
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow()
  }
});
```
このコード内では主に二つのモジュールを使用しています。

- [app](https://www.electronjs.org/docs/api/app) : アプリの起動・終了の制御を行う
- [BrowserWindow](https://www.electronjs.org/docs/api/browser-window) : アプリ画面の制御を行う

参考：[Electron公式ドキュメント 3分でわかるElectronアプリ開発](https://www.electronjs.org/docs/tutorial/first-app#installing-electron)
参考：[30分で出来る、JavaScript (Electron) でデスクトップアプリを作って配布するまで](https://qiita.com/nyanchu/items/15d514d9b9f87e5c0a29)

###mainWindowの設定
`new BrowserWindow`の作成の際に指定したオプション"webPreferences"の項目について解説します。

- nodeIntegration : レンダラープロセスでNode.jsの機能を使えるようにするか。false推奨
- contextIsolation : それぞれのプロセスを別々のJavaScriptコンテキストで実行するかどうか(詳細は`preload.js`の項目で解説)。
- preload : レンダラープロセス実行前に読み込まれるスクリプトを指定。

###nodeIntegration: falseの重要性

先ほども述べたとおり、レンダラープロセスで使えるのは基本的にはブラウザ上で使えるJavaScriptです。
しかし、ブラウザ上のJavaScriptにはセキュリティ上の理由で制限されている機能・実現不可能なことがあります。
例えば、`<input type="file"/>`で設置されたフォームから入力されたファイルについて、JavaScript側でvalue値を取得しようとするとファイル名のみが取得され、ローカルマシン上でのフルパスが入手できないようになっています。
参考：[javascript - C：\ fakepathを解決する方法は？](https://tutorialmore.com/questions-454408.htm)

そのため、例えば「ユーザーのローカルPC上にあるファイルを選択・中身を表示させるようなアプリを作るために、選択したファイルのパスをレンダラープロセスで取得したい」という場合は、Node APIを使う必要があります(Node.jsはサーバーサイドの環境なので、OSの機能を使うことができます)。

しかし、レンダラープロセス(=ユーザーが触れるアプリ画面(ブラウザ画面))でNode.jsの機能を使うことを認めてしまうと、クロスサイトスクリプティング(XSS)が発生することがあり危険です。
> クロスサイトスクリプティングは、Webサイト閲覧者側がWebページを制作することのできる動的サイト（例：TwitterなどのSNSや掲示板等）に対して、自身が制作した不正なスクリプトを挿入することにより起こすサイバー攻撃です。
> 出所：[クロスサイトスクリプティングとは？仕組みと事例から考える対策](https://cybersecurity-jp.com/security-measures/18427)

つまり、アプリの画面からNode APIを使ってOSの機能を呼び出して、

- ファイルデータの改ざん・消去
- ローカルマシンの情報を取得・外部に送信

ということが可能になってしまいます。

実際に、ElectronのアプリでXSSを発生させ、ローカルマシンのデータを全消去させたという実験記事があるので挙げておきます。
[ElectronアプリのXSSでrm -fr /を実行する](https://qiita.com/akameco/items/cc6fc949e7c9f0d1a42a)

そのため、nodeIntegrationの値をfalseにしてレンダラープロセスでOSの機能にアクセスさせないことが推奨されているのです。

ここで、Electronアプリ開発の際に極めて重要な心構えが公式ドキュメントに記載されていたので、引用しておきます。

> Electron で開発する時、Electron はブラウザではないということを意識することが重要です。 使い慣れたウェブ技術を使用して、機能あふれるデスクトップアプリケーションを構築できますが、あなたのコードの方がはるかに大きな力を発揮します。 JavaScript はファイルシステム、ユーザシェルなどにアクセスできます。 これはつまり、質の高いネイティブアプリケーションを作成することができる反面、あなたの書くコードに与えられた権限に応じて固有のセキュリティリスクが増加するということです。

>それを念頭に置いて、信頼できないソースからの任意のコンテンツを表示するということは、Electron が扱うことを意図しない重大なセキュリティリスクを引き起こすということに注意してください。 実際、人気のある Electron アプリ (Atom、Slack、Visual Studio Code、等) は、主にローカル (あるいは信頼されており、なおかつ Node integration を使用しないリモート) のコンテンツを取り扱います。もしあなたのアプリケーションがオンライン上のリソースからコードを実行する場合、あなたの責任の下でそのコードが悪意のあるものではないことを確認する必要があります。

> [Electron公式 セキュリティ、ネイティブ機能、あなたの責任](https://www.electronjs.org/docs/tutorial/security)

しかし、このままではNode.jsで提供されている多くの便利なnpmモジュールやElectron APIがレンダラープロセスで利用不可になってしまいます。
そのため、Node.jsのモジュールの中でレンダラープロセスで利用したいものを選び、そのモジュールだけを使用できるようにするという方法をとります。

##4.`preload.js`の作成
###`preload.js`の機能
nodeIntegrationの値をfalseのまま、レンダラープロセスでNode.jsのモジュールを利用できるようにする方法の一つに`preload.js`の作成があります。

`preload.js`は、nodeIntegrationの値に関わらず、`require('モジュール名')`でNode APIにアクセスすることができます。
そのため、「`preload.js`で読み込んだモジュールをグローバルに共有→それをレンダラープロセスで使用」という形で、レンダラープロセスでのNode APIの利用を可能にできるのです。
(このモジュール共有を行うために、mainWindowの設定でcontextIsolationをfalseにする必要があります)

###ファイル作成
`src`フォルダの中に、`preload.js`を作成し、例えば以下のように書き込みます。

```bash
$ mkdir src
$ cd src
$ touch preload.js
```

```preload.js
const electron = require('electron');

process.once('loaded', () => {
    global.ipcRenderer = electron.ipcRenderer;
    global.app = electron.remote.app;
});
```
このようにすることで、レンダラープロセスで以下のようにすることで`ipcRenderer`と`app`モジュールが使えるようになります。

```(レンダラープロセス)index.js
//nodeIntegrationをfalseにしたことで、以下は使えなくなった
//const {ipcRenderer, app} = require('electron');

const ipcRenderer = window.ipcRenderer;
const app = window.app;
```

参考：[Electron で nodeIntegration: false にする方法](https://qiita.com/umamichi/items/8781e426e9cd4a88961b)
参考：[Electron IPC通信を行う方法まとめ](https://webbibouroku.com/Blog/Article/electron-ipc)
参考：[Electron Webviewのセキュリティで注意すべきこと](https://qiita.com/RyosukeCla/items/6f072947bfcba3819ef0)

##5.レンダラープロセスの作成
先ほど`main.js`の中で指定したレンダラープロセス`index.html`を、`src`フォルダの中に作成します。

```bash
$ touch index.html
```
作成した`index.html`の中に、アプリの画面をHTMLで書いていきます。
ここでは動作確認をするために、hello,world!だけ記述します。

```src/index.html
<h1>hello,world!</h1>
```
今後画面を装飾・機能を追加ということをしたい場合は、`index.css`や`index.js`を読み込んでいけば実現可能です。

##6.アプリの起動
###起動コマンドを打つ
今の状態でアプリがどうなっているのかを、実際に起動して確かめてみましょう。
ルートディレクトリ直下の`node_modules/.bin`の中に`electron`コマンドが入っているので、それを実行します。

```bash
$ cd app
$ node_modules/.bin/electron .
```
すると、以下のような画面が立ち上がるはずです。
<img width="850" alt="Electron.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520877/1c37b82d-4321-c4b2-f47e-a29bda98b607.png">
先ほどHTMLに書いたhello,worldが表示されていますので成功です。

###起動ショートカットを設定する
アプリを起動するたびに、先ほどのような長いパスを打つのは面倒です。
そのため、簡単なショートカットで同様に起動できるように`package.json`に設定を追加しましょう。

```package.json
{
  ...(略)...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "electron ."
  },
  ...(略)...
}
```
すると、以下のコマンドでアプリの起動が行えるようになります。

```bash
$ npm start
```
`package.json`のscriptsの詳しい仕様については、以下の記事を参考にしてください。
参考：[npm公式ドキュメント npm-scripts](https://docs.npmjs.com/misc/scripts)
参考：[package.json の scripts](https://qiita.com/shuhei/items/87aadb0cc3d113b94234)

#アプリ開発に使える便利機能
hello,worldができたら、自分の思うがままにアプリの機能をどんどん豊富にしていく段階です。
ここでは、Electronアプリ開発で使える便利な機能・ライブラリを紹介します。

##デベロッパーツールの表示
アプリ画面のデバッグ等に使えるデベロッパーツールは、メインプロセス内に以下のコードを追記することで表示させることができます。

```main.js
app.on('ready', function(){
　...(略)...
  mainWindow.webContents.openDevTools()
　...(略)...
});
```
追記することで、アプリ起動時にChromeのデベロッパーツールが表示されます。
<img width="834" alt="devtool.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520877/ba76cbec-b238-367e-e485-b24bbaa3def6.png">
注意：アプリをビルドして完成させ、配布するというときには該当箇所をコメントアウト・デベロッパーツールを非表示にさせることを忘れないでください。

##よく使えるプロセスモジュール
プロセスモジュールを見れば、Electronでどんなことができるのかが大体わかります。
ここでは、公式ドキュメントに掲載してあるモジュールをざっくり紹介します。

|モジュール名|説明|
|:--|:--|
|[autoUpdater](https://www.electronjs.org/docs/api/auto-updater)|アプリの自動アップデート機能の追加(Mac,Winのみ)|
|[BrowserView](https://www.electronjs.org/docs/api/browser-view)|BrowserWindowに追加でウェブコンテンツを埋め込む子ウィンドウの制御|
|[contentTracing](https://www.electronjs.org/docs/api/content-tracing)|Chromiumからのトレースデータを収集|
|[dialog](https://www.electronjs.org/docs/api/dialog)|ローカルファイルの選択・新規作成・保存|
|[globalShortcut](https://www.electronjs.org/docs/api/global-shortcut)|ショートカットキーの登録・操作の管理|
|[inAppPurchase](https://www.electronjs.org/docs/api/in-app-purchase)|Mac App Store のアプリ内購入機能の提供|
|[net](https://www.electronjs.org/docs/api/net)| HTTP/HTTPS リクエストの発行|
|[netLog](https://www.electronjs.org/docs/api/net-log)|ネットワークイベントのロギング|
|[Notification](https://www.electronjs.org/docs/api/notification)|OSのデスクトップ通知の作成|
|[powerMonitor](https://www.electronjs.org/docs/api/power-monitor)|PCの電源の状態を取得|
|[powerSaveBlocker](https://www.electronjs.org/docs/api/power-save-blocker)|システムの省電力モードの制御|
|[protocol](https://www.electronjs.org/docs/api/protocol)|カスタムプロトコルの登録|
|[screen](https://www.electronjs.org/docs/api/screen)|画面サイズ、ディスプレイ、カーソルの位置等の情報取得|
|[session](https://www.electronjs.org/docs/api/session)|ブラウザーセッション、クッキー、キャッシュ、プロキシの設定管理|
|[systemPreferences](https://www.electronjs.org/docs/api/system-preferences)|システム環境設定の取得|
|[TouchBar](https://www.electronjs.org/docs/api/touch-bar)|タッチバーレイアウトの作成|
|[Tray](https://www.electronjs.org/docs/api/tray)|システムの通知領域にアイコンやコンテキストメニューを追加|
|[webContents](https://www.electronjs.org/docs/api/web-contents)|ウェブページの描画・制御|
|[desktopCapturer](https://www.electronjs.org/docs/api/desktop-capturer)|デクストップのスクリーンショットやビデオキャプチャの制御|
|[webFrame](https://www.electronjs.org/docs/api/web-frame)|ウェブページの描画のカスタマイズ|
|[clipboard](https://www.electronjs.org/docs/api/clipboard)|システムのクリップボードを利用したコピー・ペーストの操作提供|
|[crashReporter](https://www.electronjs.org/docs/api/crash-reporter)|クラッシュレポートをリモートサーバーに送信|
|[nativeImage](https://www.electronjs.org/docs/api/native-image)|trayやDockやアプリケーションのアイコン画像ファイル作成|
|[shell](https://www.electronjs.org/docs/api/shell)|デフォルトのアプリケーションを使用してのファイル・URL管理|

##アプリケーションメニューをつける
ウィンドウ上部に設定されるアプリケーションメニューを作るためには、`Menu`や`MenuItem`モジュールを使用します。

- Menu：[Electron公式ドキュメント Menu](https://www.electronjs.org/docs/api/menu)
- MenuItem：[Electron公式ドキュメント MenuItem](https://www.electronjs.org/docs/api/menu-item)

設置のためには、以下のコードをメインプロセス側に記述します。

```main.js
//アプリケーションメニュー
const Menu = electron.Menu

//メニューバー内容
let template = [{
  label: 'Your-App',
  submenu: [{
    label: 'アプリを終了',
    accelerator: 'Cmd+Q',
    click: function(){
      app.quit();
    }
  }]
}, {
  label: 'Window',
  submenu: [{
    label: '最小化',
    accelerator: 'Cmd+M',
    click: function(){
      mainWindow.minimize();
    }
  }, {
    label: '最大化',
    accelerator: 'Cmd+Ctrl+F',
    click: function(){
      mainWindow.maximize();
    }
  }, {
    type: 'separator'
  }, {
    label: 'リロード',
    accelerator: 'Cmd+R',
    click: function(){
      BrowserWindow.getFocusedWindow().reload();
    }
  }]
}]

// Electronの初期化完了後に実行
app.on('ready', function() {
  //メニューバー設置
  const menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);

  ...(略)...
});
```
すると、以下のようなアプリケーションメニューが表示されます。
(写真ではElectronとなっているところは、アプリをパッケージしたらYour-Appになります)
<img width="326" alt="menu.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520877/4187b770-742f-4692-049e-e25382f6b198.png">
参考：[JavaScript (Electron) を使ってアプリの見栄えを整える](https://qiita.com/nyanchu/items/9a1c910bbca55e9d2f3c)

##組み込み式DBの導入(NeDB)
###セットアップ
NeDBは、JavaScriptで使える組込データベースです。APIはMongoDBのサブセットなので、Mongoを使ったことがある人にとっては扱いやすいかと思います。
インストールにはnpmを使います。

```bash
$ npm install --save nedb
```
実際に使うためには、`preload.js`とレンダラープロセス側に以下を記述します。

```preload.js
const electron = require('electron');

process.once('loaded', () => {
    global.app = electron.remote.app;
    global.Datastore = require('nedb');
});
```
```(レンダラープロセス)index.js
const app = window.app;
const Datastore = window.Datastore;

const db = new Datastore({ 
    filename: app.getPath('userData')+'/member.db',
    autoload: true
});
```
注意：永続的なDBにするため指定するdbファイルのパスをNode APIの`app.getPath('userData')`でアプリケーションデータディレクトリを指定しないと、パッケージした後に動かなくなります。
参考：[electron-vueのproductionビルドで気をつけるところ](https://yanamura.hatenablog.com/entry/2018/03/21/065537)

###基本操作
ここまで準備ができると、insertやfindなどの普通のDB操作が可能になります。
例えば、insertは以下のように行います。

```js
var doc = {
    //example
    first_name: Smith,
    last_name: Sam
};
db.insert(doc, function(err, newDoc){
    //処理
});
```
他の操作については参考文献に譲ります。
参考：[NeDB を使ってみた](https://qiita.com/tinymouse/items/0731eef4aebf2779bd0b)
参考：[NeDBの基本](https://hajipy.net/2018/08/nedb-basic/)

#アプリのビルド
##1.electron-builderのインストール
ルートディレクトリ直下で以下のコマンドを実行して、electron-builderをインストールします。

```bash
$ npm install -D electron-builder
```
注意：上記のようにdevDependenciesとして追加しないと、ビルド時にエラーが出ます。

##2.アイコン画像の作成
アプリのアイコンを作成します。外部ツールを使って好きなようにデザインしてください。
ファイルの形式については以下の通りです。

- Mac用: icnsファイル
- Windows用: .icoファイル

参考：[Electronの各Platform向けアプリアイコンを作成する](https://horimisli.me/entry/generating-electron-icons/)
今回は、mac用のアイコンを`assets/mac`に、Windows用のアプリを`assets/win`に置きました。

##3.ビルド設定を記述
`package.json`にビルド用の設定を追加します。
この時、buildキーは一番上の階層(=nameやversionと同じ階層)に設置してください。

```package.json
{
  ...(略)...
　"build": {
    "appId": "com.electron.yourapp",
    "directories": {
      "output": "dist"
    },
    "files": [
      "assets",
      "src",
      "package.json",
      "package-lock.json"
    ],
    "mac": {
      "icon": "assets/mac/icon_mac.icns",
      "target": [
        "dmg"
      ]
    },
    "win": {
      "icon": "assets/win/icon_win.ico",
      "target": "nsis"
    },
    "nsis":{
      "oneClick": false,
      "allowToChangeInstallationDirectory": true
    }
  },
  ...(略)...
}
```
項目の意味は以下の通りです。

- appId : アプリのBundle ID。
- directories
 - output : ビルドしたアプリの格納先
- files : ビルドに含めるファイル
- mac : Mac用にビルドするときの設定
 - icon : アイコンファイルの相対パス
 - target : パッケージ後のファイル形式
- win : Windows用にビルドするときの設定
 - icon : アイコンファイルの相対パス
 - target : パッケージ後のファイル形式
- nsis : インストーラ生成ツールNSISの設定
 - oneClick : インストールから実行まで一気に行うかどうか
 - allowToChangeInstallationDirectory : インストール先の変更を許可するかどうか

参考：[electron-builder公式ドキュメント Common Configuration](https://www.electron.build/configuration/configuration)
参考：[electron-builderでwindows用インストーラーを作る時の設定](https://doudemo.info/setting-windows-installer-with-electron-builder/)

##4.ビルドコマンドを実行
ルートディレクトリ直下の`node_modules/.bin`の中に`electron-builder`コマンドが入っているので、それを実行します。

```bash
$ node_modules/.bin/electron-builder --mac --x64
# Mac用のインストーラー(.dmg)が作成される
$ node_modules/.bin/electron-builder --win --x64
# Windows用のインストーラー(.exe)が作成される
```
コマンド実行後に、`dist`ディレクトリ内にアプリのインストーラーが作成されていればビルド成功です。

- Mac用: `your-app-name-1.0.0.dmg`
- Windows用: `your-app-name Setup 1.0.0.exe`

このインストーラーを起動すれば、アプリがPCにインストールされて動き出します。お疲れ様でした。

参考：[electronでリリース用パッケージを作る](https://h3poteto.hatenablog.com/entry/2018/04/14/234353)
参考：[electron-builderを使ってdmgファイルを生成する](http://shibe97.hatenablog.com/entry/2017/02/17/090000)