SWC : le compilateur JavaScript/TypeScript écrit en Rust
Guide complet et sourcé de SWC : architecture, benchmarks, comparaison avec tsc et Babel, configuration, intégrations courantes, et migration depuis tsc.
La plupart des projets TypeScript utilisent tsc pour compiler. C’est le compilateur officiel de Microsoft : polyvalent, fiable, et lent. Sur un projet de taille raisonnable, une compilation complète prend plusieurs secondes. Sur un grand monorepo, plusieurs dizaines de secondes.
SWC (Speedy Web Compiler) résout la partie “lent” en déplaçant la compilation vers Rust. Il n’est pas un remplacement complet de tsc — il ne vérifie pas les types — mais pour la transformation de code, il est entre 20x et 70x plus rapide que Babel, et beaucoup plus rapide que tsc --transpileOnly.
Cet article couvre le fonctionnement interne, les benchmarks, les comparaisons avec les alternatives, la configuration, et comment migrer depuis tsc.
1. Ce qu’est SWC
SWC (Speedy Web Compiler) est un compilateur JavaScript/TypeScript écrit en Rust, créé par DongYoon Kang (kdy1), qui travaille chez Vercel. Le projet est open source sous licence Apache 2.0.
Source : github.com/swc-project/swc
SWC prend du code JavaScript, TypeScript, JSX ou TSX en entrée, et produit du JavaScript en sortie — ciblant une version d’ES spécifiée (ES3 à ES2022+). Il peut aussi minifier le code (en remplacement de Terser).
Ce que SWC ne fait pas : vérifier les types. C’est une décision délibérée d’architecture, expliquée en section 5.
Adoptions notables :
- Next.js 12 (2021) — SWC a remplacé Babel pour les transformations de fichiers, et Terser pour la minification. Depuis Next.js 15, la minification SWC n’est plus optionnelle.
- Parcel 2 — utilise SWC pour la transformation
- Deno — utilise SWC pour analyser et transformer TypeScript
- Turbopack (le bundler Rust de Vercel) — utilise SWC en interne
- Rspack (le bundler Rust de ByteDance, compatible webpack) — expose SWC via
builtin:swc-loader
Source : swc.rs/docs/customers
2. Architecture interne
Pipeline de compilation
SWC traite chaque fichier en trois phases :
Source (TS/JS/JSX/TSX)
│
▼
┌───────────────┐
│ Parser │ → AST (swc_ecma_ast)
│ (swc_ecma_ │
│ parser) │
└───────┬───────┘
│
▼
┌───────────────┐
│ Transforms │ → lowering ES target, JSX transform,
│ (visitors) │ decorators, etc.
└───────┬───────┘
│
▼
┌───────────────┐
│ Code gen │ → JavaScript + source map
└───────────────┘
Chaque fichier est traité en isolation, sans connaissance du reste du programme. C’est ce qui permet la parallélisation et la vitesse — mais aussi ce qui empêche la vérification de types.
Parallélisme
L’API asynchrone (transform()) distribue les fichiers sur plusieurs threads Rust. Sur 4 cœurs, SWC atteint un speedup de 70x vs Babel, contre 20x sur un seul thread.
L’API synchrone (transformSync()) bloque le thread Node.js — à éviter dans les outils qui transforment de nombreux fichiers simultanément.
Support WASM
SWC compile en WebAssembly via le package @swc/wasm. C’est ce qui a permis son adoption dans des environnements sans binaires natifs (edge runtimes, certains CI). Next.js a cité cette propriété comme un critère de choix de SWC face à d’autres alternatives.
Structure en crates Rust
SWC est un monorepo de crates :
swc_ecma_parser— parsing JS/TS/JSXswc_ecma_ast— définitions des nœuds AST (utiliseAtompour les strings internées)swc_ecma_transforms— visiteurs de transformation (lowering, JSX, decorators…)swc_core— crate racine qui regroupe l’écosystème
Source : github.com/swc-project/swc
3. Benchmarks
SWC vs Babel
Depuis le billet officiel swc.rs/blog/perf-swc-vs-babel :
Benchmark synchrone (transformSync) :
| Outil | ops/sec |
|---|---|
| SWC (target ES2018) | 2 555 |
| SWC (target ES3) | 616 |
| Babel (target ES5) | 34 |
SWC en ES3 est 18x plus rapide que Babel en ES5, alors qu’il fait plus de travail (ES3 nécessite plus de transformations qu’ES5).
Benchmark async, 4 opérations concurrentes :
| Outil | ops/sec |
|---|---|
| SWC (ES2018) | 4 884 |
| Babel (ES5) | 27 |
~180x plus rapide avec la concurrence.
Résumé de la page d’accueil SWC :
- 20x plus rapide que Babel sur un seul thread
- 70x plus rapide que Babel sur 4 cœurs
Next.js — chiffres réels
Source : nextjs.org/docs/architecture/nextjs-compiler
| Métrique | Gain |
|---|---|
| Compilation | 17x plus rapide qu’avec Babel |
| Fast Refresh | ~3x plus rapide |
| Builds | ~5x plus rapides |
| Minification (SWC vs Terser) | 7x plus rapide |
SWC vs Oxc (2024)
Oxc est un nouveau toolchain Rust créé dans l’écosystème VoidZero (Evan You). Les benchmarks de septembre 2024 montrent :
| Taille du fichier | Oxc | SWC | Ratio |
|---|---|---|---|
| 100 lignes | 0,14 ms | 0,7 ms | 5x |
| 1 000 lignes | 0,9 ms | 5,7 ms | 6,3x |
| 10 000 lignes | 14,9 ms | 35,9 ms | 2,4x |
Oxc est aussi 20% moins gourmand en mémoire et son package natif darwin-arm64 fait 2 MB contre 37 MB pour SWC.
Source : oxc.rs/blog/2024-09-29-transformer-alpha
Oxc est plus récent et moins intégré que SWC — son transformateur était en alpha en septembre 2024.
4. Ce que SWC fait — et ne fait pas
Fonctionnalités
| Fonctionnalité | SWC | Notes |
|---|---|---|
| Transpilation JS/TS/JSX/TSX | ✅ | |
| Minification | ✅ | Remplace Terser |
| Source maps | ✅ | |
| Plugins (WASM) | ✅ | Écrits en Rust, compilés en .wasm |
| Vérification de types | ❌ | Par design |
Génération de .d.ts | ❌ | Utiliser tsc --emitDeclarationOnly |
| Bundling | ❌ | spack est déprécié |
Packages npm
npm i -D @swc/core @swc/cli
@swc/core— API Node.js (bindings natifs)@swc/cli— CLI (npx swc ./src -d ./dist)@swc/wasm— build WebAssembly@swc/jest— transformateur Jestswc-loader— loader webpack
5. Comparaison avec les alternatives
SWC vs tsc
C’est la comparaison la plus importante à comprendre.
tsc fait deux choses distinctes : vérifier les types ET transpiler. SWC ne fait que transpiler. Ce n’est pas un bug — c’est une conséquence directe du traitement fichier par fichier.
La vérification de types nécessite une analyse complète du programme : résoudre les imports, inférer les types génériques, vérifier les assignements à travers les fichiers. SWC n’a pas ces informations.
Concrètement : du code TypeScript avec des erreurs de type compile sans erreur avec SWC.
| SWC | tsc | |
|---|---|---|
| Transpile TS → JS | ✅ | ✅ |
| Vérifie les types | ❌ | ✅ |
Génère des .d.ts | ❌ | ✅ |
| Traitement | Fichier par fichier | Programme entier |
| Vitesse | Très rapide | Lent sur les grandes bases |
| Usage recommandé | Build, dev server | CI type check |
Le workflow recommandé est de combiner les deux :
# Build rapide (dev et prod) :
npx swc src -d dist
# Vérification des types (CI, pre-commit) :
npx tsc --noEmit
Source : swc.rs/docs/migrating-from-tsc
SWC vs Babel
Babel est le précédent standard de fait pour la transpilation JavaScript. Les deux outils font le même travail — transformer du JS/TS moderne en JS compatible.
| SWC | Babel | |
|---|---|---|
| Langage | Rust | JavaScript |
| Vitesse | 20x–70x plus rapide | Référence |
| Support TS/JSX | Intégré | Via plugins |
| Écosystème de plugins | Petit (plugins WASM) | Vaste (milliers de plugins JS) |
| Format de config | .swcrc (JSON) | .babelrc / babel.config.js |
| Maturité | Production-ready | Très mature |
La structure du .swcrc est intentionnellement proche de .babelrc pour faciliter la migration.
Si un projet a des plugins Babel personnalisés complexes, la migration vers SWC nécessite de les réécrire en Rust — ce qui n’est pas trivial. Les plugins SWC disponibles couvrent les cas les plus courants : styled-components, Lingui, Effector, etc.
SWC vs esbuild
esbuild est un bundler + transpiler écrit en Go, également très rapide.
| SWC | esbuild | |
|---|---|---|
| Langage | Rust | Go |
| Rôle | Transpilation | Bundling + transpilation |
| Bundling | Non (spack déprécié) | Oui |
| Plugins | WASM (Rust) | Go + JS |
| Contrôle des transforms | Fin (visiteurs AST) | Plus limité |
| Utilisé par | Next.js, Parcel, Rspack | Vite (dev), beaucoup d’autres |
Les deux outils sont complémentaires plutôt que concurrents : Vite utilise esbuild pour le dev server et Rollup pour le build de production. Si Vite intègre Rolldown (basé sur Oxc) à l’avenir, esbuild sera progressivement remplacé.
SWC vs Oxc
Oxc (JavaScript Oxidation Compiler) est le projet le plus récent. Son transformateur était en alpha en septembre 2024. Il est plus rapide que SWC dans les benchmarks, mais moins mature et moins intégré.
SWC reste le choix stable pour les projets qui ont besoin d’une transpilation rapide aujourd’hui, en particulier avec Next.js, Rspack, ou comme remplaçant de Babel.
6. Configuration (.swcrc)
SWC se configure via un fichier .swcrc à la racine du projet. Le format est JSON avec un schéma disponible :
{
"$schema": "https://swc.rs/schema.json"
}
Configuration TypeScript de base
{
"$schema": "https://swc.rs/schema.json",
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": false,
"decorators": false
},
"target": "es2017"
},
"module": {
"type": "commonjs"
},
"sourceMaps": true
}
Configuration TypeScript + React (TSX)
{
"$schema": "https://swc.rs/schema.json",
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
},
"transform": {
"react": {
"runtime": "automatic"
}
},
"target": "es2017"
},
"sourceMaps": true
}
runtime: "automatic" correspond au nouveau JSX transform de React 17+ — plus besoin d’importer React dans chaque fichier JSX.
Options clés de jsc
jsc.parser.syntax : "ecmascript" ou "typescript". Utiliser "typescript" pour les fichiers .ts et .tsx.
jsc.target : version ES cible. Valeurs : "es3", "es5", "es2015" à "es2022". La valeur par défaut est "es5".
jsc.loose : active les transformations “loose” (moins conformes au spec ES, mais plus légères). Même comportement que l’option Babel de même nom.
jsc.externalHelpers : si true, SWC importe ses helpers depuis @swc/helpers plutôt que de les inliner dans chaque fichier — réduit la taille du bundle.
jsc.transform.legacyDecorator : true pour les decorators expérimentaux TypeScript (NestJS, TypeORM, MobX).
jsc.transform.decoratorMetadata : true pour émettre les métadonnées de type pour reflect-metadata (dependency injection).
Source : swc.rs/docs/configuration/compilation
Options env (browserslist)
{
"env": {
"targets": {
"chrome": "79",
"firefox": "70"
},
"mode": "usage",
"coreJs": "3.22"
}
}
env est l’alternative à jsc.target quand on cible des navigateurs spécifiques plutôt qu’une version ES. Similaire à @babel/preset-env.
7. Intégrations courantes
CLI
# Transpiler un fichier
npx swc ./src/index.ts -o ./dist/index.js
# Transpiler un répertoire
npx swc ./src -d ./dist
# Mode watch
npx swc ./src -d ./dist --watch
Vite + React
Vite propose un template officiel React + SWC :
npm create vite@latest my-app -- --template react-swc-ts
Ou en ajoutant le plugin à un projet existant :
npm i -D @vitejs/plugin-react-swc
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
export default defineConfig({
plugins: [react()],
});
Ce plugin remplace @vitejs/plugin-react (qui utilise Babel) pour les transformations JSX et React Fast Refresh.
webpack
npm i -D swc-loader @swc/core
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(t|j)sx?$/,
exclude: /node_modules/,
use: {
loader: "swc-loader",
options: {
jsc: {
parser: { syntax: "typescript", tsx: true },
target: "es2017",
},
},
},
},
],
},
};
Source : swc.rs/docs/usage/swc-loader
Jest
npm i -D jest @swc/core @swc/jest
// jest.config.js
module.exports = {
transform: {
"^.+\\.(t|j)sx?$": ["@swc/jest"],
},
};
@swc/jest est le remplaçant de ts-jest. Il est significativement plus rapide car il ne vérifie pas les types pendant les tests — il transforme simplement le TypeScript en JavaScript. Comme pour le build, la vérification des types reste la responsabilité d’un tsc --noEmit séparé.
Source : swc.rs/docs/usage/jest
8. Migration depuis tsc
Ajustements requis dans tsconfig.json
Pour que tsc --noEmit (utilisé pour la vérification de types) reste cohérent avec la façon dont SWC transforme le code, deux options sont essentielles :
{
"compilerOptions": {
"isolatedModules": true,
"verbatimModuleSyntax": true,
"esModuleInterop": true
}
}
isolatedModules: true — force tsc à émettre des avertissements sur les fonctionnalités TypeScript qui nécessitent une connaissance inter-fichiers : les const enum, les namespaces sans import, etc. SWC ne pouvant pas traiter ces cas correctement, cette option aligne tsc sur les contraintes de SWC.
verbatimModuleSyntax: true — introduit dans TypeScript 5.0, remplace la combinaison isolatedModules + importsNotUsedAsValues. Force l’utilisation explicite de import type pour les imports qui ne sont que des types, de sorte que SWC peut les supprimer sans ambiguïté.
esModuleInterop: true — aligne le comportement d’interopérabilité CommonJS de tsc avec celui de SWC et Babel.
Source : swc.rs/docs/migrating-from-tsc
Ce que SWC ignore de tsconfig.json
SWC ne lit pas tsconfig.json par défaut. Il a son propre fichier .swcrc. Certaines options de tsconfig.json n’ont pas d’équivalent dans SWC :
| Option tsconfig | Comportement SWC |
|---|---|
paths / baseUrl | Non supporté — SWC ne résout pas les alias. Configurer au niveau du bundler ou utiliser tsconfig-paths. |
outDir | Géré par les flags de @swc/cli, pas par .swcrc |
declaration / declarationDir | Non supporté — utiliser tsc --emitDeclarationOnly |
composite / incremental | Non supporté |
noEmit | Sans objet (SWC émet toujours) |
Étapes de migration
1. Installer SWC
npm i -D @swc/core @swc/cli
2. Créer .swcrc
{
"$schema": "https://swc.rs/schema.json",
"jsc": {
"parser": {
"syntax": "typescript"
},
"target": "es2017"
},
"sourceMaps": true
}
3. Mettre à jour tsconfig.json
{
"compilerOptions": {
"isolatedModules": true,
"verbatimModuleSyntax": true,
"esModuleInterop": true,
"noEmit": true
}
}
noEmit: true dans tsconfig.json empêche tsc d’émettre des fichiers — il sert uniquement à la vérification de types.
4. Mettre à jour les scripts npm
{
"scripts": {
"build": "swc src -d dist",
"typecheck": "tsc --noEmit",
"dev": "swc src -d dist --watch"
}
}
5. Adapter le pipeline CI
# Exemple GitHub Actions
- name: Type check
run: npm run typecheck
- name: Build
run: npm run build
La vérification de types et le build deviennent deux étapes indépendantes. Le build est rapide (SWC) ; le type check peut être parallélisé ou mis en cache séparément.
9. Pièges et limitations connues
const enum
C’est le piège le plus courant lors d’une migration.
Les const enum TypeScript sont inlinés par tsc à la compilation — leur valeur numérique remplace chaque usage. SWC, traitant les fichiers en isolation, ne peut pas inliner les valeurs d’un const enum défini dans un autre fichier.
Problème :
// a.ts
export const enum Status {
Active = 1,
Inactive = 0,
}
// b.ts
import { Status } from "./a";
console.log(Status.Active); // SWC ne peut pas résoudre cette valeur
Solution : Utiliser un enum régulier, ou déclarer les valeurs dans un .d.ts avec declare const enum + valeurs pré-calculées.
isolatedModules: true dans tsconfig.json détecte ces cas à l’étape de vérification de types.
Source : github.com/swc-project/swc/issues/486
Path aliases non résolus
SWC supprime les annotations TypeScript mais ne transforme pas les chemins d’import. Un import @/utils/foo restera @/utils/foo dans le JS émis.
Selon l’outil de build utilisé :
- webpack : utiliser
resolve.aliasdanswebpack.config.js - Vite : utiliser
resolve.aliasdansvite.config.ts - Node.js pur : utiliser le package
tsconfig-pathsoumodule-alias
Vérification de types absente du build
Le piège le plus dangereux en production.
Après la migration, certains projets retirent tsc du pipeline en pensant que SWC le remplace complètement. Des erreurs de types qui auraient été détectées par tsc passent en production.
En pratique, conserver
tsc --noEmitcomme étape obligatoire dans la CI est la règle la plus importante de la migration.
Decorators TC39 vs decorators expérimentaux
SWC supporte les deux modes, mais les decorators TC39 (le standard, TypeScript 5.0+ sans experimentalDecorators) ont des cas limites connus. Par exemple, certaines implémentations de addInitializer sur des ClassFieldDecoratorContext ne sont pas appelées correctement pendant la construction.
Pour les projets utilisant NestJS, TypeORM, ou MobX (qui utilisent les decorators expérimentaux), configurer :
{
"jsc": {
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
}
}
}
Et dans tsconfig.json :
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Plugins SWC : taille et compatibilité de version
Les plugins WASM sont liés à une version spécifique de swc_core. Un plugin compilé contre swc_core v0.95.x ne fonctionnera pas avec v0.98.x. Depuis @swc/core 1.15.0, une compatibilité rétroactive a été introduite, mais les plugins tiers doivent être mis à jour régulièrement.
La page swc.rs/docs/plugin/selecting-swc-core donne la matrice de compatibilité entre swc_core, @swc/core, et les versions de Next.js.
10. Conclusion
SWC est stable, largement adopté, et constitue le choix par défaut pour accélérer les pipelines TypeScript qui s’appuient encore sur Babel ou sur tsc --transpileOnly.
La distinction fondamentale à retenir : SWC transpile, tsc vérifie les types. Les deux sont nécessaires dans un projet TypeScript sérieux — SWC pour la vitesse des builds, tsc --noEmit pour la sûreté du code.
Pour les nouveaux projets React, le template Vite react-swc-ts est le point d’entrée le plus simple. Pour les projets Next.js, SWC est déjà activé par défaut depuis la version 12.
Oxc est une alternative plus rapide qui mérite d’être suivie, mais son écosystème est encore en construction. SWC reste le choix le plus intégré en 2026.
Sources :