Phoenix : changer de bundler
Tags: elixir, phoenix, webpack, parcel
Published: 2019-03-29 20:01:55

---
title: Phoenix : changer de bundler
published: true
description: Exemple de changement de bundler au sein de Phoenix en utilisant Parcel plutôt que Webpack
cover_image: https://thepracticaldev.s3.amazonaws.com/i/nwfjogn36lospzcf659y.png
canonical_url: https://maximeblanc.fr/blog/phoenix-changer-de-bundler
tags: #elixir, #phoenix, #webpack, #parcel
---

Depuis sa version 1.4, Phoenix embarque Webpack pour bundler les différentes dépendances. Nous allons voir qu'il est facile de changer d'outil en prenant [Parcel](https://parceljs.org) comme exemple.
Cet outil a l'avantage de ne pas nécessiter de configuration pour fonctionner.

## Initialisation

Créons un nouveau projet, sans "ecto" pour ne pas avoir besoin de configurer une base de données.

```shell
mix phx.new app --no-ecto
```

Lançons le serveur de Phoenix.

```shell
mix phx.server
```

Phoenix exécute Webpack pour nous et écoute les changements de fichiers pour nous apporter les modifications en hot reload.

![phoenix-launch-webpack](https://thepracticaldev.s3.amazonaws.com/i/e5vss7cdew0xx0rzvtyo.png)

Si j'édite le fichier `assets/css/app.css` pour changer la couleur de fond du body on peut voir le changement sans recharger la page.

```css
/* This file is for your main application css. */

@import "./phoenix.css";

body {
  background: darkgray;
}
```

![webpack-change-color](https://thepracticaldev.s3.amazonaws.com/i/m9gzbiqplq37po3bghfn.png)

Notre but est donc de garder ce comportement, qui offre un confort de développement, mais en utilisant un autre outil.

Webpack build nos assets à destination de `priv/static`. Phoenix nécessite que nos fichiers statiques soient buildés à destination de ce dossier. [Cela reste néanmoins configurable](https://hexdocs.pm/plug/Plug.Static.html)

![webpack-build](https://thepracticaldev.s3.amazonaws.com/i/rddnm8321om1epvab885.png)

Avant de continuer coupons notre serveur et supprimons le dossier généré par Webpack pour partir sur une base propre

```shell
rm -rf priv/static
```

## Configuration de Parcel au sein de Phoenix

Installons Parcel ainsi qu'un plugin magique dont le détail ne rentre pas dans le cadre de cet article.

```shell
cd assets/ && npm install --save-dev parcel-bundler parcel-plugin-static-files-copy
```

Dans le fichiers `config/dev.exs` il faut remplacer

```elixir
config :app, AppWeb.Endpoint,
  http: [port: 4000],
  debug_errors: true,
  code_reloader: true,
  check_origin: false,
  watchers: [
    node: [
      "node_modules/webpack/bin/webpack.js",
      "--mode",
      "development",
      "--watch-stdin",
      cd: Path.expand("../assets", __DIR__)
    ]
  ]
```

par

```elixir
config :app, AppWeb.Endpoint,
  http: [port: 4000],
  debug_errors: true,
  code_reloader: true,
  check_origin: false,
  watchers: [
    node: [
      "node_modules/.bin/parcel",
      "watch",
      "js/app.js",
      "-d",
      "../priv/static",
      cd: Path.expand("../assets", __DIR__)
    ]
  ]
```

Désormais lorsque nous lançons un `mix phx.server` nous pouvons voir que Phoenix lance désormais Parcel.

![phoenix-launch-parcel](https://thepracticaldev.s3.amazonaws.com/i/vrnxkb0eqfiesej2yogc.png)

Et le dossier de destination est bien `priv/static`

![build-parcel-priv](https://thepracticaldev.s3.amazonaws.com/i/i9nujjx3va2vkbgdaus2.png)

Mais malheureusement, nos css et js ne sont plus chargés

![broken-page](https://thepracticaldev.s3.amazonaws.com/i/xdg3nkxf08k5a93kg2e1.png)

Webpack générait un dossier `css` et un dossier `js` contenant chacun leur bundle. Parcel se passe des dossiers intermédiaires. Il faut donc aller mettre à jour les chemins des fichiers importés dans le template `lib/app_web/templates/layout/app.html.eex`.

On remplace

```elixir
<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
  </head>
  <body>
    ###
    <script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
  </body>
</html>
```

par

```elixir
<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="stylesheet" href="<%= Routes.static_path(@conn, "/app.css") %>"/>
  </head>
  <body>
    ###
    <script type="text/javascript" src="<%= Routes.static_path(@conn, "/app.js") %>"></script>
  </body>
</html>
```

Et 🎉, ça ne marche pas 😢.

Ce comportement vient du fait que Phoenix autorise uniquement les fichiers respectant certains patterns à être servi comme fichiers statiques. Ces patterns sont configurables dans le fichiers `lib/app_web/endpoint.ex`

```elixir
  plug Plug.Static,
    at: "/",
    from: :app,
    gzip: false,
    only: ~w(css fonts images js favicon.ico robots.txt)
```

Cette configuration signifie que seul les fichiers présents dans les dossiers `css` `fonts` `images` `js` ou les fichiers `favicon.ico` et `robots.txt` sont autorisés à être servi par Phoenix. Ces dossiers doivent bien entendu être présent dans `priv/static` 😉.

```elixir
  plug Plug.Static,
    at: "/",
    from: :app,
    gzip: false,
    only: ~w(app.css fonts images app.js favicon.ico robots.txt)
```

Comme nous n'avons pas de dossiers intermédiaires `css` et `js` nous les remplaçons par les fichiers `app.css` et `app.js` généré par Parcel.

Maintenant l'application fonctionne correctement. Si j'édite le fichier `assets/js/app.js` pour y ajouter un `console.log`

```js
import "phoenix_html"
import css from "../css/app.css"

console.log("Meeeeeeeeh")
```

Un "Meeeeeeeeh" apparait dans la console de notre navigateur sans avoir besoin de recharger la page 🎉.

![meeeeeh](https://thepracticaldev.s3.amazonaws.com/i/hfxw2yb5q9qzcrz0joxh.png)

## Nettoyage

Maintenant que nous avons ajouté Parcel il ne nous reste plus qu'a supprimer les vestiges de Webpack :

```shell
cd assets && npm uninstall babel-loader copy-webpack-plugin css-loader mini-css-extract-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin webpack webpack-cli && rm webpack.config.js
```

## Conclusion

Peut importe que vous choisissiez de garder Webpack, d'utiliser Rollup, Parcel… Si vous choisissez de modifier la configuration de votre bundler, il faut penser à :

- configurer correctement son bundler (facile avec Parcel)
- changer le script surveillant les modifications de fichiers (dev.exs)
- veiller à ce que le fichiers statiques puissent être servi correctement (endpoint.ex)
- modifier les chemins dans les templates (app.html.eex)

## Liens utiles

- Je me suis inspiré de [ce dépôt Github](https://github.com/ben-pr-p/elixir-phoenix-parcel-example). La base de code reprend la structure d'une ancienne version de Phoenix, ne l'utilisez pas tel quel si vous voulez être à jour.
- [Mon ancien article](https://medium.com/@_MaximeBlanc/phoenix-changer-de-bundler-e53f00110740)
- [La documentation sur la configuration du Endpoint](https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#module-runtime-configuration)