shadow-cljs

[shadow-cljs](https://github.com/thheller/shadow-cljs)というツールが話題になっていたので触ってまとめてみた。
npm界隈を知らないので認識が違うこともあるかもしれない。

バージョンは`0.18.7`を使用している。

## 目的
READMEによると以下の目的があって作られた物らしい

- ClojureScriptのデフォルトのビルドapiを親切な物で置換する
- ClojureScriptのビルドと既存のJavaScriptとのビルドツール間の連携を行いやすくする


## 特徴
- npm/yarnがあればインストールできる
    - Javaがインストールされていない場合は[node-jre](https://github.com/schreiben/node-jre)にフォールバックする
        - node-jreはnpm/yarnでインストールすることができる
- 1つビルドの設定から、開発用ビルド/リリースビルドのような異なるタスクが実行できる。
    - ClojureScriptのAPIの場合、タスク毎にそれぞれ異なるビルドの設定が必要で冗長になりがち
- 単一のcliツールとして利用することができる
- Clojureのライブラリとして使うこともできる

## 機能
- ビルドができる
- npmモジュールとの密な連携
    - npmモジュールを特別な設定無くnamespaceとして使える
    - externも大体ケアされる
- figwheelのようにファイルの変更を検知→ホットローディングが可能
    - ~~演出は無く、コンソール出力のみ~~ -> shadow-cljsロゴが表示されるようになった
- cljs-replが起動できる
- 依存関係を解決できる(内部でleiningenが使っている物を使用している)

## インストール

npm/yarnでインストールすることができる。

```sh
# yarn
yarn global add shadow-cljs

# npm
npm install -g shadow-cljs
```

## ざっくりとした利用方法

とりあえずcliツールとして使う場合のみを考慮する

### shadow-cljs.ednをプロジェクトのルートに用意する

- ビルドの設定ファイル
- `shadow-cljs init`でスケルトンを生成することができる
- 以下のような感じ

```clj
{:source-paths
 ["src"] ;; ソースコードがあるパスを書く

 :dependencies 
 ;; 1. Clojure,ClojureScriptはshadow-cljs本体に含まれるため指定しない
 ;; 2. Leiningenと同じ形式で指定する
 [[reagent "0.7.0"]]

 :builds
 {:my-app ;; build-id。複数可能
  {:target :browser ;; nodeやカスタムターゲットを設定できる
   :output-dir "shadow-out/js" ;; ビルドを出力をするディレクトリ
   :asset-path "/js" ;; 開発時の依存モジュールをGETしに行く際のルートディレクトリ
   ;; メインのネームスペースを指定する
   :modules {:main {:entries [my-app.app]}}
   ;; コードのホットリロード時に実行される関数を指定する
   :devtools {:before-load my-app.app/before-load
              :after-load my-app.app/after-load }
   ;;ClojureScriptコンパイラがサポートしているパラメータを渡せる
   :closure-defines {myapp.app.foo "foobar"}}}}
```

### ビルドする
- `shadow-cljs <compile|watch|cljs-repl|release> <build-id>` でビルドする
- `compile` - 開発時の設定で一度だけビルドする
- `watch` - ソースの変更を検知してビルドを行う。コードのホットリロードはこれで行う
- `cljs-repl` - ビルドにreplクライアントを含め、それに接続する
- `release` - ClojureScriptコンパイラで言うところの `:optimizations :advanced` でビルドする。
   - 開発用(ホッロリロード/repl)コードがビルドに含まれない

## 詳細

この辺を読んだら仕事のプロジェクトはビルドできた

- [README](https://github.com/thheller/shadow-cljs)
- [このWikiページ](https://github.com/thheller/shadow-cljs/wiki/ClojureScript-for-the-browser)

## 感想
- ClojureScriptの開発時に欲しい機能が1つにまとまっている
- leiningen/bootに依存していない
- ビルドの設定が開発時とリリース時で共通にできるのでスッキリする

以上の点からClojureScriptのアプリケーションを開発するだけであれば非常に魅力的なツールだという印象を持った。
Clojureのライブラリとしても利用できるようなので、好みが合うのであればfigwheelやcljsbuildをこれで置換しても良いと思う。

## 余談

1.　warningが親切だった

たまたまwarningが含まれるプロジェクトをビルドしてみたのだが、
出力が親切だったのが新鮮だった。

```
[:my-app] Build completed. (161 files, 45 compiled, 1 warnings, 23.37s)

------ WARNING #1 --------------------------------------------------------------
 File: clojure/reflect.cljs
--------------------------------------------------------------------------------
  29 |   [sym cb]
  30 |   (query-reflection (str "var=" (js/encodeURIComponent (str sym)))
  31 |                     #(cb (evaluate-javascript %))))
  32 | 
  33 | (defn macroexpand
-------^------------------------------------------------------------------------
 macroexpand already refers to: cljs.core/macroexpand being replaced by: clojure.reflect/macroexpand
--------------------------------------------------------------------------------
  34 |   "Queries the reflection api with a quoted macro form, then calls the
  35 |   callback function with the macroexpanded form, as a string."
  36 |   [form]
  37 |   (query-reflection (str "macroform=" (js/encodeURIComponent (str form))) println))
  38 | 
--------------------------------------------------------------------------------
```

2.　Readmeの内容が間違っているっぽかったので[プルリク送った](https://github.com/thheller/shadow-cljs/pull/86)