Skip to content

El empaquetador de Bun implementa una bandera --compile para generar un binario independiente desde un archivo TypeScript o JavaScript.

bash
bun build ./cli.ts --compile --outfile mycli
ts
console.log("¡Hola mundo!");

Esto empaqueta cli.ts en un ejecutable que puede ejecutarse directamente:

bash
./mycli
txt
¡Hola mundo!

Todos los archivos y paquetes importados se empaquetan en el ejecutable, junto con una copia del runtime de Bun. Todas las APIs integradas de Bun y Node.js son compatibles.


Compilación cruzada a otras plataformas

La bandera --target te permite compilar tu ejecutable independiente para un sistema operativo, arquitectura o versión de Bun diferente de la máquina en la que estás ejecutando bun build.

Para construir para Linux x64 (la mayoría de servidores):

bash
bun build --compile --target=bun-linux-x64 ./index.ts --outfile myapp

# Para soportar CPUs anteriores a 2013, usa la versión baseline (nehalem)
bun build --compile --target=bun-linux-x64-baseline ./index.ts --outfile myapp

# Para soportar explícitamente solo CPUs de 2013 en adelante, usa la versión modern (haswell)
# modern es más rápida, pero baseline es más compatible.
bun build --compile --target=bun-linux-x64-modern ./index.ts --outfile myapp

Para construir para Linux ARM64 (ej. Graviton o Raspberry Pi):

bash
# Nota: la arquitectura predeterminada es x64 si no se especifica arquitectura.
bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp

Para construir para Windows x64:

bash
bun build --compile --target=bun-windows-x64 ./path/to/my/app.ts --outfile myapp

# Para soportar CPUs anteriores a 2013, usa la versión baseline (nehalem)
bun build --compile --target=bun-windows-x64-baseline ./path/to/my/app.ts --outfile myapp

# Para soportar explícitamente solo CPUs de 2013 en adelante, usa la versión modern (haswell)
bun build --compile --target=bun-windows-x64-modern ./path/to/my/app.ts --outfile myapp

# nota: si no se proporciona extensión .exe, Bun la agregará automáticamente para ejecutables de Windows

Para construir para macOS arm64:

bash
bun build --compile --target=bun-darwin-arm64 ./path/to/my/app.ts --outfile myapp

Para construir para macOS x64:

bash
bun build --compile --target=bun-darwin-x64 ./path/to/my/app.ts --outfile myapp

Objetivos soportados

El orden de la bandera --target no importa, siempre que estén delimitados por -.

--targetSistema OperativoArquitecturaModernBaselineLibc
bun-linux-x64Linuxx64glibc
bun-linux-arm64Linuxarm64N/Aglibc
bun-windows-x64Windowsx64-
bun-windows-arm64Windowsarm64-
bun-darwin-x64macOSx64-
bun-darwin-arm64macOSarm64N/A-
bun-linux-x64-muslLinuxx64musl
bun-linux-arm64-muslLinuxarm64N/Amusl

Constantes en tiempo de construcción

Usa la bandera --define para inyectar constantes en tiempo de construcción en tu ejecutable, como números de versión, marcas de tiempo de construcción o valores de configuración:

bash
bun build --compile --define BUILD_VERSION='"1.2.3"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/cli.ts --outfile mycli

Estas constantes se incrustan directamente en tu binario compilado en tiempo de construcción, proporcionando cero sobrecarga en tiempo de ejecución y habilitando optimizaciones de eliminación de código muerto.

NOTE

Para ejemplos comprehensivos y patrones avanzados, consulta la [guía de Constantes en tiempo de construcción](/es/guides/runtime/build-time-constants).

Despliegue a producción

Los ejecutables compilados reducen el uso de memoria y mejoran el tiempo de inicio de Bun.

Normalmente, Bun lee y transpila archivos JavaScript y TypeScript en import y require. Esto es parte de lo que hace que tanto de Bun "simplemente funcione", pero no es gratis. Cuesta tiempo y memoria leer archivos del disco, resolver rutas de archivos, analizar, transpilar e imprimir código fuente.

Con ejecutables compilados, puedes mover ese costo de tiempo de ejecución a tiempo de construcción.

Al desplegar a producción, recomendamos lo siguiente:

bash
bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp

Compilación de bytecode

Para mejorar el tiempo de inicio, habilita la compilación de bytecode:

