Cómo estructurar el package.json para exportar varios módulos desde una misma librería con soporte ESM.
Vamos a ver cómo crear una librería que exporte varios paquetes o módulos para que luego puedan usarse en otro proyecto. Para ello la clave está en definir correctamente cómo se exportan esos componentes desde el package.json
. Esto se vuelve especialmente relevante en entornos ESM (ECMAScript Modules), donde la resolución de imports y rutas debe ser explícita y consistente.
Veamos paso a paso cómo estructurar una librería modular y exportarla correctamente usando el campo exports
de package.json
.
Supongamos que tenemos una librería con la siguiente estructura:
my-lib/├── src/│ ├── index.ts│ ├── alpha.ts│ └── beta.ts├── dist/│ ├── index.js│ ├── alpha.js│ └── beta.js├── package.json├── tsconfig.json
Cada archivo representa un módulo independiente: alpha
, beta
, y una entrada principal en index.ts
.
El archivo principal index.ts
sirve como índice de los paquetes que serán exportados.
export * from './alpha';export * from './beta';
Luego creamos dos archivos de ejemplo que representan el contenido de los paquetes a exportar.
export function alphaFn() { return 'alpha';}
export function betaFn() { return 'beta';}
Después de compilar a JavaScript usando TypeScript con module: "ESNext"
y outDir: "dist"
, tenemos los archivos .js
correspondientes en la carpeta dist
.
package.json
Para que la librería sea consumida correctamente por otras aplicaciones o paquetes ESM, debemos declarar los puntos de entrada en el campo exports
. Esto permite exponer explícitamente qué módulos se pueden importar desde fuera.
{ "name": "my-lib", "version": "1.0.0", "type": "module", "main": "./dist/index.js", "exports": { ".": { "import": "./dist/index.js" }, "./*": { "import": "./dist/*.js" } }}
Con esta configuración, los consumidores pueden hacer lo siguiente:
import { alphaFn } from 'my-lib/alpha';import { betaFn } from 'my-lib/beta';
O también:
import { alphaFn, betaFn } from 'my-lib';
"type": "module"
es necesario para que Node.js trate todos los archivos .js
como módulos ESM.exports
, todo lo que no esté declarado explícitamente queda fuera del acceso externo. Esto ayuda a mantener una API pública clara. Como en el ejemplo podemos usar wildcards (./dist/*.js
) si tenemos muchas librerías a exportar.typesVersions
o exports
con rutas a los .d.ts
equivalentes.Por ejemplo:
"types": "./dist/index.d.ts","exports": { ".": { "import": "./dist/index.js", "types": "./dist/index.d.ts" }, "./*": { "import": "./dist/*.js", "types": "./dist/*.d.ts" }}
Así permitimos a los editores y herramientas de tipado detectar los tipos automáticamente sin necesidad de configuraciones adicionales.
Exportar múltiples paquetes desde una librería ESM es una cuestión de estructurar bien el package.json
. Declarar explícitamente cada entrada en el campo exports
nos da mayor control sobre la API pública y permite que los usuarios accedan a los módulos.
Una vez configurado, el mantenimiento de la librería es más predecible, especialmente cuando se trabaja con múltiples equipos o cuando la librería crece. Nos permite también ofrecer imports individuales, lo cual puede ser útil para evitar importar más código del necesario.