Настройка pragma для JSX

JSX не является нативным синтаксисом JavaScript. Он преобразуется в вызовы функций во время сборки, и именно здесь появляется понятие JSX pragma — инструкции, определяющей, какая функция должна использоваться для создания элементов.

По умолчанию классический JSX трансформируется в:

React.createElement("div", null, "text");

Однако эта функция не является обязательной. JSX может компилироваться в любые вызовы, если задать соответствующий pragma.

Базовый механизм JSX трансформации

Любой JSX-код:

const element = <h1>Hello</h1>;

после трансформации становится функцией:

const element = h("h1", null, "Hello");

Имя функции h, React.createElement или любое другое определяется настройками трансформации.

JSX pragma — это способ явно указать трансформеру, какую функцию использовать.

Классический pragma в комментарии

Исторически JSX pragma задавался прямо в файле через комментарий:

/** @jsx h */

После этого JSX компилируется в вызовы h():

/** @jsx h */

const title = <h1>Hello</h1>;

Результат:

const title = h("h1", null, "Hello");

Этот подход до сих пор поддерживается в некоторых трансформерах, но считается устаревшим в современных сборках.

JSX pragma и Parcel

В сборщике Parcel JSX трансформация выполняется через Babel или встроенные трансформеры (в зависимости от конфигурации проекта). Parcel не требует ручной настройки JSX pragma в большинстве стандартных React-проектов, однако поддерживает его при кастомных конфигурациях.

Parcel автоматически определяет среду и применяет React JSX transform:

  • для React 17+ используется новый automatic runtime
  • для старых проектов — classic runtime

Разница принципиальна:

Classic runtime

React.createElement("div", null);

Требует:

import React from "react";

или явно заданного pragma.

Automatic runtime

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

jsx("div", {});

React импортируется автоматически трансформером.

Настройка JSX pragma через Babel в Parcel

Parcel позволяет подключать Babel-конфигурацию через .babelrc или babel.config.json.

Пример настройки custom pragma:

{
  "plugins": [
    ["@babel/plugin-transform-react-jsx", {
      "pragma": "h",
      "pragmaFrag": "Fragment"
    }]
  ]
}

После этого JSX:

const app = <div>text</div>;

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

const app = h("div", null, "text");

Настройка фрагментов

Фрагменты также можно переназначить:

{
  "pragmaFrag": "Fragment"
}

или даже:

{
  "pragmaFrag": "h.Fragment"
}

TypeScript и JSX pragma в Parcel

Если используется TypeScript, JSX pragma управляется через tsconfig.json.

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

{
  "compilerOptions": {
    "jsx": "react",
    "jsxFactory": "h",
    "jsxFragmentFactory": "Fragment"
  }
}

React 17+ стиль

{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

или

{
  "jsx": "react-jsxdev"
}

В этом режиме pragma не требуется, поскольку используется автоматический runtime.

Кастомные JSX runtime (Preact, Emotion и др.)

JSX pragma особенно важен при работе с альтернативными библиотеками.

Preact

Для Preact часто используется:

{
  "pragma": "h",
  "pragmaFrag": "Fragment"
}

или через Babel:

{
  "plugins": [
    ["@babel/plugin-transform-react-jsx", {
      "pragma": "h",
      "pragmaFrag": "Fragment"
    }]
  ]
}

Emotion (css-in-js)

Emotion использует собственный JSX runtime:

{
  "plugins": [
    ["@emotion/babel-plugin-jsx-pragmatic", {
      "export": "jsx",
      "import": "__css",
      "module": "@emotion/react"
    }]
  ]
}

После этого JSX начинает учитывать стилизации на этапе трансформации.

Automatic runtime и отключение pragma

Современные версии React и Parcel ориентированы на автоматический runtime, где JSX превращается в:

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

и pragma больше не нужен.

Для включения:

{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

или Babel:

{
  "runtime": "automatic"
}

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

Parcel сам по себе не требует прямой настройки pragma, но его поведение зависит от следующих факторов:

  • наличие Babel-конфигурации
  • наличие TypeScript
  • версия React
  • выбранный JSX runtime

Parcel выполняет:

  1. анализ исходного файла
  2. определение JSX
  3. применение соответствующего трансформера
  4. генерацию JavaScript AST
  5. оптимизацию и бандлинг

При отсутствии пользовательских настроек используется дефолтная стратегия React automatic runtime.

Совместимость различных pragma стратегий

Стратегия JSX трансформация Требует React import Современность
Classic pragma React.createElement да устаревшая
Custom pragma h() / custom fn зависит средняя
Automatic runtime jsx() из runtime нет современная

Частые ошибки при настройке pragma

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

Если установлен automatic runtime, но Babel настроен на pragma, возникает конфликт:

  • JSX компилируется дважды разными системами
  • появляются дубли импортов
  • ломается сборка

Смешивание TypeScript jsx и Babel pragma

Если в tsconfig.json стоит:

"jsx": "react-jsx"

а Babel задаёт:

"pragma": "h"

TypeScript уже трансформирует JSX до Babel, и pragma игнорируется.

Отсутствие импортов при classic runtime

При использовании classic режима требуется:

import React from "react";

иначе код не соберётся, даже если JSX pragma задан.

Гибридные сценарии

Parcel позволяет использовать гибридные конфигурации:

  • TypeScript выполняет первичную трансформацию
  • Babel добавляет кастомные pragma
  • Parcel выполняет финальную оптимизацию

Такой подход применяется в библиотеках UI-фреймворков и design systems, где требуется полный контроль над JSX деревом.

Практическая модель выбора pragma

Выбор стратегии зависит от архитектуры:

  • стандартный React-проект — automatic runtime
  • библиотека компонентов — custom pragma
  • Preact-проекты — h pragma
  • CSS-in-JS с глубокой интеграцией — специализированный pragma

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