• Saltar al contenido principal
  • Skip to secondary menu
  • Saltar a la barra lateral principal
  • Saltar al pie de página
  • Inicio
  • Secciones
    • Ciencia de datos
    • Criptografía
    • Herramientas
    • Machine Learning
    • Noticias
    • Opinión
    • Productividad
    • Programación
      • JavaScript
      • Julia
      • Matlab
      • Python
      • R
  • Programación
    • JavaScript
    • Julia
    • Matlab
    • Python
    • R
  • Laboratorio
    • Estadística
      • Calculadora del Tamaño Muestral en Encuestas
      • Calculadora de estadísticos descriptivos
      • Test de normalidad
      • Calculadora de contrastes de hipotesis
      • Calculadora de tamano del efecto
      • Simulador de Regresión Lineal con Ruido
      • Visualizador de PCA
      • Visualizador de Series Temporales
      • Simulador de Regresión Logística
      • Simulador de K-Means
      • Simulador de DBSCAN
      • Detector de la Ley de Benford
      • Ajuste de Curvas
      • Calculadora de Matrices
    • Probabilidad
      • Calculadora de Probabilidad de Distribuciones
      • Calculadora de Probabilidades de Lotería
      • Simulador del Problema de Monty Hall
      • Simulador de la Estrategia Martingala
    • Finanzas
      • Calculadora de Préstamos e Hipotecas
      • Conversor TIN ↔ TAE
      • Calculadora DCA con ajuste por inflación
      • Calculadora XIRR con Flujos Irregulares
      • Simulador FIRE (Financial Independence, Retire Early)
    • Negocios
      • CLV
      • Scoring
    • Herramientas
      • Formateador / Minificador de JSON
      • Conversor CSV ↔ JSON
      • Comparador y Formateador de Texto y JSON
      • Formateador y Tester de Expresiones Regulares
      • Inspector de JWT
      • Generador y verificador de hashes
      • Codificador / Decodificador Base64 y URL
      • Conversor de bases numericas
      • Conversor de Timestamp Unix
      • Conversor de colores
      • Generador de UUIDs
    • Juegos
      • Tres en Raya
      • Nim con Q-Learning
    • Más
      • Método D’Hondt
      • Generador de Contraseñas Seguras
  • Noticias
  • Boletín
  • Contacto
  • Tienda
    • Libros
    • Equipamiento de oficina
    • Equipamiento en movilidad

Analytics Lane

Ciencia e ingeniería de datos aplicada

  • Ciencia de datos
  • Machine Learning
  • IA Generativa
  • Python
  • Pandas
  • NumPy
  • R
  • Excel

Migrar tslane a un paquete dual CommonJS y ESM con tsup y Vitest

TypeScript

octubre 1, 2026 Por Daniel Rodríguez Deja un comentario
Tiempo de lectura: 5 minutos

tslane es la plantilla que uso como punto de partida para mis librerías TypeScript, nacida de la serie de 2020 y actualizada varias veces desde entonces. La más reciente, sobre consistencia de nombres con ESLint. En esta entrada aplico el razonamiento de la primera entrada de esta serie a este proyecto, pero con una diferencia clave respecto a la anterior: tslane no es una aplicación, es una plantilla para librerías que otros van a consumir. Eso descarta pasar a "type": "module" sin más, porque convertiría en ESM-only a cualquier librería nacida de esta plantilla, rompiendo a quien la use desde un proyecto CommonJS.

Como es una plantilla sin código propio que mantener ni versiones publicadas a las que dar soporte, este es precisamente el mejor momento para decidir esto bien: cualquier librería nueva partirá ya con la decisión correcta tomada, sin arrastrar deuda técnica proyecto a proyecto.

Tabla de contenidos

  • 1. Punto de partida
  • 2. Por qué paquete dual y no ESM-only
  • 3. Sustituir tsc + Webpack por tsup
  • 4. Actualizar package.json
  • 5. Ajustar tsconfig.json
  • 6. Migrar los tests de Jest a Vitest
  • 7. Actualizar prepack.js
  • 8. Verificar la publicación
  • 9. Conclusiones

