Skip to content

Il bundler di Bun implementa un flag --compile per generare un binario standalone da un file TypeScript o JavaScript.

bash
bun build ./cli.ts --compile --outfile mycli
ts
console.log("Hello world!");

Questo crea un bundle di cli.ts in un eseguibile che può essere eseguito direttamente:

bash
./mycli
txt
Hello world!

Tutti i file e pacchetti importati sono bundled nell'eseguibile, insieme a una copia del runtime Bun. Tutte le API built-in di Bun e Node.js sono supportate.


Cross-compilazione per altre piattaforme

Il flag --target ti permette di compilare il tuo eseguibile standalone per un sistema operativo, architettura o versione di Bun diversa dalla macchina su cui stai eseguendo bun build.

Per build per Linux x64 (la maggior parte dei server):

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

# Per supportare CPU precedenti al 2013, usa la versione baseline (nehalem)
bun build --compile --target=bun-linux-x64-baseline ./index.ts --outfile myapp

# Per supportare esplicitamente solo CPU dal 2013 in poi, usa la versione modern (haswell)
# modern è più veloce, ma baseline è più compatibile.
bun build --compile --target=bun-linux-x64-modern ./index.ts --outfile myapp

Per build per Linux ARM64 (es. Graviton o Raspberry Pi):

bash
# Nota: l'architettura predefinita è x64 se nessuna architettura è specificata.
bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp

Per build per Windows x64:

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

# Per supportare CPU precedenti al 2013, usa la versione baseline (nehalem)
bun build --compile --target=bun-windows-x64-baseline ./path/to/my/app.ts --outfile myapp

# Per supportare esplicitamente solo CPU dal 2013 in poi, usa la versione modern (haswell)
bun build --compile --target=bun-windows-x64-modern ./path/to/my/app.ts --outfile myapp

# nota: se nessuna estensione .exe è fornita, Bun la aggiungerà automaticamente per eseguibili Windows

Per build per macOS arm64:

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

Per build per macOS x64:

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

Target supportati

L'ordine del flag --target non importa, purché siano delimitati da -.

--targetSistema OperativoArchitetturaModernBaselineLibc
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

Costanti a build-time

Usa il flag --define per iniettare costanti a build-time nel tuo eseguibile, come numeri di versione, timestamp di build o valori di configurazione:

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

Queste costanti sono incorporate direttamente nel tuo binario compilato a build-time, fornendo zero overhead runtime e abilitando ottimizzazioni di dead code elimination.

NOTE

Per esempi completi e pattern avanzati, vedi la [guida Costanti a build-time](/it/guides/runtime/build-time-constants).

Deployment in produzione

Gli eseguibili compilati riducono l'uso di memoria e migliorano il tempo di avvio di Bun.

Normalmente, Bun legge e transpila file JavaScript e TypeScript su import e require. Questo è parte di ciò che rende molto di Bun "just work", ma non è gratuito. Costa tempo e memoria leggere file da disco, risolvere percorsi file, parsare, transpilare e stampare codice sorgente.

Con eseguibili compilati, puoi spostare quel costo da runtime a build-time.

Quando deployi in produzione, raccomandiamo quanto segue:

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

Compilazione bytecode

Per migliorare il tempo di avvio, abilita la compilazione bytecode:

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

Usando la compilazione bytecode, tsc avvia 2x più veloce:

La compilazione bytecode sposta l'overhead di parsing per file di input grandi da runtime a bundle time. La tua app avvia più veloce, in cambio di rendere il comando bun build un po' più lento. Non offusca il codice sorgente.

Cosa fanno questi flag?

L'argomento --minify ottimizza la dimensione del codice di output transpilato. Se hai un'applicazione grande, questo può risparmiare megabyte di spazio. Per applicazioni più piccole, potrebbe comunque migliorare il tempo di avvio un po'.

L'argomento --sourcemap incorpora una sourcemap compressa con zstd, così che errori e stacktrace puntano alle loro posizioni originali invece della posizione transpilata. Bun decomprimerà e risolverà automaticamente la sourcemap quando si verifica un errore.

L'argomento --bytecode abilita la compilazione bytecode. Ogni volta che esegui codice JavaScript in Bun, JavaScriptCore (il motore) compilerà il tuo codice sorgente in bytecode. Possiamo spostare questo lavoro di parsing da runtime a bundle time, risparmiandoti tempo di avvio.


Incorporare argomenti runtime

--compile-exec-argv="args" - Incorpora argomenti runtime disponibili tramite process.execArgv:

