# Elixir Phoenix 1.6 Esbuild + SCSS

[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/

One day I was trying to upgrade one of my Phoenix 1.5 apps to Phoenix 1.6. In that app, I customize [Bootstrap] styles using SCSS.
I want to talk about what I found and stumbled on when setting up SCSS.

## TIL

- All explained in [Phoenix 1.6 Asset Management documentation](https://hexdocs.pm/phoenix/asset_management.html).
- For JS and plain CSS, [phoenixframework/esbuild] with default settings is good
- For SCSS, we can use both [phoenixframework/esbuild] and [CargoSense/dart_sass], but we need to think through how we want build our assets rather than just copy snippets.

## [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.

## [phoenixframework/esbuild]

- builds and bundles JS and CSS files
- preconfigured in a Phoenix 1.6 app
- installation of the esbuild executable is done 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 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 configuration
  - `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`
    - watcher-specific settings for development

For example, here is what we would do when we want to use [flatpickr] (javascript datetime picker).

**install the library from NPM**

```
cd my-phoenix-app

npm install --prefix assets --save flatpickr
```

**import the library in JS**

- Esbuild will find libraries from `node_modules`.
- CSS can be imported to a JS file as well.

```js
// some js file where we want to use flatpickr

// import js
import flatpickr from 'flatpickr'

// import css
import 'flatpickr/dist/flatpickr.css'

// do something with flatpickr
flatpickr(this.el, {})
```

It is a lot easier than I expected. But one is that by default exbuild does not know how to handle SCSS files.

## [CargoSense/dart_sass]

- similar to [phoenixframework/esbuild], but specially for SCSS
- Three files are involved for configuration just like [phoenixframework/esbuild]
  - `mix.exs`
  - `config/config.exs`
  - `config/dev.exs`

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

After all, I ended up on this strategy.

- make `assets/scss` directory and put all the `.scss` files in it
- let [CargoSense/dart_sass] compile `assets/scss/index.scss` and output `assets/scss/index.css`
- import `assets/scss/index.css` in `assets/js/app.js`

This way there will be no conflict between [phoenixframework/esbuild] and [CargoSense/dart_sass].
[CargoSense/dart_sass] is in the position of helping [phoenixframework/esbuild] with comiling SCSS, and once SCSS is converted to CSS, the rest of work is done by [phoenixframework/esbuild].

Finally, my `config/config.exs` looks 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", "scss/index.css"],
    cd: Path.expand("../assets", __DIR__)
  ]

...
```

And I add the intermediate `assets/scss/index.css` file in `.gitignore`

```bash
# Ignore assets that are produced by build tools.
/priv/static/assets/
/assets/scss/*.css
```

:tada::tada::tada:

