Трансформация JSX в production и development

JSX в экосистеме JavaScript рассматривается не как самостоятельный язык, а как синтаксическое расширение, которое транслируется в вызовы функций создания элементов. В контексте SWC преобразование JSX выполняется на этапе компиляции и напрямую влияет на поведение кода в development и production-сборках.

Базовая задача трансформации заключается в превращении декларативного JSX в эквивалентные выражения JavaScript с учётом выбранного JSX runtime, режима сборки и целевой среды выполнения.

Основные входные формы JSX:

  • Classic runtime: React.createElement(…)
  • Automatic runtime (React 17+): импортируемые функции jsx, jsxs, jsxDEV
  • Пользовательские фабрики элементов

Выходная форма зависит от конфигурации трансформера и окружения, в котором выполняется сборка.


Архитектура JSX-трансформации в SWC

JSX-пайплайн в SWC разделяется на несколько стадий:

  • парсинг JSX-узлов в AST
  • нормализация выражений и атрибутов
  • выбор runtime (classic или automatic)
  • генерация вызовов функций
  • внедрение dev-специфичных обёрток (при необходимости)

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


Automatic runtime и влияние на production-сборку

Automatic runtime изменяет структуру выходного кода JSX. Вместо глобального объекта React используются импортируемые функции.

Пример входного JSX:

const el = <Button disabled={true}>Text</Button>;

Production-преобразование

В production-режиме SWC генерирует компактный вызов:

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

const el = _jsx(Button, {
  disabled: true,
  children: "Text"
});

Особенности production-режима:

  • отсутствуют проверки типов пропсов
  • отсутствуют dev-only ветки
  • не включаются дополнительные метаданные
  • минимизация структуры объекта props
  • агрессивное упрощение литералов

Дополнительно SWC может инлайнить JSX calls, если включена минификация.


Development runtime и расширенная семантика JSX

Development-режим ориентирован на диагностику, отладку и интеграцию с fast refresh.

Тот же JSX в development может быть преобразован в:

import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";

const el = _jsxDEV(Button, {
  disabled: true,
  children: "Text"
}, undefined, false, {
  fileName: "Component.jsx",
  lineNumber: 3,
  columnNumber: 11
}, this);

Дополнительные параметры development-runtime

  • fileName — источник модуля
  • lineNumber, columnNumber — позиция JSX-узла
  • this-контекст для legacy-совместимости
  • dev-flag для включения дополнительных проверок

Эти данные используются для:

  • улучшенных stack trace
  • предупреждений React runtime
  • интеграции с React Fast Refresh
  • локализации ошибок

Различия поведения между development и production

Структурные различия AST-генерации

Production:

  • минимальный набор аргументов
  • отсутствие метаданных
  • сокращённые runtime-вызовы

Development:

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

Различия в обработке children

JSX children обрабатываются по-разному в зависимости от режима:

  • одиночный child может быть передан как прямое значение
  • множественные children агрегируются в массив
  • в development добавляются проверки валидности типов children

Различия в оптимизациях

Production-пайплайн включает:

  • folding строковых конкатенаций внутри JSX
  • упрощение boolean-атрибутов
  • удаление неизменяемых выражений
  • агрессивную нормализацию props

Development-пайплайн:

  • сохраняет структуру выражений для диагностики
  • избегает опасных преобразований, влияющих на читаемость stack trace

Конфигурация JSX-трансформации в SWC

Основные параметры конфигурации задаются через jsc.transform.react.

Режим runtime

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

Возможные значения:

  • classic
  • automatic

Development mode

{
  "jsc": {
    "transform": {
      "react": {
        "development": true
      }
    }
  }
}

Включает:

  • jsxDEV генерацию
  • метаданные исходного кода
  • дополнительные проверки runtime

importSource

Используется для кастомных JSX runtimes:

{
  "jsc": {
    "transform": {
      "react": {
        "runtime": "automatic",
        "importSource": "@emotion/react"
      }
    }
  }
}

В этом случае SWC генерирует:

import { jsx } from "@emotion/react/jsx-runtime";

Интеграция JSX-трансформации с минификацией

JSX-трансформация тесно связана с минификатором SWC.

В production pipeline:

  • JSX сначала трансформируется в JS-вызовы
  • затем применяется minify pass

Минификация дополнительно влияет на JSX output:

  • сокращение идентификаторов компонентов
  • инлайнинг простых JSX-структур
  • устранение промежуточных переменных

Обработка условного JSX в production и development

Production

{isActive && <Modal />}

Трансформируется в:

isActive && _jsx(Modal, {});

Дополнительно:

  • удаляются недостижимые ветви при compile-time inference
  • упрощаются boolean выражения

Development

isActive && _jsxDEV(Modal, {}, undefined, false, { ...meta }, this);

Сохраняются:

  • диагностические метки
  • информация об источнике выражения

JSX Fragment трансформация

Fragments имеют отдельную стратегию генерации.

Production:

<>Hello</>
import { Fragment as _Fragment } from "react/jsx-runtime";

_jsx(_Fragment, {
  children: "Hello"
});

Development:

_jsxDEV(_Fragment, {
  children: "Hello"
}, undefined, false, meta, this);

Влияние режима сборки на Fast Refresh

Fast Refresh зависит от стабильности JSX output.

Development mode обеспечивает:

  • детерминированные идентификаторы компонентов
  • сохранение state при HMR
  • привязку к file-level boundaries

Production mode полностью исключает:

  • runtime-refresh метки
  • dev-only инъекции
  • HMR-совместимые сигнатуры

JSX и Tree Shaking в SWC pipeline

Хотя JSX напрямую не отвечает за tree shaking, его трансформация влияет на возможности удаления кода:

  • production runtime генерирует чистые функции вызова
  • отсутствуют побочные dev-объекты
  • упрощается анализ зависимостей

Особенно важно при использовании automatic runtime, где импорт функций становится статически анализируемым.


Специфика обработки spread-атрибутов

JSX spread:

<Button {...props} disabled />

Production:

  • spreads остаются неизменными, если не включены дополнительные оптимизации
  • порядок merge сохраняется

Development:

  • добавляются проверки корректности props (в зависимости от runtime)
  • сохраняется структура для диагностики конфликтов

Оптимизационные различия при компиляции вложенных JSX

Вложенные конструкции:

<div>
  <Header title={<Title />} />
</div>

Production трансформация:

  • минимизация промежуточных объектов
  • инлайн JSX-выражений при возможности

Development:

  • сохранение вложенной структуры вызовов
  • расширенные метаданные для каждой вложенной JSX-единицы

Роль JSX-трансформации в общей производительности сборки

JSX pipeline в SWC характеризуется:

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

Разница между development и production режимами проявляется не только в результате генерации кода, но и в стоимости самой трансформации: development pipeline включает дополнительные шаги генерации метаданных, что увеличивает объём работы компилятора, тогда как production стремится к минимальному числу операций преобразования.


Совместимость JSX runtime с экосистемой React

SWC поддерживает несколько поколений JSX runtime:

  • legacy React.createElement
  • React 17 automatic runtime
  • кастомные runtimes через importSource

Это обеспечивает совместимость:

  • с устаревшими проектами
  • с современными React 18+ приложениями
  • с альтернативными UI-фреймворками, использующими JSX-подобный синтаксис