bash
bun build --compile --compile-exec-argv="--smol --user-agent=MyBot" ./app.ts --outfile myapp
ts
// Nell'app compilata
console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"]

Disabilitare caricamento automatico config

Di default, gli eseguibili standalone cercano file .env e bunfig.toml nella directory dove l'eseguibile è eseguito. Puoi disabilitare questo comportamento a build time per esecuzione deterministica indipendentemente dalla directory di lavoro dell'utente.

bash
# Disabilita caricamento .env
bun build --compile --no-compile-autoload-dotenv ./app.ts --outfile myapp

# Disabilita caricamento bunfig.toml
bun build --compile --no-compile-autoload-bunfig ./app.ts --outfile myapp

# Disabilita entrambi
bun build --compile --no-compile-autoload-dotenv --no-compile-autoload-bunfig ./app.ts --outfile myapp

Puoi anche configurare questo tramite l'API JavaScript:

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

Agire come la CLI Bun

NOTE

Nuovo in Bun v1.2.16

Puoi eseguire un eseguibile standalone come se fosse la CLI bun stessa impostando la variabile d'ambiente BUN_BE_BUN=1. Quando questa variabile è impostata, l'eseguibile ignorerà il suo entrypoint bundled e invece esporrà tutte le funzionalità della CLI di Bun.

Per esempio, considera un eseguibile compilato da uno script semplice:

bash
echo "console.log(\"you shouldn't see this\");" > such-bun.js
bun build --compile ./such-bun.js
txt
[3ms] bundle 1 modules
[89ms] compile such-bun

Normalmente, eseguire ./such-bun con argomenti eseguirebbe lo script.

bash
# L'eseguibile esegue il proprio entrypoint di default
./such-bun install
txt
you shouldn't see this

Tuttavia, con la variabile d'ambiente BUN_BE_BUN=1, agisce proprio come il binario bun:

bash
# Con la variabile d'ambiente, l'eseguibile agisce come la 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]

Questo è utile per costruire strumenti CLI su Bun che potrebbero aver bisogno di installare pacchetti, bundlare dipendenze, eseguire file diversi o locali e altro senza dover scaricare un binario separato o installare bun.


Eseguibili full-stack

NOTE

Nuovo in Bun v1.2.17

Il flag --compile di Bun può creare eseguibili standalone che contengono sia codice server che client, rendendolo ideale per applicazioni full-stack. Quando importi un file HTML nel tuo codice server, Bun bundla automaticamente tutte le risorse frontend (JavaScript, CSS, ecc.) e le incorpora nell'eseguibile. Quando Bun vede l'import HTML sul server, avvia un processo di build frontend per bundlare JavaScript, CSS e altre risorse.

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

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

console.log(`Server in esecuzione su http://localhost:${server.port}`);
html
<!DOCTYPE html>
<html>
  <head>
    <title>My App</title>
    <link rel="stylesheet" href="./styles.css" />
  </head>
  <body>
    <h1>Hello World</h1>
    <script src="./app.ts"></script>
  </body>
</html>
ts
console.log("Hello from the client!");
css
body {
  background-color: #f0f0f0;
}

Per buildare questo in un singolo eseguibile:

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

Questo crea un binario self-contained che include:

  • Il tuo codice server
  • Il runtime Bun
  • Tutte le risorse frontend (HTML, CSS, JavaScript)
  • Qualsiasi pacchetto npm usato dal tuo server

Il risultato è un singolo file che può essere deployato ovunque senza bisogno di Node.js, Bun o dipendenze installate. Basta eseguire:

bash
./myapp

Bun gestisce automaticamente il serving delle risorse frontend con i corretti header MIME type e cache. L'import HTML è rimpiazzato con un oggetto manifest che Bun.serve usa per servire efficientemente risorse pre-bundlate.

Per maggiori dettagli sulla costruzione di applicazioni full-stack con Bun, vedi la guida full-stack.


Worker

Per usare worker in un eseguibile standalone, aggiungi l'entrypoint del worker agli argomenti CLI:

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

Poi, referenzia il worker nel tuo codice:

ts
console.log("Hello from Bun!");

// Ognuno di questi funzionerà:
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);

Quando aggiungi più entrypoint a un eseguibile standalone, saranno bundlati separatamente nell'eseguibile.

In futuro, potremmo rilevare automaticamente usi di percorsi staticamente noti in new Worker(path) e poi bundlarli nell'eseguibile, ma per ora, dovrai aggiungerlo manualmente al comando shell come nell'esempio sopra.

