ClojureScriptからES6 module+JSXのReact Componentを使用するまで

この記事は[Clojure Advent Calendar 2016](http://qiita.com/advent-calendar/2016/clojure)18日目の記事です。
前回はxorphitusさんの[ClojureでLINE BOTを作るためのSDKを作る](http://qiita.com/xorphitus/items/2df261ac5951d09a436b)でした！

# 背景

ClojureScriptからSPAを作る時、[Reagent](https://reagent-project.github.io/)や[om](https://github.com/omcljs/om)を良く使用しています。
これらを使う時、素のReact Componentを拝借して使いたい時が多々有るのですが、世に出回っているReact ComponentはECMAScript 6 modulesとして出回っている物が大半です。
しかし、ClojureScriptからこれらを使用する時に詰まることが多かったため、まとめておきます。

# cljsjsでbundleされた物を使う

[cljsjs](http://cljsjs.github.io/)では、既にClojureScript向けにbundleされたライブラリが配布されています。
しかし、cljsjsで配布されていないライブラリを利用するには自分で[cljsjsへの登録](https://github.com/cljsjs/packages/wiki/Creating-Packages)を行う必要があり、一つのReact Componentを使用するためだけの為にPRを提案しなければならないのはとても現実的ではありません。

よって、どうにかプロジェクト内で完結させる方法はないかと以下の方法を試しました。

# ClojureScriptのJavaScript Module Support

ClojureScriptには(Alpha版ですが)CommonJS, AMD, ECMAScript 6 Module等のModulesを読み込み、ClojureScriptのModulesと同様に扱う機能があります。

例として[このReact Component](https://github.com/mathieudutour/react-progress-button)をロードさせるには、

```clj
lein new reagent my-project
cd my-project
npm install react-progress-button
```

以上のようにReact Componentをインストールした上で、project.cljに追記します。

```clj:project.clj
~省略~
            {:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"]
             :compiler
             {:main "my-project.dev"
              :asset-path "/js/out"
              :output-to "target/cljsbuild/public/js/app.js"
              :output-dir "target/cljsbuild/public/js/out"
              :source-map true
              :foreign-libs [{:file "node_modules/react-progress-button/src/index.js"
                              :provides ["ProgressButton"]
                              :requires ["cljsjs/react"]
                              :module-type :es6}] ;; 追記
              :optimizations :none
              :pretty-print  true}}

~省略~
```

しかし、この機能だけではJSX形式で書かれたReact Componentを読み込めない上、もし[こちらの手順](http://mneise.github.io/posts/2015-08-04-week-9-and-10.html)の様にJSXを処理したとしても、[こちらの仕様により](http://dev.clojure.org/jira/browse/CLJS-1314)React Component側のimportを書き換えない限りClojureScript側でインストールされているReactとReact Componentが必要とするReactが不整合を起こし、正常にロードする事は出来ませんでした。

よって、最終的に極めてdirtyな解決策に辿りつきました…

# Webpackによりbundleする

Babel, Webpackを使いClojureScriptから直に使用できる形にbundleします。

プロジェクト直下に `.babelrc` , `webpack.config.js` を準備します。

```json:.babelrc
{
	"presets": ["es2015", "react", "stage-0"]
}
```

```js:webpack.config.js
module.exports = {
	entry: {
		ProgressButton: "./node_modules/react-progress-button/src/index.js"
	},
	module: {
		loaders: [
			{
				test: /\.js$/,
				loader: "babel-loader"
			}],
	},
	output: {
		path: __dirname + "/resources/public/js/vendor/",
		filename: '[name].js',
		libraryTarget: "umd",
		library: "[name]"
	},
	externals: {
		"react": {root: "React"}
	}
}
```

project.cljに必要な設定を追記します。

```clj:project.clj
  :npm {:dependencies [[webpack "1.13.1"]
                       [react "15.1.0"]
                       [react-dom "15.1.0"]
                       [babel-core "6.10.4"]
                       [babel-loader "6.2.4"]
                       [babel-preset-es2015 "6.9.0"]
                       [babel-preset-stage-0 "6.5.0"]
                       [babel-preset-react "6.11.1"]
                       [react-progress-button "5.0.0"]]} ;; 追記
~省略~
  :plugins [[lein-environ "1.0.2"]
            [lein-cljsbuild "1.1.1"]
            [lein-npm "0.6.2"] ;; 追記
            [lein-asset-minifier "0.2.7"
             :exclusions [org.clojure/clojure]]]
~省略~
  {:builds {:min
            {:source-paths ["src/cljs" "src/cljc" "env/prod/cljs"]
             :compiler
             {:output-to "target/cljsbuild/public/js/app.js"
              :output-dir "target/uberjar"
              :optimizations :advanced
              :pretty-print  false}}
            :app
            {:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"]
             :compiler
             {:main "es6-module-test.dev"
              :asset-path "/js/out"
              :output-to "target/cljsbuild/public/js/app.js"
              :output-dir "target/cljsbuild/public/js/out"
              :source-map true
              :language-in :ecmascript6
              :foreign-libs [{:file "resources/public/js/vendor/ProgressButton.js"
                              :provides ["ProgressButton"]
                              :requires ["cljsjs/react"]}] ;; 追記
              :optimizations :none
              :pretty-print  true}}
~追記~
```

後は開発前にWebpackを実行すればOKです。

```bash
lein npm install
webpack
lein figwheel
```

# 総括

ClojureScriptからJavaScriptの外部モジュールやJSXを扱うのは簡単では無く、極力cljsjsで使える物を選んで使うのが良さそうでした。
また、もしもっとスマートな方法を知っていれば教えてください🙇