bash
bun build --compile --minify --sourcemap --bytecode ./path/to/my/app.ts --outfile myapp

Usando compilación de bytecode, tsc inicia 2x más rápido:

La compilación de bytecode mueve la sobrecarga de análisis para archivos de entrada grandes de tiempo de ejecución a tiempo de empaquetado. Tu aplicación inicia más rápido, a cambio de hacer el comando bun build un poco más lento. No oscurece el código fuente.

¿Qué hacen estas banderas?

El argumento --minify optimiza el tamaño del código de salida transpilado. Si tienes una aplicación grande, esto puede ahorrar megabytes de espacio. Para aplicaciones más pequeñas, aún podría mejorar un poco el tiempo de inicio.

El argumento --sourcemap incrusta un sourcemap comprimido con zstd, para que los errores y stacktraces apunten a sus ubicaciones originales en lugar de la ubicación transpilada. Bun automáticamente descomprimirá y resolverá el sourcemap cuando ocurra un error.

El argumento --bytecode habilita la compilación de bytecode. Cada vez que ejecutas código JavaScript en Bun, JavaScriptCore (el motor) compilará tu código fuente en bytecode. Podemos mover este trabajo de análisis de tiempo de ejecución a tiempo de empaquetado, ahorrándote tiempo de inicio.


Incrustar argumentos de runtime

--compile-exec-argv="args" - Incrusta argumentos de runtime disponibles vía process.execArgv:

bash
bun build --compile --compile-exec-argv="--smol --user-agent=MyBot" ./app.ts --outfile myapp
ts
// En la aplicación compilada
console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"]

Deshabilitar carga automática de configuración

Por defecto, los ejecutables independientes buscan archivos .env y bunfig.toml en el directorio donde se ejecuta el ejecutable. Puedes deshabilitar este comportamiento en tiempo de construcción para ejecución determinística sin importar el directorio de trabajo del usuario.

bash
# Deshabilitar carga de .env
bun build --compile --no-compile-autoload-dotenv ./app.ts --outfile myapp

# Deshabilitar carga de bunfig.toml
bun build --compile --no-compile-autoload-bunfig ./app.ts --outfile myapp

# Deshabilitar ambos
bun build --compile --no-compile-autoload-dotenv --no-compile-autoload-bunfig ./app.ts --outfile myapp

También puedes configurar esto vía la API de JavaScript:

ts
await Bun.build({
  entrypoints: ["./app.ts"],
  compile: {
    autoloadDotenv: false, // Deshabilitar carga de .env
    autoloadBunfig: false, // Deshabilitar carga de bunfig.toml
  },
});

Actuar como el CLI de Bun

NOTE

Nuevo en Bun v1.2.16

Puedes ejecutar un ejecutable independiente como si fuera el CLI bun mismo estableciendo la variable de entorno BUN_BE_BUN=1. Cuando esta variable está establecida, el ejecutable ignorará su entrypoint empaquetado y en su lugar expondrá todas las características del CLI de Bun.

Por ejemplo, considera un ejecutable compilado desde un script simple:

bash
echo "console.log(\"no deberías ver esto\");" > such-bun.js
bun build --compile ./such-bun.js
txt
[3ms] bundle 1 modules
[89ms] compile such-bun

Normalmente, ejecutar ./such-bun con argumentos ejecutaría el script.

bash
# El ejecutable ejecuta su propio entrypoint por defecto
./such-bun install
txt
no deberías ver esto

Sin embargo, con la variable de entorno BUN_BE_BUN=1, actúa igual que el binario bun:

bash
# Con la variable de entorno, el ejecutable actúa como el CLI `bun`
BUN_BE_BUN=1 ./such-bun install
txt
bun install v1.2.16-canary.1 (1d1db811)
Checked 63 installs across 64 packages (no changes) [5.00ms]

Esto es útil para construir herramientas CLI sobre Bun que puedan necesitar instalar paquetes, empaquetar dependencias, ejecutar archivos diferentes o locales y más sin necesitar descargar un binario separado o instalar bun.


Ejecutables full-stack

NOTE

Nuevo en Bun v1.2.17

La bandera --compile de Bun puede crear ejecutables independientes que contienen código de servidor y cliente, haciéndolo ideal para aplicaciones full-stack. Cuando importas un archivo HTML en tu código de servidor, Bun automáticamente empaqueta todos los assets del frontend (JavaScript, CSS, etc.) y los incrusta en el ejecutable. Cuando Bun ve la importación HTML en el servidor, inicia un proceso de construcción del frontend para empaquetar JavaScript, CSS y otros assets.

