Esbuild, le bundler incroyablement rapide 💹 et prometteur 📈 !

#webpack#js#bundler
Article disponible en: English

Cela fait plusieurs annĂ©es que je joue avec les bundlers JS. Restant convaincu de la nĂ©cessitĂ© de l’usage de ces outils (ne me laissez pas croire que vous ne packagez pas vos module JS en prod 😅), j’ai beaucoup jouĂ© avec webpack. Notamment pour des sujets de performance, d’optimisation et d’usage de plugins custom.

Je pense encore qu’en 2021, webpack reste la solution la plus industrielle et aboutie pour bundler mes applications web. J’entends que les outils comme parcel et rollup restent de bonnes alternatives. Cependant, webpack a probablement la plus grosse communautĂ© et est utilisĂ© par de nombreux projets.

Mais ne nous voilons pas la face, aujourd’hui on se satisfait de ces outils de bundling malgrĂ© des performances qui restent assez pauvres. Je travaille tous les jours sur un projet de plusieurs milliers de “modules” rĂ©solus par webpack et c’est parfois une souffrance đŸ„±.

Malgrés une utilisation intensive de cache et de workers, webpack montre certaines limitations pour packager des larges applications.

En quoi esbuild parait intéressant ?

Je ne vois pas de façon plus simple de l’exprimer que de vous expliquer simplement:

La premiĂšre fois oĂč j’ai lancĂ© esbuild sur mon app web de test, j’ai cru qu’il avait plantĂ© alors qu’en fait il s’était exĂ©cutĂ© Ă  une vitesse absoluement dingue.

Pour l’installer c’est pas compliquĂ© :

yarn add -D esbuild
npm install --save-dev esbuild

Ou bien mĂȘme avec NPX

npx esbuild --version

Étant Ă©crit en Go, sachez qu’une version WASM et des binaires pour les principales architectures sont disponible. esbuild fait le pari du natif de Go pour profiter un maximum de solutions de parallĂ©lisation et une meilleure gestion de la mĂ©moire.

Un API pauvre by design

Globalement l’API d’esbuild est vraiment simple, en 30 minutes vous avez lu toute la doc des paramĂ©trages possibles. On est bien loin des 3-4 heures nĂ©cessaires pour lire l’ensemble de la doc d’un webpack par exemple. MalgrĂ© une configuration qui pourrait paraitre limitĂ©e, je reste agrĂ©ablement surpris. J’ai l’impression qu’on est vraiment pas loin d’avoir la “juste grammaire” dont on a besoin pour faire du bundling.

esbuild propose 3 modes de consommation :

CLI

esbuild app.jsx --bundle --minify --sourcemap --target=chrome58,firefox57,safari11,edge16

GO

package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:       []string{"app.jsx"},
    Bundle:            true,
    MinifyWhitespace:  true,
    MinifyIdentifiers: true,
    MinifySyntax:      true,
    Engines: []api.Engine{
      {api.EngineChrome, "58"},
      {api.EngineFirefox, "57"},
      {api.EngineSafari, "11"},
      {api.EngineEdge, "16"},
    },
    Write: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

JS

require('esbuild').buildSync({
  entryPoints: ['app.jsx'],
  bundle: true,
  minify: true,
  sourcemap: true,
  target: ['chrome58', 'firefox57', 'safari11', 'edge16'],
  outfile: 'out.js',
})

À mes yeux le CLI reste hyper pratique pour tester des choses, mais dans un usage plus “industriel” on prĂ©fĂšrera quand mĂȘme le format JS ou GO.

Des mécaniques de plugins

Evan Wallace le crĂ©ateur et core mainteneur d’esbuild ne s’en cache pas, il ne souhaite pas que son outil rĂ©ponde Ă  100% des besoins qu’on peut avoir dans le monde du web. Cependant, cela ne signifie pas qu’on ne peut pas se servir de cette outil dans des cas spĂ©cifiques.

Comme on peut le voir avec les autres bundler, esbuild propose la mĂ©canique de plugins qui vous permettent beaucoup de choses. Pour Ă©viter de maintenir tous ces besoins spĂ©cifiques, le crĂ©ateur compte donc sur la communautĂ© pour crĂ©er tous les plugins qu’on peut souhaiter. Et clairement, la communautĂ© est la, je vous laisse voir cette page qui liste quelques plugins.

Les features les plus intéressantes

