Skip to content

import Build from "/snippets/cli/build.mdx";

Il bundler nativo veloce di Bun può essere usato tramite il comando CLI bun build o l'API JavaScript Bun.build().

A Colpo D'occhio

  • JS API: await Bun.build({ entrypoints, outdir })
  • CLI: bun build <entry> --outdir ./out
  • Watch: --watch per rebuild incrementali
  • Targets: --target browser|bun|node
  • Formati: --format esm|cjs|iife (sperimentale per cjs/iife)

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './build',
});

CLI

bash
bun build ./index.tsx --outdir ./build

È veloce. I numeri sotto rappresentano le performance sul benchmark three.js di esbuild.

Perché bundlare?

Il bundler è un pezzo chiave di infrastruttura nell'ecosistema JavaScript. Come breve panoramica del perché il bundling è così importante:

  • Ridurre richieste HTTP. Un singolo pacchetto in node_modules può consistere di centinaia di file, e grandi applicazioni possono avere dozzine di tali dipendenze. Caricare ciascuno di questi file con una richiesta HTTP separata diventa insostenibile molto velocemente, quindi i bundler sono usati per convertire il nostro codice sorgente applicazione in un numero minore di "bundle" auto-contenuti che possono essere caricati con una singola richiesta.
  • Trasformazioni codice. Le app moderne sono comunemente costruite con linguaggi o strumenti come TypeScript, JSX e CSS modules, che devono tutti essere convertiti in JavaScript e CSS plain prima di poter essere consumati da un browser. Il bundler è il posto naturale per configurare queste trasformazioni.
  • Funzionalità framework. I framework si affidano a plugin del bundler e trasformazioni codice per implementare pattern comuni come routing file-system, co-locazione codice client-server (pensa a getServerSideProps o loader di Remix), e server components.
  • Applicazioni Full-stack. Il bundler di Bun può gestire sia codice server che client in un singolo comando, abilitando build di produzione ottimizzate ed eseguibili single-file. Con import HTML a build-time, puoi bundlare l'intera tua applicazione — asset frontend e server backend — in una singola unità deployabile.

Tuffiamoci nell'API del bundler.

NOTE

Il bundler di Bun non è inteso per rimpiazzare `tsc` per typechecking o generazione di dichiarazioni di tipo.

Esempio base

Costruiamo il nostro primo bundle. Hai i seguenti due file, che implementano una semplice app React renderizzata client-side.

tsx
import * as ReactDOM from "react-dom/client";
import { Component } from "./Component";

const root = ReactDOM.createRoot(document.getElementById("root")!);
root.render(<Component message="Sup!" />);
tsx
export function Component(props: { message: string }) {
  return <h1>{props.message}</h1>;
}

Qui, index.tsx è l'"entrypoint" della nostra applicazione. Comunemente, questo sarà uno script che esegue qualche side effect, come avviare un server o—in questo caso—inizializzare un root React. Poiché stiamo usando TypeScript & JSX, dobbiamo bundlare il nostro codice prima che possa essere inviato al browser.

Per creare il nostro bundle:

ts
await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
});
bash
bun build ./index.tsx --outdir ./out

Per ogni file specificato in entrypoints, Bun genererà un nuovo bundle. Questo bundle sarà scritto su disco nella directory ./out (risolta dalla directory di lavoro corrente). Dopo aver eseguito la build, il file system appare così:

file system
text
.
├── index.tsx
├── Component.tsx
└── out
    └── index.js

Il contenuto di out/index.js apparirà più o meno così:

out/index.js
js
// out/index.js
// ...
// ~20k righe di codice
// incluso il contenuto di `react-dom/client` e tutte le sue dipendenze
// qui è dove le funzioni $jsxDEV e $createRoot sono definite

// Component.tsx
function Component(props) {
  return $jsxDEV(
    "p",
    {
      children: props.message,
    },
    undefined,
    false,
    undefined,
    this,
  );
}

// index.tsx
var rootNode = document.getElementById("root");
var root = $createRoot(rootNode);
root.render(
  $jsxDEV(
    Component,
    {
      message: "Sup!",
    },
    undefined,
    false,
    undefined,
    this,
  ),
);

Watch mode

Come il runtime e il test runner, il bundler supporta nativamente la watch mode.

bash
bun build ./index.tsx --outdir ./out --watch

Tipi di contenuto

Come il runtime di Bun, il bundler supporta nativamente un array di tipi di file. La tabella seguente scompone l'insieme di "loader" standard del bundler. Referenzia Bundler > Tipi di file per documentazione completa.

