Compilando TypeScript avanzado con esbuild-plugin-tsc

Qué es y para qué sirve

esbuild-plugin-tsc es un plugin para esbuild que delega la transpilación de ficheros TypeScript al compilador oficial tsc. Esto resulta útil cuando tu código depende de características que esbuild aún no cubre, como emitDecoratorMetadata, o cuando necesitas que los metadatos de los decoradores queden perfectamente reflejados en runtime.

Instalación y configuración inicial

npm i -D esbuild-plugin-tsc typescript

El siguiente fragmento muestra una configuración mínima para compilar un proyecto con decoradores:

// build.js
import { build } from 'esbuild';
import tscPlugin from 'esbuild-plugin-tsc';

await build({
  entryPoints: ['src/main.ts'],
  bundle: true,
  outfile: 'dist/app.js',
  platform: 'node',
  plugins: [tscPlugin()]
}).catch(() => process.exit(1));

Filtrado selectivo de ficheros

Evita que todo el árbol de fuentes pase por tsc. Usa la opción filter para restringir la compilación solo a los módulos que realmente lo necesitan:

import { build } from 'esbuild';
import tscPlugin from 'esbuild-plugin-tsc';

await build({
  entryPoints: ['src/index.ts'],
  bundle: true,
  outfile: 'dist/bundle.js',
  plugins: [
    tscPlugin({
      filter: /src\/decorated\/.*\.ts$/,
      tsconfig: 'tsconfig.build.json'
    })
  ]
});

Integración con monorepos

Si tu repositorio contiene varios paquetes, puedes centralizar la configuración de TypeScript en un único tsconfig.base.json y luego extenderlo:

// packages/api/build.js
import { build } from 'esbuild';
import tscPlugin from 'esbuild-plugin-tsc';

await build({
  entryPoints: ['src/server.ts'],
  bundle: true,
  outfile: 'lib/server.js',
  plugins: [
    tscPlugin({
      tsconfig: '../../tsconfig.base.json',
      filter: /\.ts$/
    })
  ]
});

Combinación con otros plugins

esbuild-plugin-tsc puede coexistir con plugins de esbuild que gestionan estilos, assets o alias. El orden de los plugins importa: coloca tscPlugin antes que cualquier otro que transforme TypeScript para evitar conflictos.

import { build } from 'esbuild';
import tscPlugin from 'esbuild-plugin-tsc';
import { sassPlugin } from 'esbuild-sass-plugin';

await build({
  entryPoints: ['src/ui.tsx'],
  bundle: true,
  outfile: 'dist/ui.js',
  plugins: [
    tscPlugin({ filter: /\.tsx?$/ }),
    sassPlugin({ type: 'style' })
  ]
});

Consideraciones de rendimiento

Al usar tsc solo sobre un subconjunto de ficheros mantienes la mayor parte de la velocidad de esbuild. Para proyectos grandes, combina:

  • filter para reducir el volumen de código procesado por tsc
  • incremental: true en tsconfig.json para aprovechar la compilación incremental
  • esbuild en modo watch para recargas rápidas durante el desarrollo

Ejemplo real: API con routing por decoradores

// src/controllers/UserController.decorated.ts
import { Controller, Get } from '@nestjs/common';

@Controller('users')
export class UserController {
  @Get()
  list() {
    return [{ id: 1, name: 'Ada' }];
  }
}
// build.js
import { build } from 'esbuild';
import tscPlugin from 'esbuild-plugin-tsc';

await build({
  entryPoints: ['src/main.ts'],
  bundle: true,
  platform: 'node',
  target: 'node18',
  outfile: 'dist/main.js',
  plugins: [
    tscPlugin({
      filter: /\.decorated\.ts$/,
      tsconfig: 'tsconfig.json'
    })
  ]
});

Etiquetas: Esbuild tsc TypeScript decorators emitDecoratorMetadata

Publicado el 9-5 11:21