Je ne vais pas ici citer les features qui me semblent le coeur d’un bundler Web comme le code splitting, l’injection, la minification. Cependant, j’ai Ă©tĂ© Ă©tonnĂ© par quelques features qu’on ne retrouve pas ailleurs.

Une architecture simple Ă  comprendre

Clairement, ce qui fait la force d’esbuild par rapport Ă  ses concurrents est son architecture qui peut se rĂ©sumer simplement. On comprend bien qu’en alliant parallĂ©lisation des Ă©tapes du build et reduction du nombre de lecture de l’AST. Je vous invite Ă  lire plus d’explications dans la doc.

schéma d'architecture d'esbuild

Browser targets

Par defaut esbuild vous permet de définir la cible de votre compilation. Quel niveau de javascript vous souhaitez atteindre ?

Habituellement on utilise une suite d’outils comme @babel/preset-env et une browserlist pour faire en sorte de gĂ©nĂ©rer le JS compatible avec notre ciblage. Babel c’est gĂ©nial, je l’utilise tous les jours mais l’amoncellement d’outils diffĂ©rents pour le bundling n’est clairement pas une bonne solution Ă  mes yeux. Cela ajoute beaucoup de complexitĂ© :

  • au lieu d’apprendre a utiliser un simple outil de bundler, je dois apprendre en plus un outil de transpilation ciblĂ©
  • j’ai Ă  maintenir deux dĂ©pendances
  • passer par une librairy tierse peut rĂ©duire les performances (c’est un peu le pari d’esbuild)

Le mode server

esbuild est tellement rapide qu’il peut se permettre de vous exposer un server HTTP sur un dossier qui contient le rĂ©sultat de votre compilation Ă  chaque requĂȘte. Les autres outils se basent en gĂ©nĂ©ral sur un mode watch qui surveille les fichiers qui changent pour lancer un build.

Le mode watch existe aussi avec esbuild, mais le mode serve me parait encore plus sympa car il vous suffit de refresh votre browser pour avoir la derniĂšre version de votre application en local.

require('esbuild')
  .serve(
    {
      servedir: 'www',
    },
    {
      entryPoints: ['src/app.js'],
      outdir: 'www/js',
      bundle: true,
    }
  )
  .then((server) => {
    // Call "stop" on the web server when you're done
    server.stop()
  })

Mais du coup on arrĂȘte tout et on part la dessus ?

Gagnons du temps, la réponse pour moi est clairement non.

Comme le dit le crĂ©ateur dans la FAQ de la doc en toute honnĂȘtetĂ©, le projet n’est pas Ă  considĂ©rer comme Ă©tant en alpha. Cependant, l’outil en lui-mĂȘme ne possĂšde pas encore toutes les features qui ferait de lui un bon remplacant des bundler de la gĂ©nĂ©ration prĂ©cĂ©dente. Je pense notamment Ă  l’absence de HMR natif, ou bien encore un code splitting perfectible.

Il faut cependant ne pas rester fermĂ© sur cette question. Clairement esbuild a de trĂšs gros points forts qui manquent Ă  l’écosystĂšme actuel. La communautĂ©, encore naissante, est plutĂŽt active et les Ă©changes dans les Issues et les PR du repo sont hyper intĂ©ressants.

Ce que j’apprĂ©cie vraiment dans ce projet, ce sont les parties pris : un focus sur les performances, une api qui reste simple. Enfin, pour une fois qu’un bundler n’a pas 1000 dĂ©pendances et me rajoute 100Mo dans mon dossier node_modules, c’est assez beau pour le noter.

Je finirais en vous disant qu’ esbuild n’est pas la seule alternative qui nous est proposĂ© dans cette nouvelle gĂ©nĂ©ration de bundler. Je compte justement faire ce genre d’analyse sur les outils comme Vite ou bien Snowpack.


Ecrit par Antoine Caron qui vit et travaille Ă  Lyon et dĂ©veloppe des choses utiles Ă  Bedrock. Tu devrais le suivre sur Twitter. N’hĂ©sites pas Ă  aller voir ses confĂ©rences sur la page dĂ©diĂ©e. Si vous voulez voir mon parcours pro, mon CV est disponible ici en 🇬🇧.

Si vous aimez le contenu de ce blog, ou bien qu'il vous a aidé, s'il vous plait, considérez donner à la fondation Abbé Pierre que je soutiens personnellement.
“On n’est jamais heureux que dans le bonheur qu’on donne. Donner, c’est recevoir.”