EstensioniDettagli
.js .jsx .cjs .mjs .mts .cts .ts .tsxUsa il transpiler built-in di Bun per parsare il file e transpilare sintassi TypeScript/JSX in JavaScript vanilla. Il bundler esegue un insieme di trasformazioni default inclusa dead code elimination e tree shaking. Al momento Bun non tenta di down-convertire la sintassi; se usi sintassi ECMAScript recente, quella sarà riflessa nel codice bundled.
.jsonI file JSON sono parsati e inlinati nel bundle come oggetto JavaScript.

js<br/>import pkg from "./package.json";<br/>pkg.name; // => "my-package"<br/>
.jsoncJSON con commenti. I file sono parsati e inlinati nel bundle come oggetto JavaScript.

js<br/>import config from "./config.jsonc";<br/>config.name; // => "my-config"<br/>
.tomlI file TOML sono parsati e inlinati nel bundle come oggetto JavaScript.

js<br/>import config from "./bunfig.toml";<br/>config.logLevel; // => "debug"<br/>
.yaml .ymlI file YAML sono parsati e inlinati nel bundle come oggetto JavaScript.

js<br/>import config from "./config.yaml";<br/>config.name; // => "my-app"<br/>
.txtIl contenuto del file di testo è letto e inlinato nel bundle come stringa.

js<br/>import contents from "./file.txt";<br/>console.log(contents); // => "Hello, world!"<br/>
.htmlI file HTML sono elaborati e qualsiasi asset referenziato (script, fogli di stile, immagini) è bundled.
.cssI file CSS sono bundled insieme in un singolo file .css nella directory di output.
.node .wasmQuesti file sono supportati dal runtime di Bun, ma durante il bundling sono trattati come asset.

Asset

Se il bundler incontra un import con un'estensione non riconosciuta, tratta il file importato come un file esterno. Il file referenziato è copiato così com'è in outdir, e l'import è risolto come un percorso al file.

ts
// bundle entrypoint
import logo from "./logo.svg";
console.log(logo);
ts
// bundled output
var logo = "./logo-a7305bdef.svg";
console.log(logo);

Il comportamento esatto del file loader è anche impattato da naming e publicPath.

Plugin

Il comportamento descritto in questa tabella può essere sovrascritto o esteso con plugin. Referenzia la pagina Bundler > Loader per documentazione completa.

API

entrypoints

Un array di percorsi corrispondenti agli entrypoint della nostra applicazione. Un bundle sarà generato per ogni entrypoint.

JavaScript

ts
const result = await Bun.build({
  entrypoints: ["./index.ts"]
});

CLI

bash
bun build ./index.ts

outdir

La directory dove i file di output saranno scritti.

JavaScript

ts
const result = await Bun.build({
  entrypoints: ['./index.ts'],
  outdir: './out'
});
// => { success: boolean, outputs: `BuildArtifact[]`, logs: `BuildMessage[]` }

CLI

bash
bun build ./index.ts --outdir ./out

Se outdir non è passato all'API JavaScript, il codice bundled non sarà scritto su disco. I file bundled sono restituiti in un array di oggetti BuildArtifact. Questi oggetti sono Blob con proprietà extra; vedi Outputs per documentazione completa.

ts
const result = await Bun.build({
  entrypoints: ["./index.ts"],
});

for (const res of result.outputs) {
  // Può essere consumato come blob
  await res.text();

  // Bun imposterà header Content-Type e Etag
  new Response(res);

  // Può essere scritto manualmente, ma dovresti usare `outdir` in questo caso.
  Bun.write(path.join("out", res.path), res);
}

Quando outdir è impostato, la proprietà path su un BuildArtifact sarà il percorso assoluto a dove è stato scritto.

target

L'ambiente di esecuzione inteso per il bundle.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.ts'],
  outdir: './out',
  target: 'browser', // default
})

CLI

bash
bun build ./index.ts --outdir ./out --target browser

Dipendendo dal target, Bun applicherà diverse regole di risoluzione moduli e ottimizzazioni.

Default. Per generare bundle che sono intesi per esecuzione da un browser. Prioritizza la condizione di export "browser" quando risolve gli import. Importare qualsiasi modulo built-in, come node:events o node:path funzionerà, ma chiamare alcune funzioni, come fs.readFile non funzionerà.

Per generare bundle che sono intesi per essere eseguiti dal runtime di Bun. In molti casi, non è necessario bundlare codice server-side; puoi eseguire direttamente il codice sorgente senza modifica. Tuttavia, bundlare il tuo codice server può ridurre i tempi di avvio e migliorare le performance di esecuzione. Questo è il target da usare per costruire applicazioni full-stack con import HTML a build-time, dove sia codice server che client sono bundled insieme.

