JavaScript
Clojure
Node.js
ClojureScript
More than 1 year has passed since last update.

shadow-cljsというツールが話題になっていたので触ってまとめてみた。
npm界隈を知らないので認識が違うこともあるかもしれない。

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

目的

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

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

特徴

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

機能

  • ビルドができる
  • figwheelのようにファイルの変更を検知→ホットローディングが可能
    • 演出は無く、コンソール出力のみ
  • cljs-replが起動できる
  • 依存関係を解決できる(内部でleiningenが使っている物を使用している)

インストール

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

# yarn
yarn global add shadow-cljs

# npm
npm install -g shadow-cljs

ざっくりとした利用方法

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

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

  • ビルドの設定ファイル
  • shadow-cljs initでスケルトンを生成することができる
  • 以下のような感じ
{: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)コードがビルドに含まれない

詳細

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

感想

  • 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の内容が間違っているっぽかったのでプルリク送った