La PR #32504 de Bun, fusionnée le 20 juin 2026, transforme le portage Rust amont du React Compiler en transformation intégrée à bun build. Activez-la avec --react-compiler côté CLI ou reactCompiler: true sur Bun.build, et Bun mémoïsera vos composants et hooks .jsx et .tsx pendant la build, sans plugin Babel, sans fichier de configuration, sans rien à installer. La fonctionnalité est désactivée par défaut et marquée expérimentale à la fois dans les définitions de types et dans la documentation du bundler.
C'est le premier bundler à livrer le React Compiler comme transformation native. Vite, Next.js avec Turbopack, webpack et Rsbuild passent tous par un plugin Babel ou SWC aujourd'hui. Le chemin de Bun saute complètement cette étape intermédiaire.
Ce qui a été livré
L'intégration ferme l'issue #24356, la demande de fonctionnalité de longue date pour un support de première classe du React Compiler dans le bundler, et remplace une PR #31785 antérieure qui dépendait d'une crate oxc_react_compiler qui n'existait pas à l'époque. La nouvelle PR porte l'espace de travail Rust du compilateur directement depuis facebook/react plutôt que de passer par Oxc, ce qui explique pourquoi la description de la PR amont facebook/react#36173 invitait explicitement les intégrations de bundlers via l'adaptateur react_compiler_oxc et que Bun a pris un chemin différent.
Une PR de suivi #32545 livrée le même jour corrige trois commentaires de revue issus de la PR fusionnée, dont un bug subtil où reactCompilerOutputMode: 'client' activait silencieusement le compilateur même lorsque reactCompiler: false était positionné. Le mode de sortie est désormais stocké séparément et n'est appliqué que lorsque le compilateur est activé, ce qui correspond au comportement documenté dans bun.d.ts.
L'architecture : AST Bun directement vers HIR
Le compilateur vit dans src/react_compiler/, une unique crate d'environ 62 k LOC. L'essentiel est un portage à l'octet près de l'espace de travail Rust amont, avec les chemins d'import réécrits et les derives serde et serde_json dont Bun n'a pas besoin retirés. Les crates amont qui sont portées intégralement : hir/, ssa/, inference/, typeinference/, optimization/, validation/, reactive_scopes/, diagnostics/ et utils/. Les structures de données du chemin chaud ont été densifiées : HashMap<SmallId, _> devient Vec<_>, HashSet<ValueReason> devient EnumSet (u16), et les points-to sets deviennent SmallVec<[_; 4]>. L'API IndexMap et IndexSet est adaptée au-dessus de bun_collections::ArrayHashMap à base d'arène.
Les quatre couches qui touchent l'AST (lowering, codegen, pipeline, et la colle program/imports) sont réimplémentées contre bun_ast, avec la table de correspondance de types dans src/react_compiler/DESIGN.md qui documente comment les nœuds AST de Bun correspondent à l'AST de forme Babel que le compilateur attend.
Le hook de compilation se déclenche dans visit_stmts(FnBody), entre sa phase de visite et sa phase de mangle inline. La détection de candidats sur S::Function, S::Local, S::ExportDefault et S::Expr enregistre la Ref du binding et le bit wrapper memo/forwardRef ; visit_func et la visite des arrow functions copient les arguments, flags et locations de la fonction dans une struct Copy PendingCompile ; le hook appelle maybe_compile_pending, qui construit un G::Fn local à la pile et exécute maybe_compile_node. Le corps compilé atterrit dans le buffer stmts vivant pour que la phase de mangle existante s'exécute dessus. Les nouveaux arguments et flags remontent via un unique champ CompileResult. Pas de pointeurs bruts, pas de passe supplémentaire ; le chemin non-RC ajoute un seul is_some() par déclaration de niveau racine.
Le compilateur honore aussi les suppressions // eslint-disable react-hooks/*. Le lexer exécute une vérification de sous-chaîne par commentaire, gardée par le drapeau de fonctionnalité, et propage la suppression comme un bit de flag sur G::Fn et E::Arrow ; le compilateur saute toute fonction qui le porte.
Les chiffres
La PR livre un benchmark sur une grosse base de code React (environ 860 composants compilés, 1400 slots de mémo). Le même code, sur la même machine :
| Temps mural | vs plugin Babel | |
|---|---|---|
Référence (reactCompiler: false) | 394 ms | - |
reactCompiler: true | 465 ms (1,18x la base) | ~20x plus rapide que Babel |
| Plugin Babel (même entrée) | 9,15 s | 1x |
La build complète de l'exécutable autonome via --compile, qui bundle tout plus la passe React Compiler, s'exécute en 3,62 s avec le portage Rust contre 13,04 s avec le plugin Babel, soit 3,6x plus rapide en bout en bout.
Ce ne sont pas des micro-benchmarks synthétiques. La base de code est réelle, les composants compilent vers de vrais appels de mémoïsation _c(N) avec des vérifications de cache $[0] !== label, et l'import react/compiler-runtime que Bun injecte se résout contre l'installation de React 19+ livrée avec l'application. Bun note que le surcoût baseline+RC (394 ms à 465 ms, ~18%) provient de la construction HIR et de la passe SSA ; le reste du bundler (parser, mangle, minify) est inchangé.
À quoi ressemble l'API
CLI :
bun build ./app.tsx --react-compiler --target browser
Bun.build :
await Bun.build({
entrypoints: ["./app.tsx"],
reactCompiler: true,
// reactCompilerOutputMode: "client", // par défaut pour target browser
// reactCompilerOutputMode: "ssr", // par défaut pour target bun/node
target: "browser",
});
reactCompilerOutputMode vaut par défaut "client" quand target est "browser" et "ssr" quand target est "bun" ou "node". Le mode SSR ignore le runtime useMemoCache pour que la sortie rendue côté serveur reste compatible avec un cache entre requêtes. La sémantique compilationMode: "infer" est héritée du compilateur amont, donc seuls les composants et hooks sont compilés ; les directives "use no memo" sont honorées, et node_modules est ignoré.
Ce que cela signifie pour la course aux bundlers
C'est la première fois que le portage Rust du React Compiler est livré comme transformation au moment du build plutôt que comme bibliothèque dans laquelle d'autres outils doivent se brancher. L'intégration Oxc v0.135 mi-juin a ajouté le compilateur comme crate Rust appelable, mais le seul bundler à l'avoir effectivement câblé depuis est Bun. Vite 8 et Vite 8.1 passent toujours par babel-plugin-react-compiler ; Next.js avec Turbopack utilise le portage SWC ; webpack utilise Babel. Le choix de Bun de porter l'amont directement dans sa propre couche AST est un arbitrage délibéré : il évite le coût de conversion cross-AST et la surface de dépendance, au prix d'avoir à resynchroniser périodiquement avec facebook/react.
Le chemin de maintenance est câblé. scripts/sync-react-compiler.sh récupère par sparse-fetch facebook/react et affiche un diff fichier par fichier entre src/react_compiler/UPSTREAM_PORTED et la pointe amont, groupé en ports de crate entiers (qui s'appliquent mécaniquement) et en ports côté frontière AST (qui se re-portent via la table de correspondance de types). --fixtures resynchronise le corpus de tests. Tant que l'API amont reste de forme AST Babel, le coût du suivi du portage est à peu près proportionnel à la fréquence à laquelle l'amont touche les couches de frontière.
À surveiller
Trois signaux intéressants à suivre sur les prochaines semaines :
- Les notes de release de Bun v1.3.15 lorsqu'elles arriveront, qui devraient regrouper la PR #32504 plus la PR de suivi et promouvoir la fonctionnalité de
bun buildexpérimental à un drapeau stable. - L'adaptateur Oxc
react_compiler_oxclivré comme crate stable dans une release Oxc, qui est le chemin que Vite et Rolldown prendront très probablement pour obtenir les mêmes chiffres de performance sans porter l'amont. - Tout changement dans l'« API publique » amont du React Compiler depuis « AST Babel + scope info » vers une forme plus native aux bundlers, qui permettrait à Oxc (et via lui Vite, Next.js, Rsbuild) de se passer entièrement de leurs propres crates d'adaptation.



