ClojureScript による Electron アプリ開発


![cljs-hello-electron_1080.gif](https://qiita-image-store.s3.amazonaws.com/0/20642/ca51b258-d2ec-d58c-104a-606c350589d6.gif "cljs-hello-electron_1080.gif")

## はじめに

最近、[Electron](http://electron.atom.io/)(旧atom-shell) を触って遊んでいました。[公式のドキュメント](http://electron.atom.io/docs/)が充実しており、Web開発の経験があれば、Electron を使い手軽にクロスプラットフォームなデスクトップアプリケーションを作成することができます。今回は、JavaScript の代わりに ClojureScript を使いこの Electron を使ったアプリ開発をしてみたいと思います。

方法としては、Electron の公式ドキュメントにある[Quick Start](http://electron.atom.io/docs/tutorial/quick-start/)の動作確認から入り、徐々に ClojureScript に置き換えつつ、 ClojureScript で Electron アプリを開発する環境を整えていきたいと思います。

## 全体の流れ

まずは、全体の流れをざっと確認しておきます。

- Step0: Quick Start の動作確認
- Step1: Main Process を ClojureScript に置き換え (dev環境)
- Step2: Main Process を ClojureScript に置き換え (production環境)
- Step3: Renderer Process を ClojureScript に置き換え (dev環境)
- Step4: Renderer Process を ClojureScript に置き換え (production環境)
- Step5: Renderer Process に Figwheel を設定
- Step6: Main Process に Figwheel を設定
- Step7: SCSS の設定
- Step8: externs の設定

最終的に完成したコードは github においてあります。また、各 Step が完了した段階のコードは対応するタグとして作成してあります。

```
$ git clone https://github.com/snufkon/cljs-hello-electron
$ git checkout -b step0 step0
```

各 Step におけるあまり重要でない変更点の解説は省きたいと思いますので、上記のようにタグをチェックアウトしてソースを確認してみてください。


## Step0: Quick Start の動作確認

まずは、[Quick Start](http://electron.atom.io/docs/tutorial/quick-start/)に説明されている Electron の最小構成で動作を確認しておきます。

**必要なファイルを配置**

```
$ mkdir cljs-hello-electron
$ touch cljs-hello-electron/{package.json,main.js,index.html}
```

で必要なファイルを作成します。`main.js`, `index.html` については Quick Start のページに表示されているコードをそのままコピーしてください。 `package.json` については以下のように記述しておきます。


```json:package.json
{
  "name"    : "hello-electron",
  "version" : "0.1.0",
  "main"    : "main.js"
}
```


**動作確認**

Electron アプリをテスト起動させるため、npm で electron コマンドをインストールしておきます。

```
$ npm install -g electron
```

無事に electron コマンドのインストールが完了したら、`cljs-hello-electron` ディレクトリに移動し、

```
$ electron .
```

を実行することで、作成したアプリケーションの起動が確認できると思います。

![step0.png](https://qiita-image-store.s3.amazonaws.com/0/20642/e53b03d8-896e-9933-1d38-e2419f0631cf.png "step0.png")

## Step1: Main Process を ClojureScript に置き換え (dev環境)

Electron では `package.json` の `main` に記載し実行するスクリプトを Main Process と呼びます。この Main Process がアプリケーション全体の管理を行います。また、Main Process から生成される各ウィンドウ(BrowserWindowインスタンス)で表示を行うスクリプトを Renderer Process と呼びます。

Step1 として Main Process にあたる `main.js` を ClojureScript からビルドする環境を整えたいと思います。

先に完成後のディレクトリを示すと以下のようになります。

```
cljs-hello-electron
├── app
│   └── dev
│       ├── index.html
│       ├── js
│       └── package.json
├── project.clj
└── src
    └── cljs
        └── main
            └── hello_electron
                └── core.cljs
```

Step0 でディレクトリ直下に配置していた、`index.html`, `package.json` は `app/dev` に移動します。

また、`main.js` は Leiningen によりビルド行い `app/dev/js/main/main.js` に出力するように設定します。そのため、`package.json` の `main` で指定している js の相対パスを以下のように変更します。

```json:package.json
{
  "name"    : "hello-electron",
  "version" : "0.1.0",
  "main"    : "js/main/main.js" // <-- 変更
}
```

**project.clj の追加**

leiningen で使用する `project.clj` は以下となります。

```clj:project.clj
(defproject hello-electron "0.1.0-SNAPSHOT"
  :description "Hello Electron form ClojureScript"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [org.clojure/clojurescript "1.9.293"]]
  :plugins [[lein-cljsbuild "1.1.4"]]
  :clean-targets [:target-path "app/dev/js/main"]
  :cljsbuild
  {:builds
   {:dev-main {:source-paths ["src/cljs/main"]  ;; Main Process に関するソースを配置する場所
               :compiler {:target :nodejs       ;; Main Process 側は target に nodejs を指定
                          :main "hello-electron.core" ;; エントリーポイント
                          :output-to "app/dev/js/main/main.js" ;; package.json ではこの main.js を指定する
                          :output-dir "app/dev/js/main"
                          :optimizations :none}}}})
```

`lein cljsbuild once dev-main` で `src/cljs/main` 以下に配置した cljs のソースをビルドし、開発用の `main.js` を出力させます。この段階では、`src/cljs/main` 以下には `main.js` をそのまま cljs に置き換えた `src/cljs/hello_electron/core.cljs` のみ配置してあります。


**main.js を core.cljs へ変更**

```clj:core.cljs
(ns hello-electron.core
  (:require [cljs.nodejs :as nodejs]))

(nodejs/enable-util-print!)

(defonce path          (js/require "path"))
(defonce url           (js/require "url"))

(defonce electron      (js/require "electron"))
(defonce app           (.-app electron))
(defonce BrowserWindow (.-BrowserWindow electron))

(def win (atom nil))

(defn create-window
  []
  (reset! win (BrowserWindow. (clj->js {:width 800 :height 600})))
  (.loadURL @win (.format url (clj->js {:pathname (.join path (js* "__dirname") "../../../index.html")
                                        :protocol "file"
                                        :slashes true})))
  (.openDevTools (.-webContents @win))
  (.on @win "closed" (fn []
                       ;; (println "window: closed")
                       (reset! win nil))))

(defn -main [& args]
  (.on app "ready" (fn []
                     ;; (println "app: ready")
                     (create-window)))
  
  (.on app "window-all-closed"
     (fn []
       ;; (println "app: window-all-closed")
       (when (not= (.-platform nodejs/process) "darwin")
         (.quit app))))
  
  (.on app "activate"
     (fn []
       ;; (println "app: activate")
       (when-not @win
         (create-window)))))

(set! *main-cli-fn* -main)
```

`main.js` の中身をそのまま cljs に置き換えました。

`(set! *main-cli-fn* -main)`

では Node.js 上で実行する際にコマンドライン引数を受け取る関数を指定しています。

参考: [Running ClojureScript on Node.js](https://github.com/clojure/clojurescript/wiki/Quick-Start#running-clojurescript-on-nodejs)

**動作確認**

以下で、ビルドおよびアプリケーションの動作確認をすることができます。

```
$ lein cljsbuild once dev-main
$ electron app/dev
```

Step0 の js版 と同様のアプリケーションが起動することを確認してください。


## Step1: Main Process を ClojureScript に置き換え (production環境)

dev 環境の設定に引き続き、Main Process の production 環境の設定を行います。

設定完了後のディレクトリ構成は以下のようになります。

```
cljs-hello-electron
├── app
│   ├── dev
│   │   ├── index.html
│   │   ├── js
│   │   └── package.json
│   └── prod
│       ├── index.html <-- app/dev からコピー
│       ├── js        
│       └── package.json <-- app/dev からコピー
├── project.clj
└── src
    └── cljs
        ├── dev-main  <-- dev-main ビルド時のみ読み込ませるファイルを配置
        │   └── hello_electron
        │       └── config.cljs
        ├── main
        │   └── hello_electron
        │       └── core.cljs
        └── prod-main <-- prod-main ビルド時のみ読み込ませるファイルを配置
            └── hello_electron
                └── config.cljs
```

**project.clj への追記**

上記のディレクトリ構造に対して、production 環境をビルドする設定を追加した `project.clj` は以下となります。

```clj:project.clj
(defproject hello-electron "0.1.0-SNAPSHOT"
  :description "Hello Electron form ClojureScript"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [org.clojure/clojurescript "1.9.293"]]
  :plugins [[lein-cljsbuild "1.1.4"]]
  ;; ビルドを行うコマンドと、ビルド結果を削除するコマンドを定義しておく
  :aliases {"clean-dev"  ["clean"]
            "clean-prod" ["with-profile" "prod" "clean"]
            "clean-all"  ["do"
                          ["clean-dev"]
                          ["clean-prod"]]
            "build-dev"  ["do"
                          ["clean-dev"]
                          ["cljsbuild" "once" "dev-main"]]
            "build-prod" ["do"
                          ["clean-prod"]
                          ["cljsbuild" "once" "prod-main"]]
            "build-all"  ["do"
                          ["build-dev"]
                          ["build-prod"]]}
  ;; dev, prod 用の profile を追加
  :profiles {:dev  {:clean-targets [:target-path "app/dev/js/main"]}
             :prod {:clean-targets [:target-path "app/prod/js/main"]}}
  :cljsbuild
  {:builds
   {:dev-main {:source-paths ["src/cljs/main"
                              "src/cljs/dev-main"] ;; <-- 追加
               :compiler {:target :nodejs
                          :main "hello-electron.core"
                          :output-to "app/dev/js/main/main.js"
                          :output-dir "app/dev/js/main"
                          :optimizations :none}}
    ;; 追加
    :prod-main {:source-paths ["src/cljs/main"
                               "src/cljs/prod-main"]
                :compiler {:target :nodejs
                           :output-to "app/prod/js/main/main.js"
                           :output-dir "app/prod/js/main"
                           :optimizations :simple}}}})

```

production ビルド用の `prod-main` を追加しました。`dev-main` との違いは

- `:source-paths` に指定するディレクトリを変更
- `:main` の記述を削除
- `:optimizations` に `:simple` を指定

といった点です。

Node.js 上では `:optimizations` を `:advanced` にしても `:simple` や `:none` を設定した場合とほとんど違いが生じないため、`:optimizations` には `:simple` を指定しています。

参考: [Running ClojureScript on Node.js](https://github.com/clojure/clojurescript/wiki/Quick-Start#running-clojurescript-on-nodejs)


**dev, prod 用の config.cljs を用意**

`src/cljs/dev-main/hello_electron/`, `src/cljs/prod-main/hello_electron` に `config.cljs` を配置します。

環境別に `config.cljs` を用意するのは `:optimizations` の違いにより `__dirname` で取得できるパス位置が `dev-main` でのビルドと `prod-main` のビルドで異なるためです。

```clj:config.cljs(dev)
(ns hello-electron.config)

(defonce path (js/require "path"))
(defonce url  (js/require "url"))

(def index-url (->> {:pathname (.join path (js* "__dirname") "../../../index.html") ;; <-- prod とパスが異なる
                     :protocol "file"
                     :slashes true}
                    clj->js
                    (.format url)))
```

```clj:config.cljs(prod)
(ns hello-electron.config)

(defonce path (js/require "path"))
(defonce url  (js/require "url"))

(def index-url (->> {:pathname (.join path (js* "__dirname") "../../index.html") ;; <-- dev とパスが異なる
                     :protocol "file"
                     :slashes true}
                    clj->js
                    (.format url)))
```

**core.cljsを修正**

`src/cljs/main/hello_electron/core.cljs` を上記の `config.cljs` を利用する形に変更します。

```clj:core.cljs(変更点のみ)
(ns hello-electron.core
  (:require [cljs.nodejs :as nodejs]
            [hello-electron.config :as config])) ;; <-- 追加

...

(defn create-window
  []
  (reset! win (BrowserWindow. (clj->js {:width 800 :height 600})))
  (.loadURL @win config/index-url) ;; <-- 変更
  (.openDevTools (.-webContents @win))
  (.on @win "closed" (fn []
                       ;; (println "window: closed")
                       (reset! win nil))))

...
```

`:source-paths` の設定により `core.cljs ` で使われる `config.cljs` は

- `dev-main` でビルドした場合 `src/cljs/dev-main/hello_electron/config.cljs`
- `prod-main` でビルドした場合: `src/cljs/prod-main/hello_electron/config.cljs`

となります。


**動作確認**

`aliases`に設定した、`build-prod` コマンドでビルドを行います。

```
$ lein build-prod
```

`app/prod/js/{main,front}/main.js` が作成されていることを確認してください。

確認ができたら、

```
$ electron app/prod
```

で アプリケーションが起動させ、Step2 の dev 環境と同じ結果なることを確認してください。


## Step3: Renderer Process を ClojureScript に置き換え (dev環境)

`index.html` で行っている Renderer Process 上の表示を js で行うようにします。また、その js を cljs をビルドすることで生成させる環境を設定します。

**script タグを追加**

```
<script type="text/javascript" src="js/front/main.js"></script>
```

まずは、`index.html` に js を読み込ませるための script タグを追加します。

**project.clj に dev-front ビルドの設定を追加**

次に、project.clj にビルド用の設定を追加します。

```clj:project.clj
:dev-front {:source-paths ["src/cljs/front"]
                :compiler {:main "hello-electron.core"
                           :output-to "app/dev/js/front/main.js"
                           :output-dir "app/dev/js/front"
                           :asset-path "js/front"
                           :optimizations :none}}
```

ビルドに利用するファイルは `src/cljs/front` 以下に配置し、ビルド結果は `app/dev/js/front` 以下に出力させます。

**表示を行う ClojureScript のコードを記述**

表示元となる cljs は以下のように記述しておきます。

```clj:core.cljs
(ns hello-electron.core)

(let [node (-> "h1" js/document.getElementsByTagName (aget 0))]
  (set! (.-innerHTML node) "Hello ClojureScript World!"))
```

単純に `index.html` に `<h>`タグで表示されているテキストを "Hello ClojureScript World!" に差し替えるコードとなります。

**動作確認**

```
$ lein build-dev
```

で開発環境用のビルドを行い、 `app/dev/js/{main,front}/main.js` が作成されることを確認します。

確認できたら、

```
$ electron app/dev
```

で dev 環境のアプリケーションを起動し、画面に "Hello ClojureScript World!" と表示されることを確認してください。


## Step4: Renderer Process を ClojureScript に置き換え (production環境)

Renderer Process 側に production ビルド用の環境を整えます。

**script タグを追加**

dev と同様 `index.html` に script タグを追加します。

**project.clj に prod-front ビルドの設定を追加**

次に、`project.clj` にビルド用の設定を追加します。

```clj:project.clj
:prod-front {:source-paths ["src/cljs/front"]
                 :compiler {:output-to "app/prod/js/front/main.js"
                            :output-dir "app/prod/js/front"
                            :optimizations :advanced}}
```

`dev-front` の設定との違いは以下になります。

- `:main` の設定を削除
- `:asset-path` の設定を削除
- `:optimization` を `:advanced` に設定

**動作確認**

```
$ lein build-prod
```

で production 環境用のビルドを行い、 `app/prod/js/{main,front}/main.js` が作成されることを確認します。

確認できたら

```
$ electron app/prod
```

で production 環境のアプリケーションを起動し、Step3 と同じ結果となることを確認してください。


# Step5: Renderer Process に Figwheel を設定

現状の設定では、開発時に Renderer Process 側のコードを変更した場合、その動作を確認するためには Step3 の動作確認に記載した

```
$ lein build-dev
$ electron app/dev
```

を毎回行う必要があり煩雑です。

`lein cljsbuild auto dev-front` でソースを監視した状態でビルドし、また、アプリケーション上で画面をリロードすることにより、上記のようにターミナル上で毎回コマンド実行を繰り返す必要はなくなりますが、やはり煩雑です。

そこで、[lein-figwheel](https://github.com/bhauman/lein-figwheel)を使い、ソースの変更を検知し、画面を自動更新させる設定を行います。


**project.clj に figwheel の設定を追加**

まず `project.clj` に figwheel の設定を追加します。

```
[lein-figwheel "0.5.8"]
```

を `:plugins` に

```
:figwheel-front {:figwheel {:server-port 3500
                 :server-logfile "figwheel-front.log"}}
```

を `:profiles` に追加します。Main Process 側に設定する figwheel と使用するポートを分けるため、各々にプロファイルを用意し、そこに使用するポートを記載しておきます。

`figwheel-front` プロファイルを使用して、figwheel を実行するための `alias` も設定しておきます。

```
"figwheel-front" ["with-profile" "figwheel-front" "figwheel" "dev-front"]
```

を `:aliases` に追加。

最後に `dev-front` の設定に `:figwheel true` を追加しておきます。

```
:dev-front {:source-paths ["src/cljs/front"]
                :figwheel true ;; <-- 追加
                :compiler {:main "hello-electron.core"
                           :output-to "app/dev/js/front/main.js"
                           :output-dir "app/dev/js/front"
                           :asset-path "js/front"
                           :optimizations :none}}
```

**動作確認**

まず、`aliases` に設定した `figwheel-front` を実行し、figwheel を Renderer Process 上で走らせておきます。

```
$ lein figwheel-front
```

次に、別のターミナルを開き dev 環境でアプリケーションを起動します。

```
$ electron app/dev
```

この状態で `src/cljs/front/hello_electron/core.cljs` に変更を加えてみます。

"Hello ClojureScript World!" の文字列を "Hello Figwheel" などの適当な文字列に変更し、変更がアプリケーションの表示に反映されることを確認してください。

もしファイルの更新が検知されずに、画面の再描画が実行されない場合は `project.clj` に以下を追加して確認してみてください。(step5 タグの `project.clj` には設定済み)

```
:figwheel {:hawk-options {:watcher :polling}}
```

# Step6: Main Process に Figwheel を設定

Main Process 側も現状の設定では、動作確認が煩雑なため figwheel の設定を追加します。

**wsモジュールをインストール**

Node.js 上で figwheel を使うためには `ws` というモジュールが必要です。

cljs-hello-electron のディレクトリ直下に

```json:package.json
{
  "name": "hello-electron",
  "version": "0.0.0",
  "description": ""
}
```

を作成し

```
$ npm install ws --save-dev
```

でインストールしておきます。

**project.clj へ記述を追加**

`project.clj` で変更する箇所は Renderer Process に figwheel を設定した際とほぼ同じです。

`aliases` に以下を追加。

```
"figwheel-main"  ["with-profile" "figwheel-main" "figwheel" "dev-main"]
```

`profiles` に以下を追加。

```
:figwheel-main  {:figwheel {:server-port 3500
                 :server-logfile "figwheel-main.log"}}
:figwheel-front {:figwheel {:server-port 3600 ;; <-- ポート変更した
                 :server-logfile "figwheel-front.log"}}
```

また、`dev-main` には

```clj
:figwheel {:on-jsload "hello-electron.core/reload"}
```

を追加します。ソース変更を figwheel が検知した際に、`hello-electron.core/reload` 関数を実行するように設定しています。


**core.cljs を Reloadable なコードへ修正**

現状のコードでは、ソースファイルの変更を検知しても既に作成済みのウィンドウに対して何も変化は起こりません。コードを以下のように修正し、ソース変更を検知したらウィンドウを作成し直すように記述します。

```clj:core.cljs
(ns hello-electron.core
  (:require [cljs.nodejs :as nodejs]
            [hello-electron.config :as config]))

(nodejs/enable-util-print!)

(defonce electron      (js/require "electron"))
(defonce app           (.-app electron))
(defonce BrowserWindow (.-BrowserWindow electron))

(defonce win (atom nil))

(defn- create-window
  []
  (reset! win (BrowserWindow. (clj->js {:width 800 :height 600 :show false})))
  (.loadURL @win config/index-url)
  (.openDevTools (.-webContents @win))
  (.on @win "closed" (fn []
                       ;; (println "window: closed")
                       (reset! win nil)))
  (.on @win "ready-to-show" (fn []
                              ;; (println "window: ready-to-show")
                              (.showInactive @win))))

(defn- destroy-window
  []
  (.destroy @win)
  (reset! win nil))

(defn- remove-listeners
  []
  (.removeAllListeners app "ready")
  (.removeAllListeners app "activate")
  (.removeAllListeners app "window-all-closed"))

(defn- add-listeners
  []
  (.on app "ready" create-window)
  (.on app "activate" (fn []
                        ;;(println "app: activate")
                        (when-not @win
                          (create-window))))
  (.on app "window-all-closed"
     (fn []
       ;; (println "app: window-all-closed")
       (when (not= (.-platform nodejs/process) "darwin")
         (.quit app)))))

(defn -main [& args]
  (add-listeners))

(defn reload
  []
  (remove-listeners)
  (add-listeners)
  (destroy-window)
  (create-window))

(set! *main-cli-fn* -main)
```

元のコードを reloadable なコードに変更しました。

アプリケーション起動時は以下の流れでウィンドウが作成されます。

- `-main` 関数実行
- `add-listeners` 関数実行
  - app オブジェクトのイベントリスナーを登録しておく
- Electron の初期化完了で `ready` イベントを受信
- `add-listeners` で登録した `create-window` 関数を実行

また、ソースファイルの変更後は以下の流れでウィンドウを作成します。

- `reload` 関数実行
- `remove-listeners` 関数で、現状のアプリケーションに登録されているリスナーを全て削除
- `add-listenrs` 関数でリスナーを登録し直し
- `destroy-window` 関数で現状のウィンドウを削除
- `create-window` 関数でウィンドウを作り直し


**動作確認**

Step5 と同様、まずは Main Process 上で figwheel を走らせておきます。

```
$ lein figwheel-main
```

次に、別のターミナルを開きアプリケーションを起動します。

```
$ electron app/dev
```

アプリケーションが起動したら、`src/cljs/main/hello_electron/core.cljs` に変更を加えてみます。

ウィンドウサイズを `:width 500` などの適当な値に変更し、現状のウィンドウが破棄され、指定したサイズで新しいウィンドウが作成されることを確認してください。


# Step7: SCSS の設定

Quick Start の `index.html` では css が指定されていませんが、実プロジェクトでは必要になってくると思います。

そこで、

- scss から css への変換
- css の変更検知からの画面更新

をできるように設定しておきます。


**<link>タグを追加**

`app/{dev,prod}/index.html` に

```
<link rel="stylesheet" href="css/style.css">
```

を追加しておきます。

**scss から css への変換するための設定**

[lein-sassy](https://github.com/vladh/lein-sassy)プラグインを使い、scss を css へ変換させます。

まず、`project.clj` に以下の設定を追加します。

```
[lein-sassy "1.0.7"]
```

を `:plugins` に追加。

```
:sass {:src "src/scss"
       :dst "app/dev/css"
       :style :nested}
```

を `dev` プロファイルに追加。

```
:sass {:src "src/scss"
       :dst "app/prod/css"
       :style :compressed}
```

を `prod` プロファイルに追加。


変換元となる scss は `src/scss/style.scss` に配置しておきます。

```scss:style.scss
body {
  background-color: #ccc;
}
```

**cssの変更による画面更新の設定**

Renderer Process 側の fighweel に css 監視の設定を追加します。

`project.clj` の `figwheel-front` を以下のように設定します。

```
:figwheel-front {:figwheel {:server-port 3600
                            :server-logfile "figwheel-front.log"
                            :css-dirs ["app/dev/css"]}} ;; <-- 追加
```

**動作確認**

まず、scss から css の変換が正しく行われるか確認しておきます。

```
$ lein sass once
```

で `src/scss/style.scss` から `app/dev/css/style.css` が作成されること

また、

```
$ lein with-profile prod sass once
```

で `src/scss/style.scss` から `app/prod/css/style.css` が作成されることを確認してください。

次に Renderer Process 側の figwheel と合わせて動作確認します。

```
$ lein build-dev
$ lein figwheel-front
```

で figwheel を起動します。次に別のターミナルで

```
$ lein sass watch
```

を実行し scss の変更を監視させておきます。

最後にまた別のターミナルでアプリケーションを起動します。

```
$ electron app/dev
```

この状態で `style.scss` の `background-color: #ccc;` を `background-color: #green;` などの適当な値に変更し、起動中のアプリケーション画面の背景が更新されることを確認してください。

# Step8: externs の設定

現状のコードでは特に必要ありませんが、こちらも css 同様に実プロジェクトでは必要になってくると思います。

[lein-externs](https://github.com/ejlo/lein-externs)プラグインを利用し、externs ファイルを作成したいと思います。

**project.clj への変更**

```
[lein-externs "0.1.6"]
```

を `:plugins` に追加。


```
"externs-prod"   ["externs" "prod-front" "app/prod/js/externs_front.js"]
```

を `:aliases` に追加。

```
:prod-front {:source-paths ["src/cljs/front"]
                 :compiler {:output-to "app/prod/js/front/main.js"
                            :output-dir "app/prod/js/front"
                            :optimizations :advanced
                            :externs ["app/prod/js/externs_front.js"]}} ;; <-- 追加
```

`prod-front` に `:externs` の記述を追加。

**動作確認**

```
$ lein externs-prod
```

で `app/prod/js/externs_front.js` が作成されることを確認してください。


# production, dev 環境での動作確認

長くなってしまいましたが、ざっと設定が完了しました。最後に、production 環境、dev 環境での動作確認方法を記載しておきます。

**production 環境**

production 環境の動作確認は以下で実行できます。

```
$ lein build-prod
$ electron app/prod
```

**dev 環境**

dev 環境の動作確認は以下で実行できます。

```
$ lein build-dev
$ electron app/dev
```

また、figwheel を利用した開発時の設定は以下の手順で行います。

```
$ lein figwheel-main
```

で Main Process 用の figwheel を起動。

別のターミナルで

```
$ lein figwheel-front
```

を実行し、Renderer Process 用の figwheel を起動。

また、別のターミナルを開き

```
$ lein sass watch
```

を実行し、lein-sassy プラグインにより scss の変更を監視。

最後に別のターミナルで

```
$ electron app/dev
```

を実行し、アプリケーションを起動します。

この状態で各ファイルに変更を加えると以下の更新処理が走ります。(冒頭 gif 画像の状態)

- src/main 以下のファイル変更: ウィンドウ再作成
- src/front 以下のファイル変更:ウィンドウ画面更新
- src/scss 以下のファイル変更: ウィンドウ画面更新


# おわりに

駆け足となりましたが、Electron の Quick Start から開始し、徐々に ClojureScript に移行しながら、Electron アプリを開発する方法を紹介しました。開発環境として、figwheel を Main Process 側、Renderer Process 側の両方に設定したため、Reloadable なコードを書いていくことが要求されますが、そうすることにより、ビルド、アプリケーション起動といった煩雑な作業をなしく、効率よく開発をすすめていける環境が構築できたと思います。

また、パッケージを作成する方法については紹介していませんが、[electron-packager](https://github.com/electron-userland/electron-packager)をインストールしておけば、

```
$ electron-packager app/prod --platform=darwin --version=1.4.7
```

で簡単にパッケージを作成することができると思います。(Macの場合)


今回は、ClojureScript で Electron アプリを開発する一から作成しましたが、[@kara_d](https://twitter.com/kara_d) さんが作成している [descjop](https://github.com/karad/lein_template_descjop)というテンプレートを利用する方法もあります。 Main Process 側への figwheel の設定はありませんが、Window 環境での開発を考慮した設定などが記載されています。実際に開発を行う方は合わせて、確認してみることをお勧めします。

実際にアプリ開発を進めていくと、今回説明した構成や設定では、問題が出てくることもあると思いますが、ひとまず、開発を始める際の足掛かりとしていただければと思います。


# 参考資料

- Electron
  - [Documentation](http://electron.atom.io/docs/)
  - [Quick Start](http://electron.atom.io/docs/tutorial/quick-start/)
- ClojureScript
  - [Compiler Options](https://github.com/clojure/clojurescript/wiki/Compiler-Options)
  - [Quick Start](https://github.com/clojure/clojurescript/wiki/Quick-Start)