Сборка плагина в Wasm

Плагины SWC в WebAssembly опираются на модель компилятора, в которой основная логика трансформации JavaScript/TypeScript кода выносится в изолированный модуль, выполняемый в WASM-окружении. Такой подход позволяет совмещать производительность Rust-экосистемы с переносимостью и безопасностью WebAssembly.

SWC (Speedy Web Compiler) использует Rust как базовый язык реализации, а плагины в WASM становятся дополнительным уровнем расширения, подключаемым во время компиляции. При этом ключевая особенность заключается в том, что плагин не имеет прямого доступа к памяти процесса компилятора, а взаимодействует через сериализованные структуры AST.


Общая модель выполнения WASM-плагина

Плагин в WebAssembly выполняется как изолированный модуль, который получает на вход сериализованное представление AST (Abstract Syntax Tree), выполняет трансформации и возвращает изменённое дерево.

Основные этапы выполнения:

  • сериализация AST в промежуточный формат
  • передача данных в WASM-модуль
  • десериализация внутри плагина
  • трансформация структуры
  • повторная сериализация результата
  • возврат в SWC-пайплайн

Такая схема исключает прямую работу с памятью компилятора и минимизирует риск некорректных состояний.


Базовая структура WASM-плагина

Плагин SWC в WebAssembly обычно строится на основе crate swc_plugin, предоставляющего ABI-слой для взаимодействия с компилятором.

Ключевые компоненты:

  • swc_plugin_macro — генерация экспортируемых функций
  • swc_ecma_ast — описание JavaScript AST
  • swc_ecma_visit — трейты для обхода дерева
  • serde — сериализация данных между host и wasm

Минимальная структура Rust-плагина:

use swc_ecma_ast::*;
use swc_ecma_visit::{VisitMut, VisitMutWith};
use swc_plugin::plugin_transform;

struct TransformVisitor;

impl VisitMut for TransformVisitor {
    fn visit_mut_expr(&mut self, n: &mut Expr) {
        n.visit_mut_children_with(self);
    }
}


pub fn process_transform(program: Program) -> Program {
    let mut v = TransformVisitor;
    let mut program = program;
    program.visit_mut_with(&mut v);
    program
}

Этот код демонстрирует основу: входной Program, проход по дереву и возврат модифицированной структуры.


Сборочная цепочка Rust → WASM

Процесс компиляции плагина включает несколько уровней трансформации исходного Rust-кода.

  1. Компиляция в WebAssembly target

Используется целевая платформа wasm32-unknown-unknown.

Основная команда сборки:

cargo build --target wasm32-unknown-unknown --release

На этом этапе происходит:

  • трансляция Rust IR в WASM bytecode
  • удаление стандартной библиотеки (std заменяется на core)
  • оптимизация через LLVM

  1. Подключение swc_plugin инфраструктуры

Плагин не может работать как обычный WASM-модуль. SWC требует специфический ABI-слой.

Добавление зависимостей:

[dependencies]
swc_plugin = "0.x"
swc_ecma_ast = "0.x"
swc_ecma_visit = "0.x"
serde = { version = "1", features = ["derive"] }

Механизм swc_plugin генерирует экспортируемые функции:

  • обработка входного AST
  • управление памятью
  • сериализация/десериализация

  1. Генерация ABI-обвязки

Макрос #[plugin_transform] создаёт точку входа, совместимую с runtime SWC.

Сгенерированный WASM экспорт включает:

  • функцию инициализации
  • функцию трансформации AST
  • функцию очистки памяти

Это позволяет SWC загружать плагин динамически.


Формат передачи данных между SWC и WASM

Передача данных осуществляется через сериализацию структуры AST в бинарный формат.

Используется схема:

  • Rust AST → serde serialization → byte buffer
  • byte buffer → WASM memory
  • WASM → десериализация обратно в AST

Ключевой момент заключается в том, что WebAssembly не оперирует объектами высокого уровня, поэтому все структуры приводятся к линейной памяти.


Работа с AST внутри плагина

AST SWC описывается через набор структур:

  • Program
  • Module
  • Stmt
  • Expr
  • Pat

Пример обхода выражений:

impl VisitMut for TransformVisitor { fn
visit_mut_call_expr(&mut self, n: &mut CallExpr) { if let
Callee::Expr(expr) = &n.callee { if let Expr::Ident(ident) =
&**expr { if ident.sym == *"console" { n.args.clear(); } } }

n.visit_mut_children_with(self); } }

На уровне WASM-плагина выполняется модификация структуры без аллокаций в JS-окружении, что повышает производительность.


Ограничения WebAssembly-архитектуры

Модель WASM накладывает ряд ограничений на плагины SWC.

Изоляция памяти

Плагин не имеет доступа к внешним структурам компилятора напрямую. Любые данные должны быть сериализованы.

Отсутствие системных вызовов

WASM-окружение не позволяет:

  • обращаться к файловой системе
  • выполнять сетевые операции
  • использовать произвольные системные API

Ограниченная стандартная библиотека

Rust std либо отсутствует, либо заменяется на облегчённый core, что влияет на:

  • работу с коллекциями
  • форматирование строк
  • обработку ошибок

Оптимизация размера и скорости WASM-плагина

Размер WASM-модуля критичен для времени загрузки и инициализации.

Используются следующие техники оптимизации:

RUSTFLAGS="-C lto=fat" cargo build --release --target wasm32-unknown-unknown

Удаление неиспользуемого кода

LLVM удаляет мёртвые функции и структуры, уменьшая бинарный размер.

Минимизация зависимостей

Каждая дополнительная crate увеличивает размер итогового WASM.


Интеграция с SWC runtime

После сборки WASM-плагин загружается SWC runtime и подключается к пайплайну трансформаций.

Схема выполнения:

  1. загрузка WASM модуля
  2. инициализация памяти
  3. передача AST
  4. выполнение process_transform
  5. получение результата
  6. продолжение компиляции

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


Управление памятью в WASM-плагинах

Память в WebAssembly линейная и управляется вручную через allocator, предоставляемый SWC runtime.

Основные принципы:

  • входные данные размещаются в выделенном буфере
  • временные структуры живут внутри вызова transform
  • результат копируется обратно в host

Отсутствие garbage collector требует строгого контроля владения данными, что снижает накладные расходы.


Сериализация AST и стоимость преобразований

Сериализация AST является одной из самых дорогих операций в pipeline.

Факторы влияния:

  • глубина дерева выражений
  • количество узлов
  • наличие сложных конструкций (JSX, TS types)

Оптимизация заключается в:

  • уменьшении объёма передаваемых данных
  • частичной трансформации (node-level filtering)
  • кешировании промежуточных результатов

Безопасность выполнения WASM-плагинов

WebAssembly выступает как sandbox-уровень, ограничивающий возможности плагина.

Обеспечиваются свойства:

  • memory safety
  • отсутствие прямых указателей
  • контроль границ памяти
  • детерминированное выполнение

Это делает SWC безопасной средой для выполнения сторонних трансформаций кода, включая плагины из внешних источников.


Модель расширения компилятора через WASM

Архитектура SWC с WASM-плагинами строится на принципе разделения ответственности:

  • ядро SWC: парсинг, базовые трансформации, генерация кода
  • WASM-плагин: пользовательская логика трансформации AST

Такой подход позволяет:

  • добавлять новые трансформации без перекомпиляции SWC
  • изолировать ошибки плагинов
  • поддерживать мульти-языковую экосистему расширений

Поток данных в полном цикле компиляции

Полный pipeline включает:

  • парсинг JavaScript/TypeScript
  • генерация AST
  • запуск WASM-плагинов
  • применение встроенных трансформаций SWC
  • генерация конечного кода

WASM-этап находится в середине цепочки и играет роль модификатора промежуточного представления.


Особенности отладки WASM-плагинов

Отладка осложняется отсутствием стандартных инструментов Rust в WASM-среде.

Используются подходы:

  • логирование через host callbacks
  • сериализация промежуточных AST-состояний
  • тестирование на уровне вход/выход

Часто применяется изоляция трансформаций в минимальные тестовые кейсы для воспроизводимости поведения.


Производственные сценарии использования

WASM-плагины SWC применяются для:

  • транспиляции нестандартных синтаксических расширений
  • оптимизации AST перед генерацией кода
  • статического анализа кода
  • автоматической модификации импортов и модулей

Такая архитектура особенно эффективна в больших монорепозиториях, где требуется единый компиляторный pipeline с расширяемой логикой трансформаций.