esbuild - Desarrollo sin dolor.

Jun 8 '22

Post Original

Herramientas:

  • Node v16
  • Fastify v4.0 (Puedes usar el server que quieras [express, koa, nest])
  • esbuild v0.14
  • npm v8.5 / yarn v1.22

Porque esbuild?

Nuestras actuales herramientas de compilación para la web son entre 10 y 100 veces más lentas de lo que podrían ser. El objetivo principal del proyecto esbuild bundler es traer una nueva era de rendimiento de la herramienta de construcción, y crear un bundler moderno y fácil de usar en el camino.

Empecemos 🚀🚀

Crear carpeta del proyecto

$ mkdir esbuild-test
$ cd esbuild-test

# npm
$ npm init -y

# yarn
$ yarn init -y
Enter fullscreen mode Exit fullscreen mode

Instalaciones

# npm
$ npm i esbuild fastify

# yarn
$ yarn add esbuild fastify
Enter fullscreen mode Exit fullscreen mode

Quickstart

// index.js
import Fastify from 'fastify'

Fastify()
    .get('/', (_request, reply) => reply.send({ hello: 'world' }))
    .listen({ port: 3000 }, (_error, address) => console.log(`Listening on ${address}`))

Enter fullscreen mode Exit fullscreen mode

Creamos los scripts

Explicación de los flags:

  • minify: El código minificado generalmente es equivalente al código de desarrollo pero es más pequeño, lo que significa que se descarga más rápido pero es más difícil de depurar. Minifica el código en producción pero no en desarrollo.

  • bundle: Agrupa cualquier dependencia importada en el archivo de entrada (en este caso index.js).

  • platform: Por defecto, el bundler de esbuild está configurado para generar código destinado al navegador. Si su código empaquetado está destinado a ejecutarse en node en su lugar, debe establecer la plataforma a node.

  • external: Puede marcar un archivo o un paquete como externo para excluirlo de su construcción. En lugar de ser empaquetado, se conservará la importación (usando require para los formatos iife y cjs y usando import para el formato esm) y se evaluará en tiempo de ejecución en su lugar.

  • outfile: Esta opción establece el nombre del archivo de salida para la operación de construcción. Sólo es aplicable si hay un único punto de entrada. Si hay varios puntos de entrada, debe utilizar la opción outdir para especificar un directorio de salida.

