読者です 読者をやめる 読者になる 読者になる

Aqutras Members' Blog

株式会社アキュトラスのメンバーが、技術情報などを楽しく書いています。

RailsでCoffeeScriptを共存させつつES6やJSXを使う方法

こんにちは。いるかさんです。今回は技術系の記事となります!

社内のとあるRailsプロジェクトで、「ES6を使おうよ!」という動きが高まっています。
(ES6については、よしこさんのスライドが大変分かりやすいので、ぜひご一読ください。)

主に 「Coffee && JSX な lint がない」 という点と、 「Coffeeを使っていくよりES6を使うようにした方が余分な学習コストが減るし今後に生きる」 という点で検討されていました。
しかし、現在はCoffeeScriptで開発しているため、一度にES6へ置き換えるのは難しいです。
そこで、以下の要件を満たすように環境を整えました。

  • CoffeeScript+JSXは今までどおり使うことができる
  • ES6を使うことができる
  • ES6 で JSX を書くことができる (Reactを利用しているため)
  • CoffeeScriptからES6やES6なJSXを読み込んで使うことができる
  • ついでにnpmも使えたら嬉しいな(・∀・)

候補1 sprockets-es6

結論から先に書くと、sprockets-es6は実験用であり、実運用向けではありません。
sprockets-es6 (https://github.com/TannerRogalsky/sprockets-es6) は、
かなり手軽に導入できて、ES6を使うことができるライブラリです。
READMEに記載されているように、Gemfileに追記してbundleするだけで、ES6が使えました。
導入時に、sprocketsのバージョンが2系だと、bundle update sprocketsする必要があります。

gem 'sprockets-es6', require: 'sprockets/es6'

問題点

import/export が使えないという噂*1を見かけました。本当かどうかは調べていません。
また、READMEにも以下のように書かれています。そのためか、Issueも投げられるようになっていません。

This plugin is primarily experimental and will never reach a stable 1.0. The purpose is to test out BabelJS features on Sprockets 3.x and include it by default in Sprockets 4.x.
(このプラグインは実験的なものなので安定版を出すつもりはないよ!このプラグインの目的は、BabelJSの機能をSprockets3系で試すためなのだ。)

よって、製品に使うのには向いていないと言えるでしょう。

候補2 browserify-rails

browserify-rails https://github.com/browserify-rails/browserify-rails は、sprocketsを通して、
browserifyを利用して、CommonJSのモジュールの仕組み (requireとか使えるようになる) を実現してくれるライブラリです。
要するに、js でrequireが使えるようになる、ということです。
さらにbrowserifyには、トランスパイル機能がついています。これを使って、ES6やJSXを利用しよう!という目論見です。

f:id:Dltn:20160226153350p:plain

これを使ってあげれば、CoffeeScriptはそのままに、es6やjsxが使えそうですね! npmも使うことができるので、一石二鳥です(*°∀°)=3
今回は、当該プロジェクトではsprockets-coffee-reactを使っていたので、CoffeeScript はbrowserify-railsのトランスパイル設定を書かず、ES6やJSXだけ対象にしました。

導入

Gemfilebrowserify-railsを書いて、bundle installします。
さらに、npm initしてnpm install browserify browserify-incremental --save-devします。
これだけで導入は終わりです。

トランスパイルの設定

トランスパイルの設定は、config/application.rb に書きます。

  • Coffeeをトランスパイルしたい場合

npm install coffeeify --saveしてから、以下の設定を追加します。

config.browserify_rails.commandline_options = "-t coffeeify --extension=\".js.coffee\""
  • ES6 + JSX をトランスパイルしたい場合

npm install babelify babel-preset-es2015 babel-preset-react --save-devしてから、以下の設定を追加します。

config.browserify_rails.commandline_options = "-t babelify"

さらに、babel の設定を .babelrc に書きます。

{
  "presets": ["es2015", "react"]
}

これでトランスパイルも自在にコントロールできるようになりました。

CoffeeScriptや外部からES6で書かれたクラスなどを利用する

CoffeeScriptでは、ES6のimportが使えません。CoffeeScriptの文法エラーになるからです。
そこで、グローバル領域に置いておく方法と、requireする方法で、
CoffeeScriptや外部からES6の資源を利用することができました。

ES6側でグローバル領域に定義しておく

ES6/JSX側でwindowに引っ付けておくことで解決する方法です。
あまり綺麗な解決法じゃないですね(;´∀`)
react-railsreact_componentを使って、JSX + ES6 で書かれたReactコンポーネントを描画してみましたが、ちゃんと読み込めました。
これならどこからでも参照できるので、ライブラリ的なものはこれでいいかもしれません。

export default class Hoge extends React.Component {
/* 中略 */
}
window.Hoge = Hoge;

require して利用する

Coffeeのコンパイル時に、requireを用いても、ただの関数なので、エラーにはなりません。
そして、browserify-railsによって、requireをよしなにしてくれます。
これだと、それっぽく利用することができました。

  • Mofu.es6
export default class Mofu {
  static get hoge() {
    return 'bar';
  }
}
  • MofuUser.coffee
Mofu = require('./Mofu.es6').default
console.log(Mofu.hoge)

npmの利用

browserify-railsの導入により、npmを利用して、jsのライブラリを利用することができます。これは嬉しい!
適当にpackage.jsonを用意し、npm install hogehoge --saveしてあげれば、インストールされます。 node_modules.gitignoreに入れておきます。

  • package.json
{
  "name": "moffumofu",
  "dependencies": {
    "browserify": "~> 10.2.4",
    "browserify-incremental": "^3.0.1"
  }
}

npmでインストールした後は、使いたいファイルでrequireを使って読み込んであげれば使えます。

browserify-railsの問題点 テストの時間が長い

当該プロジェクトにbrowserify-railsを導入してCircleCIでテストしたところ、タイムアウトしました。
これは、事前にassets:precompileしておくことで回避*2できました。
ちなみに、ローカル環境でのテストは、そこまで時間がかかるという印象は受けませんでした。

(余談) JSX + ES6 でReactを書く時のつまづきポイント

既存の、Coffeeで書かれたReactComponentをJSX+ES6に書き直してみましたが、そのままではうまく動きませんでした。
以下の記事を参考にして、全て解決しました。感謝感謝。

getInitialState

getInitialStateは使われず、constructorの中で直接this.stateにハッシュを入れてやることで解決しました。そういう仕様らしい。

callback

callbackのthisが、オブジェクトのインスタンスじゃなくなってました。これは、.bind(this)をつけたり、アロー関数にして解決しました。

終わりに

今回の方法では、cssを含むライブラリをnpmでインストールしても、そのCSSをうまくRailsで使う方法をまだ見つけられていません。
おそらく、アセットパスの都合などもあり、一筋縄ではいかないだろうという感じです。
そこで、bower-railsを使うと、bower:install / bower:resolveでよしなにしてくれるらしいので、
次はbower-railsを導入して、cssも含むjsライブラリ関係のバージョン管理が楽にできるようにしていこうと思っています。

実は、このES6導入、社内ではPullRequest段階でまだマージされてません。アサインされてる人、レビュー( `・∀・´)ノヨロシク