Publier une bibliothèque JavaScript avec Rslib
Publiez des bibliothèques JavaScript avec Rslib : configurez les builds ESM et CJS, générez les déclarations TypeScript, incluez les styles et détectez les erreurs en CI.
Rslib est un outil de build de bibliothèques reposant sur Rsbuild et Rspack. À partir d’une seule configuration, il produit des sorties ESM et CommonJS, des déclarations TypeScript, des composants compilés pour votre framework et des assets traités. Il s’adresse aux packages que vous publiez sur npm, et non aux applications que vous déployez.
Cette distinction est importante. Un bundle d’application n’a besoin de fonctionner que dans un seul onglet de navigateur. Une bibliothèque, elle, est importée par les bundlers, les versions de Node.js et les configurations TypeScript d’autres développeurs. Elle casse alors de façons que votre propre application de test ne révèle jamais : un require() qui échoue, des types résolus en any, un composant livré sans ses styles. Ce guide accompagne un package depuis son premier build jusqu’à une vérification pré-publication en CI, avec Rslib 1.0, sorti le 3 septembre 2026.
Points clés
- Rslib 1.0 dispose d’une configuration et d’une API JavaScript stables qui suivent le Semantic Versioning. Il nécessite Node.js 20.19+ ou 22.12+.
- La documentation de Rslib recommande le pur ESM pour les nouveaux packages, et un build double ESM/CJS uniquement lorsqu’un consommateur précis ne peut pas charger d’ESM.
- Sous Node.js
^20.19.0ou>=22.12.0, du code CommonJS peut faire unrequire()d’un package pur ESM, à condition qu’aucun module chargé, point d’entrée compris, n’utilise deawaitde premier niveau (top-levelawait). - Le mode bundle (
bundle: true) convient aux SDK, aux CLI et aux utilitaires Node.js. Le mode bundleless (bundle: false) conserve l’arborescence des fichiers sources et convient aux bibliothèques de composants. rsbuild-plugin-publintetrsbuild-plugin-arethetypeswrongpeuvent s’exécuter dansrslib builduniquement en CI. Les erreurs de packaging font alors échouer le build avantnpm publish.
Pourquoi publier une bibliothèque est-il plus difficile que builder une application ?
Publier une bibliothèque est plus difficile que builder une application, car un package publiable doit réussir quatre choses à la fois :
- Les formats de sortie : il lui faut une sortie ESM, et éventuellement CJS, qui se charge dans chaque runtime pris en charge.
- Les déclarations : les fichiers de déclaration doivent se résoudre quel que soit le paramètre
moduleResolutiondu consommateur. - Les styles : les composants doivent être livrés avec leur CSS.
- Le champ
exports: celui dupackage.jsondoit faire pointer chaque condition vers un fichier qui existe réellement.
Chacun de ces aspects relève généralement d’un outil différent : un bundler pour le JavaScript, tsc pour les déclarations, une étape PostCSS pour les styles, et une map exports écrite à la main pour faire tenir l’ensemble. Dès que l’un de ces outils modifie un chemin de sortie, le package casse.
Qu’est-ce que Rslib ?
Rslib est un outil de build de bibliothèques qui prend en charge ces quatre tâches en un seul build :
- Un tableau
libdéfinit chaque format de sortie. - L’option
dtsgénère les déclarations. En mode bundleless, elle corrige aussi les alias de chemins et les extensions d’import qu’elles contiennent, pour les aligner sur les fichiers JavaScript produits. - La gestion du CSS dépend du mode de build.
- Publint détecte les erreurs dans
exports.
Rslib n’écrit pas votre champ exports à votre place : il vérifie celui que vous avez écrit.
Un build Rslib minimal, de bout en bout
Le moyen le plus rapide de démarrer consiste à générer un projet ou à lancer une seule commande CLI. Pour un package existant, ajoutez un fichier de configuration avec une entrée lib par format. D’après le guide de démarrage rapide, Rslib v1 nécessite Node.js 20.19+ ou 22.12+.
npm create rslib@latest
# or, in an existing package
npm add @rslib/core -D
Un build à format unique ne nécessite pas de fichier de configuration :
npx rslib build --entry src/index.ts --format esm --dts
--entry définit le point d’entrée source, --format esm choisit le format de module, et --dts génère les déclarations. Pour une double sortie, créez rslib.config.ts :
// rslib.config.ts
import { defineConfig } from '@rslib/core';
export default defineConfig({
lib: [
{ format: 'esm', dts: true },
{ format: 'cjs', dts: true },
],
syntax: 'es2023',
});
Tout ce qui est défini au niveau racine s’applique à chaque sortie. Rslib le fusionne avec chaque élément de lib, si bien que chaque entrée ne liste que ce qui diffère. Rslib 1.0 permet aussi d’omettre complètement lib lorsque vous n’avez besoin que d’une seule sortie ESM par défaut. Ajoutez un script de build :
{ "scripts": { "build": "rslib build" } }
Après le build, le répertoire de sortie est dist par défaut. Il contient un build ESM, un build CJS et les fichiers de déclaration.
Les extensions de fichiers suivent lib.autoExtension :
- Avec
"type": "module", la sortie ESM reçoit.jset la sortie CJS.cjs. - Sans ce champ, CJS reçoit
.jset ESM.mjs.
Par défaut, les deux formats peuvent générer des déclarations portant le même nom .d.ts. Les options dts permettent de contrôler les extensions de déclaration par format.
Dans votre map exports, faites pointer la condition import vers le point d’entrée ESM et la condition require vers le point d’entrée CJS, en associant à chacune les déclarations correspondantes. Recopiez les chemins réels depuis votre propre dist plutôt que de les deviner.
Pour les sorties ESM et CJS, Rslib ne bundle pas par défaut les dependencies, optionalDependencies ni peerDependencies. Une peer dependency comme React reste donc externe. Les devDependencies, en revanche, sont bundlées.
Faut-il publier en ESM uniquement ou en double ESM/CJS ?
Le guide des formats de sortie de Rslib préconise de publier les nouveaux packages en pur ESM, et de n’ajouter CJS que lorsqu’un consommateur en a clairement besoin.
Une double sortie coûte plus qu’une seconde étape de build. Vous avez deux jeux d’entrées exports à maintenir correctement et deux fois plus de configurations à tester. Un consommateur qui charge les deux formats obtient en outre deux instances de votre package, ce qui peut casser l’état partagé et les vérifications d’identité de type instanceof.
Beaucoup de consommateurs CommonJS n’ont plus du tout besoin d’un build CJS. Sous Node.js ^20.19.0 ou >=22.12.0, ils peuvent charger directement un package pur ESM. Deux conditions doivent être remplies :
- votre
exportsdoit permettre à un appelrequire()d’atteindre le point d’entrée ESM ; - aucun module du graphe chargé ne doit utiliser de
awaitde premier niveau.
const utils = require('my-esm-only-package');
Ne publiez une double sortie que dans deux cas :
- certains de vos utilisateurs CommonJS utilisent une version de Node, un outil ou un runtime incapable de charger de l’ESM via
require(); - un utilisateur demande un fichier CJS distinct.
Les exemples de packages Node.js couvrent la partie package.json de l’une ou l’autre option.
Vérifications avant publication
Deux plugins Rsbuild détectent les bugs de packaging dans un build Rslib avant que npm ne s’en charge :
rsbuild-plugin-publintrecherche les erreurs courantes dans votrepackage.json, l’organisation de vos fichiers et votre configurationexports.rsbuild-plugin-arethetypeswrongvérifie que vos types se chargent toujours lorsqu’un consommateur utilise un autre paramètre de résolution de modules.
L’annonce de Rslib 1.0 montre les deux plugins s’exécutant uniquement lorsque CI est défini :
// rslib.config.ts
import { defineConfig } from '@rslib/core';
import { pluginAreTheTypesWrong } from 'rsbuild-plugin-arethetypeswrong';
import { pluginPublint } from 'rsbuild-plugin-publint';
export default defineConfig({
lib: [
{ format: 'esm', dts: true },
{ format: 'cjs', dts: true },
],
plugins: [
pluginPublint({ enable: Boolean(process.env.CI) }),
pluginAreTheTypesWrong({ enable: Boolean(process.env.CI) }),
],
});
Installez les deux en tant que dépendances de développement. Les builds locaux restent rapides, et l’étape rslib build que vous exécutez déjà en CI devient le point de contrôle avant publication.
La génération des déclarations gagne aussi en rapidité sur les gros projets :
- Avec TypeScript 7 ou ultérieur, Rslib utilise automatiquement tsgo tout en conservant la vérification des types.
- L’option expérimentale
dts.isolatedest encore plus rapide, mais elle ignore la vérification des types. Exécutez donc une étape de type-check séparée en parallèle.
Lors d’un test mené par l’équipe Rslib sur le dépôt Rsbuild, la génération des déclarations a pris 9,7 s avec TypeScript 6, 4,1 s avec TypeScript 7 et 2,3 s avec les déclarations isolées. Les gains obtenus sur votre propre projet seront différents.
Bibliothèques de composants et mode bundleless
Utilisez le mode bundleless pour les bibliothèques de composants et le mode bundle pour les SDK et les CLI.
Avec bundle: false, Rslib transforme chaque fichier source en un fichier de sortie distinct, en reproduisant l’arborescence de vos sources. Le tree shaking et le chargement à la demande sont ainsi laissés au bundler du consommateur. Les compilateurs React, Vue, Svelte et Solid s’intègrent via des plugins Rsbuild, et create-rslib inclut un template pour chaque framework.
// rslib.config.ts
import { pluginReact } from '@rsbuild/plugin-react';
import { defineConfig } from '@rslib/core';
export default defineConfig({
bundle: false,
output: { target: 'web' },
plugins: [pluginReact()],
});
Les composants importent leurs styles de manière classique :
import './button.css';
import styles from './button.module.css';
Les CSS Modules et PostCSS fonctionnent sans configuration supplémentaire, et la manière dont Rslib produit les styles varie selon le mode de build. Sass, Less, Stylus et Tailwind CSS s’ajoutent via des plugins Rsbuild. Les bibliothèques Vue, Svelte et Solid utilisent la même structure de configuration avec leur propre plugin.
L’annonce de Rslib 1.0 présente un benchmark portant sur 10 000 composants React. Par rapport à la version 0.7.0, la version 1.0 :
- réduit le temps de build sans cache d’environ 24,3 % ;
- réduit le temps de build avec cache d’environ 56,7 % ;
- produit une sortie plus légère d’environ 32,2 % avant gzip et de 4,1 % après.
Sortie Module Federation
Rslib peut aussi builder une bibliothèque en tant que remote Module Federation avec format: 'mf'. Plusieurs applications la chargent alors au runtime au lieu de l’installer depuis npm. Privilégiez cette option lorsque le déploiement indépendant du code partagé compte davantage que des releases de packages versionnées.
Rslib vs tsup vs le mode bibliothèque de Vite
Rslib, tsup et le mode bibliothèque de Vite se distinguent davantage par leur périmètre que par leur vitesse :
- tsup repose sur esbuild et se concentre sur le bundling de TypeScript.
- Le mode bibliothèque de Vite prend un outil de build d’applications et le fait pointer vers un point d’entrée de bibliothèque.
- Rslib est conçu spécifiquement pour les bibliothèques, sur la base de Rsbuild et Rspack. Une seule configuration couvre les formats de sortie, les modes bundle et bundleless, les déclarations, la compilation des frameworks, les assets et les vérifications pré-publication.
Si votre application utilise déjà Rsbuild ou Rspack, la bibliothèque peut partager ses plugins et sa configuration.
L’article sur l’état actuel des bundlers JavaScript montre la place de Rspack dans l’écosystème global. Si vous préférez assembler votre propre pipeline à partir d’un bundler généraliste et de plugins, commencez par ce guide de prise en main de Rollup. Pour les migrations, Rslib propose un guide de migration depuis tsc, et son guide de démarrage rapide renvoie vers un guide équivalent pour tsup.
Conclusion
Une bibliothèque n’est prête à être publiée que lorsque ses formats, ses déclarations, ses styles et sa map exports sont cohérents entre eux, et Rslib les build et les vérifie ensemble. Commencez en pur ESM et n’ajoutez une entrée CJS au tableau lib que lorsqu’un consommateur en a besoin. Activez publint et arethetypeswrong en CI avant votre prochaine release.
FAQ
Comment inclure une dépendance dans ma sortie Rslib au lieu de la laisser externe ?
Définissez output.autoExternal dans l'entrée lib et désactivez le champ de dépendances que vous souhaitez bundler, par exemple dependencies défini à false. Rslib v1 déprécie lib.autoExternal au profit de output.autoExternal de Rsbuild, mais l'ancienne option fonctionne toujours. L'externalisation automatique ne s'applique qu'en mode bundle. En mode bundleless, les dépendances ne sont jamais bundlées. Pour ne garder externes que certains packages précis, listez-les dans output.externals.
Rslib peut-il regrouper toutes les déclarations TypeScript dans un seul fichier ?
Oui. Installez @microsoft/api-extractor en tant que dépendance de développement et définissez dts.bundle à true dans l'entrée lib. C'est API Extractor qui se charge du regroupement des déclarations, et dts.bundle vaut false par défaut. Les déclarations des dépendances directes non externalisées sont intégrées automatiquement. Définir dts.bundle.bundledPackages remplace cette liste par défaut, ce qui est utile lorsque vous devez inclure des dépendances transitives.
Rslib peut-il produire un bundle UMD pour un CDN ou une balise script ?
Oui. Définissez format à 'umd' dans une entrée lib et définissez lib.umdName avec le nom global exposé par le bundle, par exemple MyLibrary. L'externalisation automatique est désactivée par défaut pour la sortie UMD : les dépendances sont donc bundlées, sauf si vous les listez dans output.externals. Chaque entrée de cette liste associe un package à une variable globale du navigateur, par exemple react à React. lib.autoExtension est activé par défaut pour UMD.
Comment générer des déclarations .d.cts pour le build CommonJS ?
Activez dts.autoExtension dans chaque entrée lib. Cette option suit les mêmes règles que lib.autoExtension : avec type défini à module dans package.json, les déclarations ESM utilisent .d.ts et les déclarations CJS .d.cts. L'option est désactivée par défaut, si bien que les sorties ESM et CJS peuvent se retrouver avec le même nom de fichier .d.ts. Faites pointer les types de la condition require vers le fichier .d.cts.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k