// package.json
{
  "name": "esbuild-test",
  "version": "1.0.0",
  "license": "MIT",
  "scripts": {
    "start": "node dist",
    "build": "esbuild index.js --minify --bundle --platform=node --target=node16 --external:./node_modules/* --outfile=dist/index.js"
  },
  "dependencies": {
    "esbuild": "^0.14.43",
    "fastify": "^4.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Ejecutamos los comandos

# npm
$ npm run build
$ npm start

# yarn
$ yarn build
$ yarn start
Enter fullscreen mode Exit fullscreen mode

terminal1

Hot Reload

Ya creamos nuestro server, pero para ser un buen ambiente de desarrollo esto debería de soportar hot reload, sino que martirio tener que estar reiniciando el servidor manualmente.
Para esto esbuild ya cuenta con un watch que re-construye el proyecto al guardar un archivo después de modificarlo.
Pero veámoslo en acción. si actualizamos nuestro script dev agregando este nuevo flag:

// package.json
{
  "name": "esbuild-test",
  "version": "1.0.0",
  "license": "MIT",
  "scripts": {
    "start": "node dist",
    "dev": "npm run build -- --watch", // <- Justo aqui
    "build": "esbuild index.js --minify --bundle --platform=node --target=node16 --external:./node_modules/* --outfile=dist/index.js"
  },
  "dependencies": {
    "esbuild": "^0.14.43",
    "fastify": "^4.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Ahora si lo corremos y modificamos nuestro index.js esbuild se encargara de re-transpilar nuestro codigo.

# npm
$ npm run dev

# yarn
$ yarn dev
Enter fullscreen mode Exit fullscreen mode

terminal2

Pero tenemos un detalle, este servidor no se levanta.
Esto funcionara si ejecutamos start justo después de correr build con watch? intentemos.

// package.json
{
  "name": "esbuild-test",
  "version": "1.0.0",
  "license": "MIT",
  "scripts": {
    "start": "node dist",
    "dev": "npm run build -- --watch && npm start", // <- Justo aqui
    "build": "esbuild index.js --minify --bundle --platform=node --target=node16 --external:./node_modules/* --outfile=dist/index.js"
  },
  "dependencies": {
    "esbuild": "^0.14.43",
    "fastify": "^4.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Corremos dev.

# npm
$ npm run dev

# yarn
$ yarn dev
Enter fullscreen mode Exit fullscreen mode

terminal3

Parece que nunca llegamos a start. Claro, por que watch deja colgado el evento y la terminal no avanza al siguiente comando. Es hora de usar la API de JavaScript en vez del CLI. creamos un esbuild.dev.js en la raíz de nuestro proyecto y activaremos las mismas flags que en el CLI.
Para esta parte el watch recibe un objeto con una función onRebuild, la cual se ejecutara cada vez que se actualicen los archivos.

// esbuild.dev.js
require('esbuild').build({
    entryPoints: ['./index.js'],
    watch: {
        onRebuild: () => console.log('\nRebuilt...\n')
    },
    bundle: true,
    minify: true,
    platform: 'node',
    target: 'node16',
    external: ['/node_modules/*'],
    outfile: './dist/index.js',
})
    .then(() => console.log("\nDone 🚀\n"))
    .catch(() => process.exit(1))
Enter fullscreen mode Exit fullscreen mode

Lanzando procesos con node

Para este ultimo paso usaremos child_process de Node.js, en pocas palabras esto nos permite lanzar procesos desde Node.js, así que actualicemos nuestro esbuild.dev.js.

// esbuild.dev.js
const { spawn } = require('child_process')

let server;

require('esbuild').build({
    entryPoints: ['./index.js'],
    watch: {
        onRebuild: () => {
            console.log('\nRebuilt...\n')
            if (server) server.kill('SIGINT') // Si ya hay un servidor ejecutandose, lo matamos
            server = spawn('node', ['./dist'], { stdio: 'inherit' }) // Al terminar el re-build lanza el servidor
        }
    },
    bundle: true,
    minify: true,
    platform: 'node',
    target: 'node16',
    external: ['/node_modules/*'],
    outfile: './dist/index.js',
})
    .then(() => {
        console.log("\nDone 🚀\n")
        server = spawn('node', ['./dist'], { stdio: 'inherit' }) // Al terminar el build lanza el servidor
    })
    .catch(() => process.exit(1))
Enter fullscreen mode Exit fullscreen mode

Probemoslo. Modifica el comando dev.

// package.json
{
  "name": "esbuild-test",
  "version": "1.0.0",
  "license": "MIT",
  "scripts": {
    "start": "node dist",
    "dev": "node esbuild.dev.js", // <- Justo aqui
    "build": "esbuild index.js --minify --bundle --platform=node --target=node16 --external:./node_modules/* --outfile=dist/index.js"
  },
  "dependencies": {
    "esbuild": "^0.14.43",
    "fastify": "^4.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

terminal4

Listo 🚀

esbuild nos ah servido para remplazar:

  • Webpack
  • Nodemon
  • tsc

Espera un segundo... TSC? Claro, esbuild también soporta Typescript (más info). desarrollo sin dolor = Typescript. Solo cambiemos la extensión de index.js -> index.ts y el punto de entrada en nuestro esbuild.dev.js:

// esbuild.dev.js
/* ... code */
require('esbuild').build({
    entryPoints: ['./index.ts'], // <- Justo aqui
    watch: {
        onRebuild: () => {
            /* ... code */
        }
    },
    bundle: true,
    minify: true,
    platform: 'node',
    target: 'node16',
    external: ['/node_modules/*'],
    outfile: './dist/index.js',
})
/* ... code */
Enter fullscreen mode Exit fullscreen mode

Y listo. Ejecutamos dev. Todo va sobre ruedas e igual de rápido.

Así debería quedar nuestro código al final.
terminal5

Conclusión

Esta vez explicamos todo paso a paso pero siendo sinceros te tomaría un copiar y pegar para levantar esto en tu próximo proyecto.

Déjame en los comentarios que piensas

y Happy Hacking 👨‍💻🎉

Powered by dev.to