Типичные ошибки и способы их устранения

Одна из наиболее частых проблем при сборке связана с разрешением модулей. Сообщение вида Could not resolve "react" или Could not resolve "./utils" возникает, когда механизм резолва esbuild не может найти указанный путь.

Причины обычно сводятся к следующим:

  • отсутствует зависимость в node_modules
  • некорректный относительный путь
  • отсутствие расширения файла при специфической конфигурации проекта
  • использование alias без настройки плагинов

В случае монорепозиториев проблема усиливается из-за hoisting зависимостей и особенностей менеджеров пакетов (pnpm, yarn workspaces).

Пример проблемного импорта:

import { helper } fr om "utils/helper";

Если utils не зарегистрирован как alias, резолв завершится ошибкой.

Решения:

  • корректировка относительных путей:
import { helper } from "../utils/helper.js";
  • настройка alias через plugin:
import { resolve } from "path";

export const aliasPlugin = {
  name: "alias",
  setup(build) {
    build.onResolve({ filter: /^utils\// }, args => {
      return {
        path: resolve(args.resolveDir, "src/" + args.path.replace("utils/", ""))
      };
    });
  }
};

Ошибка: несовместимость ESM и CommonJS

Типичная проблема проявляется в окружениях Node.js при смешивании require и import.

Симптомы:

  • require is not defined
  • Cannot use import statement outside a module
  • неожиданные пустые экспорты

Esbuild по умолчанию способен конвертировать модули, но ошибки возникают при неверно заданном format.

Конфигурационные причины:

format: "esm" // или "cjs"

Пример конфликта:

// CommonJS модуль
module.exports = {
  value: 1
};
// ESM импорт
import mod from "./mod.js";

При неправильной сборке экспорт может оказаться в mod.default или быть недоступным вовсе.

Решение сводится к унификации формата сборки:

  • либо полностью ESM
  • либо строго CommonJS
  • либо явная интероперабельность через default

Ошибка: Top-level await is not available

Данная ошибка возникает при использовании top-level await в средах или конфигурациях, где он не поддерживается.

Esbuild поддерживает top-level await в ESM, но только при корректном указании формата:

format: "esm"
target: "es2022"

Проблемные случаи:

  • сборка в iife или cjs
  • слишком низкий target
  • последующий запуск в Node < 14.8

Ошибка: неверный loader для файлов

Esbuild требует явного указания loader для нестандартных типов файлов.

Симптомы:

  • No loader is configured for ".png"
  • No loader is configured for ".css"

Пример некорректной сборки:

build({
  entryPoints: ["src/index.js"],
  bundle: true,
  outfile: "dist/out.js"
});

При импорте изображения:

import logo from "./logo.png";

Решение — настройка loader:

loader: {
  ".png": "file",
  ".css": "css",
  ".svg": "dataurl"
}

Особое внимание требуется при работе с CSS-модулями и ассетами, так как поведение отличается от Webpack.


Ошибка: несовместимость платформы бинарника esbuild

Esbuild использует нативные бинарные сборки. При установке возможны ошибки:

  • The package was not installed correctly for your platform
  • Failed to install esbuild

Причины:

  • несоответствие архитектуры (arm64 vs x64)
  • блокировка postinstall скриптов
  • использование нестандартных окружений (Docker, CI)

В Docker часто возникает ситуация:

esbuild: unsupported platform linux-arm64

Решение сводится к явной переустановке с нужной платформой:

npm install esbuild --platform=linux --arch=x64

Ошибка: Could not load "fs" / "path" в браузерной сборке

Node.js встроенные модули недоступны в браузерном окружении. Esbuild не полифилит их автоматически.

Симптомы:

  • Module "fs" has been externalized
  • path is not available in the browser

Причина — попытка бандлинга серверного кода в клиентский.

Решения:

  • явное исключение модулей:
external: ["fs", "path", "os"]
  • разделение клиентской и серверной сборки

Ошибка: Could not resolve entry point

Возникает при неверно указанном входном файле.

Пример:

entryPoints: ["src/app.ts"]

Проблемы:

  • файл не существует
  • опечатка в расширении
  • неправильная рабочая директория запуска

