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-x64Linuxx64✅✅glibc
bun-linux-arm64Linuxarm64✅N/Aglibc
bun-windows-x64Windowsx64✅✅-
bun-windows-arm64Windowsarm64❌❌-
bun-darwin-x64macOSx64✅✅-
bun-darwin-arm64macOSarm64✅N/A-
bun-linux-x64-muslLinuxx64✅✅musl
bun-linux-arm64-muslLinuxarm64✅N/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