Skip to content

Бандлер Bun реализует флаг --compile для генерации автономного бинарного файла из TypeScript или JavaScript файла.

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

Это связывает cli.ts в исполняемый файл который можно запустить напрямую:

bash
./mycli
txt
Hello world!

Все импортируемые файлы и пакеты связываются в исполняемый файл вместе с копией среды выполнения Bun. Все встроенные API Bun и Node.js поддерживаются.


Кросс-компиляция для других платформ

Флаг --target позволяет компилировать автономный исполняемый файл для другой операционной системы архитектуры или версии Bun чем машина на которой вы запускаете bun build.

Для сборки под Linux x64 (большинство серверов):

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

# Для поддержки CPU до 2013 года используйте базовую версию (nehalem)
bun build --compile --target=bun-linux-x64-baseline ./index.ts --outfile myapp

# Для явной поддержки только CPU с 2013 года и новее используйте современную версию (haswell)
# modern быстрее но baseline более совместим
bun build --compile --target=bun-linux-x64-modern ./index.ts --outfile myapp

Для сборки под Linux ARM64 (например Graviton или Raspberry Pi):

bash
# Примечание: архитектура по умолчанию x64 если не указана архитектура
bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp

Для сборки под Windows x64:

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

# Для поддержки CPU до 2013 года используйте базовую версию (nehalem)
bun build --compile --target=bun-windows-x64-baseline ./path/to/my/app.ts --outfile myapp

# Для явной поддержки только CPU с 2013 года и новее используйте современную версию (haswell)
bun build --compile --target=bun-windows-x64-modern ./path/to/my/app.ts --outfile myapp

# примечание: если расширение .exe не указано Bun автоматически добавит его для исполняемых файлов Windows

Для сборки под macOS arm64:

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

Для сборки под macOS x64:

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

Поддерживаемые таргеты

Порядок флага --target не имеет значения пока они разделены -.

--targetОперационная системаАрхитектураModernBaselineLibc
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

Константы времени сборки

Используйте флаг --define для внедрения констант времени сборки в ваш исполняемый файл таких как номера версий метки времени сборки или значения конфигурации:

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

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

NOTE

Комплексные примеры и расширенные паттерны смотрите в [руководстве по константам времени сборки](/ru/guides/runtime/build-time-constants).

Развертывание в продакшене

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

Обычно Bun читает и транспилирует JavaScript и TypeScript файлы при import и require. Это часть того что заставляет большую часть Bun «просто работать» но это не бесплатно. Чтение файлов с диска разрешение путей файлов парсинг транспиляция и печать исходного кода требуют времени и памяти.

Со скомпилированными исполняемыми файлами вы можете перенести эти расходы из времени выполнения во время сборки.

При развертывании в продакшен мы рекомендуем следующее:

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

Компиляция байт-кода

Для улучшения времени запуска включите компиляцию байт-кода:

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

Используя компиляцию байт-кода tsc запускается в 2 раза быстрее:

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

Что делают эти флаги?

Аргумент --minify оптимизирует размер транспилированного выходного кода. Если у вас большое приложение это может сэкономить мегабайты пространства. Для небольших приложений это все еще может немного улучшить время запуска.

Аргумент --sourcemap встраивает карту исходного кода сжатую с zstd чтобы ошибки и трассировки стека указывали на их оригинальные расположения вместо транспилированного расположения. Bun автоматически декомпрессирует и разрешает карту исходного кода при возникновении ошибки.

Аргумент --bytecode включает компиляцию байт-кода. Каждый раз когда вы запускаете JavaScript код в Bun JavaScriptCore (движок) компилирует ваш исходный код в байт-код. Мы можем перенести эту работу по парсингу из времени выполнения во время сборки экономя вам время запуска.


Встраивание аргументов времени выполнения

--compile-exec-argv="args" - Встраивает аргументы времени выполнения которые доступны через process.execArgv:

bash
bun build --compile --compile-exec-argv="--smol --user-agent=MyBot" ./app.ts --outfile myapp
ts
// В скомпилированном приложении
console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"]

Отключение автоматической загрузки конфигурации

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

bash
# Отключить загрузку .env
bun build --compile --no-compile-autoload-dotenv ./app.ts --outfile myapp

# Отключить загрузку bunfig.toml
bun build --compile --no-compile-autoload-bunfig ./app.ts --outfile myapp

# Отключить и то и другое
bun build --compile --no-compile-autoload-dotenv --no-compile-autoload-bunfig ./app.ts --outfile myapp

Вы также можете настроить это через JavaScript API:

ts
await Bun.build({
  entrypoints: ["./app.ts"],
  compile: {
    autoloadDotenv: false, // Отключить загрузку .env
    autoloadBunfig: false, // Отключить загрузку bunfig.toml
  },
});

Действие как CLI Bun

NOTE

Новое в Bun v1.2.16

Вы можете запустить автономный исполняемый файл как если бы это был сам CLI bun установив переменную окружения BUN_BE_BUN=1. Когда эта переменная установлена исполняемый файл игнорирует свою встроенную точку входа и вместо этого предоставляет все функции CLI Bun.

Например рассмотрим исполняемый файл скомпилированный из простого скрипта:

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

Обычно запуск ./such-bun с аргументами выполнял бы скрипт.

bash
# Исполняемый файл запускает свою точку входа по умолчанию
./such-bun install
txt
you shouldn't see this

Однако с переменной окружения BUN_BE_BUN=1 он действует точно как бинарный файл bun:

bash
# С переменной окружения исполняемый файл действует как 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]

Это полезно для создания CLI-инструментов поверх Bun которые могут потребовать установку пакетов связывание зависимостей запуск разных или локальных файлов и многое другое без необходимости загрузки отдельного бинарного файла или установки bun.


Полнофункциональные исполняемые файлы

NOTE

Новое в Bun v1.2.17

Флаг --compile Bun может создавать автономные исполняемые файлы которые содержат как серверный так и клиентский код что делает его идеальным для полнофункциональных приложений. Когда вы импортируете HTML-файл в ваш серверный код Bun автоматически связывает все фронтенд-активы (JavaScript CSS и т.д.) и встраивает их в исполняемый файл. Когда Bun видит импорт HTML на сервере он запускает процесс фронтенд-сборки для связывания JavaScript CSS и других активов.

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

Для сборки этого в единый исполняемый файл:

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

Это создает самодостаточный бинарный файл который включает:

  • Ваш серверный код
  • Среду выполнения Bun
  • Все фронтенд-активы (HTML CSS JavaScript)
  • Любые npm-пакеты используемые вашим сервером

Результат — один файл который можно развернуть где угодно без необходимости установки Node.js Bun или каких-либо зависимостей. Просто запустите:

bash
./myapp

Bun автоматически обрабатывает обслуживание фронтенд-активов с правильными MIME-типами и заголовками кэша. Импорт HTML заменяется объектом манифеста который Bun.serve использует для эффективной подачи предварительно связанных активов.

Подробнее о создании полнофункциональных приложений с Bun смотрите в руководстве по полнофункциональной сборке.


Worker

Для использования workers в автономном исполняемом файле добавьте точку входа worker в аргументы CLI:

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

Затем сослайтесь на worker в вашем коде:

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

// Любой из этих вариантов будет работать:
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);

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

В будущем мы можем автоматически обнаруживать использования статически известных путей в new Worker(path) и затем связывать их в исполняемый файл но пока вам нужно добавить их в команду оболочки вручную как в примере выше.

Если вы используете относительный путь к файлу не включенному в автономный исполняемый файл он попытается загрузить этот путь с диска относительно текущей рабочей директории процесса (и затем выдаст ошибку если он не существует).


SQLite

Вы можете использовать импорты bun:sqlite с bun build --compile.

По умолчанию база данных разрешается относительно текущей рабочей директории процесса.

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

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

Это означает что если исполняемый файл находится в /usr/bin/hello терминал пользователя находится в /home/me/Desktop он будет искать /home/me/Desktop/my.db.

bash
cd /home/me/Desktop
./hello

Встраивание активов и файлов

Автономные исполняемые файлы поддерживают встраивание файлов.

Для встраивания файлов в исполняемый файл с bun build --compile импортируйте файл в ваш код.

ts
// это становится внутренним путем к файлу
import icon from "./icon.png" with { type: "file" };
import { file } from "bun";

export default {
  fetch(req) {
    // Встроенные файлы могут передаваться в потоке из объектов Response
    return new Response(file(icon));
  },
};

Встроенные файлы можно читать с помощью функций Bun.file или функции Node.js fs.readFile"node:fs").

Например для чтения содержимого встроенного файла:

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)

Встраивание баз данных SQLite

Если ваше приложение хочет встроить базу данных SQLite установите type: "sqlite" в атрибуте импорта и атрибут embed в "true".

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

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

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

Встраивание аддонов N-API

Вы можете встраивать файлы .node в исполняемые файлы.

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

console.log(addon.hello());

К сожалению если вы используете @mapbox/node-pre-gyp или другие подобные инструменты вам нужно убедиться что файл .node импортируется напрямую иначе он не будет связан корректно.

Встраивание директорий

Для встраивания директории с bun build --compile используйте shell-глоб в вашей команде bun build:

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

Затем вы можете сослаться на файлы в вашем коде:

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

export default {
  fetch(req) {
    // Встроенные файлы могут передаваться в потоке из объектов Response
    return new Response(file(icon));
  },
};

Это честно говоря обходной путь и мы ожидаем улучшить это в будущем с более прямым API.

Список встроенных файлов

Для получения списка всех встроенных файлов используйте Bun.embeddedFiles:

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

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

Bun.embeddedFiles возвращает массив объектов Blob которые вы можете использовать для получения размера содержимого и других свойств файлов.

ts
embeddedFiles: Blob[]

Список встроенных файлов исключает связанный исходный код такой как файлы .ts и .js.

Хеш содержимого

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

Для отключения хеша содержимого передайте --asset-naming в bun build --compile вот так:

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

Минификация

Для уменьшения размера исполняемого файла немного передайте --minify в bun build --compile. Это использует минифайер Bun для уменьшения размера кода. В целом бинарный файл Bun все еще слишком велик и нам нужно сделать его меньше.


Специфичные для Windows флаги

При компиляции автономного исполняемого файла в Windows есть две платформо-специфичные опции которые можно использовать для настройки метаданных в сгенерированном файле .exe:

  • --windows-icon=path/to/icon.ico для настройки иконки исполняемого файла.
  • --windows-hide-console для отключения фонового терминала который можно использовать для приложений которым не нужен TTY.

Подпись кода на macOS

Для подписи кода автономного исполняемого файла на macOS (что исправляет предупреждения Gatekeeper) используйте команду codesign.

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

Мы рекомендуем включать файл entitlements.plist с разрешениями 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>

Для подписи кода с поддержкой JIT передайте флаг --entitlements в codesign.

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

После подписи кода проверьте исполняемый файл:

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

Разделение кода

Автономные исполняемые файлы поддерживают разделение кода. Используйте --compile с --splitting для создания исполняемого файла который загружает чанки с разделением кода во время выполнения.

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

Неподдерживаемые аргументы CLI

В настоящее время флаг --compile может принимать только одну точку входа за раз и не поддерживает следующие флаги:

  • --outdir — используйте вместо этого outfile (кроме случаев использования с --splitting).
  • --public-path
  • --target=node или --target=browser
  • --no-bundle - мы всегда связываем все в исполняемый файл.

Bun от www.bunjs.com.cn