Настройка pragmas: jsxFactory, jsxFragmentFactory

JSX в процессе компиляции превращается в обычные вызовы функций. Каждое JSX-выражение транслируется в так называемую JSX factory — функцию, которая создаёт виртуальные элементы. Конфигурация этой фабрики определяет, во что именно будет преобразован JSX-код: в React.createElement, в h (как в Vue/Preact-подобных реализациях) или в кастомную функцию.

В SWC эта логика управляется через настройки трансформации React в секции jsc.transform.react. Хотя в экосистеме TypeScript используются параметры jsxFactory и jsxFragmentFactory, в SWC им соответствуют pragma и pragmaFrag, а также режим runtime.


jsxFactory: концепция и соответствие в SWC

Параметр jsxFactory в TypeScript задаёт функцию, которая будет использоваться для преобразования JSX-элементов.

Пример TypeScript-конфигурации:

{
  "compilerOptions": {
    "jsxFactory": "h"
  }
}

JSX:

const el = <div className="box">Hello</div>;

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

const el = h("div", { className: "box" }, "Hello");

Эквивалент в SWC

В SWC аналогичная настройка задаётся через:

{
  "jsc": {
    "transform": {
      "react": {
        "pragma": "h"
      }
    }
  }
}

Значение pragma полностью заменяет jsxFactory в режиме classic.

Поведение pragma

pragma определяет базовую функцию, которая вызывается для каждого JSX-элемента, за исключением фрагментов. Она принимает три основных аргумента:

  • тип элемента (“div”, “span” или компонент)
  • props
  • children

Пример результата трансформации:

h("button", { disabled: true }, "Click");

jsxFragmentFactory: роль и трансформация фрагментов

Фрагменты JSX:

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

В TypeScript параметр jsxFragmentFactory задаёт функцию, создающую фрагменты без DOM-обёртки:

{
  "compilerOptions": {
    "jsxFragmentFactory": "Fragment"
  }
}

Результат трансформации:

Fragment(null, [A, B]);

Эквивалент jsxFragmentFactory в SWC

В SWC эта роль выполняется параметром pragmaFrag:

{
  "jsc": {
    "transform": {
      "react": {
        "pragma": "h",
        "pragmaFrag": "Fragment"
      }
    }
  }
}

Поведение pragmaFrag

pragmaFrag определяет функцию, которая используется исключительно для JSX-фрагментов.

Пример трансформации:

<>
  <Item />
  <Item />
</>

После компиляции:

Fragment(null, [
  Item(null),
  Item(null)
]);

Режим classic в SWC и связь с фабриками

SWC поддерживает два основных режима JSX-трансформации:

  • classic — используется pragma и pragmaFrag
  • automatic — используется новый JSX runtime без явных фабрик

Конфигурация classic:

{
  "jsc": {
    "transform": {
      "react": {
        "runtime": "classic",
        "pragma": "h",
        "pragmaFrag": "Fragment"
      }
    }
  }
}

В этом режиме весь JSX-код зависит от указанных фабрик. Любое изменение pragma напрямую влияет на итоговый JavaScript.


Режим automatic и игнорирование jsxFactory

В режиме automatic концепции jsxFactory и jsxFragmentFactory фактически перестают использоваться.

{
  "jsc": {
    "transform": {
      "react": {
        "runtime": "automatic"
      }
    }
  }
}

JSX:

const el = <div>Hello</div>;

Преобразуется в:

import { jsx as _jsx } from "react/jsx-runtime";

const el = _jsx("div", { children: "Hello" });

Фрагменты:

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

Преобразуются в:

import { Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";

_jsxs(_Fragment, { children: [A, B] });

В этом режиме:

  • pragma игнорируется
  • pragmaFrag игнорируется
  • фабрики заменяются импортами из runtime

Сопоставление TypeScript и SWC

TypeScript SWC (classic) Назначение
jsxFactory pragma функция создания элементов
jsxFragmentFactory pragmaFrag функция создания фрагментов

Ключевое отличие состоит в том, что SWC централизует управление через jsc.transform.react, а TypeScript распределяет настройки по compilerOptions.


Кастомные фабрики и гиперскрипт-подход

Использование нестандартных фабрик позволяет полностью заменить модель JSX.

Пример гиперскрипт-функции:

function h(type, props, ...children) {
  return {
    type,
    props: props || {},
    children
  };
}

SWC-конфигурация:

{
  "jsc": {
    "transform": {
      "react": {
        "runtime": "classic",
        "pragma": "h",
        "pragmaFrag": "Fragment"
      }
    }
  }
}

JSX:

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

Результат:

h(
  "div",
  null,
  h("span", null, "1"),
  h("span", null, "2")
);

Такой подход используется в лёгких UI-библиотеках, где React не нужен, но JSX синтаксис сохраняется.


Особенности поведения props и children

При использовании pragma SWC придерживается следующих правил трансформации:

  • одиночный child передаётся как аргумент
  • несколько children собираются в массив
  • null и undefined могут быть опущены в зависимости от оптимизации
  • props всегда передаются вторым аргументом, даже если пустые

Пример:

<Button disabled />

Результат:

h("Button", { disabled: true });

Влияние типов на трансформацию JSX

Хотя SWC является транспайлером, он учитывает синтаксическую структуру JSX:

  • Intrinsic elements (div, span) передаются как строки
  • Components (MyComponent) передаются как идентификаторы
  • Fragments всегда используют pragmaFrag

Пример различия:

<div />
<MyComponent />

Результат:

h("div", null);
h(MyComponent, null);

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

Несовпадение runtime и pragma

Если установлен runtime: automatic, но задан pragma, он будет проигнорирован, что приводит к ложному ощущению «неработающей конфигурации».

Отсутствие pragmaFrag

При использовании фрагментов без pragmaFrag возможны ошибки компиляции или fallback на дефолтный Fragment из React (если он подразумевается окружением).

Несовместимость с кастомными UI-фреймворками

При интеграции с Preact, Solid-подобными системами или собственными runtime важно синхронизировать:

  • имя фабрики
  • сигнатуру функции
  • формат children (массив или variadic args)

Особенности оптимизации при трансформации

SWC может выполнять дополнительные оптимизации:

  • инлайнинг статических children
  • свёртка пустых props в null
  • устранение лишних обёрток в фрагментах при одном элементе
  • упрощение React.createElement-подобных вызовов до прямых фабрик

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


Практическая модель выбора между jsxFactory и jsxFragmentFactory в SWC

При проектировании трансформации ключевым становится выбор архитектурного режима:

  • classic + pragma + pragmaFrag — полный контроль над фабриками, максимальная гибкость
  • automatic — минимальная конфигурация, зависимость от JSX runtime

Фактически jsxFactory и jsxFragmentFactory в SWC существуют как концептуальная совместимость с TypeScript, но реализуются через pragma-модель, что делает конфигурацию более унифицированной внутри компилятора.