Особенно часто проявляется в CI, где cwd отличается от локальной среды.


Ошибка: некорректная обработка CSS

При использовании встроенной поддержки CSS могут возникать проблемы:

  • стили не попадают в бандл
  • отсутствует инжект в runtime
  • потеря классов при minify

Типичная конфигурация:

loader: {
  ".css": "css"
}

При этом важно различать режимы:

  • css — встроенная обработка
  • file — вынесение в отдельный файл
  • text — импорт как строка

Ошибка часто возникает при смешивании подходов.


Ошибка: конфликты с TypeScript конфигурацией

Esbuild компилирует TypeScript без полной проверки типов, но ошибки конфигурации могут привести к неожиданным результатам.

Проблемные ситуации:

  • игнорирование tsconfig.json
  • несовпадение target
  • использование path mapping без плагина

Пример:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

Без соответствующего резолв-плагина:

build({
  plugins: [tsconfigPaths()]
});

alias не будет работать.


Ошибка: define и потеря process.env

При переносе Node.js кода в бандл часто возникает ситуация, когда process.env становится undefined.

Esbuild не подставляет переменные окружения автоматически.

Пример:

console.log(process.env.NODE_ENV);

Решение через define:

define: {
  "process.env.NODE_ENV": '"production"'
}

Типичная ошибка — отсутствие строкового JSON-формата, что приводит к синтаксическим сбоям.


Ошибка: слишком большой бандл и деградация производительности

При увеличении количества зависимостей возможны:

  • длительная сборка
  • рост потребления памяти
  • зависания при watch-режиме

Причины:

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

Оптимизационные меры:

  • использование splitting: true
  • включение tree shaking через ESM
  • исключение тяжёлых зависимостей

Ошибка: watch режим не обновляет файлы

Watch может переставать реагировать на изменения при:

  • ограничениях файловой системы (Docker volumes)
  • WSL особенностях
  • переполнении watcher lim it

Признаки:

  • отсутствие пересборки
  • устаревший output

Решение часто связано с увеличением лимитов системы или переходом на polling:

watch: {
  usePolling: true
}

Ошибка: несовместимость плагинов

Esbuild plugin API чувствителен к порядку и типам обработчиков.

Симптомы:

  • плагины не срабатывают
  • конфликт onResolve и onLoad
  • дублирование трансформаций

Типичная проблема — перекрытие фильтров:

onResolve({ filter: /.*/ })

что блокирует более специфичные правила.

Корректная стратегия — приоритизация фильтров от узких к широким.


Ошибка: динамические импорты и код-сплиттинг

Dynamic import поддерживается, но при неверной конфигурации может приводить к:

  • отсутствию чанков
  • ошибкам загрузки в runtime
  • некорректным путям в outdir

Пример:

import("./module.js");

Требуется включение:

splitting: true,
format: "esm"

Без этого esbuild объединяет код в один файл, игнорируя разделение.


Ошибка: несовместимость target и синтаксиса

При установке слишком старого target современные конструкции ломаются:

  • optional chaining
  • nullish coalescing
  • private fields

Симптомы:

  • синтаксические ошибки в output
  • падение сборки

Пример корректной настройки:

target: "es2020"

Ошибка: неправильная работа incremental build

Incremental режим может возвращать устаревшие результаты при:

  • изменении конфигурации без пересоздания контекста
  • изменении плагинов во время работы
  • некорректном кэшировании

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


Ошибка: проблемы с sourcemap

Sourcemaps могут:

  • не совпадать с исходным кодом
  • отсутствовать в production сборке
  • ломаться при minify

Причины:

  • несовместимость sourcemap: inline и outfile
  • постобработка другими инструментами
  • отсутствие корректного sourceRoot

Ошибка: конфликт minify и сторонних трансформеров

При использовании внешних трансформеров (например, Babel до esbuild) возможны:

  • дублирование оптимизаций
  • ломка AST
  • неожиданные переименования переменных

Решение заключается в разделении ролей:

  • esbuild — bundling и transpile
  • внешние инструменты — только специализированные преобразования