Tutti i bundle generati con target: "bun" sono marcati con un pragma speciale // @bun, che indica al runtime di Bun che non c'è bisogno di re-transpilare il file prima dell'esecuzione.

Se qualsiasi entrypoint contiene uno shebang Bun (#!/usr/bin/env bun) il bundler userà di default target: "bun" invece di "browser".

Quando usi target: "bun" e format: "cjs" insieme, il pragma // @bun @bun-cjs è aggiunto e la funzione wrapper CommonJS non è compatibile con Node.js.

Per generare bundle che sono intesi per essere eseguiti da Node.js. Prioritizza la condizione di export "node" quando risolve gli import, e outputta .mjs. In futuro, questo farà automaticamente polyfill del global Bun e altri moduli built-in bun:*, anche se questo non è ancora implementato.

format

Specifica il formato del modulo da usare nei bundle generati.

Bun fa default a "esm", e fornisce supporto sperimentale per "cjs" e "iife".

format: "esm" - ES Module

Questo è il formato default, che supporta sintassi ES Module incluso top-level await, import.meta, e altro.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  format: "esm",
})

CLI

bash
bun build ./index.tsx --outdir ./out --format esm

Per usare sintassi ES Module nei browser, imposta format a "esm" e assicurati che il tuo tag <script type="module"> abbia type="module" impostato.

format: "cjs" - CommonJS

Per buildare un modulo CommonJS, imposta format a "cjs". Quando scegli "cjs", il target default cambia da "browser" (esm) a "node" (cjs). I moduli CommonJS transpilati con format: "cjs", target: "node" possono essere eseguiti sia in Bun che Node.js (assumendo che le API in uso sono supportate da entrambi).

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  format: "cjs",
})

CLI

bash
bun build ./index.tsx --outdir ./out --format cjs

format: "iife" - IIFE

TODO: documentare IIFE una volta che supportiamo globalNames.

jsx

Configura il comportamento della trasformata JSX. Permette controllo fine su come JSX è compilato.

Esempio runtime classic (usa factory e fragment):

ts
await Bun.build({
  entrypoints: ["./app.tsx"],
  outdir: "./out",
  jsx: {
    factory: "h",
    fragment: "Fragment",
    runtime: "classic",
  },
});
bash
# Configurazione JSX è gestita tramite bunfig.toml o tsconfig.json
bun build ./app.tsx --outdir ./out

Esempio runtime automatic (usa importSource):

ts
await Bun.build({
  entrypoints: ["./app.tsx"],
  outdir: "./out",
  jsx: {
    importSource: "preact",
    runtime: "automatic",
  },
});
bash
# Configurazione JSX è gestita tramite bunfig.toml o tsconfig.json
bun build ./app.tsx --outdir ./out

splitting

Se abilitare code splitting.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  splitting: false, // default
})

CLI

bash
bun build ./index.tsx --outdir ./out --splitting

Quando true, il bundler abilitterà code splitting. Quando multiple entrypoint importano entrambi lo stesso file, modulo, o insieme di file/moduli, è spesso utile splittare il codice condiviso in un bundle separato. Questo bundle condiviso è conosciuto come chunk. Considera i seguenti file:

ts
import { shared } from "./shared.ts";
ts
import { shared } from "./shared.ts";
ts
export const shared = "shared";

Per bundlare entry-a.ts e entry-b.ts con code-splitting abilitato:

JavaScript

ts
await Bun.build({
  entrypoints: ['./entry-a.ts', './entry-b.ts'],
  outdir: './out',
  splitting: true,
})

CLI

bash
bun build ./entry-a.ts ./entry-b.ts --outdir ./out --splitting

Eseguendo questa build risulterà nei seguenti file:

file system
text
.
├── entry-a.tsx
├── entry-b.tsx
├── shared.tsx
└── out
    ├── entry-a.js
    ├── entry-b.js
    └── chunk-2fce6291bf86559d.js

Il file generato chunk-2fce6291bf86559d.js contiene il codice condiviso. Per evitare collisioni, il nome del file include automaticamente un hash del contenuto di default. Questo può essere customizzato con naming.

plugins

Una lista di plugin da usare durante il bundling.

ts
await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
  plugins: [
    /* ... */
  ],
});

Bun implementa un sistema di plugin universale sia per il runtime che il bundler di Bun. Referenzia la documentazione plugin per documentazione completa.

env

Controlla come le variabili d'ambiente sono gestite durante il bundling. Internamente, questo usa define per iniettare variabili d'ambiente nel bundle, ma rende più facile specificare le variabili d'ambiente da iniettare.

env: "inline"

Inietta variabili d'ambiente nell'output bundled convertendo riferimenti process.env.FOO in stringhe letterali contenenti i valori effettivi delle variabili d'ambiente.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  env: "inline",
})

CLI

bash
bun build ./index.tsx --outdir ./out --env inline

Per l'input sotto:

input.js
js
// input.js
console.log(process.env.FOO);
console.log(process.env.BAZ);

Il bundle generato conterrà il seguente codice:

output.js
js
// output.js
console.log("bar");
console.log("123");

env: "PUBLIC_*" (prefisso)

Inlina variabili d'ambiente che corrispondono al prefisso dato (la parte prima del carattere *), rimpiazzando process.env.FOO con il valore effettivo della variabile d'ambiente. Questo è utile per inlinare selettivamente variabili d'ambiente per cose come URL pubblici o token client-side, senza preoccuparsi di iniettare credenziali private nei bundle di output.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  
  // Inline tutte le env vars che iniziano con "ACME_PUBLIC_"
  env: "ACME_PUBLIC_*",
})

CLI

bash
bun build ./index.tsx --outdir ./out --env ACME_PUBLIC_*

Per esempio, date le seguenti variabili d'ambiente:

bash
FOO=bar BAZ=123 ACME_PUBLIC_URL=https://acme.com

E codice sorgente:

tsx
console.log(process.env.FOO);
console.log(process.env.ACME_PUBLIC_URL);
console.log(process.env.BAZ);

Il bundle generato conterrà il seguente codice:

output.js
js
console.log(process.env.FOO);
console.log("https://acme.com");
console.log(process.env.BAZ);

env: "disable"

Disabilita completamente l'iniezione di variabili d'ambiente.

sourcemap

Specifica il tipo di sourcemap da generare.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  sourcemap: 'linked', // default 'none'
})

CLI

bash
bun build ./index.tsx --outdir ./out --sourcemap linked
ValoreDescrizione
"none"Default. Nessuna sourcemap è generata.
"linked"Un file separato *.js.map è creato accanto a ogni bundle *.js usando un commento //# sourceMappingURL per collegare i due. Richiede --outdir impostato. L'URL base di questo può essere customizzato con --public-path.

js<br/>// <codice bundled qui><br/><br/>//# sourceMappingURL=bundle.js.map<br/>
"external"Un file separato *.js.map è creato accanto a ogni bundle *.js senza inserire un commento //# sourceMappingURL.

I bundle generati contengono un debug id che può essere usato per associare un bundle con la sua sourcemap corrispondente. Questo debugId è aggiunto come commento in fondo al file.

js<br/>// <codice bundle generato><br/><br/>//# debugId=<DEBUG ID><br/>
"inline"Una sourcemap è generata e aggiunta alla fine del bundle generato come payload base64.

js<br/>// <codice bundled qui><br/><br/>//# sourceMappingURL=data:application/json;base64,<sourcemap codificato qui><br/>

La sourcemap *.js.map associata sarà un file JSON contenente una proprietà debugId equivalente.

minify

Se abilitare minificazione. Default false.

NOTE

Quando si targetta `bun`, gli identificatori saranno minificati di default.

Per abilitare tutte le opzioni di minificazione:

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  minify: true, // default false
})

CLI

bash
bun build ./index.tsx --outdir ./out --minify

Per abilitare granularmente certe minificazioni:

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  minify: {
    whitespace: true,
    identifiers: true,
    syntax: true,
  },
})

CLI

bash
bun build ./index.tsx --outdir ./out --minify-whitespace --minify-identifiers --minify-syntax

external

Una lista di percorsi import da considerare esterni. Default [].

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  external: ["lodash", "react"], // default: []
})

CLI

bash
bun build ./index.tsx --outdir ./out --external lodash --external react

Un import esterno è uno che non sarà incluso nel bundle finale. Invece, lo statement di import sarà lasciato così com'è, da risolvere a runtime.

Per istanza, considera il seguente file entrypoint:

tsx
import _ from "lodash";
import { z } from "zod";

const value = z.string().parse("Hello world!");
console.log(_.upperCase(value));

Normalmente, bundlare index.tsx genererebbe un bundle contenente l'intero codice sorgente del pacchetto "zod". Se invece, vogliamo lasciare lo statement di import così com'è, possiamo marchiarlo come esterno:

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  external: ['zod'],
})

CLI

bash
bun build ./index.tsx --outdir ./out --external zod

Il bundle generato apparirà più o meno così:

out/index.js
js
import { z } from "zod";

// ...
// il contenuto del pacchetto "lodash"
// inclusa la funzione `_.upperCase`

var value = z.string().parse("Hello world!");
console.log(_.upperCase(value));

Per marchiare tutti gli import come esterni, usa il wildcard *:

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  external: ['*'],
})

CLI

bash
bun build ./index.tsx --outdir ./out --external '*'

packages

Controlla se le dipendenze di pacchetto sono incluse nel bundle o no. Valori possibili: bundle (default), external. Bun tratta qualsiasi import il cui percorso non inizia con ., .. o / come pacchetto.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.ts'],
  packages: 'external',
})

CLI

bash
bun build ./index.ts --packages external

naming

Customizza i nomi dei file generati. Default ./[dir]/[name].[ext].

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  naming: "[dir]/[name].[ext]", // default
})

CLI

bash
bun build ./index.tsx --outdir ./out --entry-naming "[dir]/[name].[ext]"

Di default, i nomi dei bundle generati sono basati sul nome dell'entrypoint associato.

file system
text
.
├── index.tsx
└── out
    └── index.js

Con multiple entrypoint, la gerarchia di file generata rifletterà la struttura directory degli entrypoint.

file system
text
.
├── index.tsx
└── nested
    └── index.tsx
└── out
    ├── index.js
    └── nested
        └── index.js

I nomi e le posizioni dei file generati possono essere customizzati con il campo naming. Questo campo accetta una stringa template che è usata per generare i filename per tutti i bundle corrispondenti agli entrypoint, dove i seguenti token sono rimpiazzati con i loro valori corrispondenti:

  • [name] - Il nome del file entrypoint, senza estensione.
  • [ext] - L'estensione del bundle generato.
  • [hash] - Un hash del contenuto del bundle.
  • [dir] - Il percorso relativo dalla root del progetto alla directory parent del file sorgente.

Per esempio:

Token[name][ext][hash][dir]
./index.tsxindexjsa1b2c3d4"" (stringa vuota)
./nested/entry.tsentryjsc3d4e5f6"nested"

Possiamo combinare questi token per creare una stringa template. Per istanza, per includere l'hash nei nomi dei bundle generati:

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  naming: 'files/[dir]/[name]-[hash].[ext]',
})

CLI

bash
bun build ./index.tsx --outdir ./out --entry-naming 'files/[dir]/[name]-[hash].[ext]'

Questa build risulterebbe nella seguente struttura file:

file system
text
.
├── index.tsx
└── out
    └── files
        └── index-a1b2c3d4.js

Quando una stringa è fornita per il campo naming, è usata solo per bundle che corrispondono agli entrypoint. I nomi di chunk e asset copiati non sono affetti. Usando l'API JavaScript, stringhe template separate possono essere specificate per ogni tipo di file generato.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  naming: {
    // valori default
    entry: '[dir]/[name].[ext]',
    chunk: '[name]-[hash].[ext]',
    asset: '[name]-[hash].[ext]',
  },
})

CLI

bash
bun build ./index.tsx --outdir ./out \
  --entry-naming '[dir]/[name].[ext]' \
  --chunk-naming '[name]-[hash].[ext]' \
  --asset-naming '[name]-[hash].[ext]'

root

La directory root del progetto.

JavaScript

ts
await Bun.build({
  entrypoints: ['./pages/a.tsx', './pages/b.tsx'],
  outdir: './out',
  root: '.',
})

CLI

bash
bun build ./pages/a.tsx ./pages/b.tsx --outdir ./out --root .

Se non specificato, è computato come il primo antenato comune di tutti i file entrypoint. Considera la seguente struttura file:

file system
text
.
└── pages
  └── index.tsx
  └── settings.tsx

Possiamo buildare entrambi gli entrypoint nella directory pages:

JavaScript

js
await Bun.build({
  entrypoints: ['./pages/index.tsx', './pages/settings.tsx'],
  outdir: './out',
})

CLI

bash
bun build ./pages/index.tsx ./pages/settings.tsx --outdir ./out

Questo risulterebbe in una struttura file come questa:

file system
text
.
└── pages
  └── index.tsx
  └── settings.tsx
└── out
  └── index.js
  └── settings.js

Poiché la directory pages è il primo antenato comune dei file entrypoint, è considerata la root del progetto. Questo significa che i bundle generati vivono al top level della directory out; non c'è directory out/pages.

Questo comportamento può essere sovrascritto specificando l'opzione root:

JavaScript

js
await Bun.build({
  entrypoints: ['./pages/index.tsx', './pages/settings.tsx'],
  outdir: './out',
  root: '.',
})

CLI

bash
bun build ./pages/index.tsx ./pages/settings.tsx --outdir ./out --root .

Specificando . come root, la struttura file generata apparirà così:

.
└── pages
  └── index.tsx
  └── settings.tsx
└── out
  └── pages
    └── index.js
    └── settings.js

publicPath

Un prefisso da aggiungere a qualsiasi percorso import nel codice bundled.

In molti casi, i bundle generati non conterranno statement di import. Dopotutto, l'obiettivo del bundling è combinare tutto il codice in un singolo file. Tuttavia ci sono diversi casi in cui i bundle generati conterranno statement di import.

  • Import asset — Quando si importa un tipo di file non riconosciuto come *.svg, il bundler delega al file loader, che copia il file in outdir così com'è. L'import è convertito in una variabile
  • Moduli esterni — File e moduli possono essere marcati come esterni, nel qual caso non saranno inclusi nel bundle. Invece, lo statement di import sarà lasciato nel bundle finale.
  • Chunking. Quando splitting è abilitato, il bundler può generare file "chunk" separati che rappresentano codice condiviso tra multiple entrypoint.

In qualsiasi di questi casi, i bundle finali possono contenere percorsi ad altri file. Di default questi import sono relativi. Ecco un esempio di un semplice import asset:

ts
import logo from "./logo.svg";
console.log(logo);
ts
var logo = "./logo-a7305bdef.svg";
console.log(logo);

Impostare publicPath prefisserà tutti i percorsi file con il valore specificato.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  publicPath: 'https://cdn.example.com/', // default è undefined
})

CLI

bash
bun build ./index.tsx --outdir ./out --public-path 'https://cdn.example.com/'

Il file di output ora apparirà più o meno così.

out/index.js
js
var logo = "https://cdn.example.com/logo-a7305bdef.svg";

define

Una mappa di identificatori globali da rimpiazzare a build time. Le chiavi di questo oggetto sono nomi di identificatori, e i valori sono stringhe JSON che saranno inlinate.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  define: {
    STRING: JSON.stringify("value"),
    "nested.boolean": "true",
  },
})

CLI

bash
bun build ./index.tsx --outdir ./out --define STRING='"value"' --define nested.boolean=true

loader

Una mappa di estensioni file a nomi loader built-in. Questo può essere usato per customizzare rapidamente come certi file sono caricati.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  loader: {
    ".png": "dataurl",
    ".txt": "file",
  },
})

CLI

bash
bun build ./index.tsx --outdir ./out --loader .png:dataurl --loader .txt:file

Un banner da aggiungere al bundle finale, può essere una direttiva come use client per react o un blocco di commento come una licenza per il codice.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  banner: 'use client;'
})

CLI

bash
bun build ./index.tsx --outdir ./out --banner 'use client";'

Un footer da aggiungere al bundle finale, può essere qualcosa come un blocco di commento per una licenza o giusto un divertente easter egg.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  footer: '// built with love in SF'
})

CLI

bash
bun build ./index.tsx --outdir ./out --footer '// built with love in SF'

drop

Rimuove chiamate funzione da un bundle. Per esempio, --drop=console rimuoverà tutte le chiamate a console.log. Gli argomenti alle chiamate saranno anche rimossi, indipendentemente dal fatto che quegli argomenti possano avere side effects. Droppare debugger rimuoverà tutti gli statement debugger.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  drop: ["console", "debugger", "anyIdentifier.or.propertyAccess"],
})

CLI

bash
bun build ./index.tsx --outdir ./out --drop console --drop debugger

Outputs

La funzione Bun.build restituisce una Promise<BuildOutput>, definita come:

ts
interface BuildOutput {
  outputs: BuildArtifact[];
  success: boolean;
  logs: Array<object>; // vedi docs per dettagli
}

interface BuildArtifact extends Blob {
  kind: "entry-point" | "chunk" | "asset" | "sourcemap";
  path: string;
  loader: Loader;
  hash: string | null;
  sourcemap: BuildArtifact | null;
}

L'array outputs contiene tutti i file che sono stati generati dalla build. Ogni artifact implementa l'interfaccia Blob.

ts
const build = await Bun.build({
  /* */
});

for (const output of build.outputs) {
  await output.arrayBuffer(); // => ArrayBuffer
  await output.bytes(); // => Uint8Array
  await output.text(); // string
}

Ogni artifact contiene anche le seguenti proprietà:

ProprietàDescrizione
kindChe tipo di output di build è questo file. Una build genera entrypoint bundled, "chunk" code-split, sourcemap, bytecode e asset copiati (come immagini).
pathPercorso assoluto al file su disco
loaderIl loader usato per interpretare il file. Vedi Bundler > Loader per vedere come Bun mappa estensioni file al loader built-in appropriato.
hashL'hash del contenuto del file. Sempre definito per asset.
sourcemapIl file sourcemap corrispondente a questo file, se generato. Solo definito per entrypoint e chunk.

Simile a BunFile, oggetti BuildArtifact possono essere passati direttamente in new Response().

ts
const build = await Bun.build({
  /* */
});

const artifact = build.outputs[0];

// L'header Content-Type è automaticamente impostato
return new Response(artifact);

Il runtime di Bun implementa pretty-printing speciale di oggetti BuildArtifact per rendere il debugging più facile.

ts
// build.ts
const build = await Bun.build({
  /* */
});

const artifact = build.outputs[0];
console.log(artifact);
bash
bun run build.ts

BuildArtifact (entry-point) {
  path: "./index.js",
  loader: "tsx",
  kind: "entry-point",
  hash: "824a039620219640",
  Blob (74756 bytes) {
    type: "text/javascript;charset=utf-8"
  },
  sourcemap: BuildArtifact (sourcemap) {
    path: "./index.js.map",
    loader: "file",
    kind: "sourcemap",
    hash: "e7178cda3e72e301",
    Blob (24765 bytes) {
      type: "application/json;charset=utf-8"
    },
    sourcemap: null
  }
}

Bytecode

L'opzione bytecode: boolean può essere usata per generare bytecode per qualsiasi entrypoint JavaScript/TypeScript. Questo può migliorare notevolmente i tempi di avvio per grandi applicazioni. Supportato solo per formato "cjs", supporta solo "target": "bun" e dipendente da una versione corrispondente di Bun. Questo aggiunge un file .jsc corrispondente per ogni entrypoint.

JavaScript

ts
await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
  bytecode: true,
})

CLI

bash
bun build ./index.tsx --outdir ./out --bytecode

Executables

Bun supporta "compilare" un entrypoint JavaScript/TypeScript in un eseguibile standalone. Questo eseguibile contiene una copia del binario di Bun.

bash
bun build ./cli.tsx --outfile mycli --compile
./mycli

Referenzia Bundler > Executables per documentazione completa.

Log ed errori

Su fallimento, Bun.build restituisce una promise rifiutata con un AggregateError. Questo può essere loggato alla console per pretty printing della lista di errori, o letto programmaticamente con un blocco try/catch.

ts
try {
  const result = await Bun.build({
    entrypoints: ["./index.tsx"],
    outdir: "./out",
  });
} catch (e) {
  // TypeScript non permette annotazioni sulla clausola catch
  const error = e as AggregateError;
  console.error("Build Fallita");

  // Esempio: Usando il formatter built-in
  console.error(error);

  // Esempio: Serializzando il fallimento come stringa JSON.
  console.error(JSON.stringify(error, null, 2));
}

La maggior parte delle volte, un try/catch esplicito non è necessario, poiché Bun stamperà ordinatamente eccezioni non catturate. È sufficiente usare solo un await top-level sulla chiamata Bun.build.

Ogni elemento in error.errors è un'istanza di BuildMessage o ResolveMessage (sottoclassi di Error), contenente informazioni dettagliate per ogni errore.

ts
class BuildMessage {
  name: string;
  position?: Position;
  message: string;
  level: "error" | "warning" | "info" | "debug" | "verbose";
}

class ResolveMessage extends BuildMessage {
  code: string;
  referrer: string;
  specifier: string;
  importKind: ImportKind;
}

Su successo build, l'oggetto restituito contiene una proprietà logs, che contiene warning e messaggi info del bundler.

ts
const result = await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
});

if (result.logs.length > 0) {
  console.warn("Build riuscita con warning:");
  for (const message of result.logs) {
    // Bun farà pretty print dell'oggetto messaggio
    console.warn(message);
  }
}

Reference

ts
interface Bun {
  build(options: BuildOptions): Promise<BuildOutput>;
}

interface BuildConfig {
  entrypoints: string[]; // lista di percorsi file
  outdir?: string; // directory di output
  target?: Target; // default: "browser"
  /**
   * Formato del modulo di output. Top-level await è supportato solo per `"esm"`.
   *
   * Può essere:
   * - `"esm"`
   * - `"cjs"` (**sperimentale**)
   * - `"iife"` (**sperimentale**)
   *
   * @default "esm"
   */
  format?: "esm" | "cjs" | "iife";
  /**
   * Oggetto di configurazione JSX per controllare il comportamento della trasformata JSX
   */
  jsx?: {
    runtime?: "automatic" | "classic";
    importSource?: string;
    factory?: string;
    fragment?: string;
    sideEffects?: boolean;
    development?: boolean;
  };
  naming?:
    | string
    | {
        chunk?: string;
        entry?: string;
        asset?: string;
      };
  root?: string; // root del progetto
  splitting?: boolean; // default true, abilita code splitting
  plugins?: BunPlugin[];
  external?: string[];
  packages?: "bundle" | "external";
  publicPath?: string;
  define?: Record<string, string>;
  loader?: { [k in string]: Loader };
  sourcemap?: "none" | "linked" | "inline" | "external" | boolean; // default: "none", true -> "inline"
  /**
   * Condizioni `exports` di package.json usate quando si risolvono gli import
   *
   * Equivalente a `--conditions` in `bun build` o `bun run`.
   *
   * https://nodejs.org/api/packages.html#exports
   */
  conditions?: Array<string> | string;

  /**
   * Controlla come le variabili d'ambiente sono gestite durante il bundling.
   *
   * Può essere uno di:
   * - `"inline"`: Inietta variabili d'ambiente nell'output bundled convertendo riferimenti `process.env.FOO`
   *   in stringhe letterali contenenti i valori effettivi delle variabili d'ambiente
   * - `"disable"`: Disabilita completamente l'iniezione di variabili d'ambiente
   * - Una stringa che termina con `*`: Inlina variabili d'ambiente che corrispondono al prefisso dato.
   *   Per esempio, `"MY_PUBLIC_*"` includerà solo env vars che iniziano con "MY_PUBLIC_"
   */
  env?: "inline" | "disable" | `${string}*`;
  minify?:
    | boolean
    | {
        whitespace?: boolean;
        syntax?: boolean;
        identifiers?: boolean;
      };
  /**
   * Ignora annotazioni di dead code elimination/tree-shaking come @__PURE__ e campi
   * "sideEffects" di package.json. Questo dovrebbe essere usato solo come workaround temporaneo per annotazioni
   * incorrette nelle librerie.
   */
  ignoreDCEAnnotations?: boolean;
  /**
   * Forza emissione di annotazioni @__PURE__ anche se minify.whitespace è true.
   */
  emitDCEAnnotations?: boolean;

  /**
   * Genera bytecode per l'output. Questo può migliorare drammaticamente i tempi di avvio a freddo,
   * ma renderà l'output finale più grande e aumenterà leggermente l'uso di memoria.
   *
   * Bytecode è attualmente supportato solo per CommonJS (`format: "cjs"`).
   *
   * Deve essere `target: "bun"`
   * @default false
   */
  bytecode?: boolean;
  /**
   * Aggiunge un banner al codice bundled come "use client";
   */
  banner?: string;
  /**
   * Aggiunge un footer al codice bundled come un blocco di commento tipo
   *
   * `// made with bun!`
   */
  footer?: string;

  /**
   * Rimuove chiamate funzione a property access corrispondenti.
   */
  drop?: string[];

  /**
   * - Quando impostato a `true`, la promise restituita rifiuta con un AggregateError quando un fallimento di build avviene.
   * - Quando impostato a `false`, restituisce un {@link BuildOutput} con `{success: false}`
   *
   * @default true
   */
  throw?: boolean;

  /**
   * Percorso file tsconfig.json custom da usare per risoluzione percorsi.
   * Equivalente a `--tsconfig-override` nella CLI.
   */
  tsconfig?: string;

  outdir?: string;
}

interface BuildOutput {
  outputs: BuildArtifact[];
  success: boolean;
  logs: Array<BuildMessage | ResolveMessage>;
}

interface BuildArtifact extends Blob {
  path: string;
  loader: Loader;
  hash: string | null;
  kind: "entry-point" | "chunk" | "asset" | "sourcemap" | "bytecode";
  sourcemap: BuildArtifact | null;
}

type Loader =
  | "js"
  | "jsx"
  | "ts"
  | "tsx"
  | "css"
  | "json"
  | "jsonc"
  | "toml"
  | "yaml"
  | "text"
  | "file"
  | "napi"
  | "wasm"
  | "html";

interface BuildOutput {
  outputs: BuildArtifact[];
  success: boolean;
  logs: Array<BuildMessage | ResolveMessage>;
}

declare class ResolveMessage {
  readonly name: "ResolveMessage";
  readonly position: Position | null;
  readonly code: string;
  readonly message: string;
  readonly referrer: string;
  readonly specifier: string;
  readonly importKind:
    | "entry_point"
    | "stmt"
    | "require"
    | "import"
    | "dynamic"
    | "require_resolve"
    | "at"
    | "at_conditional"
    | "url"
    | "internal";
  readonly level: "error" | "warning" | "info" | "debug" | "verbose";

  toString(): string;
}

Bun a cura di www.bunjs.com.cn