Relay через SWC

## Relay через SWC ### Назначение Relay и роль SWC в процессе компиляции Relay представляет собой экосистему для работы с GraphQL, разработанную компанией [Meta](https://meta.com?utm_source=chatgpt.com). Основная идея Relay заключается в тесной интеграции компонентов пользовательского интерфейса и GraphQL-запросов, что позволяет автоматически оптимизировать получение данных, обеспечивать типобезопасность и повышать производительность клиентских приложений. В классической архитектуре Relay важную роль играет компилятор Relay Compiler, который анализирует исходный код приложения, извлекает GraphQL-запросы и генерирует дополнительные артефакты. Эти артефакты используются во время выполнения для нормализации данных, кэширования и статической проверки запросов. SWC способен выступать частью этой цепочки преобразований. Благодаря высокой скорости работы и архитектуре на языке Rust библиотека позволяет выполнять трансформации JavaScript и TypeScript значительно быстрее традиционных решений на базе Babel. Для проектов, использующих Relay, SWC может брать на себя задачи обработки исходного кода и интегрироваться с Relay Compiler, обеспечивая быстрые сборки даже для крупных приложений. --- ## Архитектура Relay-компиляции Типичный процесс работы Relay выглядит следующим образом: 1. Разработчик описывает GraphQL-фрагменты внутри компонентов. 2. Relay Compiler анализирует исходный код. 3. Из исходников извлекаются GraphQL-операции. 4. Выполняется проверка схемы GraphQL. 5. Генерируются артефакты Relay. 6. SWC компилирует приложение в конечный JavaScript-код. Схема обработки: ```text Исходный код React │ ▼ Relay Compiler │ ├── Проверка GraphQL ├── Генерация типов └── Генерация артефактов │ ▼ SWC │ ▼ Скомпилированный код ``` В современных проектах Relay и SWC обычно работают совместно, а не заменяют друг друга. --- ## Relay-трансформация в SWC Одной из особенностей Relay является использование специального тега `graphql`. Пример: ```jsx import { graphql } from 'react-relay'; const UserFragment = graphql` fragment UserFragment on User { id name email } `; ``` В исходном коде присутствует строковый GraphQL-документ, который необходимо заменить ссылкой на сгенерированный артефакт. После прохождения Relay-компиляции код может выглядеть примерно так: ```js const UserFragment = require('./__generated__/UserFragment.graphql'); ``` Подобная замена существенно уменьшает объем работы во время выполнения приложения. SWC способен выполнять такие преобразования через специальные плагины. --- ## Установка Relay Compiler Для работы Relay необходимы соответствующие зависимости: ```bash npm install react-relay relay-runtime npm install --save-dev relay-compiler ``` Либо: ```bash yarn add react-relay relay-runtime yarn add -D relay-compiler ``` После установки становится доступной команда: ```bash relay-compiler ``` --- ## Конфигурация Relay Базовая конфигурация может быть описана в файле: ```json { "src": "./src", "schema": "./schema.graphql", "artifactDirectory": "./src/__generated__", "language": "typescript" } ``` Распространённый вариант размещения конфигурации: ```js module.exports = { src: "./src", schema: "./schema.graphql", artifactDirectory: "./src/__generated__", language: "typescript" }; ``` Основные параметры: | Параметр | Назначение | | ----------------- | ------------------------------ | | src | Корневая директория исходников | | schema | GraphQL-схема | | artifactDirectory | Папка генерации файлов | | language | JavaScript или TypeScript | | excludes | Исключённые каталоги | | eagerEsModules | Генерация ES-модулей | --- ## Подключение SWC Установка: ```bash npm install -D @swc/core @swc/cli ``` Базовый конфигурационный файл: ```json { "jsc": { "parser": { "syntax": "typescript", "tsx": true }, "target": "es2022" }, "module": { "type": "es6" } } ``` Relay и SWC работают независимо: ```bash relay-compiler swc src -d dist ``` Сначала создаются артефакты Relay, затем выполняется компиляция приложения. --- ## Использование SWC-плагинов для Relay Плагин SWC получает доступ к AST программы и способен искать вызовы `graphql`. Исходный код: ```jsx graphql` query UserQuery { viewer { id } } ` ``` AST-узел будет представлен как Tagged Template Expression. Упрощённая схема: ```text TaggedTemplateExpression ├── tag: graphql └── template ``` Плагин может: 1. Найти тег `graphql`. 2. Извлечь GraphQL-документ. 3. Передать его Relay Compiler. 4. Заменить выражение импортом артефакта. --- ## Пример логики SWC-плагина Псевдокод обхода дерева: ```rust impl VisitMut for RelayTransformer { fn visit_mut_tagged_tpl( &mut self, tagged: &mut TaggedTpl ) { if is_graphql_tag(tagged) { replace_with_generated_artifact(tagged); } } } ``` Такой подход аналогичен работе Babel Relay Plugin, но выполняется значительно быстрее благодаря инфраструктуре SWC. --- ## Генерация артефактов Relay Для каждого запроса Relay создаёт отдельный файл. Исходный запрос: ```jsx const query = graphql` query UserProfileQuery($id: ID!) { user(id: $id) { id name } } `; ``` Сгенерированный артефакт: ```js UserProfileQuery.graphql.js ``` Внутри находятся: * описание операции; * информация о типах; * структура фрагментов; * данные для нормализации ответа; * параметры запроса. Файл может содержать сотни строк служебного кода. --- ## Работа с фрагментами Relay активно использует фрагменты. Пример: ```jsx const fragment = graphql` fragment UserCard_user on User { id name avatar } `; ``` Фрагменты позволяют: * переиспользовать запросы; * локализовать требования к данным; * автоматически объединять запросы; * избегать дублирования GraphQL-кода. Relay Compiler генерирует отдельные артефакты для каждого фрагмента. SWC затем заменяет исходные шаблоны ссылками на эти артефакты. --- ## Интеграция с React Типичный компонент Relay: ```tsx import { graphql, useFragment } from 'react-relay'; const userFragment = graphql` fragment UserInfo_user on User { id name } `; export function UserInfo(props) { const user = useFragment( userFragment, props.user ); return
{user.name}
; } ``` После обработки: 1. Relay Compiler создаёт артефакт. 2. SWC компилирует JSX. 3. В итоговом бандле остаются ссылки на готовые структуры данных. --- ## Оптимизация размера бандла Relay через SWC позволяет добиться нескольких важных оптимизаций. ### Исключение строк GraphQL Исходный код: ```jsx graphql` query Example { viewer { id } } `; ``` После трансформации длинная строка GraphQL исчезает из итогового бандла. Преимущества: * уменьшение размера сборки; * ускорение парсинга JavaScript; * снижение потребления памяти. ### Tree Shaking SWC способен удалять неиспользуемые модули. Пример: ```js import UserQuery from './UserQuery.graphql'; ``` Если импорт нигде не применяется: ```js const unused = UserQuery; ``` и код впоследствии удаляется, артефакт может не попасть в финальную сборку. --- ## Совместная работа с TypeScript Relay хорошо сочетается с TypeScript. Фрагмент: ```tsx const fragment = graphql` fragment User_user on User { id name } `; ``` Генерируется тип: ```ts export type User_user$data = { readonly id: string; readonly name: string; }; ``` Компонент: ```tsx import { User_user$data } from './__generated__/User_user.graphql'; type Props = { user: User_user$data; }; ``` SWC компилирует TypeScript без необходимости запуска полноценного компилятора TypeScript для генерации JavaScript-кода. --- ## Watch-режим Relay и SWC Во время разработки часто используются два параллельных процесса. Relay: ```bash relay-compiler --watch ``` SWC: ```bash swc src -d dist --watch ``` При изменении GraphQL-запроса происходит: ```text Изменение файла │ ▼ Relay Watch │ ▼ Новый артефакт │ ▼ SWC Watch │ ▼ Пересборка ``` Такой подход обеспечивает практически мгновенную обратную связь даже в больших проектах. --- ## Интеграция с Next.js Многие современные версии Next.js используют SWC в качестве основного компилятора. В проектах с Relay процесс обычно выглядит следующим образом: ```text React │ ▼ Relay Compiler │ ▼ Next.js SWC │ ▼ Production Bundle ``` Relay отвечает за GraphQL-инфраструктуру, а SWC выполняет: * трансформацию JSX; * обработку TypeScript; * минификацию; * tree shaking; * генерацию production-кода. --- ## Интеграция с Vite При использовании Vite возможна следующая конфигурация: ```bash npm install vite @swc/core ``` Relay-компилятор запускается отдельно: ```json { "scripts": { "relay": "relay-compiler", "dev": "vite", "build": "vite build" } } ``` Во многих проектах запускается дополнительный процесс: ```bash npm run relay -- --watch ``` Это обеспечивает постоянную актуальность артефактов. --- ## Кэширование Relay-артефактов Одним из преимуществ Relay является возможность переиспользования ранее сгенерированных файлов. Если запрос не изменился: ```graphql query ViewerQuery { viewer { id } } ``` Relay может пропустить повторную генерацию соответствующего артефакта. В сочетании с быстрыми трансформациями SWC это значительно сокращает время инкрементальной сборки. --- ## Отладка Relay-трансформаций При возникновении проблем полезно проверять несколько этапов. Проверка генерации артефактов: ```bash relay-compiler ``` Проверка структуры каталога: ```text src ├─ components ├─ pages └─ __generated__ ``` Проверка наличия файла: ```text UserProfileQuery.graphql.ts ``` Проверка корректности схемы: ```graphql type Query { viewer: User } ``` Ошибки схемы выявляются Relay Compiler ещё до этапа компиляции SWC. --- ## Производительность Relay через SWC Использование SWC даёт Relay-проектам ряд преимуществ: * быстрое преобразование TypeScript; * высокая скорость обработки JSX; * минимальные накладные расходы на трансформации; * эффективная работа в watch-режиме; * сокращение времени CI/CD-сборок; * уменьшение нагрузки на процессор во время разработки. Особенно заметна разница в крупных монорепозиториях, содержащих сотни GraphQL-фрагментов и десятки тысяч строк React-кода, где Relay Compiler отвечает за генерацию артефактов, а SWC обеспечивает максимально быструю компиляцию клиентского приложения.