Punto de partida

// tsconfig.json (antes)
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "declaration": true,
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}
// package.json (antes, extracto)
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
  "build": "npx tsc",
  "bundle": "npx webpack --config-name=production",
  "coverage": "npx jest --collectCoverage",
  "pack": "npx del-cli ./dist && npm run build && node prepack.js && cd dist && npm pack && cd ..",
  "test": "npx jest"
},
"devDependencies": {
  "jest": "^30.0.0",
  "ts-jest": "^29.4.0",
  "ts-loader": "^9.5.2",
  "webpack": "^5.99.9",
  "webpack-cli": "^6.0.1",
  "npm-dts-webpack-plugin": "^1.3.13"
  // ...
}

El build actual combina dos pipelines: tsc para generar dist/ con las declaraciones y Webpack para un bundle adicional. Por otro lado, los tests usan Jest + ts-jest. Vamos a sustituir ambos por herramientas pensadas específicamente para este caso.

Por qué migrar tus proyectos TypeScript a ES Modules en 2026
En Analytics Lane
Por qué migrar tus proyectos TypeScript a ES Modules en 2026

Por qué paquete dual y no ESM-only

Como se explicaba en la primera entrada de esta serie, declarar "type": "module" a secas convierte cualquier require('tu-libreria') en un error para quien la consuma. Como no sabemos de antemano si las librerías nacidas de esta plantilla las usarán solo en proyectos propios modernos o también terceros en CommonJS, la opción que no cierra puertas es publicar ambas salidas y dejar que Node, o el bundler de quien te consuma, elija la correcta automáticamente a través del campo "exports" del package.json.

Publicidad


Sustituir tsc + Webpack por tsup

npm uninstall webpack webpack-cli ts-loader npm-dts-webpack-plugin
npm install --save-dev tsup
// tsup.config.ts
import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['src/index.ts'],
  // Paquete dual: genera index.js (ESM) + index.cjs (CommonJS)
  format: ['esm', 'cjs'],
  // Genera index.d.ts (ESM) + index.d.cts (CommonJS), en línea con
  // el campo "exports" del package.json
  dts: true,
  sourcemap: true,
  clean: true,
  target: 'es2020',
});

Con esto, un único npm run build genera las cuatro salidas (index.js, index.cjs, index.d.ts, index.d.cts) que antes exigían dos herramientas distintas.

Actualizar package.json

{
  "type": "module",
  "main": "./dist/index.cjs",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": {
        "types": "./dist/index.d.ts",
        "default": "./dist/index.js"
      },
      "require": {
        "types": "./dist/index.d.cts",
        "default": "./dist/index.cjs"
      }
    },
    "./package.json": "./package.json"
  },
  "files": ["dist"],
  "scripts": {
    "build": "tsup",
    "dev": "tsup --watch",
    "test": "vitest run",
    "coverage": "vitest run --coverage",
    "typecheck": "tsc --noEmit",
    "doc": "npx typedoc --out docs src",
    "pack": "npx del-cli ./dist && npm run build && node prepack.js && cd dist && npm pack && cd .."
  }
}

El campo "exports" es el que resuelve de verdad el paquete dual: quien haga import recibe la build ESM con sus tipos, quien haga require recibe la build CJS con los suyos. Los campos main/module/types se mantienen como alternativa para herramientas más antiguas que todavía no leen "exports".

Ajustar tsconfig.json

Como ahora tsup (esbuild) es quien transpila de verdad, el tsconfig.json pasa a usarse sobre todo para el chequeo de tipos en el editor y en CI, no para generar el build:

{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["ES2020"],
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "declaration": true,
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true
  },
  "include": ["src"],
  "exclude": ["dist", "node_modules", "tests"]
}

