Использование JSX с Preact, Solid и другими библиотеками

esbuild поддерживает трансформацию JSX как часть встроенного компилятора, без необходимости Babel или дополнительных транспилеров. Основной принцип работы заключается в том, что JSX не исполняется напрямую — он преобразуется в вызовы функций, определяемые настройками jsxFactory, jsxFragment и jsxImportSource.

JSX в контексте Esbuild может работать в двух режимах:

  • Classic runtime — явное указание функций создания элементов
  • Automatic runtime — автоматическое подключение JSX-рантайма через jsxImportSource

Разница между ними определяет совместимость с библиотеками вроде Preact и Solid.


Базовая настройка JSX в Esbuild

Основные параметры конфигурации:

import { build } from 'esbuild';

build({
  entryPoints: ['src/index.jsx'],
  bundle: true,
  outfile: 'dist/app.js',
  jsx: 'transform',
  jsxFactory: 'h',
  jsxFragment: 'Fragment'
});

Параметры:

  • jsx: 'transform' — включает преобразование JSX
  • jsxFactory — функция создания элементов
  • jsxFragment — функция для фрагментов <>...</>

Такой режим используется для библиотек, которые требуют явного runtime, например Preact в legacy-режиме.


Использование Preact с Esbuild

Preact совместим с JSX через два основных подхода: классический runtime и автоматический runtime.

Классический режим (h / Fragment)

build({
  entryPoints: ['src/index.jsx'],
  bundle: true,
  outfile: 'dist/app.js',
  jsxFactory: 'h',
  jsxFragment: 'Fragment'
});

И код приложения:

import { h, Fragment } from 'preact';

const App = () => (
  <>
    <h1>Приложение</h1>
  </>
);

Здесь JSX превращается в:

h(Fragment, null,
  h('h1', null, 'Приложение')
);

Автоматический runtime Preact

Современный подход использует preact/jsx-runtime:

build({
  entryPoints: ['src/index.jsx'],
  bundle: true,
  outfile: 'dist/app.js',
  jsx: 'automatic',
  jsxImportSource: 'preact'
});

Код:

const App = () => (
  <h1>Приложение</h1>
);

Автоматически компилируется в импортированные функции JSX runtime без явного h.


JSX с Solid

Solid использует собственную модель реактивности и требует автоматического JSX runtime.

Конфигурация Esbuild

build({
  entryPoints: ['src/index.jsx'],
  bundle: true,
  outfile: 'dist/app.js',
  jsx: 'automatic',
  jsxImportSource: 'solid-js'
});

Пример компонента

function Counter() {
  return <button>Клик</button>;
}

Solid не использует виртуальный DOM. JSX напрямую компилируется в реактивные вызовы:

import { template as _$template } from "solid-js/web";

и далее создаются оптимизированные DOM-узлы.

Особенности интеграции

  • jsxImportSource: 'solid-js' обязателен
  • Babel не требуется
  • Esbuild генерирует совместимый output при условии правильного runtime

Различия JSX runtime моделей

Classic runtime

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

  • старом React-подобном API
  • Preact legacy

Характеристики:

  • требуется jsxFactory
  • ручной h-вызов
  • отсутствие автоматических импортов

Automatic runtime

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

  • Preact modern
  • Solid
  • React 17+ совместимые окружения

Характеристики:

  • jsx: 'automatic'
  • jsxImportSource
  • автоматическое подключение функций
  • меньше boilerplate

Настройка TypeScript совместимости

При использовании TypeScript с JSX в Esbuild важно согласовать настройки:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  }
}

или для Solid:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "solid-js"
  }
}

Esbuild при этом игнорирует TypeScript JSX трансформацию и применяет собственную.


Кастомный JSX runtime

Esbuild позволяет подключать любую библиотеку, предоставляющую JSX runtime.

Пример собственной реализации

build({
  entryPoints: ['src/index.jsx'],
  bundle: true,
  outfile: 'dist/app.js',
  jsx: 'automatic',
  jsxImportSource: './my-jsx-runtime'
});

Структура runtime:

export function jsx(type, props) {
  return { type, props };
}

export function jsxs(type, props) {
  return { type, props };
}

export function Fragment(props) {
  return props.children;
}

Оптимизация вывода JSX

Esbuild выполняет несколько оптимизаций:

  • удаление лишних JSX-обёрток
  • инлайнинг Fragment
  • сворачивание статических узлов
  • минимизация runtime вызовов

Пример:

<div>
  <span>1</span>
  <span>2</span>
</div>

может быть преобразован в минимальный набор вызовов без промежуточных структур.


Совместимость с библиотеками UI

JSX в Esbuild корректно работает с большинством современных UI-решений:

  • Preact (через preact/jsx-runtime)
  • Solid (через solid-js)
  • React-подобные библиотеки через compatibility layers
  • кастомные renderers

Ключевым фактором остаётся выбор jsxImportSource, определяющий, какие функции будут импортироваться при трансформации.


Частые ошибки конфигурации

Несоответствие runtime

Если используется jsx: 'automatic', но не задан jsxImportSource, результатом становится некорректный импорт или падение сборки.

Неверный jsxFactory

При использовании Preact в classic режиме отсутствие h приводит к runtime error.

Смешивание режимов

Комбинация jsxFactory и jsx: 'automatic' приводит к игнорированию части настроек.


Модель трансформации JSX в Esbuild

Внутренне процесс выглядит следующим образом:

  1. Парсинг JSX-дерева
  2. Определение runtime режима
  3. Замена JSX на вызовы функций
  4. Подстановка импортов (jsxImportSource)
  5. Минификация результата

Эта модель делает Esbuild быстрым по сравнению с многостадийными трансформерами.


Поддержка фрагментов и вложенных выражений

Фрагменты:

<>
  <A />
  <B />
</>

в classic режиме:

Fragment(null, A(), B());

в automatic режиме:

import { Fragment } from "runtime";
jsx(Fragment, {}, A(), B());

Использование JSX в больших приложениях

При масштабировании приложений важную роль играет единообразие runtime:

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

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


Расширенные сценарии интеграции

Микрофронтенды

Разные части приложения могут использовать разные JSX runtime при условии изоляции сборок.

SSR

При серверной сборке JSX трансформируется аналогично, но runtime заменяется на серверные реализации render функций.

Library mode

Esbuild позволяет собирать UI-библиотеки, где JSX остаётся входным синтаксисом, а output содержит только runtime-агностичные вызовы.