boot という Clojure/ClojureScript(以下 CLJS) のビルドツールを使って、ClojureScript のフロントエンド開発環境構築をしたときの覚書です。
boot とは?
Clojure/CLJS のバージョン管理やタスク管理を一括でできるビルドツールです。
Clojure/CLJS のビルドツールには Leiningen と boot がよく使われますが、今回は boot を使ってみます。
この Leiningen や boot は、Ruby でいう Rake や Bundler、NodeJS でいう npm や Gulp、Grunt のようなものです。
boot を使用するためには、build.boot
というファイルを作成し、使用するライブラリやタスクを書いていきます。Ruby の Gemfile や NodeJS の package.json のような役割をするファイルですね。
boot の位置づけのイメージを掴むために他言語と比較してみましょう。
言語 | 言語のバージョン管理ツール(例) | ライブラリの管理ファイル(例) | タスク管理ファイル(例) | 環境構築コマンド(例) | タスク管理ツール(例) |
---|---|---|---|---|---|
Clojure/CLJS | boot | build.boot | build.boot | 不要(※) | boot |
Ruby | rbenv、rvm | Gemfile | Rakefile | bundle install |
rake |
NodeJS | ndenv、nvm | package.json | gulpfile.js | npm install |
gulp |
(※ ライブラリはタスク初回実行時にインストールされる)
boot のインストール
Mac OS の場合は brew で入ります。
$ brew install boot-clj
$ boot -V
#http://boot-clj.com
#Wed Feb 03 11:29:32 JST 2016
BOOT_CLOJURE_NAME=org.clojure/clojure
BOOT_CLOJURE_VERSION=1.7.0
BOOT_VERSION=2.5.5
他の環境など詳しくは ドキュメントをご参考ください。
環境構築
新規にプロジェクトを作ります。build.boot
、html/index.html
、src/cljs/my_cljs_project/core.cljs
の 3 つのファイルを作っておきます。
my-cljs-project/
├── build.boot
├── html
│ └── index.html
└── src
└── cljs
└── my_cljs_project
└── core.cljs
今回 boot でやりたいことは以下の 4 つです。
- 開発環境構築(
boot dev
コマンド)- ローカルで Web サーバを立てる
- ファイル更新を監視して変更時にコンパイルさせる
- コンパイル後にブラウザ自動リロードさせる
- CLJS の repl 環境構築
- プロダクション用ビルド(
boot build
コマンド)- コンパイルして minify する
boot の設定ファイルを書いていく
では設定ファイルを書いていきます。build.boot
で CLJS のバージョン管理、ライブラリのバージョン管理、タスクを追加していきます。
;; 使用言語とライブラリのバージョン管理
(set-env!
;; ソースファイルのパス
:source-paths #{"src/cljs"}
;; 静的ファイルのパス
:resource-paths #{"html"}
:dependencies '[
;; CLJS のバージョン管理
[org.clojure/clojurescript "1.7.170"]
;; ローカルでサーバ立てるためのライブラリ
[adzerk/boot-cljs "1.7.170-3"]
[pandeiro/boot-http "0.7.0"]
;; ブラウザリロードするためのライブラリ
[adzerk/boot-reload "0.4.2"]
;; CLJS の repl 導入のためのライブラリ
[adzerk/boot-cljs-repl "0.3.0"]
[com.cemerick/piggieback "0.2.1"]
[weasel "0.7.0"]
[org.clojure/tools.nrepl "0.2.12"]
])
;; boot コマンドで使う CLJS のタスクを追加
(require '[adzerk.boot-cljs :refer [cljs]]
'[pandeiro.boot-http :refer [serve]]
'[adzerk.boot-reload :refer [reload]]
'[adzerk.boot-cljs-repl :refer [cljs-repl start-repl]])
;; 開発環境構築タスク(boot dev で実行される)
(deftask dev
"Launch Development Environment"
[]
(comp
(serve :dir "target"
:port 5678)
(watch)
(reload)
(cljs-repl)
(cljs :source-map true
:optimizations :none)
;; target ディレクトリにコンパイルされる
(target :dir #{"target"})))
;; プロダクション用ビルドタスク(boot build で実行される)
(deftask build
"Build for Production"
[]
(comp
(cljs :optimizations :advanced
;; global の namespace 衝突を避けるために (function(){...};)() で閉じる
:compiler-options {:output-wrapper :true})
;; build ディレクトリにコンパイルされる
(target :dir #{"build"})))
ソースファイル追記
HTML と CLJS にお決まりの呪文を書いておきましょう
index.html
<!doctype html>
<html>
<head>
<title>Hello, World!</title>
</head>
<body>
<h1>Hello, World!</h1>
<script src="main.js"></script>
</body>
</html>
core.cljs
(ns my-cljs-project.core)
(enable-console-print!)
(println "Hello, World!")
実行
ローカルで開発
boot dev
コマンドを実行してみます。
$ boot dev
Starting reload server on ws://localhost:52338
Writing boot_reload.cljs...
Writing boot_cljs_repl.cljs...
2016-02-03 11:22:27.093:INFO::clojure-agent-send-off-pool-0: Logging initialized @10658ms
2016-02-03 11:22:27.176:INFO:oejs.Server:clojure-agent-send-off-pool-0: jetty-9.2.10.v20150310
2016-02-03 11:22:27.208:INFO:oejs.ServerConnector:clojure-agent-send-off-pool-0: Started ServerConnector@20587634{HTTP/1.1}{0.0.0.0:5678}
2016-02-03 11:22:27.209:INFO:oejs.Server:clojure-agent-send-off-pool-0: Started @10774ms
Started Jetty on http://localhost:5678
Starting file watcher (CTRL-C to quit)...
nREPL server started on port 52340 on host 127.0.0.1 - nrepl://127.0.0.1:52340
Writing main.cljs.edn...
Compiling ClojureScript...
• main.js
Writing target dir(s)...
Implicit target dir is deprecated, please use the target task instead.
Set BOOT_EMIT_TARGET=no to disable implicit target dir.
Elapsed time: 16.374 sec```
ブラウザで http://localhost:5678
を開くと、画面とディベロッパーコンソールに Hello, World!
が出力されているのが確認できます。
ファイル変更監視と自動リロードの確認
core.cljs
の (println "Hello, World!")
を (println "Hello, CLJS!")
に変更して保存してみます。
即座にでコンパイルされてディベロッパーコンソールに Hello, CLJS!
が出力されるのが確認できます。
ブラウザと CLJS の repl を接続
別のシェルを開いて、boot repl -c
を実行し、repl が起動したら、(start-repl)
を入力し実行します。通信が確立すると、ブラウザのディベロッパーコンソールと接続できるようになります。
$ boot repl -c
Implicit target dir is deprecated, please use the target task instead.
Set BOOT_EMIT_TARGET=no to disable implicit target dir.
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.7.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_66-b17
Exit: Control+D or (exit) or (quit)
Commands: (user/help)
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Find by Name: (find-name "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Examples from clojuredocs.org: [clojuredocs or cdoc]
(user/clojuredocs name-here)
(user/clojuredocs "ns-here" "name-here")
boot.user=> (start-repl)
<< started Weasel server on ws://127.0.0.1:51643 >>
<< waiting for client to connect ... Connection is ws://localhost:51643
Writing boot_cljs_repl.cljs...
connected! >>
To quit, type: :cljs/quit
nil
cljs.user=>
ここまでで、ターミナルから repl でブラウザと通信できるようになりました。
試しにターミナルから以下を出力してみます。
cljs.user=> (js/console.info "Hello, from terminal")
nil
ブラウザのディベロッパーコンソールに、Hello, from terminal
と出力されたのを確認できました。
他にもこの repl から DOM 操作をしたり、CLJS ソースの関数を実行したりできます。
プロダクション用にビルドする
以下のコマンドを実行してみます。
$ boot build
minify されたファイルが build
ディレクトリに作られます。
build
├── index.html
├── main.js
└── main.out
├── ...
ビルド時は main.out
下の JS ファイルは不要になります。main.js
のみ読み込めば動くようになるので、プロダクション環境に index.html
と main.js
のみデプロイすれば動くようになります。
終わりに
ここまでで、CLJS でフロントエンド開発するための最小限構成の構築方法を紹介をしました。
それでは Enjoy, Clojure coding! :D