De paso subimos el target de es5 a es2020, es5 solo tiene sentido si necesitas dar soporte a navegadores muy antiguos, y no es el caso de esta plantilla.

Publicidad


Migrar los tests de Jest a Vitest

npm uninstall jest ts-jest @types/jest
npm install --save-dev vitest @vitest/coverage-v8
// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    include: ['tests/**/*.test.ts'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html', 'lcov'],
      include: ['src/**/*.ts'],
    },
  },
});

La API de Vitest (describe, it, expect…) es prácticamente idéntica a la de Jest, así que los tests existentes en tests/ deberían necesitar pocos o ningún cambio más allá de sustituir jest.fn()/jest.mock() por vi.fn()/vi.mock() si los usas. Al estar construido sobre esbuild, Vitest soporta ESM y TypeScript de forma nativa, sin la fricción que da configurar Jest sobre un proyecto ESM.

Actualizar prepack.js

El script prepack.js genera el package.json que se publica dentro de dist/. Con el build anterior (un único index.js en CommonJS) bastaba con apuntar main/types/module a ese archivo. Con el paquete dual, necesita generar también el "exports" correcto:

// prepack.js
import { readFileSync, writeFileSync, copyFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';

const __dirname = dirname(fileURLToPath(import.meta.url));
const pkg = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf-8'));

const distPkg = {
  ...pkg,
  main: './index.cjs',
  module: './index.js',
  types: './index.d.ts',
  exports: {
    '.': {
      import: { types: './index.d.ts', default: './index.js' },
      require: { types: './index.d.cts', default: './index.cjs' },
    },
    './package.json': './package.json',
  },
};

delete distPkg.files;
delete distPkg.devDependencies;
delete distPkg.scripts;

writeFileSync(
  join(__dirname, 'dist', 'package.json'),
  JSON.stringify(distPkg, null, 2) + '\n',
);
copyFileSync(join(__dirname, 'README.md'), join(__dirname, 'dist', 'README.md'));

Si no se actualiza este script, cualquier librería creada desde la plantilla publicaría con paquete dual en el build, pero con metadatos de un único build en el package.json publicado —perdiendo la mitad del beneficio del cambio.

Verificar la publicación

Antes de dar la migración por buena, conviene comprobar que el paquete dual funciona de verdad desde los dos lados:

npm run pack

Y luego, en un proyecto de prueba aparte, instalar el .tgz generado y probar tanto:

// consumidor ESM
import { array } from 'tslane';

como:

// consumidor CommonJS
const { array } = require('tslane');

Si ambos funcionan sin errores, el paquete dual está correctamente configurado.

Publicidad


Conclusiones

A diferencia de expresslanets, donde el salto a ESM fue directo por no tener consumidores externos, en tslane la solución pasa por dejar montado un paquete dual CommonJS/ESM desde el principio, usando tsup en lugar de la combinación tsc + Webpack, y Vitest en lugar de Jest + ts-jest. El resultado es una plantilla que da a cada futura librería compatibilidad amplia sin que tengas que volver a pensarlo en cada proyecto nuevo.

Nota: La imagen de este artículo fue generada utilizando un modelo de inteligencia artificial.

¿Te ha parecido de utilidad el contenido?

¡Puntúalo entre una y cinco estrellas!

Puntuación promedio 0 / 5. Votos emitidos: 0

Ya que has encontrado útil este contenido...

¡Síguenos en redes sociales!

¡Siento que este contenido no te haya sido útil!

¡Déjame mejorar este contenido!

Dime, ¿cómo puedo mejorar este contenido?

Publicaciones relacionadas

  • Por qué migrar tus proyectos TypeScript a ES Modules en 2026
  • Actualizar expresslanets a ES Modules con tsx y Vitest
  • Curiosidad: Por qué la criptografía habla siempre de Alice y Bob
  • Errores comunes al interpretar resultados estadísticos
  • Método del codo: interpretación correcta y limitaciones
  • Dashboard CLV: cuatro modelos, un mismo dataset, conclusiones muy distintas
  • Pareto/NBD: cuando el abandono silencioso cambia el CLV
  • Cómo construir tu primer scorecard paso a paso

Publicado en: JavaScript Etiquetado como: TypeScript

Interacciones con los lectores

Deja una respuesta Cancelar la respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

I accept the Terms and Conditions and the Privacy Policy

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Barra lateral principal

Suscríbete a nuestro boletín

Suscríbete al boletín semanal para estar al día de todas las publicaciones.

Política de Privacidad

Analytics Lane en redes sociales

  • Amazon
  • Bluesky
  • Facebook
  • GitHub
  • Instagram
  • Mastodon
  • Pinterest
  • RSS
  • Telegram
  • Tumblr
  • Twitter
  • YouTube

Publicidad

Entradas recientes

Migrar tslane a un paquete dual CommonJS y ESM con tsup y Vitest

octubre 1, 2026 Por Daniel Rodríguez

Cómo construir tu primer scorecard paso a paso

septiembre 29, 2026 Por Daniel Rodríguez

Actualizar expresslanets a ES Modules con tsx y Vitest

septiembre 24, 2026 Por Daniel Rodríguez

Publicidad

Es tendencia

  • Creación de gráficos de barras y gráficos de columnas con Seaborn publicado el julio 18, 2023 | en Python
  • Noticias Disponible la traducción de la tercera edicción de “Hands-On Machine Learning” de Aurelien Geron publicado el junio 20, 2023 | en Noticias
  • Cómo comparar tendencias con gráficos de líneas en Matplotlib: guía práctica paso a paso publicado el junio 9, 2026 | en Python
  • Cómo comparar datos con barras en Matplotlib: agrupadas, apiladas y porcentuales publicado el mayo 12, 2026 | en Python
  • El método de Muller e implementación en Python publicado el marzo 24, 2023 | en Ciencia de datos

Publicidad

Lo mejor valorado

4.9 (24)

Seleccionar filas y columnas en Pandas con iloc y loc

4.6 (16)

Archivos JSON con Python: lectura y escritura

4.4 (14)

Ordenación de diccionarios en Python mediante clave o valor

4.7 (13)

Operaciones de filtrado de DataFrame con Pandas en base a los valores de las columnas

4.1 (11)

Aplicar el método D’Hondt en Excel

Comentarios recientes

  • Daniel Rodríguez en Curiosidad: La Paradoja de Simpson, o por qué no siempre debes fiarte de los promedios
  • Hatepi en Curiosidad: La Paradoja de Simpson, o por qué no siempre debes fiarte de los promedios
  • Daniel Rodríguez en Los récords con asterisco, o la épica del titular sin contexto – El bestiario de los indicadores económicos absurdos (parte 8 y final)
  • Juan en Los récords con asterisco, o la épica del titular sin contexto – El bestiario de los indicadores económicos absurdos (parte 8 y final)
  • bif en JSON en bases de datos: cuándo es buena idea y cuándo no

Publicidad


Footer

Analytics Lane

  • Acerca de Analytics Lane
  • Boletín de noticias
  • Contacto
  • Libros
  • Lo más popular
  • Noticias
  • Tienda
  • Tiendas afiliadas

Secciones

  • Ciencia de datos
  • Criptografía
  • Herramientas
  • Machine Learning
  • Opinión
  • Productividad
  • Programación
  • Reseñas

Sobre de Analytics Lane

En Analytics Lane tratamos de explicar los principales conceptos de la ciencia e ingeniería de datos con un enfoque práctico. Los principales temas tratados son ciencia de datos, ingeniería de datos, inteligencia artificial, machine learning, deep learning y criptografía. Además, también se habla de los principales lenguajes de programación y herramientas utilizadas por los científicos e ingenieros de datos.

Copyright © 2018-2026 Analytics Lane ·Términos y condiciones ·Política de Cookies ·Política de Privacidad ·Herramientas de privacidad ·Contacto