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.jsonCada 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.jsonPara 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.