Elixir Phoenix 1.6 Esbuild + SCSS
Tags: elixir, phoenix, esbuild, scss
Published: 2021-12-04 16:09:52

[phoenixframework/esbuild]: https://github.com/phoenixframework/esbuild
[flatpickr]: https://github.com/flatpickr/flatpickr
[CargoSense/dart_sass]: https://github.com/CargoSense/dart_sass
[phoenixdiff.org]: https://www.phoenixdiff.org/?source=1.5.13&target=1.6.2
[Bootstrap]: https://getbootstrap.com/
[SCSS]: https://sass-lang.com/
[Phoenix.Endpoint watcher]: https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#module-runtime-configuration

I want to leave a note about what I stumbled on in my setting up [SCSS] for my Phoenix 1.6 app.
I need SCSS because I enjoy customizing [Bootstrap] styles using [SCSS]. I am not planning to use Tailwind.

[にほんご](https://qiita.com/mnishiguchi/items/89ef66b7e3a9a07d9f49)

## TL;DR

- All explained in [Phoenix 1.6 Asset Management documentation](https://hexdocs.pm/phoenix/asset_management.html).
- The easiest way is to use [phoenixframework/esbuild] for JS only and use [CargoSense/dart_sass] for SCSS.
- We need to think through how we want to build our assets beforehand rather than just copy and paste snippets.

## [phoenixframework/esbuild]

- preconfigured in a newly-generated Phoenix 1.6 app
- bundles JS files into `priv/static/assets/assets/app.js`
- bundles CSS files into `priv/static/assets/assets/app.css` in case CSS files are imported into some JS files
- the esbuild executable gets installed automatically as long as [phoenixframework/esbuild] is configured correctly
- for [SCSS] we need use either:
  - [CargoSense/dart_sass]
  - esbuild plugins
- typically in development, we use [Phoenix.Endpoint watcher] so esbuild can build assets every time we make changes in our assets
- typically in production, we compile assets

**configure esbuild**

Three files are involved for configuring [phoenixframework/esbuild].

- `mix.exs`
  - elixir-related settings
- `config/config.exs`
  - esbuild-related settings
  - we need to specify as args:
    - the version of the esbuild executable we want to use
    - input file
    - output dir
    - etc
- `config/dev.exs`
  - [Phoenix.Endpoint watcher] specific settings for development

It is a lot easier than I expected. But by default esbuild does not know how to handle [SCSS] files. [CargoSense/dart_sass] comes in handy when we really want to use [SCSS].

## [CargoSense/dart_sass]

- similar to [phoenixframework/esbuild], but designed specially for [SCSS]

**configure esbuild**

Just like [phoenixframework/esbuild], three files are involved for configuring [CargoSense/dart_sass].

- `mix.exs`
- `config/config.exs`
- `config/dev.exs`

Initially I just copied and pasted all the snippets from the ducumentation thoughtlessly and one issue occurred. Because when both [phoenixframework/esbuild] and [CargoSense/dart_sass] are used and they both output the CSS build results to the same file `priv/static/assets/app.css`, which means they keep on overriding that same file.

After all, I realized the following:

- the easiest way is to use [phoenixframework/esbuild] for JS only and use [CargoSense/dart_sass] for SCSS
- it is nice to give the destination file a name other than `assets/app.css` for debuggability later on (E.g., `"assets/from_scss.css"`)

Then our HTML template reference those files separately.

```diff
<!-- 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>
```

My `config/config.exs` ended up looking like this.

```elixir
# 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"
    ],
    cd: Path.expand("../assets", __DIR__)
  ]
...
```

## [phoenixdiff.org]

If we want to migrate an app from Phoenix 1.5 to Phoenix 1.6, [phoenixdiff.org](https://www.phoenixdiff.org/?source=1.5.13&target=1.6.2) is a nice tool. It reveals all the changes between different versions.

:tada::tada::tada: