Skip to content

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

Быстрый нативный бандлер Bun можно использовать через команду CLI bun build или JavaScript API Bun.build().

Краткий обзор

  • JS API: await Bun.build({ entrypoints, outdir })
  • CLI: bun build <entry> --outdir ./out
  • Watch: --watch для инкрементальных пересборок
  • Targets: --target browser|bun|node
  • Formats: --format esm|cjs|iife (экспериментально для cjs/iife)

JavaScript

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

CLI

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

Это быстро. Цифры ниже представляют производительность на бенчмарке three.js от esbuild.

Зачем использовать бандлер?

Бандлер является ключевым элементом инфраструктуры в экосистеме JavaScript. Краткий обзор того почему связывание так важно:

  • Уменьшение HTTP-запросов. Один пакет в node_modules может состоять из сотен файлов а крупные приложения могут иметь десятки таких зависимостей. Загрузка каждого из этих файлов отдельным HTTP-запросом быстро становится непрактичной поэтому бандлеры используются для преобразования исходного кода нашего приложения в меньшее количество самодостаточных «пакетов» которые можно загрузить одним запросом.
  • Трансформации кода. Современные приложения часто создаются с использованием TypeScript JSX CSS-модулей и других инструментов все из которых должны быть преобразованы в обычный JavaScript и CSS перед использованием в браузере. Бандлер является естественным местом для настройки этих преобразований.
  • Возможности фреймворков. Фреймворки полагаются на плагины бандлера и преобразования кода для реализации общих паттернов таких как маршрутизация на основе файловой системы совместное размещение кода клиента и сервера (например getServerSideProps или загрузчики Remix) и серверные компоненты.
  • Полноценные приложения. Бандлер Bun может обрабатывать как серверный так и клиентский код в одной команде что позволяет создавать оптимизированные продакшн-сборки и однофайловые исполняемые файлы. С импортом HTML во время сборки вы можете связать все свое приложение — фронтенд-активы и бэкенд-сервер — в единый развертываемый модуль.

Перейдем к API бандлера.

NOTE

Бандлер Bun не предназначен для замены `tsc` для проверки типов или генерации объявлений типов.

Базовый пример

Создадим наш первый бандл. У вас есть следующие два файла которые реализуют простое клиентское React-приложение.

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>;
}

Здесь index.tsx является «точкой входа» нашего приложения. Обычно это скрипт который выполняет некоторый побочный эффект например запуск сервера или в данном случае инициализацию React-корня. Поскольку мы используем TypeScript и JSX нам нужно связать наш код перед отправкой в браузер.

Для создания нашего бандла:

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

Для каждого файла указанного в entrypoints Bun создаст новый бандл. Этот бандл будет записан на диск в директории ./out (разрешенной относительно текущей рабочей директории). После запуска сборки файловая система выглядит так:

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

Содержимое out/index.js будет выглядеть примерно так:

js
// out/index.js
// ...
// ~20k строк кода
// включая содержимое `react-dom/client` и всех его зависимостей
// здесь определены функции $jsxDEV и $createRoot

// 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,
  ),
);

Режим наблюдения

Как и среда выполнения и тестовый раннер бандлер поддерживает режим наблюдения нативно.

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

Типы контента

Как и среда выполнения Bun бандлер поддерживает множество типов файлов из коробки. В следующей таблице представлен набор стандартных «загрузчиков» бандлера. Полную документацию смотрите на странице Bundler > File types.

РасширенияДетали
.js .jsx .cjs .mjs .mts .cts .ts .tsxИспользует встроенный транспайлер Bun для парсинга файла и транспиляции синтаксиса TypeScript/JSX в обычный JavaScript. Бандлер выполняет набор преобразований по умолчанию включая устранение мертвого кода и встряхивание дерева. На данный момент Bun не пытается понизить синтаксис; если вы используете недавний синтаксис ECMAScript это отразится в связанном коде.
.jsonJSON-файлы парсятся и встраиваются в бандл как JavaScript-объект.

js<br/>import pkg from "./package.json";<br/>pkg.name; // => "my-package"<br/>
.jsoncJSON с комментариями. Файлы парсятся и встраиваются в бандл как JavaScript-объект.

js<br/>import config from "./config.jsonc";<br/>config.name; // => "my-config"<br/>
.tomlTOML-файлы парсятся и встраиваются в бандл как JavaScript-объект.

js<br/>import config from "./bunfig.toml";<br/>config.logLevel; // => "debug"<br/>
.yaml .ymlYAML-файлы парсятся и встраиваются в бандл как JavaScript-объект.

js<br/>import config from "./config.yaml";<br/>config.name; // => "my-app"<br/>
.txtСодержимое текстового файла читается и встраивается в бандл как строка.

js<br/>import contents from "./file.txt";<br/>console.log(contents); // => "Hello, world!"<br/>
.htmlHTML-файлы обрабатываются и любые ссылочные активы (скрипты стили изображения) связываются.
.cssCSS-файлы объединяются в один .css файл в выходной директории.
.node .wasmЭти файлы поддерживаются средой выполнения Bun но во время связывания они обрабатываются как активы.

Активы

Если бандлер встречает импорт с нераспознанным расширением он обрабатывает импортируемый файл как внешний файл. Ссылочный файл копируется как есть в outdir а импорт разрешается как путь к файлу.

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

Точное поведение файлового загрузчика также зависит от naming и publicPath.

Плагины

Поведение описанное в этой таблице может быть переопределено или расширено с помощью плагинов. Полную документацию смотрите на странице Bundler > Loaders.

API

entrypoints

Массив путей соответствующих точкам входа нашего приложения. Для каждой точки входа будет создан один бандл.

JavaScript

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

CLI

bash
bun build ./index.ts

outdir

Директория куда будут записаны выходные файлы.

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

Если outdir не передан в JavaScript API связанный код не будет записан на диск. Связанные файлы возвращаются в массиве объектов BuildArtifact. Эти объекты являются Blob с дополнительными свойствами; полную документацию смотрите в разделе Outputs.

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

for (const res of result.outputs) {
  // Можно обрабатывать как blob
  await res.text();

  // Bun установит заголовки Content-Type и Etag
  new Response(res);

  // Можно записать вручную но в этом случае следует использовать `outdir`.
  Bun.write(path.join("out", res.path), res);
}

Когда установлен outdir свойство path на BuildArtifact будет абсолютным путем к месту записи.

target

Предполагаемая среда выполнения для бандла.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.ts'],
  outdir: './out',
  target: 'browser', // по умолчанию
})

CLI

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

В зависимости от target Bun применяет различные правила разрешения модулей и оптимизации.

По умолчанию. Для генерации бандлов предназначенных для выполнения в браузере. Приоритет отдается условию экспорта "browser" при разрешении импортов. Импорт любых встроенных модулей таких как node:events или node:path будет работать но вызов некоторых функций таких как fs.readFile не будет работать.

Для генерации бандлов предназначенных для запуска средой выполнения Bun. Во многих случаях нет необходимости связывать серверный код; вы можете напрямую выполнять исходный код без изменений. Однако связывание серверного кода может сократить время запуска и улучшить производительность. Это target который следует использовать для создания полнофункциональных приложений с импортом HTML во время сборки где и серверный и клиентский код связываются вместе.

Все бандлы сгенерированные с target: "bun" помечены специальной прагмой // @bun которая указывает среде выполнения Bun что нет необходимости повторно транспилировать файл перед выполнением.

Если любая точка входа содержит shebang Bun (#!/usr/bin/env bun) бандлер по умолчанию будет использовать target: "bun" вместо "browser".

При совместном использовании target: "bun" и format: "cjs" добавляется прагма // @bun @bun-cjs и обертка CommonJS несовместима с Node.js.

Для генерации бандлов предназначенных для запуска Node.js. Приоритет отдается условию экспорта "node" при разрешении импортов и выводится .mjs. В будущем это будет автоматически полифилировать глобальный Bun и другие встроенные модули bun:* хотя это еще не реализовано.

format

Указывает формат модуля для использования в сгенерированных бандлах.

Bun по умолчанию использует "esm" и предоставляет экспериментальную поддержку "cjs" и "iife".

format: "esm" - ES Module

Это формат по умолчанию который поддерживает синтаксис ES Module включая top-level await import.meta и другие.

JavaScript

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

CLI

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

Для использования синтаксиса ES Module в браузерах установите format в "esm" и убедитесь что ваш тег <script type="module"> имеет type="module".

format: "cjs" - CommonJS

Для создания модуля CommonJS установите format в "cjs". При выборе "cjs" target по умолчанию меняется с "browser" (esm) на "node" (cjs). Модули CommonJS транспилированные с format: "cjs" target: "node" могут выполняться как в Bun так и в Node.js (при условии что используемые API поддерживаются обоими).

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: документировать IIFE как только мы поддержим globalNames.

jsx

Настройте поведение трансформации JSX. Позволяет точно контролировать как компилируется JSX.

Пример классического рантайма (использует factory и fragment):

ts
await Bun.build({
  entrypoints: ["./app.tsx"],
  outdir: "./out",
  jsx: {
    factory: "h",
    fragment: "Fragment",
    runtime: "classic",
  },
});
bash
# Настройка JSX обрабатывается через bunfig.toml или tsconfig.json
bun build ./app.tsx --outdir ./out

Пример автоматического рантайма (использует importSource):

ts
await Bun.build({
  entrypoints: ["./app.tsx"],
  outdir: "./out",
  jsx: {
    importSource: "preact",
    runtime: "automatic",
  },
});
bash
# Настройка JSX обрабатывается через bunfig.toml или tsconfig.json
bun build ./app.tsx --outdir ./out

splitting

Включает ли разделение кода.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  splitting: false, // по умолчанию
})

CLI

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

Когда true бандлер включит разделение кода. Когда несколько точек входа импортируют один и тот же файл модуль или набор файлов/модулей часто полезно разделить общий код в отдельный бандл. Этот общий бандл известен как чанк. Рассмотрим следующие файлы:

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

Для связывания entry-a.ts и entry-b.ts с включенным разделением кода:

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

В результате выполнения этой сборки будут созданы следующие файлы:

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

Сгенерированный файл chunk-2fce6291bf86559d.js содержит общий код. Чтобы избежать коллизий имя файла автоматически включает хеш содержимого по умолчанию. Это можно настроить с помощью naming.

plugins

Список плагинов для использования во время связывания.

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

Bun реализует универсальную систему плагинов как для среды выполнения так и для бандлера Bun. Полную документацию смотрите в документации по плагинам.

env

Контролирует как обрабатываются переменные окружения во время связывания. Внутри используется define для внедрения переменных окружения в бандл но упрощает указание переменных окружения для внедрения.

env: "inline"

Внедряет переменные окружения в связанный вывод преобразуя ссылки process.env.FOO в строковые литералы содержащие фактические значения переменных окружения.

JavaScript

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

CLI

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

Для ввода ниже:

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

Сгенерированный бандл будет содержать следующий код:

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

env: "PUBLIC_*" (префикс)

Встраивает переменные окружения соответствующие заданному префиксу (часть перед символом *) заменяя process.env.FOO фактическим значением переменной окружения. Это полезно для выборочного встраивания переменных окружения для таких вещей как публичные URL или клиентские токены не беспокоясь о внедрении приватных учетных данных в выходные бандлы.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  
  // Встроить все переменные окружения начинающиеся с "ACME_PUBLIC_"
  env: "ACME_PUBLIC_*",
})

CLI

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

Например при следующих переменных окружения:

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

И исходном коде:

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

Сгенерированный бандл будет содержать следующий код:

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

env: "disable"

Полностью отключает внедрение переменных окружения.

sourcemap

Указывает тип генерируемой карты исходного кода.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  sourcemap: 'linked', // по умолчанию 'none'
})

CLI

bash
bun build ./index.tsx --outdir ./out --sourcemap linked
ЗначениеОписание
"none"По умолчанию. Карта исходного кода не генерируется.
"linked"Отдельный файл *.js.map создается рядом с каждым бандлом *.js используя комментарий //# sourceMappingURL для связи обоих. Требуется установка --outdir. Базовый URL можно настроить с помощью --public-path.

js<br/>// <связанный код здесь><br/><br/>//# sourceMappingURL=bundle.js.map<br/>
"external"Отдельный файл *.js.map создается рядом с каждым бандлом *.js без вставки комментария //# sourceMappingURL.

Сгенерированные бандлы содержат debug id который можно использовать для ассоциации бандла с соответствующей картой исходного кода. Этот debugId добавляется как комментарий внизу файла.

js<br/>// <сгенерированный код бандла><br/><br/>//# debugId=<DEBUG ID><br/>
"inline"Карта исходного кода генерируется и добавляется в конец сгенерированного бандла как base64 payload.

js<br/>// <связанный код здесь><br/><br/>//# sourceMappingURL=data:application/json;base64,<закодированная карта исходного кода здесь><br/>

Ассоциированный файл карты исходного кода *.js.map будет JSON-файлом содержащим эквивалентное свойство debugId.

minify

Включает ли минификацию. По умолчанию false.

NOTE

При таргетировании на `bun` идентификаторы будут минифицироваться по умолчанию.

Для включения всех опций минификации:

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  minify: true, // по умолчанию false
})

CLI

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

Для выборочного включения определенных минификаций:

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

Список путей импорта которые следует считать внешними. По умолчанию [].

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  external: ["lodash", "react"], // по умолчанию: []
})

CLI

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

Внешний импорт — это тот который не будет включен в итоговый бандл. Вместо этого оператор импорта остается как есть для разрешения во время выполнения.

Например рассмотрим следующий файл точки входа:

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

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

Обычно связывание index.tsx создаст бандл содержащий весь исходный код пакета "zod". Если вместо этого мы хотим оставить оператор импорта как есть мы можем пометить его как внешний:

JavaScript

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

CLI

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

Сгенерированный бандл будет выглядеть примерно так:

js
import { z } from "zod";

// ...
// содержимое пакета "lodash"
// включая функцию `_.upperCase`

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

Чтобы пометить все импорты как внешние используйте подстановочный знак *:

JavaScript

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

CLI

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

packages

Контролирует включение ли зависимостей пакетов в бандл или нет. Возможные значения: bundle (по умолчанию) external. Bun обрабатывает любой импорт путь которого не начинается с ., .. или / как пакет.

JavaScript

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

CLI

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

naming

Настраивает сгенерированные имена файлов. По умолчанию ./[dir]/[name].[ext].

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  naming: "[dir]/[name].[ext]", // по умолчанию
})

CLI

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

По умолчанию имена сгенерированных бандлов основаны на имени соответствующей точки входа.

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

С несколькими точками входа иерархия сгенерированных файлов будет отражать структуру директорий точек входа.

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

Имена и расположения сгенерированных файлов можно настроить с помощью поля naming. Это поле принимает строку шаблона которая используется для генерации имен файлов для всех бандлов соответствующих точкам входа где следующие токены заменяются их соответствующими значениями:

  • [name] - Имя файла точки входа без расширения.
  • [ext] - Расширение сгенерированного бандла.
  • [hash] - Хеш содержимого бандла.
  • [dir] - Относительный путь от корня проекта до родительской директории исходного файла.

Например:

Токен[name][ext][hash][dir]
./index.tsxindexjsa1b2c3d4"" (пустая строка)
./nested/entry.tsentryjsc3d4e5f6"nested"

Мы можем комбинировать эти токены для создания строки шаблона. Например чтобы включить хеш в имена сгенерированных бандлов:

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]'

Эта сборка приведет к следующей структуре файлов:

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

Когда для поля naming предоставлена строка она используется только для бандлов соответствующих точкам входа. Имена чанков и скопированных активов не затрагиваются. Используя JavaScript API можно указать отдельные строки шаблона для каждого типа сгенерированного файла.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  naming: {
    // значения по умолчанию
    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

Корневая директория проекта.

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 .

Если не указано вычисляется как первый общий предок всех файлов точек входа. Рассмотрим следующую структуру файлов:

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

Мы можем собрать обе точки входа в директории 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

Это приведет к следующей структуре файлов:

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

Поскольку директория pages является первым общим предком файлов точек входа она считается корнем проекта. Это означает что сгенерированные бандлы находятся на верхнем уровне директории out; нет директории out/pages.

Таким поведением можно переопределить указав опцию 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 .

Указав . как root сгенерированная структура файлов будет выглядеть так:

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

publicPath

Префикс который будет добавлен к любым путям импорта в связанном коде.

Во многих случаях сгенерированные бандлы не будут содержать операторов импорта. В конце концов цель связывания — объединить весь код в один файл. Однако есть ряд случаев когда сгенерированные бандлы будут содержать операторы импорта.

  • Импорт активов — При импорте нераспознанного типа файла такого как *.svg бандлер передает файловому загрузчику который копирует файл в outdir как есть. Импорт преобразуется в переменную
  • Внешние модули — Файлы и модули могут быть помечены как внешние в этом случае они не будут включены в бандл. Вместо этого оператор импорта останется в итоговом бандле.
  • Разделение на чанки. Когда включено splitting бандлер может генерировать отдельные файлы «чанков» которые представляют код общий для нескольких точек входа.

В любом из этих случаев итоговые бандлы могут содержать пути к другим файлам. По умолчанию эти импорты относительные. Вот пример простого импорта актива:

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

Установка publicPath добавит префикс ко всем путям файлов с указанным значением.

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  publicPath: 'https://cdn.example.com/', // по умолчанию undefined
})

CLI

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

Файл вывода теперь будет выглядеть примерно так.

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

define

Карта глобальных идентификаторов которые будут заменены во время сборки. Ключи этого объекта — имена идентификаторов а значения — JSON-строки которые будут встроены.

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

Карта расширений файлов к встроенным именам загрузчиков. Это можно использовать для быстрой настройки загрузки определенных файлов.

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

Заголовок который будет добавлен в итоговый бандл это может быть директива like use client для react или блок комментариев такой как лицензия для кода.

JavaScript

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

CLI

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

Подвал который будет добавлен в итоговый бандл это может быть что-то вроде блока комментариев для лицензии или просто пасхалка.

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

Удаляет вызовы функций из бандла. Например --drop=console удалит все вызовы console.log. Аргументы вызовов также будут удалены независимо от того могут ли эти аргументы иметь побочные эффекты. Удаление debugger удалит все операторы 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

Функция Bun.build возвращает Promise<BuildOutput> определенный как:

ts
interface BuildOutput {
  outputs: BuildArtifact[];
  success: boolean;
  logs: Array<object>; // смотрите документацию для деталей
}

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

Массив outputs содержит все файлы сгенерированные сборкой. Каждый артефакт реализует интерфейс 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
}

Каждый артефакт также содержит следующие свойства:

СвойствоОписание
kindКакой тип вывода файла. Сборка генерирует связанные точки входа чанки с разделением кода карты исходного кода байт-код и скопированные активы (например изображения).
pathАбсолютный путь к файлу на диске
loaderЗагрузчик использованный для интерпретации файла. Смотрите Bundler > Loaders чтобы увидеть как Bun сопоставляет расширения файлов с соответствующим встроенным загрузчиком.
hashХеш содержимого файла. Всегда определен для активов.
sourcemapФайл карты исходного кода соответствующий этому файлу если сгенерирован. Определен только для точек входа и чанков.

Подобно BunFile объекты BuildArtifact можно передавать напрямую в new Response().

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

const artifact = build.outputs[0];

// Заголовок Content-Type устанавливается автоматически
return new Response(artifact);

Среда выполнения Bun реализует специальную печать объектов BuildArtifact для упрощения отладки.

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

Опция bytecode: boolean может использоваться для генерации байт-кода для любых точек входа JavaScript/TypeScript. Это может значительно улучшить время запуска для больших приложений. Поддерживается только для формата "cjs" только поддерживает "target": "bun" и зависит от соответствующей версии Bun. Это добавляет соответствующий файл .jsc для каждой точки входа.

JavaScript

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

CLI

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

Executables

Bun поддерживает «компиляцию» точки входа JavaScript/TypeScript в автономный исполняемый файл. Этот исполняемый файл содержит копию бинарного файла Bun.

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

Полную документацию смотрите на странице Bundler > Executables.

Logs and errors

При неудаче Bun.build возвращает отклоненный промис с AggregateError. Это можно вывести в консоль для красивого вывода списка ошибок или программно прочитать с помощью блока try/catch.

ts
try {
  const result = await Bun.build({
    entrypoints: ["./index.tsx"],
    outdir: "./out",
  });
} catch (e) {
  // TypeScript не позволяет аннотациям в пункте catch
  const error = e as AggregateError;
  console.error("Build Failed");

  // Пример: Использование встроенного форматирования
  console.error(error);

  // Пример: Сериализация ошибки как JSON-строки.
  console.error(JSON.stringify(error, null, 2));
}

В большинстве случаев явный try/catch не нужен так как Bun аккуратно выводит неперехваченные исключения. Достаточно просто использовать top-level await на вызове Bun.build.

Каждый элемент в error.errors является экземпляром BuildMessage или ResolveMessage (подклассы Error) содержащим подробную информацию для каждой ошибки.

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;
}

При успешной сборке возвращенный объект содержит свойство logs которое содержит предупреждения бандлера и информационные сообщения.

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

if (result.logs.length > 0) {
  console.warn("Build succeeded with warnings:");
  for (const message of result.logs) {
    // Bun красиво выведет объект сообщения
    console.warn(message);
  }
}

Reference

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

interface BuildConfig {
  entrypoints: string[]; // список путей к файлам
  outdir?: string; // выходная директория
  target?: Target; // по умолчанию: "browser"
  /**
   * Формат выходного модуля. Top-level await поддерживается только для `"esm"`.
   *
   * Может быть:
   * - `"esm"`
   * - `"cjs"` (**экспериментально**)
   * - `"iife"` (**экспериментально**)
   *
   * @default "esm"
   */
  format?: "esm" | "cjs" | "iife";
  /**
   * Объект конфигурации JSX для контроля поведения трансформации 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; // корень проекта
  splitting?: boolean; // по умолчанию true включает разделение кода
  plugins?: BunPlugin[];
  external?: string[];
  packages?: "bundle" | "external";
  publicPath?: string;
  define?: Record<string, string>;
  loader?: { [k in string]: Loader };
  sourcemap?: "none" | "linked" | "inline" | "external" | boolean; // по умолчанию: "none", true -> "inline"
  /**
   * Условия экспорта package.json используемые при разрешении импортов
   *
   * Эквивалентно `--conditions` в `bun build` или `bun run`.
   *
   * https://nodejs.org/api/packages.html#exports
   */
  conditions?: Array<string> | string;

  /**
   * Контролирует как обрабатываются переменные окружения во время связывания.
   *
   * Может быть одним из:
   * - `"inline"`: Внедряет переменные окружения в связанный вывод преобразуя ссылки `process.env.FOO`
   *   в строковые литералы содержащие фактические значения переменных окружения
   * - `"disable"`: Полностью отключает внедрение переменных окружения
   * - Строка оканчивающаяся на `*`: Встраивает переменные окружения соответствующие заданному префиксу.
   *   Например `"MY_PUBLIC_"` включит только переменные окружения начинающиеся с "MY_PUBLIC_"
   */
  env?: "inline" | "disable" | `${string}*`;
  minify?:
    | boolean
    | {
        whitespace?: boolean;
        syntax?: boolean;
        identifiers?: boolean;
      };
  /**
   * Игнорировать аннотации устранения мертвого кода/встряхивания дерева такие как @__PURE__ и поля
   * package.json "sideEffects". Это следует использовать только как временное решение для некорректных
   * аннотаций в библиотеках.
   */
  ignoreDCEAnnotations?: boolean;
  /**
   * Принудительно добавлять аннотации @__PURE__ даже если minify.whitespace равен true.
   */
  emitDCEAnnotations?: boolean;

  /**
   * Генерировать байт-код для вывода. Это может значительно улучшить время
   * холодного запуска но сделает итоговый вывод больше и немного увеличит
   * использование памяти.
   *
   * Байт-код в настоящее время поддерживается только для CommonJS (`format: "cjs"`).
   *
   * Должен быть `target: "bun"`
   * @default false
   */
  bytecode?: boolean;
  /**
   * Добавить заголовок в связанный код такой как "use client";
   */
  banner?: string;
  /**
   * Добавить подвал в связанный код такой как блок комментариев
   *
   * `// made with bun!`
   */
  footer?: string;

  /**
   * Удалить вызовы функций к соответствующим доступа к свойствам.
   */
  drop?: string[];

  /**
   * - Когда установлено в `true` возвращенный промис отклоняется с AggregateError при ошибке сборки.
   * - Когда установлено в `false` возвращает {@link BuildOutput} с `{success: false}`
   *
   * @default true
   */
  throw?: boolean;

  /**
   * Путь к пользовательскому файлу tsconfig.json для использования при разрешении путей.
   * Эквивалентно `--tsconfig-override` в 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 от www.bunjs.com.cn