ts
import { serve } from "bun";
import index from "./index.html";

const server = serve({
  routes: {
    "/": index,
    "/api/hello": { GET: () => Response.json({ message: "Hola desde la API" }) },
  },
});

console.log(`Servidor ejecutándose en http://localhost:${server.port}`);
html
<!DOCTYPE html>
<html>
  <head>
    <title>Mi App</title>
    <link rel="stylesheet" href="./styles.css" />
  </head>
  <body>
    <h1>Hola Mundo</h1>
    <script src="./app.ts"></script>
  </body>
</html>
ts
console.log("¡Hola desde el cliente!");
css
body {
  background-color: #f0f0f0;
}

Para construir esto en un único ejecutable:

bash
bun build --compile ./server.ts --outfile myapp

Esto crea un binario autocontenido que incluye:

  • Tu código de servidor
  • El runtime de Bun
  • Todos los assets del frontend (HTML, CSS, JavaScript)
  • Cualquier paquete npm usado por tu servidor

El resultado es un único archivo que puede desplegarse en cualquier lugar sin necesitar Node.js, Bun o cualquier dependencia instalada. Solo ejecuta:

bash
./myapp

Bun automáticamente maneja servir los assets del frontend con los tipos MIME y cabeceras de caché apropiados. La importación HTML se reemplaza con un objeto manifiesto que Bun.serve usa para servir eficientemente assets pre-empaquetados.

Para más detalles sobre construir aplicaciones full-stack con Bun, consulta la guía full-stack.


Worker

Para usar workers en un ejecutable independiente, agrega el entrypoint del worker a los argumentos del CLI:

bash
bun build --compile ./index.ts ./my-worker.ts --outfile myapp

Luego, referencia el worker en tu código:

ts
console.log("¡Hola desde Bun!");

// Cualquiera de estos funcionará:
new Worker("./my-worker.ts");
new Worker(new URL("./my-worker.ts", import.meta.url));
new Worker(new URL("./my-worker.ts", import.meta.url).href);

Cuando agregas múltiples entrypoints a un ejecutable independiente, se empaquetarán por separado en el ejecutable.

En el futuro, podríamos detectar automáticamente usos de rutas estáticamente conocidas en new Worker(path) y luego empaquetarlas en el ejecutable, pero por ahora, necesitarás agregarlo al comando de shell manualmente como en el ejemplo anterior.

Si usas una ruta relativa a un archivo no incluido en el ejecutable independiente, intentará cargar esa ruta desde el disco relativa al directorio de trabajo actual del proceso (y luego fallará si no existe).


SQLite

Puedes usar imports bun:sqlite con bun build --compile.

Por defecto, la base de datos se resuelve relativa al directorio de trabajo actual del proceso.

ts
import db from "./my.db" with { type: "sqlite" };

console.log(db.query("select * from users LIMIT 1").get());

Eso significa que si el ejecutable está ubicado en /usr/bin/hello, la terminal del usuario está ubicada en /home/me/Desktop, buscará /home/me/Desktop/my.db.

bash
cd /home/me/Desktop
./hello

Incrustar assets y archivos

Los ejecutables independientes soportan incrustar archivos.

Para incrustar archivos en un ejecutable con bun build --compile, importa el archivo en tu código.

ts
// esto se convierte en una ruta de archivo interna
import icon from "./icon.png" with { type: "file" };
import { file } from "bun";

export default {
  fetch(req) {
    // Los archivos incrustados pueden transmitirse desde objetos Response
    return new Response(file(icon));
  },
};

Los archivos incrustados pueden leerse usando las funciones de Bun.file o la función fs.readFile de Node.js (en "node:fs").

Por ejemplo, para leer el contenido del archivo incrustado:

ts
import icon from "./icon.png" with { type: "file" };
import { file } from "bun";

const bytes = await file(icon).arrayBuffer();
// await fs.promises.readFile(icon)
// fs.readFileSync(icon)

Incrustar bases de datos SQLite

Si tu aplicación quiere incrustar una base de datos SQLite, establece type: "sqlite" en el atributo de import y el atributo embed a "true".

ts
import myEmbeddedDb from "./my.db" with { type: "sqlite", embed: "true" };

