🤔

Elixirのesbuildラッパーは何をしているのか

2021/12/09に公開

この記事はfukuoka.ex Elixir/Phoenix アドベントカレンダー Advent Calendar 2021の9日目です🎄

8日目は@Gsannさんの【Elixir】ElixirとOOP、プログラミングスタイルの違い でした。

今日のテーマ

アドベントカレンダー1日目にPhoenix v1.6の変更点をまとめました。

https://zenn.dev/koga1020/articles/940d3be7fd9a31294ac1

この記事の中で紹介している アセット管理の章 冒頭で次のように記述しています。

Phoenix v1.6からは、新しいアプリケーションはesbuildを使い、Elixirのesbuildラッパーを介してアセットを準備します。このように esbuild と直接統合されたことで、新しく生成されたアプリケーションは、Node.jsや外部のビルドシステム(Webpackなど)に依存することがなくなりました。

Phoenix v1.6ではwebpackに代わり、esbuildを利用したJavaScriptのバンドルが標準仕様となりました。

そこでこの記事では、esbuildとはなんぞや、またそのラッパーとはなんぞやというのをまとめてみたいと思います。

esbuildとは

esbuildはGolang製のJavaScriptのbundlerです。extremely fast と謳ってあることから、パフォーマンスがウリなようです。

An extremely fast JavaScript bundler

https://esbuild.github.io/

three.jsというJavaScriptの3Dツールを10回復製したもの(?)をバンドルするのにかかった時間がグラフで表示されており、爆速具合がアピールされています...!

Image from Gyazo

なぜそんなにも速いの?という質問はFAQにまとまっています。

https://esbuild.github.io/faq/

  • Goで記述されており、ネイティブコードにコンパイルされる
  • 並列処理を行なっている
  • 0からパフォーマンスを意識して作った

などなど、高速な理由が挙げられています。

そんなesbuildですが、Phoenix v1.6からはwebpackの代わりとして標準搭載されました。FAQの「Used by other projects」の部分にもPhoenixが事例として挙げられています!なんだか嬉しい!

Used by other projects
The API is already being used as a library within many other developer tools. For example, Vite and Snowpack are using esbuild to transform TypeScript into > JavaScript and Amazon CDK (Cloud Development Kit) and Phoenix are using esbuild to bundle code.

このesbuildがPhoenixでどのように利用されるのか、深堀りしてみましょう。

Elixirのesbuildラッパー

Phoenix Frameworkのorganizationでesbuildという同じ名前でライブラリが実装されています。

https://github.com/phoenixframework/esbuild

先に紹介したGolang製のesbuildをElixirプロジェクトでも楽に導入できるように、インストール〜実行までを支援してくれます。

Esbuild.install/0を見るのが1番分かりやすいのですが、

  • 実行したos、architectureの情報を取得(これでDLすべき実行ファイルを特定)
  • npmで公開されている実行ファイルをダウンロード、
  • _build に保存(デフォルトの挙動)

という流れで実行されるようです。

本家esbuildにもドキュメントに記載がある方法ですね。

あとは _build にDLした実行ファイルをOptionをよしなにつけて System.cmd/3 で実行します(コード参考

こう見ると「実行ファイルをDLして _build に置いて実行する」という意外とシンプルな実装です。これがEsbuildライブラリが担っている役割です。

Phoenixと組み合わせる

Phoenix.Endpointには :watcher というオプションがあり、Phoenixサーバーの起動と合わせて、Elixir関数の実行や、任意のコマンドを実行できます。

:watchers - a set of watchers to run alongside your server. It expects a list of tuples containing the executable and its arguments. Watchers are guaranteed to run in the application directory, but only when the server is enabled. For example, the watcher below will run the "watch" mode of the webpack build tool when the server starts. You can configure it to whatever build tool or command you want:

https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#module-runtime-configuration

phoenix v1.6ではこのwatcherの仕組みを活用して、dev.exs にesbuildのダウンロードと実行の設定があらかじめ組み込まれています。

config/dev.exs
  watchers: [
    # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
    esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]}
  ]
  1. Esbuildライブラリの install_and_run/2 を実行してサーバー起動時にesbuildのDLと実行まで行う
  2. esbuildは --watch オプション付きで実行

という流れです。Phoenixが行なっているのはサーバー起動時にElixirの Esbuild ライブラリを介して esbuild を実行するまでで、変更の監視自体はesbuild側で行なっているというところがポイントです。

この仕組みにより、ユーザーがあまり意識せずとも「開発環境でPhoenixサーバーを起動するだけでassets/配下のビルドと監視が走るぞ...!」となるわけです。

dev環境でサーバーを起動後、実際に _build ディレクトリの中を見てみるとesbuildの存在が確認できると思います。

$ ls -1 _build       
dev
esbuild-darwin-64

また、本番環境にdeployする際に実行する mix assets.deploy もオプションの違いはありながらも、結局はEsbuildライブラリを介して esbuild を実行しています。

mix.exs
  defp aliases do
    [
      ...
      "assets.deploy": ["esbuild default --minify", "phx.digest"]
    ]
  end

まとめ

Phoenix v1.6で標準搭載されたesbuildについて紹介しました。「結局は _build に落としてきた実行ファイルを実行している」というのが分かれば色々と読み解きやすいと思います。

明日は@tuchiroさんの記事です。お楽しみに!

Discussion