この記事はClojure Advent Calendar 201618日目の記事です。
前回はxorphitusさんのClojureでLINE BOTを作るためのSDKを作るでした!
背景
ClojureScriptからSPAを作る時、Reagentやomを良く使用しています。
これらを使う時、素のReact Componentを拝借して使いたい時が多々有るのですが、世に出回っているReact ComponentはECMAScript 6 modulesとして出回っている物が大半です。
しかし、ClojureScriptからこれらを使用する時に詰まることが多かったため、まとめておきます。
cljsjsでbundleされた物を使う
cljsjsでは、既にClojureScript向けにbundleされたライブラリが配布されています。
しかし、cljsjsで配布されていないライブラリを利用するには自分でcljsjsへの登録を行う必要があり、一つのReact Componentを使用するためだけの為にPRを提案しなければならないのはとても現実的ではありません。
よって、どうにかプロジェクト内で完結させる方法はないかと以下の方法を試しました。
ClojureScriptのJavaScript Module Support
ClojureScriptには(Alpha版ですが)CommonJS, AMD, ECMAScript 6 Module等のModulesを読み込み、ClojureScriptのModulesと同様に扱う機能があります。
例としてこのReact Componentをロードさせるには、
lein new reagent my-project
cd my-project
npm install react-progress-button
以上のようにReact Componentをインストールした上で、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を読み込めない上、もしこちらの手順の様にJSXを処理したとしても、こちらの仕様によりReact Component側のimportを書き換えない限りClojureScript側でインストールされているReactとReact Componentが必要とするReactが不整合を起こし、正常にロードする事は出来ませんでした。
よって、最終的に極めてdirtyな解決策に辿りつきました…
Webpackによりbundleする
Babel, Webpackを使いClojureScriptから直に使用できる形にbundleします。
プロジェクト直下に .babelrc
, webpack.config.js
を準備します。
{
"presets": ["es2015", "react", "stage-0"]
}
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に必要な設定を追記します。
: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です。
lein npm install
webpack
lein figwheel
総括
ClojureScriptからJavaScriptの外部モジュールやJSXを扱うのは簡単では無く、極力cljsjsで使える物を選んで使うのが良さそうでした。
また、もしもっとスマートな方法を知っていれば教えてください🙇