Передача опций в плагин

Модель конфигурации плагинов в SWC

Архитектура плагинов в SWC построена вокруг строгой сериализации конфигурации: любые параметры, передаваемые в плагин, приводятся к JSON-совместимому виду и проходят через единый слой инициализации трансформации. Это означает, что границы между JavaScript-кодом и Rust-реализацией плагина определяются форматом входных данных, а не динамическими объектами исполнения.

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

Базовая структура передачи опций

В большинстве сценариев плагины подключаются через объект конфигурации трансформации:

{
  "jsc": {
    "transform": {},
    "experimental": {
      "plugins": []
    }
  }
}

Каждый элемент массива plugins описывает отдельный плагин и его параметры:

{
  "jsc": {
    "experimental": {
      "plugins": [
        ["plugin-path-or-name", { "optionA": true, "optionB": 123 }]
      ]
    }
  }
}

Первый элемент массива определяет идентификатор плагина (путь к WASM-модулю или зарегистрированное имя), второй — объект опций, сериализуемый в JSON.

Сериализация опций и ограничения типов

Опции плагина проходят через строгий JSON-слой. Это накладывает ограничения на допустимые типы данных:

  • поддерживаются строки, числа, булевы значения;
  • массивы должны содержать сериализуемые элементы;
  • объекты обязаны быть плоскими или рекурсивно JSON-совместимыми;
  • функции, классы и символы исключаются из модели передачи.

Подобная модель обеспечивает совместимость между JavaScript runtime и Rust-исполнением, где конфигурация десериализуется в структуры с фиксированной схемой.

Особое значение имеет стабильность типов: любое несоответствие схеме приводит к ошибке инициализации плагина ещё до этапа трансформации AST.

Передача опций через @swc/core API

При использовании программного API через пакет @swc/core конфигурация передаётся напрямую в функцию трансформации:

import { transform } from "@swc/core";

const output = await transform(sourceCode, {
  jsc: {
    parser: {
      syntax: "ecmascript"
    },
    experimental: {
      plugins: [
        [
          "/path/to/plugin.wasm",
          {
            debug: false,
            level: 2,
            prefix: "log"
          }
        ]
      ]
    }
  }
});

В этом случае объект конфигурации проходит два этапа обработки:

  1. сериализация в промежуточный формат SWC;
  2. десериализация в Rust-структуры внутри компилятора.

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

Передача сложных конфигураций

При увеличении сложности плагина возникает необходимость группировки параметров. Для этого используется вложенная структура:

[
  "plugin.wasm",
  {
    "featureFlags": {
      "minify": true,
      "inline": false
    },
    "rules": {
      "consoleRemoval": {
        "enabled": true,
        "exclude": ["error", "warn"]
      }
    }
  }
]

Такая форма позволяет формировать декларативные схемы поведения плагина, при этом SWC не накладывает ограничений на глубину вложенности, пока структура остаётся JSON-валидной.

Поведение при отсутствии опций

Если второй элемент массива отсутствует, плагин получает пустую конфигурацию:

[
  "plugin.wasm"
]

Эквивалентом становится пустой объект {}. Это поведение важно для плагинов, имеющих дефолтные настройки и не требующих явной инициализации параметров.

Конфликты и приоритеты конфигураций

При построении цепочки трансформаций несколько плагинов могут использовать пересекающиеся ключи конфигурации. Однако SWC не выполняет слияние опций между плагинами. Каждый плагин изолирован и получает только свой объект параметров.

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

Опции и WASM-плагины

WASM-плагины используют дополнительный слой десериализации. Передача параметров осуществляется через линейную память модуля, а структура данных преобразуется в компактный бинарный формат.

Типичный процесс включает:

  • преобразование JSON в бинарное представление;
  • передачу через host-binding слой;
  • восстановление структуры в Rust-коде плагина.

Такой подход минимизирует накладные расходы на сериализацию при повторных вызовах трансформации.

Типизация опций на уровне TypeScript

При использовании SWC в TypeScript-окружении конфигурация плагинов часто типизируется вручную:

type PluginOptions = {
  debug: boolean;
  level: number;
  prefix?: string;
};

Однако эта типизация существует только на стороне конфигуратора. SWC не выполняет проверку типов в runtime, полагаясь исключительно на корректность JSON-структуры.

Динамическая генерация конфигураций

Конфигурации плагинов часто формируются программно:

const createPluginConfig = (env) => [
  "plugin.wasm",
  {
    debug: env !== "production",
    level: env === "production" ? 0 : 2
  }
];

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

Передача опций через .swcrc

Файл .swcrc остаётся основным способом декларативного описания конфигурации:

{
  "jsc": {
    "experimental": {
      "plugins": [
        [
          "plugin.wasm",
          {
            "removeLogs": true
          }
        ]
      ]
    }
  }
}

При этом SWC загружает конфигурацию на этапе инициализации процесса компиляции, а затем кэширует разобранную структуру до завершения процесса.

Особенности обновления конфигурации

Конфигурация плагина не является реактивной. Изменение объекта опций во время выполнения не влияет на уже загруженный экземпляр плагина. Для применения новых параметров требуется повторная инициализация трансформации.

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

Расширенные сценарии передачи данных

В сложных плагинах применяется косвенная передача данных через конфигурацию:

  • передача путей к внешним ресурсам;
  • указание списков файлов;
  • описание правил трансформации AST;
  • включение режимов оптимизации.

Пример расширенной конфигурации:

[
  "plugin.wasm",
  {
    "mode": "aggressive",
    "targets": ["es2019", "browser"],
    "excludePatterns": ["**/*.test.js"],
    "metadata": {
      "sourceMap": true,
      "preserveComments": false
    }
  }
]

Такая структура превращает плагин в параметризуемый модуль, поведение которого определяется исключительно входными данными.

Ограничения архитектуры передачи опций

Модель конфигурации SWC накладывает несколько фундаментальных ограничений:

  • отсутствие произвольного кода в конфигурации;
  • невозможность динамического исполнения функций;
  • жёсткая зависимость от сериализуемых структур;
  • изоляция каждого плагина.

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

Роль схемы конфигурации в плагине

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

Внутри плагина опции обычно десериализуются в структуру:

struct PluginOptions {
    debug: bool,
    level: i32,
    prefix: Option<String>,
}

Любое несоответствие JSON-структуры этой схеме приводит к ошибке десериализации на этапе загрузки плагина.

Поведение при некорректных опциях

Некорректные или неожиданные поля в конфигурации могут:

  • игнорироваться при строгой схеме;
  • приводить к ошибке десериализации;
  • заменяться значениями по умолчанию (в зависимости от реализации плагина).

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