Compiler sa web app pour une livraison parfaite
Pas envie déployer des centaines de méga de dépendances ? Un build Astro, next ou autre embarque bien plus de node_modules que nécessaire.
Voici quelques techniques pour être fier de son livrable. Et si tu ne veux pas te prendre la tête, jette un œil à la dernière section ;)
Tout regrouper avec esbuild ?
On aurait pu utiliser esbuild qui est simple d’utilitation :
npm exec esbuild -- \
dist/server/entry.mjs \
--bundle \
--outfile=dist/server/entry.cjs \
--external:sharp \
--platform=node \
--format=cjs \
--sourcemap=inlineEsbuild reste un excellent outil, simple et rapide pour des situations plus simples. Il faut simplement savoir qu’il a ses limites dans des circonstances spécifiques comme ici en surchouche après un premier build Astro.
Tout regrouper avec rollup
Rollup est plus avancé et va nous permettre de gérer les périmètres ESM/cjs :
import commonjs from "@rollup/plugin-commonjs";
import json from "@rollup/plugin-json";
import { nodeResolve } from "@rollup/plugin-node-resolve";
import typescript from "@rollup/plugin-typescript";
export default {
input: ["dist/server/entry.mjs"],
external: ["sharp"],
plugins: [
nodeResolve({ preferBuiltins: true }),
commonjs({ strictRequires: "auto" }),
json(),
typescript({ compilerOptions: { outDir: "optimized" } }),
],
output: {
dir: "optimized",
format: "esm",
// Preserve the commonjs boundaries, allowing deno to treat them correctly afterwards
preserveModules: true,
preserveModulesRoot: ".",
// Deno doesn't allow node modules to reach _virtual (rollup helpers/wrappers) being outside node_modules
virtualDirname: "node_modules/_virtual",
sourcemap: "inline",
},
};Gros avantage : plus besoin de se prendre la tête avec les dépendances de dev ou non, transitives ou non, du type de package manager… On laisse rollup regrouper uniquement le nécessaire !
À condition tout de même de spécifier un peu de config :
preserveModulesrevient à préserver les frontières pour chaque module ESM/cjs — dit plus simplement : pas de bundling.virtualDirnamereçoit les helpers générés par rollup, pour les polyfill ou autres compatibilités. Étant donné que ces helpers sont utilisés par les versions compilées desnode_modules, on préférera les placer aussi dansnode_modulespour éviter aux modules de taper dehors. D’ailleurs, deno ne permet pas cela.
On se retrouve donc avec un dossier optimized en sortie qui contient l’arborescence courante en version compilée optimisée.
Utiliser `deno compile` sur son application nodejs
En principe, deno n’a pas besoin de l’étape précédente. Une application développée entièrement sous deno compilera très bien sans bidouille.
Toutefois deno compile peut aussi nous rendre service dans un environnement node.
Runtime deno en production
- Augmente la sécurité en permettant la gestion des permissions
- Facilite grandement le reporting télémétrique avec OTEL
- Favorise la vitesse d’exécution par rapport à node
Un simple exécutable
- Un déploiement plus simple
- Moins de versions au runtime à gérer
- Optimisé pour limiter l’IO et les risques de corruption
Appliqué à l’optimisation rollup précédente :
deno compile \
--allow-net \
--allow-read \
--allow-env \
--allow-sys \
--no-check \
--include=optimized/dist/client \
optimized/dist/server/entry.jsDeno se voulant secure by default, on a bien spécifié manuellement les permissions souhaitées. On peut aussi se contenter de passer le flag -A à la place pour tout autoriser comme avec node.
Les intégrations de `deno compile` font le café ☕
En fait, deno peut compiler automatiquement je cite :
- Next.js
- Astro
- Fresh (1.x and 2.x)
- Remix
- React Router
- SvelteKit
- Nuxt
- SolidStart
- TanStack Start
- Vite (SSR, plus SPA/MPA projects served as static output)
Pour cela il suffit donc de cibler le dossier du projet comme point d’entrée et… c’est tout.
deno compile .Pas de deno.json, juste un projet node existant et deno fait le nécessaire pour gérer les dépendances et lancer les étapes intermédiaires.
Les sections précédentes peuvent quand même être utiles pour gagner en contrôle. Par exemple, Astro n’optimise (vraiment) pas ses dépendances de production. Deno par souci de compatibilité embarque tout node_modules. Les perfectionnistes voudront donc passer un coup d’esbuild là dessus avant de retirer ces infâmes modules…
deno compile --bundle --minify main.tsJe recommanderais tout de même de bien comprendre les mécanismes sous-jacents avant de s’y frotter. Et d’être prêt à utiliser une API expérimentale :)