Одной из ключевых настроек Esbuild является опция
platform, определяющая целевую среду выполнения собранного
кода. От неё зависит не только формат выходного файла, но и множество
внутренних решений сборщика: обработка встроенных модулей, выбор условий
экспорта пакетов, генерация совместимого кода и стратегия разрешения
зависимостей.
Для Node.js используется значение:
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
platform: 'node',
outfile: 'dist/app.js'
});
При установке platform: 'node' Esbuild начинает
рассматривать проект как серверное приложение, а не как код,
предназначенный для браузера.
Esbuild поддерживает три основных платформы:
| Платформа | Назначение |
|---|---|
browser |
Веб-приложения |
node |
Приложения Node.js |
neutral |
Универсальная среда без специальных предположений |
Пример:
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
platform: 'browser'
});
и
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
platform: 'node'
});
могут создать принципиально разные результаты даже при одинаковом исходном коде.
Одно из важнейших последствий использования
platform: 'node' связано со встроенными модулями
Node.js.
Например:
import fs from 'fs';
import path from 'path';
При браузерной сборке такие зависимости вызовут ошибку или потребуют полифилов.
При использовании Node-платформы Esbuild понимает, что данные модули существуют в среде выполнения, и обращается с ними соответствующим образом.
await esbuild.build({
bundle: true,
platform: 'node'
});
В результате модуль fs не будет заменён браузерными
заглушками.
Современные npm-пакеты часто используют поле
exports.
Пример:
{
"exports": {
"browser": "./browser.js",
"node": "./node.js",
"default": "./index.js"
}
}
Если указано:
platform: 'node'
Esbuild будет искать условие:
"node"
и выберет:
./node.js
При браузерной сборке был бы выбран другой файл.
Это позволяет пакетам поставлять специализированные версии для различных сред выполнения.
При разрешении зависимостей Esbuild анализирует несколько полей в
package.json.
Например:
{
"main": "./dist/index.js",
"module": "./dist/index.mjs"
}
Для Node-платформы используется порядок поиска, ориентированный на серверную среду.
Фактически Esbuild старается выбрать вариант, который наиболее корректно соответствует работе Node.js.
Это особенно важно при работе со смешанными CommonJS и ES-модулями.
При использовании Node.js исторически доминировал формат CommonJS.
Поэтому Esbuild автоматически подбирает формат, который лучше соответствует среде Node.
Например:
await esbuild.build({
platform: 'node',
bundle: true,
outfile: 'dist/app.js'
});
Для старых версий Node результат обычно генерируется в стиле CommonJS.
Получаемый код может содержать:
module.exports = value;
или
exports.run = run;
Однако поведение зависит и от параметра format.
Часто платформа указывается вместе с форматом модулей.
await esbuild.build({
platform: 'node',
format: 'cjs',
bundle: true
});
Результат:
const lib = require('./lib');
await esbuild.build({
platform: 'node',
format: 'esm',
bundle: true
});
Результат:
import lib from './lib.js';
Такой вариант подходит для современных проектов Node.js, использующих:
{
"type": "module"
}
Node.js активно использует CommonJS.
Исходный код:
const express = require('express');
При настройке:
platform: 'node'
Esbuild сохраняет корректную совместимость с подобными конструкциями.
Это особенно полезно при миграции старых проектов, где одновременно присутствуют:
require(...)
и
import ...
Node.js поддерживает динамическую загрузку модулей.
Исходный код:
const module = await import('./feature.js');
При использовании Node-платформы Esbuild учитывает возможности среды выполнения и не пытается преобразовать конструкцию в браузерный аналог.
В Node.js глобально существует объект:
process
Например:
console.log(process.version);
или
console.log(process.env.NODE_ENV);
При платформе browser обычно требуется дополнительная
подстановка значений.
При платформе node Esbuild предполагает наличие объекта
process во время выполнения.
Node.js предоставляет специальные переменные:
__dirname
и
__filename
Пример:
console.log(__dirname);
При сборке для Node Esbuild старается сохранить ожидаемое поведение подобных конструкций.
Для браузерной среды такие возможности отсутствуют.
Многие серверные приложения используют доступ к файлам.
Пример:
import fs from 'fs';
const text = fs.readFileSync('config.json', 'utf8');
При использовании:
platform: 'node'
Esbuild не пытается заменить обращения к файловой системе браузерными заглушками.
Код остаётся пригодным для выполнения внутри Node.js.
Множество библиотек существует исключительно для Node.js.
Например:
import express from 'express';
import bcrypt from 'bcrypt';
import pg from 'pg';
Такие пакеты часто используют:
Сборка через:
platform: 'node'
позволяет Esbuild корректно работать с подобными зависимостями.
Часто серверные приложения не включают зависимости внутрь бандла.
Пример:
await esbuild.build({
bundle: true,
platform: 'node',
external: ['express']
});
В результате:
require('express');
останется во внешнем виде.
Модуль будет загружен Node.js во время выполнения.
Это позволяет уменьшать размер итогового файла и ускорять сборку.
Типичная конфигурация:
import esbuild from 'esbuild';
await esbuild.build({
entryPoints: ['src/server.js'],
bundle: true,
platform: 'node',
target: 'node20',
outfile: 'dist/server.js'
});
Особенности такой сборки:
Параметр platform определяет среду выполнения, а
target — её версию.
Пример:
platform: 'node',
target: 'node18'
или
platform: 'node',
target: 'node22'
Esbuild анализирует поддерживаемые возможности выбранной версии Node и решает, какие преобразования необходимы.
Например, современные конструкции:
class User {
#name;
}
могут остаться без изменений при достаточно новой версии Node.
Некоторые пакеты содержат бинарные компоненты:
.node
Примеры:
При использовании platform: 'node' Esbuild понимает, что
код будет выполняться внутри Node.js, однако сами бинарные файлы
автоматически в бандл не встраиваются.
Поэтому после сборки может потребоваться копирование дополнительных ресурсов.
Tree shaking продолжает работать и в Node-проектах.
Исходный модуль:
export function used() {}
export function unused() {}
Использование:
import { used } from './utils.js';
used();
После сборки неиспользуемый код будет удалён.
Платформа Node не отключает механизм удаления мёртвого кода.
Современные серверные приложения всё чаще используют:
{
"type": "module"
}
В таком случае обычно применяется конфигурация:
await esbuild.build({
platform: 'node',
format: 'esm',
bundle: true
});
Это позволяет сохранить естественную поддержку:
import
export
await import()
без возврата к CommonJS.
Опция является правильным выбором для:
Типичный пример:
await esbuild.build({
entryPoints: ['src/cli.js'],
bundle: true,
platform: 'node',
target: 'node22',
outfile: 'bin/cli.js'
});
В этом режиме Esbuild формирует результат, максимально соответствующий ожиданиям среды Node.js: корректно разрешает серверные зависимости, использует условия экспорта для Node, учитывает встроенные модули платформы и генерирует код, ориентированный на выполнение вне браузера.