Бандлер Bun реализует флаг --compile для генерации автономного бинарного файла из TypeScript или JavaScript файла.
bun build ./cli.ts --compile --outfile mycliconsole.log("Hello world!");Это связывает cli.ts в исполняемый файл который можно запустить напрямую:
./mycliHello world!Все импортируемые файлы и пакеты связываются в исполняемый файл вместе с копией среды выполнения Bun. Все встроенные API Bun и Node.js поддерживаются.
Кросс-компиляция для других платформ
Флаг --target позволяет компилировать автономный исполняемый файл для другой операционной системы архитектуры или версии Bun чем машина на которой вы запускаете bun build.
Для сборки под Linux x64 (большинство серверов):
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):
# Примечание: архитектура по умолчанию x64 если не указана архитектура
bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myappДля сборки под Windows x64:
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:
bun build --compile --target=bun-darwin-arm64 ./path/to/my/app.ts --outfile myappДля сборки под macOS x64:
bun build --compile --target=bun-darwin-x64 ./path/to/my/app.ts --outfile myappПоддерживаемые таргеты
Порядок флага --target не имеет значения пока они разделены -.
| --target | Операционная система | Архитектура | Modern | Baseline | Libc |
|---|---|---|---|---|---|
| bun-linux-x64 | Linux | x64 | ✅ | ✅ | glibc |
| bun-linux-arm64 | Linux | arm64 | ✅ | N/A | glibc |
| bun-windows-x64 | Windows | x64 | ✅ | ✅ | - |
| ❌ | ❌ | - | |||
| bun-darwin-x64 | macOS | x64 | ✅ | ✅ | - |
| bun-darwin-arm64 | macOS | arm64 | ✅ | N/A | - |
| bun-linux-x64-musl | Linux | x64 | ✅ | ✅ | musl |
| bun-linux-arm64-musl | Linux | arm64 | ✅ | N/A | musl |
Константы времени сборки
Используйте флаг --define для внедрения констант времени сборки в ваш исполняемый файл таких как номера версий метки времени сборки или значения конфигурации:
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 «просто работать» но это не бесплатно. Чтение файлов с диска разрешение путей файлов парсинг транспиляция и печать исходного кода требуют времени и памяти.
Со скомпилированными исполняемыми файлами вы можете перенести эти расходы из времени выполнения во время сборки.
При развертывании в продакшен мы рекомендуем следующее:
bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myappКомпиляция байт-кода
Для улучшения времени запуска включите компиляцию байт-кода:
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:
bun build --compile --compile-exec-argv="--smol --user-agent=MyBot" ./app.ts --outfile myapp// В скомпилированном приложении
console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"]Отключение автоматической загрузки конфигурации
По умолчанию автономные исполняемые файлы ищут файлы .env и bunfig.toml в директории где запускается исполняемый файл. Вы можете отключить это поведение во время сборки для детерминированного выполнения независимо от рабочей директории пользователя.
# Отключить загрузку .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:
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.
Например рассмотрим исполняемый файл скомпилированный из простого скрипта:
echo "console.log(\"you shouldn't see this\");" > such-bun.js
bun build --compile ./such-bun.js[3ms] bundle 1 modules
[89ms] compile such-bunОбычно запуск ./such-bun с аргументами выполнял бы скрипт.
# Исполняемый файл запускает свою точку входа по умолчанию
./such-bun installyou shouldn't see thisОднако с переменной окружения BUN_BE_BUN=1 он действует точно как бинарный файл bun:
# С переменной окружения исполняемый файл действует как CLI `bun`
BUN_BE_BUN=1 ./such-bun installbun 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 и других активов.
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}`);<!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>console.log("Hello from the client!");body {
background-color: #f0f0f0;
}Для сборки этого в единый исполняемый файл:
bun build --compile ./server.ts --outfile myappЭто создает самодостаточный бинарный файл который включает:
- Ваш серверный код
- Среду выполнения Bun
- Все фронтенд-активы (HTML CSS JavaScript)
- Любые npm-пакеты используемые вашим сервером
Результат — один файл который можно развернуть где угодно без необходимости установки Node.js Bun или каких-либо зависимостей. Просто запустите:
./myappBun автоматически обрабатывает обслуживание фронтенд-активов с правильными MIME-типами и заголовками кэша. Импорт HTML заменяется объектом манифеста который Bun.serve использует для эффективной подачи предварительно связанных активов.
Подробнее о создании полнофункциональных приложений с Bun смотрите в руководстве по полнофункциональной сборке.
Worker
Для использования workers в автономном исполняемом файле добавьте точку входа worker в аргументы CLI:
bun build --compile ./index.ts ./my-worker.ts --outfile myappЗатем сослайтесь на worker в вашем коде:
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.
По умолчанию база данных разрешается относительно текущей рабочей директории процесса.
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.
cd /home/me/Desktop
./helloВстраивание активов и файлов
Автономные исполняемые файлы поддерживают встраивание файлов.
Для встраивания файлов в исполняемый файл с bun build --compile импортируйте файл в ваш код.
// это становится внутренним путем к файлу
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").
Например для чтения содержимого встроенного файла:
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".
import myEmbeddedDb from "./my.db" with { type: "sqlite", embed: "true" };
console.log(myEmbeddedDb.query("select * from users LIMIT 1").get());Эта база данных доступна для чтения и записи но все изменения теряются при выходе из исполняемого файла (поскольку она хранится в памяти).
Встраивание аддонов N-API
Вы можете встраивать файлы .node в исполняемые файлы.
const addon = require("./addon.node");
console.log(addon.hello());К сожалению если вы используете @mapbox/node-pre-gyp или другие подобные инструменты вам нужно убедиться что файл .node импортируется напрямую иначе он не будет связан корректно.
Встраивание директорий
Для встраивания директории с bun build --compile используйте shell-глоб в вашей команде bun build:
bun build --compile ./index.ts ./public/**/*.pngЗатем вы можете сослаться на файлы в вашем коде:
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:
import "./icon.png" with { type: "file" };
import { embeddedFiles } from "bun";
console.log(embeddedFiles[0].name); // `icon-${hash}.png`Bun.embeddedFiles возвращает массив объектов Blob которые вы можете использовать для получения размера содержимого и других свойств файлов.
embeddedFiles: Blob[]Список встроенных файлов исключает связанный исходный код такой как файлы .ts и .js.
Хеш содержимого
По умолчанию встроенные файлы имеют хеш содержимого добавленный к их имени. Это полезно для ситуаций когда вы хотите обслуживать файл из URL или CDN и иметь меньше проблем с инвалидацией кэша. Но иногда это неожиданно и вы можете захотеть оригинальное имя:
Для отключения хеша содержимого передайте --asset-naming в bun build --compile вот так:
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.
codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myappМы рекомендуем включать файл entitlements.plist с разрешениями JIT.
<?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.
codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myappПосле подписи кода проверьте исполняемый файл:
codesign -vvv --verify ./myapp
./myapp: valid on disk
./myapp: satisfies its Designated RequirementРазделение кода
Автономные исполняемые файлы поддерживают разделение кода. Используйте --compile с --splitting для создания исполняемого файла который загружает чанки с разделением кода во время выполнения.
bun build --compile --splitting ./src/entry.ts --outdir ./buildconsole.log("Entrypoint loaded");
const lazy = await import("./lazy.ts");
lazy.hello();export function hello() {
console.log("Lazy module loaded");
}./build/entryEntrypoint loaded
Lazy module loadedНеподдерживаемые аргументы CLI
В настоящее время флаг --compile может принимать только одну точку входа за раз и не поддерживает следующие флаги:
--outdir— используйте вместо этогоoutfile(кроме случаев использования с--splitting).--public-path--target=nodeили--target=browser--no-bundle- мы всегда связываем все в исполняемый файл.