Publicar una biblioteca de JavaScript con Rslib
Publica bibliotecas JavaScript con Rslib: configura builds ESM y CJS, genera declaraciones TypeScript, incluye estilos y detecta errores del paquete en CI.
Rslib es una herramienta de compilación de bibliotecas basada en Rsbuild y Rspack. A partir de una sola configuración genera salida ESM y CommonJS, declaraciones de TypeScript, componentes compilados para cada framework y recursos procesados. Está pensada para paquetes que se publican en npm, no para aplicaciones que se despliegan.
Esa distinción importa. Un bundle de aplicación solo tiene que funcionar en una pestaña del navegador. Una biblioteca, en cambio, la importan los bundlers, las versiones de Node.js y las configuraciones de TypeScript de otras personas. Además, falla de maneras que tu propia aplicación de prueba nunca revela: un require() que falla, tipos que se resuelven como any o un componente que se distribuye sin sus estilos. Esta guía lleva un paquete desde una primera compilación hasta una verificación previa a la publicación en CI con Rslib 1.0, publicado el 3 de septiembre de 2026.
Puntos clave
- Rslib 1.0 tiene una configuración y una API de JavaScript estables que siguen el versionado semántico (SemVer). Requiere Node.js 20.19+ o 22.12+.
- La documentación de Rslib recomienda ESM puro para los paquetes nuevos. Una compilación dual ESM/CJS solo se justifica cuando un consumidor concreto no puede cargar ESM.
- En Node.js
^20.19.0o>=22.12.0, el código CommonJS puede hacerrequire()de un paquete ESM puro. La condición es que ningún módulo que cargue, incluido el punto de entrada, useawaitde nivel superior. - El modo bundle (
bundle: true) es adecuado para SDK, CLI y utilidades de Node.js. El modo bundleless (bundle: false) conserva la estructura de archivos del código fuente y es adecuado para bibliotecas de componentes. rsbuild-plugin-publintyrsbuild-plugin-arethetypeswrongpueden ejecutarse dentro derslib buildsolo en CI. Así, los errores de empaquetado hacen fallar la compilación antes denpm publish.
¿Por qué es más difícil publicar una biblioteca que compilar una aplicación?
Publicar una biblioteca es más difícil porque un paquete publicable tiene que resolver bien cuatro cosas a la vez:
- Necesita salida ESM, y posiblemente CJS, que cargue en todos los entornos de ejecución que soportas.
- Necesita archivos de declaración que se resuelvan con la configuración de
moduleResolutionde cada consumidor. - Los componentes tienen que distribuirse junto con su CSS.
- El campo
exportsdelpackage.jsontiene que apuntar cada condición a un archivo que realmente exista.
Normalmente, cada una de estas tareas depende de una herramienta distinta: un bundler para el JavaScript, tsc para las declaraciones, un paso de PostCSS para los estilos y un mapa exports escrito a mano que lo mantiene todo unido. Cada vez que una de esas herramientas cambia una ruta de salida, el paquete se rompe.
¿Qué es Rslib?
Rslib es una herramienta de compilación de bibliotecas que cubre esas cuatro tareas en una sola compilación:
- Un array
libdefine cada formato de salida. - La opción
dtsgenera las declaraciones. En modo bundleless, además, corrige los alias de rutas y las extensiones de importación dentro de ellas para que coincidan con los archivos JavaScript que genera. - El tratamiento del CSS depende del modo de compilación.
- Publint detecta errores en
exports.
Rslib no escribe el campo exports por ti. Verifica el que tú escribiste.
Una compilación mínima con Rslib, de principio a fin
La forma más rápida de empezar es generar un proyecto con el scaffolding o ejecutar un único comando de la CLI. Si ya tienes un paquete, añade un archivo de configuración con una entrada lib por formato. Según el inicio rápido, Rslib v1 requiere Node.js 20.19+ o 22.12+.
npm create rslib@latest
# or, in an existing package
npm add @rslib/core -D
Una compilación de un solo formato no necesita archivo de configuración:
npx rslib build --entry src/index.ts --format esm --dts
--entry define el punto de entrada del código fuente, --format esm elige el formato de módulo y --dts emite las declaraciones. Para una salida dual, crea 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',
});
Todo lo que se define en el nivel superior se aplica a todas las salidas. Rslib lo fusiona con cada elemento de lib, así que cada entrada solo incluye lo que cambia. Rslib 1.0 también permite omitir lib por completo cuando solo necesitas una salida ESM por defecto. Añade un script de compilación:
{ "scripts": { "build": "rslib build" } }
Tras la compilación, el directorio de salida es dist por defecto. Contiene una compilación ESM, una compilación CJS y los archivos de declaración.
Las extensiones de archivo siguen lib.autoExtension. Con "type": "module", la salida ESM usa .js y la salida CJS usa .cjs. Sin esa opción, CJS usa .js y ESM usa .mjs. Por defecto, ambos formatos pueden emitir declaraciones con el mismo nombre .d.ts. Las opciones de dts controlan las extensiones de las declaraciones por formato.
En tu mapa exports, apunta la condición import a la entrada ESM y la condición require a la entrada CJS, cada una con sus declaraciones correspondientes. Copia las rutas reales de tu propio dist en lugar de adivinarlas.
En las salidas ESM y CJS, Rslib no incluye por defecto en el bundle las dependencies, optionalDependencies ni peerDependencies. Por eso, una dependencia peer como React se mantiene externa. Las devDependencies, en cambio, sí se incluyen en el bundle.
¿Conviene publicar solo ESM o una salida dual ESM/CJS?
La guía de formatos de salida de Rslib recomienda publicar los paquetes nuevos como ESM puro y añadir CJS solo cuando un consumidor lo necesite claramente. La salida dual cuesta más que un segundo paso de compilación. Tienes dos conjuntos de entradas en exports que mantener correctos y el doble de configuraciones que probar. Además, un consumidor que cargue ambos formatos obtiene dos instancias de tu paquete. Eso puede romper el estado compartido y las comprobaciones de identidad del tipo instanceof.
Muchos consumidores CommonJS ya no necesitan una compilación CJS. En Node.js ^20.19.0 o >=22.12.0 pueden cargar directamente un paquete ESM puro, siempre que se cumplan dos condiciones. Tu exports debe permitir que una llamada a require() llegue a la entrada ESM. Y ningún módulo del grafo cargado puede usar await de nivel superior:
const utils = require('my-esm-only-package');
Publica una salida dual solo en dos casos. El primero es cuando algunos de tus usuarios de CommonJS usan una versión de Node, una herramienta o un entorno de ejecución que no puede cargar ESM mediante require(). El segundo es cuando un usuario pide un archivo CJS independiente. Los ejemplos de paquetes de Node.js cubren la parte del package.json para cualquiera de las dos opciones.
Verificaciones antes de publicar
Dos plugins de Rsbuild detectan errores de empaquetado en una compilación de Rslib antes que npm:
rsbuild-plugin-publintbusca errores comunes en tupackage.json, en la estructura de archivos y en la configuración deexports.rsbuild-plugin-arethetypeswrongcomprueba si tus tipos siguen cargándose cuando un consumidor usa otra configuración de resolución de módulos.
El anuncio de Rslib 1.0 muestra ambos ejecutándose solo cuando CI está definido:
// 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) }),
],
});
Instala ambos como dependencias de desarrollo. Las compilaciones locales siguen siendo rápidas, y el paso rslib build que ya ejecutas en CI se convierte en el filtro previo a la publicación.
La generación de declaraciones también es más rápida en proyectos grandes. Con TypeScript 7 o posterior, Rslib usa tsgo automáticamente y sigue comprobando los tipos. La opción experimental dts.isolated es aún más rápida, pero omite la comprobación de tipos, así que debes ejecutar un paso de verificación de tipos por separado. En la prueba que hizo el equipo de Rslib sobre el repositorio de Rsbuild, la generación de declaraciones tardó 9,7 s con TypeScript 6, 4,1 s con TypeScript 7 y 2,3 s con declaraciones aisladas. Las mejoras en tu proyecto serán distintas.
Bibliotecas de componentes y modo bundleless
Usa el modo bundleless para bibliotecas de componentes y el modo bundle para SDK y CLI. Con bundle: false, Rslib convierte cada archivo fuente en un archivo de salida propio, con la misma estructura de carpetas que tu código fuente. Así, el tree shaking y la carga bajo demanda quedan en manos del bundler del consumidor. Los compiladores de React, Vue, Svelte y Solid se integran mediante plugins de Rsbuild, y create-rslib incluye una plantilla para cada 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()],
});
Los componentes importan los estilos de la forma habitual:
import './button.css';
import styles from './button.module.css';
CSS Modules y PostCSS funcionan sin configuración adicional, y la forma en que Rslib genera los estilos cambia según el modo de compilación. Sass, Less, Stylus y Tailwind CSS se añaden mediante plugins de Rsbuild. Las bibliotecas de Vue, Svelte y Solid usan la misma estructura de configuración con su propio plugin.
En el anuncio de Rslib 1.0, un benchmark con 10 000 componentes de React compara la versión 1.0 con la 0.7.0. La 1.0 reduce el tiempo de compilación sin caché en torno a un 24,3 % y con caché en torno a un 56,7 %. Además, genera una salida aproximadamente un 32,2 % más pequeña antes de gzip y un 4,1 % más pequeña después.
Salida para Module Federation
Rslib también puede compilar una biblioteca como remote de Module Federation con format: 'mf'. De este modo, varias aplicaciones la cargan en tiempo de ejecución en lugar de instalarla desde npm. Úsalo cuando desplegar el código compartido de forma independiente importe más que publicar versiones del paquete.
Rslib frente a tsup y al modo biblioteca de Vite
Rslib, tsup y el modo biblioteca de Vite se diferencian más en alcance que en velocidad:
- tsup está construido sobre esbuild y se centra en empaquetar TypeScript.
- El modo biblioteca de Vite toma una herramienta de compilación de aplicaciones y la dirige a un punto de entrada de biblioteca.
- Rslib está diseñado específicamente para bibliotecas sobre Rsbuild y Rspack. Una sola configuración cubre los formatos de salida, los modos bundle y bundleless, las declaraciones, la compilación de frameworks, los recursos y las verificaciones previas a la publicación.
Si tu aplicación ya usa Rsbuild o Rspack, la biblioteca puede compartir sus plugins y su configuración.
El artículo sobre el estado actual de los bundlers de JavaScript muestra el lugar que ocupa Rspack en el ecosistema. Si prefieres montar tu propio pipeline con un bundler de propósito general y plugins, empieza por esta guía para empezar con Rollup. Para migraciones, Rslib tiene una guía para migrar desde tsc, y su inicio rápido enlaza una guía equivalente para tsup.
Conclusión
Una biblioteca solo está lista para publicarse cuando sus formatos, declaraciones, estilos y mapa exports son coherentes entre sí, y Rslib los compila y verifica en conjunto. Empieza con ESM puro y añade una entrada CJS al array lib solo cuando un consumidor la necesite. Antes de tu próxima versión, activa publint y arethetypeswrong en CI.
Preguntas frecuentes
¿Cómo incluyo una dependencia en el bundle de Rslib en lugar de dejarla externa?
Define output.autoExternal dentro de la entrada lib y desactiva el campo de dependencias que quieras incluir en el bundle, por ejemplo dependencies con el valor false. Rslib v1 declara obsoleto lib.autoExternal en favor de output.autoExternal de Rsbuild, aunque la opción antigua sigue funcionando. La externalización automática solo se aplica en modo bundle. En modo bundleless, las dependencias nunca se incluyen en el bundle. Si solo quieres mantener externos ciertos paquetes, enuméralos en output.externals.
¿Puede Rslib agrupar todas las declaraciones de TypeScript en un único archivo?
Sí. Instala @microsoft/api-extractor como dependencia de desarrollo y establece dts.bundle en true en la entrada lib. API Extractor se encarga de agrupar las declaraciones, y dts.bundle tiene el valor false por defecto. Las declaraciones de las dependencias directas que no se externalizan se incorporan automáticamente. Definir dts.bundle.bundledPackages reemplaza esa lista por defecto, lo que resulta útil cuando necesitas agrupar dependencias transitivas.
¿Puede Rslib generar un bundle UMD para un CDN o una etiqueta script?
Sí. Establece format en 'umd' en una entrada lib y define lib.umdName con el nombre global que expone el bundle, por ejemplo MyLibrary. La externalización automática está desactivada por defecto en la salida UMD, así que las dependencias se incluyen en el bundle salvo que las enumeres en output.externals. Cada entrada allí asocia un paquete a una variable global del navegador, por ejemplo react a React. lib.autoExtension está activado por defecto para UMD.
¿Cómo genero declaraciones .d.cts para la compilación CommonJS?
Activa dts.autoExtension en cada entrada lib. Sigue las mismas reglas que lib.autoExtension: con type establecido en module en el package.json, las declaraciones ESM usan .d.ts y las declaraciones CJS usan .d.cts. La opción está desactivada por defecto, así que las salidas ESM y CJS pueden acabar con el mismo nombre de archivo .d.ts. Apunta los tipos de la condición require al archivo .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