## 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 обеспечивает максимально быструю компиляцию клиентского приложения.