Se usi un percorso relativo a un file non incluso nell'eseguibile standalone, proverà a caricare quel percorso da disco relativo alla directory di lavoro corrente del processo (e poi darà errore se non esiste).


SQLite

Puoi usare import bun:sqlite con bun build --compile.

Di default, il database è risolto relativo alla directory di lavoro corrente del processo.

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

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

Questo significa che se l'eseguibile si trova a /usr/bin/hello, il terminale dell'utente si trova a /home/me/Desktop, cercherà /home/me/Desktop/my.db.

bash
cd /home/me/Desktop
./hello

Incorporare risorse e file

Gli eseguibili standalone supportano l'incorporamento di file.

Per incorporare file in un eseguibile con bun build --compile, importa il file nel tuo codice.

ts
// questo diventa un percorso file interno
import icon from "./icon.png" with { type: "file" };
import { file } from "bun";

export default {
  fetch(req) {
    // I file incorporati possono essere streammati da oggetti Response
    return new Response(file(icon));
  },
};

I file incorporati possono essere letti usando le funzioni di Bun.file o la funzione fs.readFile di Node.js (in "node:fs").

Per esempio, per leggere i contenuti del file incorporato:

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)

Incorporare database SQLite

Se la tua applicazione vuole incorporare un database SQLite, imposta type: "sqlite" nell'attributo di import e l'attributo embed a "true".

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

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

Questo database è read-write, ma tutte le modifiche sono perse quando l'eseguibile esce (poiché è memorizzato in memoria).

Incorporare Addon N-API

Puoi incorporare file .node negli eseguibili.

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

console.log(addon.hello());

Sfortunatamente, se stai usando @mapbox/node-pre-gyp o altri strumenti simili, dovrai assicurarti che il file .node sia richiesto direttamente o non si bundlerà correttamente.

Incorporare directory

Per incorporare una directory con bun build --compile, usa un glob shell nel tuo comando bun build:

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

Poi, puoi referenziare i file nel tuo codice:

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

export default {
  fetch(req) {
    // I file incorporati possono essere streammati da oggetti Response
    return new Response(file(icon));
  },
};

Questo è onestamente un workaround, e ci aspettiamo di migliorare questo in futuro con un'API più diretta.

Elencare file incorporati

Per ottenere una lista di tutti i file incorporati, usa Bun.embeddedFiles:

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

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

Bun.embeddedFiles restituisce un array di oggetti Blob che puoi usare per ottenere dimensione, contenuti e altre proprietà dei file.

ts
embeddedFiles: Blob[]

La lista dei file incorporati esclude codice sorgente bundled come file .ts e .js.

Hash contenuto

Di default, i file incorporati hanno un hash di contenuto aggiunto al loro nome. Questo è utile per situazioni dove vuoi servire il file da un URL o CDN e avere meno problemi di invalidazione cache. Ma a volte, questo è inaspettato e potresti volere il nome originale invece:

Per disabilitare l'hash di contenuto, passa --asset-naming a bun build --compile così:

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

Minificazione

Per ridurre un po' la dimensione dell'eseguibile, passa --minify a bun build --compile. Questo usa il minifier di Bun per ridurre la dimensione del codice. Complessivamente però, il binario di Bun è ancora troppo grande e dobbiamo renderlo più piccolo.


Flag specifici per Windows

Quando compili un eseguibile standalone su Windows, ci sono due opzioni specifiche per piattaforma che possono essere usate per personalizzare i metadati sul file .exe generato:

  • --windows-icon=path/to/icon.ico per personalizzare l'icona del file eseguibile.
  • --windows-hide-console per disabilitare il terminale di sfondo, che può essere usato per applicazioni che non hanno bisogno di un TTY.

Code signing su macOS

Per codesignare un eseguibile standalone su macOS (che risolve gli avvisi Gatekeeper), usa il comando codesign.

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

Raccomandiamo di includere un file entitlements.plist con permessi 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>

Per codesignare con supporto JIT, passa il flag --entitlements a codesign.

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

Dopo codesigning, verifica l'eseguibile:

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

Code splitting

Gli eseguibili standalone supportano code splitting. Usa --compile con --splitting per creare un eseguibile che carica chunk code-split a runtime.

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

Argomenti CLI non supportati

Attualmente, il flag --compile può accettare solo un singolo entrypoint alla volta e non supporta i seguenti flag:

  • --outdir — usa outfile invece (eccetto quando usato con --splitting).
  • --public-path
  • --target=node o --target=browser
  • --no-bundle - bundliamo sempre tutto nell'eseguibile.

Bun a cura di www.bunjs.com.cn