2020-01-03

opal-webpack-loaderについて

この記事はOpal (Ruby-to-JavaScript compiler) Advent Calendar 2019の2日目の記事として書こうとしたものです。残念ながら記事を完成させる前に新しい年を迎えてしまったのですが、折角なので記事を完成させて公開しようと思います。

opal-webpack-loader

OpalにはRack middlewareとして動くアプリケーションサーバーとしての機能があります。この機能はSprocketsをつかってRubyからJavaScriptに変換してアセットを提供します。 ところが、この機能はOpalに組み込みになったり、gemとしてOpal本体とは別に提供されたりということ繰り返しています。(現在はopal-sprockets gemとして独立しています。) Railsで開発している人は知っていると思いますが、Sprocketsを使って開発する場合、開発時にはSproketsによるコード変換は都度行なわれるため、ソースコードを変更してもサーバーを再起動せずにアプリケーションに反映されますし、プロダクションに対してはプリコンパイルをすることで変換済みのコードをデプロイすることが出来るようになっています。 しかし、どういうわけかopal-sprocketsにはアセットをプリコンパイルする機能がありません。もちろん自前でSprocketsを呼びだすコードを書けばプリコンパイルすることはできるはずですが…

OpalはRubyで書かれたコードをJavaScriptに変換しますからコンパイルのコストがとても大きいです。 いわゆるAltJSと呼ばれる言語はJavaScriptに変換されることを前提に言語設計されているので多くの場合はコンパイルのコストが低く抑えられています。 (TypeScriptのように型チェックに大きなコストがかかるものもありますけどね!) コンパイルのコストが大きいとアセットをプリコンパイル出来ないことは大きなマイナスです。

RailsでもWebpackerが導入されたことにみられるように、WebのフロントエンドのアセットのビルドにはWebpackが主流になっています。 OpalでもSprocketsの代りにWebpackが使えることは大変意義のあることです。opal-webpack-loaderはOpalのコンパイルを可能にするWebpackのPluginです。 Webpackを使いますので、Webpackの機能を使ってアセットのプリコンパイルが出来ますし、環境に応じてアセットのURLを切り替える機能も提供しています。

インストール

opal-webpack-loaderは当然ですがWebpackが必要です。Webpackを使えるようにするためには、JavaScriptの実行環境やエコシステムを手に入れる必要があります。ここではyarn(もしくはnpm)を使える環境が手に入れば充分です。ここまではみなさんの環境に依存する話ですので個々に解決してくださいね。 それと、もちろんRubyとgemが使える環境も必要です。もうすぐクリスマスですので間も無くリリースされるRuby2.7のリリース準備版を手に入れるのもよいでしょう:P

それではgemをインストールしましょう。

$ gem install opal-webpack-loader

Opalがまだインストールされていない場合は依存関係からOpalもインストールされるのでこれで準備が整いました。

セットアップ

opal-webpack-loaderにはすぐ使いはじめるためのcliツールが付属しているので簡単にはじめられます。

$ mkdir owl-sample && cd owl-sample
$ owl-install flat

このコマンドは、カレントディレクトリにボイラープレートのコードを生成しますので、必ずデレクトリを作ってから実行してくださいね。

いくつかのファイルが作られるとともに以下のメッセージが表示されます。

Add the following lines to your Gemfile:

  gem 'opal', github: 'janbiedermann/opal', branch: 'es6_modules_1_1'
  gem 'opal-webpack-loader', '~> 0.9.9'

owl currently works only with above opal branch. If you have a existing "gem 'opal'", please replace it with above line.

Also check above output for files ending in '_owl_new' and merge their contents with the existing files.

After that run according to your preference either:

  yarn install

or:

  npm install

and then:

  bundle install

For further instructions see http://github.com/isomorfeus/opal-webpack-loader

メッセージにしたがってGemfileをつくります。 実はopal-webpack-loaderはOpalのまだマージされていない機能を使って実装されています。作者のフォークを使っています。

Gemfile

gem 'opal', github: 'janbiedermann/opal', branch: 'es6_modules_1_1'
gem 'opal-webpack-loader', '~> 0.9.9'

そして、以下のコマンドを実行しましょう。

$ yarn install
$ bundle install

Opalのコードを実行しましょう

開発用のサーバーを起動してみましょう。 次のコマンドを実行します。

$ yarn run development

webpackの開発用サーバーが起動してコンパイルが走ります。 コンパイルが問題なく終ったらブラウザで

http://localhost:3035/assets/application.js

にアクセスしましょう。 コンパイルされたJavaScriptのコードが見られるはずです。

このままでは何も起りませんので、Opalのコードを書いて実行してみましょう。

opal/application.rb

require "native"

$$.document.addEventListener("DOMContentLoaded") do
  $$.document.getElementsByClassName("content")[0].innerHTML = "<h1>Hello Opal</h1>"
end

このOpalのコードを読み込むようにopal/opal_loader.rbに次の一行を追加します。

opal/opal_loader.rb

require "application"

そして次のようなhtmlファイルを作成してブラウザで読みこんでみましょう。

index.html

<!doctype html>
<html>
<body>
  <div class="content"></div>
  <script type="text/javascript" src="http://localhost:3035/assets/application.js"></script>
</body>
</html>

どうでしょう?期待通りの結果になりましたか?

明日はプロダクションのためにプリコンパイルする方法とsinatraを使って実際のWebアプリケーションのように動かすようにします。