Loader default и auto-определение

Загрузка модулей в esbuild основана на жёстко детерминированной системе загрузчиков (loaders), где ключевую роль играет расширение файла и явная конфигурация сборщика. Поведение по умолчанию построено вокруг принципа минимального анализа содержимого и максимальной скорости: файл почти всегда обрабатывается не по содержимому, а по имени и расширению.

Каждый импортируемый файл в esbuild проходит через слой определения loader-а. Loader определяет, как именно интерпретировать содержимое модуля:

  • JavaScript/TypeScript код компилируется и бандлится как модуль
  • JSON превращается в ESM-совместимый объект
  • CSS либо инлайнятся, либо извлекаются в зависимости от настроек
  • бинарные данные могут быть встроены как base64 или data URL
  • неизвестные файлы рассматриваются как ассеты

Ключевой принцип: loader всегда выбирается до анализа содержимого файла.

Поведение по умолчанию

Если loader явно не указан, esbuild применяет встроенное правило сопоставления расширений.

Основные правила сопоставления

Расширение Loader
.js, .cjs, .mjs js
.ts ts
.tsx tsx
.jsx jsx
.json json
.css css
.txt (и похожие текстовые) text (в зависимости от контекста)
неизвестные расширения file

Это сопоставление является первой и основной стадией автоопределения.

Ключевая особенность

esbuild не анализирует содержимое файла для выбора loader-а. Нет проверки вроде “похоже ли это на JSON” или “содержит ли JSX”.

Выбор происходит исключительно по расширению.

Механизм автоопределения loader-а

Автоопределение loader-а в esbuild — это не «интеллектуальный анализ», а строгая таблица соответствий.

Порядок действий следующий:

  1. Берётся путь файла
  2. Извлекается расширение
  3. Выполняется lookup в таблице встроенных loader-ов
  4. Если совпадение найдено — используется соответствующий loader
  5. Если совпадения нет — применяется fallback (file)

Пример поведения

import data from "./config.json";
import Component from "./App.jsx";
import utils from "./utils.ts";
import image from "./logo.png";

Результат обработки:

  • config.jsonjson
  • App.jsxjsx
  • utils.tsts
  • logo.pngfile

Loader file как fallback

Особую роль играет loader file. Он применяется ко всему, что не попало под известные расширения.

Поведение loader file

  • файл не парсится как код
  • не трансформируется в AST
  • копируется в output directory
  • импорт заменяется на URL или путь к файлу

Пример:

import logo from "./assets/logo.svg";

При отсутствии явного loader-а:

  • logo.svgfile

На выходе:

export default "/assets/logo.hash.svg";

(реальный результат зависит от настройки outdir и publicPath)

Явное переопределение loader-а

Несмотря на автоопределение, конфигурация loader позволяет переопределить поведение.

import { build } from "esbuild";

build({
  entryPoints: ["src/index.js"],
  bundle: true,
  outfile: "dist/bundle.js",
  loader: {
    ".js": "jsx",
    ".png": "dataurl",
    ".svg": "text"
  }
});

Что происходит при переопределении

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

Варианты loader-ов и их роль в автоопределении

js

Используется для:

  • .js
  • .mjs
  • .cjs

Поведение:

  • парсинг как JavaScript
  • поддержка ESM и CommonJS
  • трансформация JSX/TS не выполняется

jsx

Используется для файлов с JSX-синтаксисом.

loader: {
  ".js": "jsx"
}

Это важно, так как по умолчанию .js не содержит JSX-трансформации.


ts и tsx

  • ts — TypeScript без JSX
  • tsx — TypeScript с JSX

esbuild не проверяет содержимое файла: наличие типов или JSX не определяется автоматически.


json

JSON-файлы превращаются в ES Module:

{
  "name": "app",
  "version": "1.0.0"
}

После импорта:

import pkg from "./package.json";
console.log(pkg.version);

Результат — объект JavaScript.


css

CSS обрабатывается как модуль:

  • может быть инлайнен в JS
  • может быть извлечён в отдельный файл

Автоопределение строго по .css.


text

Содержимое читается как строка.

Полезно для:

  • markdown
  • шаблонов
  • логов
  • конфигов без структурирования

binary и base64

Используются для бинарных данных:

  • binary — ArrayBuffer
  • base64 — строка base64

dataurl

Преобразует файл в Data URL:

data:image/png;base64,...

Часто используется для изображений небольшого размера.


Особенности автоопределения и важные ограничения

1. Нет анализа содержимого

Файл с расширением .js всегда считается JavaScript, даже если внутри JSON:

// config.js (на самом деле JSON по содержимому)
{
  "mode": "test"
}

esbuild обработает это как JS и выдаст ошибку.


2. Расширение — единственный источник истины

Это делает систему:

  • очень быстрой
  • предсказуемой
  • но не гибкой

3. Отсутствие магического определения JSX

JSX не распознаётся внутри .js автоматически.

// даже если здесь JSX
const el = <div />;

Без loader jsx будет синтаксическая ошибка.


4. Неизвестные расширения всегда становятся file

import config from "./config.toml";

Если loader не задан:

  • .tomlfile

И файл не парсится как структура данных.


Приоритеты определения loader-а

Иерархия выбора:

  1. Явно заданный loader в конфигурации
  2. Встроенная таблица по расширению
  3. fallback file

Важно: конфигурация всегда имеет приоритет над автоопределением.


Практические сценарии

Сценарий: монорепозиторий с нестандартными расширениями

loader: {
  ".md": "text",
  ".svg": "text",
  ".graphql": "text"
}

Здесь автоопределение полностью расширяется вручную.


Сценарий: принудительное JSX в проекте без .jsx

loader: {
  ".js": "jsx"
}

Позволяет использовать JSX без переименования файлов.


Сценарий: ассеты как data URL

loader: {
  ".png": "dataurl",
  ".jpg": "dataurl"
}

Удобно для небольших изображений и UI-иконок.


Влияние автоопределения на производительность

Отказ от анализа содержимого — ключевая оптимизация esbuild:

  • нет парсинга MIME-типа
  • нет чтения сигнатур файлов
  • нет дополнительных I/O операций
  • решение принимается за O(1) по расширению

Это одна из причин высокой скорости сборки по сравнению с инструментами, где используется более сложная логика определения типа модуля.


Типичные ошибки при работе с loader-ами

Ошибка: неправильное расширение

import App from "./App.js";

Если файл содержит JSX, но имеет .js, без переопределения:

  • будет синтаксическая ошибка

Ошибка: ожидание автораспознавания JSON

import data from "./config.txt";

Даже если внутри JSON — результат будет строка.


Ошибка: отсутствие loader для нестандартных типов

SVG, TOML, YAML не поддерживаются без явного указания.


Итоговая модель поведения

Автоопределение loader-а в esbuild можно свести к простой модели:

loader(file) =
  if (loader_override exists) → override
  else if (extension in table) → mapped loader
  else → file

Эта модель подчёркивает фундаментальный принцип esbuild: простота правил вместо анализа содержимого, что обеспечивает предсказуемость и высокую скорость обработки модулей.