28 janvier 2025
Bun : un outil tout-en-un JavaScript révolutionnaire ?
6 minutes de lecture
Bun est un runtime (et plus encore) JavaScript dont l'axe de communication principal est sa performance. Sorti en 2021, il n'a eu de cesse de gagner en popularité et de devenir un sérieux concurrent aux runtimes principaux déjà en place sur le marché : Node et Deno. La récente version 1.2 ajoute son lot de fonctionnalités et de performances, rapprochant Bun d'une parité 1:1 avec ce qu'offre Node.
Bun : plus qu'un simple runtime
Au-delà d'être un runtime, Bun offre un ensemble d'outils qui viennent remplacer des mastodontes déjà en place dans l'écosystème.
Un gestionnaire de paquets
Vous connaissez sûrement npm, yarn et PNPM les gestionnaires de paquets les plus utilisés dans l'écosystème JavaScript. Chacun présente son lot d'avantages et d'inconvénients: rapidité, support des monorepos, optimisation de l'espace disque. Bun propose un gestionnaire de paquets entre 10 et 25x plus rapide que ses concurrents. Tout comme la concurrence, les monorepos sont supportés, l'optimisation de l'espace disque est au rendez-vous et le patch de paquets est possible.
Migrer vers Bun
Migrer vers Bun peut s'avérer assez complexe en fonction du gestionnaire de paquets que vous utilisez. Une commande est disponible pour la migration, mais nécessite d'avoir un package-lock.json
à jour, donc la migration depuis Yarn ou PNPM peut s'avérer plus complexe.
# NPM
bun pm migrate
# Yarn
npx synp --source-file yarn.lock
npm i --lockfile-version 3 --frozen-lockfile
bun pm migrate
# PNPM
npm i --package-lock-only
bun pm migrate
Un bundler
Avec son bundler intégré, Bun est un sérieux concurrent à Webpack, Rspack et Vite. L'API du bundler de Bun est quasi identique à celle d'ESBuild, et par la même occasion les performances le sont aussi, avec un léger avantage pour Bun. Une migration d'ESBuild vers Bun est donc très simple. Le bundler intègre aussi la création d'exécutables, intégrant le binaire de Bun dans le fichier de sortie.
Un test runner
Bun intègre son propre test runner, venant ainsi concurrencer Jest, Vitest ou tout simplement le test runner intégré dans Node. En termes de performance, par rapport à Jest, les chiffres sont pour ainsi dire aberrants : Bun est capable d'effectuer une suite de 266 tests de SSR avec React plus rapidement que Jest ne mettrait pour afficher son numéro de version. En termes de chiffres, Bun est 100x plus rapide que Jest et 10x plus rapide que Vitest. Bun a l'avantage de ne pas nécessiter de configuration : tout est prêt à l'emploi.
L'aspect performance
Une grande partie de la communication de Bun est axée sur les performances, dépassant de loin celles de ses concurrents que ce soit pour le runtime ou pour le gestionnaire de paquets. Bien que cela soit vrai pour ce dernier, les benchmarks indiquent une certaine variation lorsqu'il s'agit du runtime, et selon les cas d'utilisation, Bun peut avoir ou non un avantage.
- Comparaison de Bun avec Node : bun-vs-node-benchmarks.pages.dev
- Génération de QR Code avec Node vs Bun : Is Bun really much faster than Node.js ?
À l'heure actuelle, la partie gestionnaire de paquet est de plus en plus adoptée à travers les framework les plus populaires de l'écosystème JS (NextJS, React-Native). Du côté du runtime, il est encore trop tôt pour dire si Bun est, à l'heure actuelle, un incontournable pour une application de grande envergure. À vrai dire, cela dépend vraiment du cas d'utilisation, et il est important de ne pas se précipiter lors du lancement d'un projet.
Quelques benchmarks :
Fonctionnalités additionnelles
L'API de Bun se veut, à terme, compatible avec celle de Node. La version 1.2 améliore cet objectif, rendant ainsi Bun compatible à 90% avec les API de Node, ce qui concerne la majeure partie des API les plus utilisées. En l'état, Bun est capable d'exécuter la plupart des applications Node sans modification. Bun offre aussi des API supplémentaires, mais cela implique une incompatibilité avec Node. Une fois que ces API sont implémentées, le passage sur du Node impliquerait un gros chantier de migration. En effet, cela nécessite d'écrire des imports spécifiques à Bun, rendant ainsi l'utilisation de Node impossible, étant donné que ces imports ne sont reconnus que par Bun.
- Support S3 : Bun propose une API permettant de communiquer avec tous les fournisseurs de stockage de type S3 (AWS S3, Cloudflare R2, MinIO, etc.). Il est aussi possible d'utiliser le protocole
s3://
lors d'une requête avecfetch
ou bien lors de l'instantiation d'un fichier avecBun.file
- Driver PostgreSQL : Bun intègre une compatibilité avec les bases de données PostgreSQL sans utiliser la librairie Postgres. En termes de performances, Bun est 50% plus rapide que la concurrence. Le support de MySQL arrivera dans une future version.
- Compilateur C : Bun intègre un compilateur de fichier C, permettant d'exécuter du code C depuis du JavaScript. Cela peut s'avérer utile lors de l'implémentation de fonctionnalités bas niveau impliquant l'addon N-API. Plus besoin de passer par des outils comme node-gyp, dont l'installation est souvent compliquée à cause de sa dépendance avec Python.
- Transpiler TypeScript : Un transpiler TypeScript est intégré à Bun, permettant de transpiler du TypeScript en JavaScript au runtime.
- FileSystemRouter : Une API similaire à ce que propose Next.JS pour effectuer du routing basé sur l'arborescence des fichiers.
Serveur HTTP
L'API de Bun pour créer un serveur HTTP est assez intéressante à l'usage. Là où un serveur traditionnel Node requiert de gérer manuellement le matching des routes, Bun propose une API incluant des routes statiques. Ainsi, une URL précise peut être associée à une réponse, par exemple du JSON ou plus simplement un fichier HTML.
import { serve } from 'bun'
import home from './home.html'
serve({
static: {
'/': home,
'json': Response.json({ message: 'Hello World' })
}
})
Il est aussi possible de s'occuper du matching manuellement :
import { serve } from 'bun'
import home from './home.html'
import notFound from './404.html'
serve({
fetch(request, response) {
if (request.url === '/') {
return home
}
if (request.url === "/json") {
return Response.json({ message: 'Hello World' })
}
return notFound
}
})
Dans les deux cas, Bun s'occupe aussi du bundling du HTML en incluant les fichiers JS et CSS dans le HTML, puis en transformant les attributs source des balises.
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<div id="root"></div>
<script type="module" src="./app.tsx"></script>
</body>
</html>
Le fichier servi par Bun sera :
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<link rel="stylesheet" href="./index-[hash].css" />
</head>
<body>
<div id="root"></div>
<script type="module" src="./index-[hash].js"></script>
</body>
</html>
À titre de comparaison, voici à quoi ressemblerait une implémentation en Node (sans librairie) :
import http from 'http'
import fs from 'fs'
const server = http.createServer((req, res) => {
if (req.url === '/') {
fs.readFile('./home.html', (err, data) => {
if (err) {
res.writeHead(404)
res.end()
} else {
res.writeHead(200, { 'Content-Type': 'text/html' })
res.end(data)
}
})
} else if (req.url === '/json') {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ message: 'Hello World' }))
} else {
fs.readFile('./404.html', (err, data) => {
if (err) {
res.writeHead(404)
res.end()
} else {
res.writeHead(404, { 'Content-Type': 'text/html' })
res.end(data)
}
})
}
})
server.listen(3000)
Faut-il adopter Bun ?
Bun propose non seulement une API très complète mais aussi des outils performants pour remplacer ceux déjà bien implémentés dans l'écosystème JavaScript. Il ne faut toutefois pas se précipiter : utiliser Bun nécessite de bien comprendre les implications de son utilisation, et de bien peser le pour et le contre. Il est possible que Bun ne soit pas la solution à tous les problèmes, et il est à mon sens encore trop tôt pour utiliser 100% du contenu proposé par Bun.
Actuellement, son fonctionnement en tant que gestionnaire de paquet est très satisfaisant, montrant un gain de temps considérable lors de l'installation de dépendances que ce soit en local ou dans un environnement de CI.
L'utilisation du runtime peut être intéressante pour des petits scripts qui ne demandent pas trop de complexité, mais je ne prendrais pour le moment pas le risque de l'utiliser sur des applications de plus grande envergure. Il reste néanmoins intéressant de suivre l'évolution de Bun, qui est d'ores et déjà un acteur majeur de l'écosystème JavaScript.