Phoenix 1.6でSCSSを使いたい


先日あるPhoenixアプリをPhoenix 1.6に更新していたときに、SCSSをどのように設定すればよいのか戸惑ったのでメモします。ちなみになぜSCSSが必要だったかというと、BootstrapSCSSでカスタマイズしていたからです。また、Tailwindは使わない方針なのです。

English version

結論

phoenixframework/esbuild

  • 新規作成のPhoenix 1.6アプリでは何もしなくても設定済。
  • JSをビルドして、まとめてくれる。(生成物:priv/static/assets/assets/app.js
  • CSSがimportされている場合は、CSSをビルドして、まとめてくれる。(生成物:priv/static/assets/assets/app.css
  • esbuildの実行プログラムはphoenixframework/esbuildさえ正しく設定されていれば、自動的にインストールされる。
  • SCSSを使いたい場合な、以下のいずれかが必要になるそうです。
  • 開発時、 Phoenix.Endpoint watcherを用いることにより、アセットに変更があるたびに自動でビルドされるようにすることができます。
  • 本番用にはデプロイの都度mix assets.deployコマンドでコンパイルをすることになると思います。
  • たまに古い生成物が悪さをする場合があります。挙動が変だったらpriv/static/assets/を確認して、不要なファイルを削除してください。

esbuildの設定

3つのファイルに渡って設定されるようです。

  • mix.exs
    • elixir関連
  • config/config.exs
    • esbuild関連
    • 実行時の引数を指定
    • 使用したいesbuildの実行プログラムのバージョン
    • ビルドしたいファイル
    • ビルド生成物を置くディレクトリー
    • など
  • config/dev.exs

phoenixframework/esbuildの基本的な使い方については思っていたより簡単でした。ただ、初期設定のままだとSCSSが使えないのです。
どうしてもSCSSが使いたいときにはCargoSense/dart_sassが便利です。

CargoSense/dart_sass

注意点

最初、何も考えずにCargoSense/dart_sassのドキュメントに書いてあるコードをそのままつかっていたのですが、それでは正しくイゴかない場合があることが判明しました。別にドキュメントが悪いわけではなく、自分がどのように自分のアセットを処理したいのかの方針を明確する必要があるのです。
例えば、phoenixframework/esbuildCargoSense/dart_sassの両方のCSSビルド生成物はの行き先がpriv/static/assets/app.cssとなっていると、JSファイルにCSSがimportされている場合に競合して同じファイルを上書きすることになってしまうのです。これに気付くまで時間を燃やすことになります。

最初はいろいろゴニョゴニョしようと試行錯誤してしてましたが、結局phoenixframework/esbuildでJS、CargoSense/dart_sassでSCSSの分業体制がベストだという結論に達しました。一番簡単で確実です。

例えば、以下のような方針が考えられます。

そして、HTMLテンプレートでそのファイルを参照するようにします。

lib/my_app_web/templates/layout/root.html.heex
-  <link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/assets/app.css")}/>
+  <link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/assets/from_scss.css")}/>
   <script defer phx-track-static type="text/javascript" src={Routes.static_path(@conn, "/assets/app.js")}></script>

例えば、config/config.exsはこんな感じになりました。

config/config.exs
...

# Configure esbuild (the version is required)
config :esbuild,
  version: "0.14.1",
  default: [
    args: [
      "js/app.js",
      "--bundle",
      "--target=es2016",
      "--outdir=../priv/static/assets",
      "--external:/fonts/*",
      "--external:/images/*"
    ],
    cd: Path.expand("../assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ]

# https://github.com/CargoSense/dart_sass
config :dart_sass,
  version: "1.44.0",
  default: [
    args: [
      "scss/index.scss",
      "../priv/static/assets/from_scss.css" # デバグしやすいよう`app.css`以外の名前にする
    ],
    cd: Path.expand("../assets", __DIR__)
  ]

...

phoenixdiff.org

古いPhoenixをPhoenix 1.6更新したい場合にはphoenixdiff.orgが便利です。差分が確認できます。