JSX представляет собой синтаксис, позволяющий описывать структуру пользовательского интерфейса в виде декларативного дерева, внешне похожего на XML. На уровне компиляции JSX не является исполняемым JavaScript и требует трансформации в вызовы функций. В контексте SWC (Speedy Web Compiler) эта трансформация реализуется как часть этапа обработки AST и тесно интегрирована с JavaScript/TypeScript парсером.
SWC выполняет преобразование JSX в стандартные JavaScript-выражения с высокой скоростью, сохраняя при этом совместимость с экосистемой React и другими библиотеками, использующими JSX-синтаксис.
Обработка JSX начинается на этапе синтаксического анализа. SWC использует собственный парсер, который поддерживает JSX как часть грамматики JavaScript/TypeScript.
Ключевая настройка включения JSX-парсинга находится в конфигурации
jsc.parser:
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
}
}
}
или для Jav * aScript:
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": true
}
}
}
При включении jsx: true парсер начинает интерпретировать
конструкции вида:
const element = <div className="box">Hello</div>;
как JSXElement AST-узлы вместо обычного сравнения операторов «меньше/больше».
SWC преобразует JSX в вызовы функций, которые создают виртуальные
элементы. Классическая схема трансформации основана на функции
React.createElement.
Пример входного кода:
const element = <button disabled>Click</button>;
После трансформации:
const element = React.createElement(
"button",
{ disabled: true },
"Click"
);
Параметры трансформации определяются настройками:
jsxFactory — функция создания элемента
jsxFragmentFactory — функция для фрагментов
pragma — устаревший аналог для классического JSX
SWC поддерживает два основных режима JSX-трансформации:
Использует явный вызов React.createElement. Требует наличия
React в области видимости.
Конфигурация:
{
"jsc": {
"transform": {
"react": {
"runtime": "classic"
}
}
}
}
Особенности:
Новый механизм трансформации, введённый в React 17+, устраняет необходимость явного импорта React.
{
"jsc": {
"transform": {
"react": {
"runtime": "automatic",
"importSource": "react"
}
}
}
}
В этом режиме SWC генерирует импорт функций из JSX runtime:
import { jsx as _jsx } from "react/jsx-runtime";
const element = _jsx("button", {
disabled: true,
children: "Click"
});
JSX-фрагменты:
<>
<span>A</span>
<span>B</span>
</>
в classic runtime:
React.createElement(
React.Fragment,
null,
React.createElement("span", null, "A"),
React.createElement("span", null, "B")
);
в automatic runtime:
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
_jsx(_Fragment, {
children: [
_jsx("span", { children: "A" }),
_jsx("span", { children: "B" })
]
});
Конфигурация jsc.transform.react управляет поведением
JSX-трансформации:
{
"jsc": {
"transform": {
"react": {
"runtime": "automatic",
"development": false,
"refresh": false,
"throwIfNamespace": true,
"useBuiltins": false
}
}
}
}
Флаг development включает дополнительные проверки и
расширенную отладочную информацию:
Пример:
{
"development": true
}
Контролирует поведение при использовании XML-неймспейсов:
<svg:rect />
При true — выбрасывается ошибка, при false —
допускается обработка.
Определяет источник JSX runtime функций:
{
"importSource": "preact"
}
В этом случае SWC генерирует:
import { jsx } from "preact/jsx-runtime";
SWC поддерживает TSX без дополнительных трансформеров. При использовании TypeScript парсер автоматически расширяет грамматику.
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
}
}
}
Типизация удаляется на этапе компиляции, а JSX остаётся для трансформации.
Пример:
type Props = {
title: string;
};
const Component = (props: Props) => {
return <h1>{props.title}</h1>;
};
После компиляции:
const Component = (props) => {
return _jsx("h1", {
children: props.title
});
};
SWC рекурсивно обходит дерево JSX и преобразует каждый узел.
const tree = (
<div>
<header>
<h1>Title</h1>
</header>
<main>
<article>
<p>Text</p>
</article>
</main>
</div>
);
Результат представляет собой вложенные вызовы функций, где каждый
уровень дерева превращается в аргументы children.
JSX поддерживает встроенные JavaScript-выражения:
const value = 10;
const el = <div>{value * 2}</div>;
SWC преобразует выражения в аргументы children:
const value = 10;
const el = _jsx("div", {
children: value * 2
});
Для массивов:
<ul>
{items.map(i => <li>{i}</li>)}
</ul>
преобразуется в:
_jsx("ul", {
children: items.map(i => _jsx("li", {
children: i
}))
});
SWC не выполняет React-специфическую оптимизацию ключей, но сохраняет
key как обычное свойство:
const list = items.map(i => <div key={i.id}>{i.name}</div>);
Трансформация:
items.map(i =>
_jsx("div", {
key: i.id,
children: i.name
})
);
Поведение SWC в JSX-трансформации близко к Babel, но отличается реализацией:
Babel чаще использует последовательность плагинов, SWC — монолитный трансформер.
SWC применяется как транспайлер в различных окружениях:
swc-loader)
@swc/cli)
Пример использования в webpack:
module.exports = {
module: {
rules: [
{
test: /\.[jt]sx?$/,
use: {
loader: "swc-loader",
options: {
jsc: {
parser: {
syntax: "typescript",
tsx: true
},
transform: {
react: {
runtime: "automatic"
}
}
}
}
}
}
]
}
};
SWC поддерживает генерацию source maps для JSX-кода, сохраняя соответствие между исходным JSX и итоговым JavaScript.
{
"sourceMaps": true,
"jsc": {
"transform": {
"react": {
"runtime": "automatic"
}
}
}
}
Source maps особенно важны при отладке вложенных JSX-структур и динамических выражений.
В режиме минификации SWC может:
development: false
Пример оптимизации:
<div><span /></div>
может быть преобразован в минимизированный вызов без промежуточных идентификаторов:
_jsx("div", { children: _jsx("span", {}) });
JSX-компоненты с пользовательскими функциями трансформируются аналогично HTML-тегам:
const App = () => <Header />;
const App = () => _jsx(Header, {});
SWC не различает функциональные и классовые компоненты на уровне трансформации — различие проявляется только во время исполнения.