В основе работы esbuild лежит строгая модель выбора трансформера
исходя из расширения входного файла. Определение того, является ли файл
JSX-кандидатом, не происходит по содержимому кода — используется только
сопоставление расширений и явно заданных правил loader.
При обработке модуля пайплайн esbuild проходит этап определения загрузчика (loader resolution). На этом этапе каждому файлу сопоставляется конкретный парсер и трансформер: JavaScript, JSX, TypeScript, TSX, JSON, текст и другие форматы.
JSX-обработка в esbuild активируется через загрузчики
jsx и tsx. Их выбор зависит от расширения
файла:
.jsx → автоматически используется loader
jsx.tsx → автоматически используется loader
tsx.js → loader js (JSX не включён).ts → loader ts (без JSX)Ключевая особенность: наличие JSX-синтаксиса в .js файле
не приводит к автоматическому переключению режима
парсинга.
| Расширение | Loader | Поддержка JSX |
|---|---|---|
| .js | js | нет |
| .jsx | jsx | да |
| .ts | ts | нет |
| .tsx | tsx | да |
| .mjs | js | нет |
| .cjs | js | нет |
Если файл .js содержит JSX-разметку:
const app = <div>Hello</div>
esbuild выдаст ошибку синтаксиса, поскольку стандартный parser для
js не ожидает JSX-узлы.
Это одно из наиболее частых расхождений между ожиданиями разработчиков и реальным поведением сборщика: JSX не определяется эвристически.
Чтобы заставить esbuild обрабатывать .js как JSX,
необходимо переопределить loader:
esbuild src/index.js --loader:.js=jsx
import * as esbuild from 'esbuild'
esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
loader: {
'.js': 'jsx'
}
})
Такой подход изменяет глобальное правило интерпретации расширения и
может повлиять на весь проект, включая сторонние зависимости, если они
резолвятся через .js.
Loader jsx и tsx отличаются не только
поддержкой синтаксиса, но и стратегией обработки
TypeScript-аннотаций.
jsx — только JavaScript + JSXtsx — TypeScript + JSXВнутренне esbuild использует разные грамматики:
При использовании JSX esbuild может автоматически добавлять runtime-импорт в зависимости от настроек:
esbuild.build({
jsx: 'automatic',
jsxImportSource: 'react'
})
При этом автоматическое определение файлов никак не влияет на выбор runtime — оно определяется отдельно от loader-механизма.
Для .tsx файлов JSX включён всегда, но TypeScript-парсер
требует строгого соблюдения синтаксиса типов.
const element: JSX.Element = <div />
Если файл имеет расширение .ts, даже при наличии JSX он
не будет интерпретирован корректно без переопределения loader:
loader: {
'.ts': 'tsx'
}
Такое переопределение используется редко и может привести к неоднозначному поведению в крупных кодовых базах.
CLI esbuild опирается исключительно на расширения и флаги:
esbuild app.js --bundle --jsx=automatic
Но ключевым параметром остаётся --loader, а не
--jsx. Флаг --jsx влияет только на
трансформацию, но не на определение возможности парсинга JSX в
.js.
JavaScript API предоставляет более точный контроль:
loaderjsxjsxFactoryjsxFragmentjsxImportSourceВ монорепозиториях часто возникает необходимость унифицировать обработку JSX:
loader: {
'.js': 'jsx',
'.mjs': 'jsx'
}
Однако такое решение влияет на:
Поэтому практикуется ограниченное применение через
include/exclude плагины.
Хотя esbuild не анализирует содержимое файла для определения JSX, плагины могут перехватывать загрузку и подменять loader:
const jsxPlugin = {
name: 'jsx-detection',
setup(build) {
build.onLoad({ filter: /\.js$/ }, async (args) => {
const source = await fs.promises.readFile(args.path, 'utf8')
if (source.includes('<') && source.includes('>')) {
return {
loader: 'jsx',
contents: source
}
}
})
}
}
Такой подход не является стандартным и используется только в специализированных сборочных системах, так как:
Loader напрямую влияет на ключ кеша модуля. Файл a.js с
loader js и тот же файл с loader jsx
рассматриваются как разные сущности в графе зависимостей.
Это означает:
Использование JSX в .js без loader:
Попытка полагаться на авто-распознавание:
Конфликт .ts и tsx:
.ts игнорируется без переопределенияГлобальное переопределение .js=jsx:
node_modulesПри использовании React 17+ с automatic runtime:
jsx: 'automatic'
и файлов .jsx/.tsx esbuild автоматически
вставляет импорты из источника, указанного в
jsxImportSource.
Loader при этом остаётся единственным механизмом, определяющим, будет ли JSX вообще обрабатываться.
Процесс можно свести к последовательности:
jsx или
tsx loader