console.log(myEmbeddedDb.query("select * from users LIMIT 1").get());

Esta base de datos es de lectura-escritura, pero todos los cambios se pierden cuando el ejecutable sale (ya que se almacena en memoria).

Incrustar Addons N-API

Puedes incrustar archivos .node en ejecutables.

ts
const addon = require("./addon.node");

console.log(addon.hello());

Desafortunadamente, si estás usando @mapbox/node-pre-gyp u otras herramientas similares, necesitarás asegurarte de que el archivo .node se requiera directamente o no se empaquetará correctamente.

Incrustar directorios

Para incrustar un directorio con bun build --compile, usa un glob de shell en tu comando bun build:

bash
bun build --compile ./index.ts ./public/**/*.png

Luego, puedes referenciar los archivos en tu código:

ts
import icon from "./public/assets/icon.png" with { type: "file" };
import { file } from "bun";

export default {
  fetch(req) {
    // Los archivos incrustados pueden transmitirse desde objetos Response
    return new Response(file(icon));
  },
};

Esto es honestamente un workaround, y esperamos mejorar esto en el futuro con una API más directa.

Listar archivos incrustados

Para obtener una lista de todos los archivos incrustados, usa Bun.embeddedFiles:

ts
import "./icon.png" with { type: "file" };
import { embeddedFiles } from "bun";

console.log(embeddedFiles[0].name); // `icon-${hash}.png`

Bun.embeddedFiles devuelve un array de objetos Blob que puedes usar para obtener el tamaño, contenido y otras propiedades de los archivos.

ts
embeddedFiles: Blob[]

La lista de archivos incrustados excluye código fuente empaquetado como archivos .ts y .js.

Hash de contenido

Por defecto, los archivos incrustados tienen un hash de contenido agregado a su nombre. Esto es útil para situaciones donde quieres servir el archivo desde una URL o CDN y tener menos problemas de invalidación de caché. Pero a veces, esto es inesperado y podrías querer el nombre original en su lugar:

Para deshabilitar el hash de contenido, pasa --asset-naming a bun build --compile así:

bash
bun build --compile --asset-naming="[name].[ext]" ./index.ts

Minificación

Para reducir un poco el tamaño del ejecutable, pasa --minify a bun build --compile. Esto usa el minificador de Bun para reducir el tamaño del código. En general, el binario de Bun sigue siendo demasiado grande y necesitamos hacerlo más pequeño.


Banderas específicas de Windows

Al compilar un ejecutable independiente en Windows, hay dos opciones específicas de plataforma que pueden usarse para personalizar metadatos en el archivo .exe generado:

  • --windows-icon=path/to/icon.ico para personalizar el ícono del archivo ejecutable.
  • --windows-hide-console para deshabilitar la terminal de fondo, que puede usarse para aplicaciones que no necesitan un TTY.

Firma de código en macOS

Para firmar con código un ejecutable independiente en macOS (lo cual corrige advertencias de Gatekeeper), usa el comando codesign.

bash
codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp

Recomendamos incluir un archivo entitlements.plist con permisos JIT.

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.disable-executable-page-protection</key>
    <true/>
    <key>com.apple.security.cs.allow-dyld-environment-variables</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
</dict>
</plist>

Para firmar con código con soporte JIT, pasa la bandera --entitlements a codesign.

bash
codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp

Después de firmar con código, verifica el ejecutable:

bash
codesign -vvv --verify ./myapp
./myapp: valid on disk
./myapp: satisfies its Designated Requirement

Code splitting

Los ejecutables independientes soportan code splitting. Usa --compile con --splitting para crear un ejecutable que carga chunks de código dividido en tiempo de ejecución.

bash
bun build --compile --splitting ./src/entry.ts --outdir ./build
ts
console.log("Entrypoint cargado");
const lazy = await import("./lazy.ts");
lazy.hello();
ts
export function hello() {
  console.log("Módulo lazy cargado");
}
bash
./build/entry
txt
Entrypoint cargado
Módulo lazy cargado

Argumentos CLI no soportados

Actualmente, la bandera --compile solo puede aceptar un único entrypoint a la vez y no soporta las siguientes banderas:

  • --outdir — usa outfile en su lugar (excepto cuando se usa con --splitting).
  • --public-path
  • --target=node o --target=browser
  • --no-bundle - siempre empaquetamos todo en el ejecutable.

Bun por www.bunjs.com.cn editar