WebAssembly-реализация Esbuild ориентирована на среду, где невозможен
или нежелателен запуск нативного бинарника: браузер, ограниченные
серверные окружения, песочницы и некоторые serverless-платформы. В
отличие от классической версии, основанной на Go-бинарнике, WASM-вариант
работает полностью внутри JavaScript-рантайма, используя компилированный
модуль .wasm.
Ключевая особенность заключается в том, что производительность остаётся высокой по сравнению с большинством чисто JS-бандлеров, но уступает нативной версии Esbuild из-за ограничений WebAssembly и накладных расходов на передачу данных между JS и WASM.
WASM-версия распространяется как отдельный пакет:
npm install esbuild-wasm
Пакет не содержит нативных бинарников и предназначен для универсального использования в браузере и Node.js (в режиме WASM).
Внутри пакета присутствуют:
Перед использованием любой функциональности Esbuild необходимо явно запустить и инициализировать WASM-движок. Это ключевое отличие от нативной версии, где бинарник поднимается автоматически.
import * as esbuild from 'esbuild-wasm';
await esbuild.initialize({
wasmURL: 'https://unpkg.com/esbuild-wasm@0.20.0/esbuild.wasm'
});
Основной объект конфигурации определяет способ загрузки и поведение WASM-движка:
.wasm файлу. Может
быть CDN, локальный сервер или статический ресурс.Самый простой вариант — загрузка с CDN:
await esbuild.initialize({
wasmURL: 'https://unpkg.com/esbuild-wasm/esbuild.wasm'
});
Этот подход удобен для прототипов, но создаёт зависимость от внешнего ресурса и увеличивает время первого запуска.
Для продакшн-сценариев предпочтительнее локальное размещение:
await esbuild.initialize({
wasmURL: '/assets/esbuild/esbuild.wasm'
});
WASM-файл размещается рядом со статическими ресурсами приложения и загружается через HTTP-сервер.
В современных сборщиках возможно импортировать WASM как ассет:
import wasmURL from 'esbuild-wasm/esbuild.wasm';
await esbuild.initialize({
wasmURL
});
Такой подход упрощает управление версиями и кэшированием.
В браузерных приложениях использование Web Worker критично для предотвращения блокировки main thread.
await esbuild.initialize({
wasmURL: '/esbuild.wasm',
worker: true
});
При включённом worker:
Инициализация должна выполняться один раз на жизненный цикл приложения. Повторный вызов может привести к ошибкам или утечкам ресурсов.
Типичная схема:
let initialized = false;
export async function initEsbuild() {
if (initialized) return;
await esbuild.initialize({
wasmURL: '/esbuild.wasm'
});
initialized = true;
}
Перед вызовом методов build или transform
необходимо гарантировать завершение инициализации.
await initEsbuild();
const result = await esbuild.transform(code, {
loader: 'tsx'
});
Если попытаться вызвать API до инициализации, будет выброшена ошибка о неготовом сервисе.
WASM-версия особенно актуальна в браузерных приложениях, где требуется:
Пример минимального окружения:
import * as esbuild from 'esbuild-wasm';
await esbuild.initialize({
wasmURL: '/esbuild.wasm',
worker: true
});
const result = await esbuild.transform(
'const x: number = 1',
{ loader: 'ts' }
);
console.log(result.code);
WebAssembly-файл обычно имеет размер в несколько мегабайт, поэтому важны стратегии оптимизации:
Рекомендуется задавать долгосрочные заголовки:
Cache-Control: public, max-age=31536000, immutable
Инициализация должна выполняться только при необходимости, например:
Несмотря на функциональную совместимость с нативным Esbuild, существуют ограничения:
.wasmХотя Node.js поддерживает нативную версию Esbuild, WASM-вариант применяется в специфических случаях:
import * as esbuild from 'esbuild-wasm';
await esbuild.initialize({
wasmURL: 'file:///node_modules/esbuild-wasm/esbuild.wasm'
});
WASM-движок использует линейную память, которая:
При длительной работе в браузере важно учитывать:
Все операции в WASM-версии строго асинхронные:
initialize — загрузка и старт рантаймаtransform — преобразование кодаbuild — сборка модулейЭто связано с тем, что WebAssembly не может блокировать основной поток JavaScript.
const output = await esbuild.build({
entryPoints: ['index.ts'],
bundle: true,
write: false
});
В крупных приложениях инициализация Esbuild обычно выносится в отдельный модуль:
// esbuildService.js
import * as esbuild from 'esbuild-wasm';
let servicePromise;
export function getEsbuildService() {
if (!servicePromise) {
servicePromise = esbuild.initialize({
wasmURL: '/esbuild.wasm',
worker: true
});
}
return servicePromise;
}
Такой подход гарантирует:
В проектах с Vite, Webpack или Rollup важно учитывать обработку
.wasm:
experiments.asyncWebAssemblyБез корректной настройки загрузка wasmURL может
завершиться ошибкой MIME-типа или 404.
При использовании sandbox-сред или iframe:
Типичный жизненный цикл выглядит следующим образом:
.wasmТакой подход минимизирует накладные расходы и обеспечивает стабильную работу даже при частых вызовах компиляции.