Структура AST в SWC

AST (Abstract Syntax Tree) в контексте SWC представляет собой строго типизированную структуру, описывающую синтаксис исходного JavaScript/TypeScript кода на уровне узлов языка. Внутренняя реализация AST ориентирована на максимальную скорость парсинга и последующей трансформации, что определяет её отличие от более универсальных, но менее производительных моделей вроде ESTree.

В основе лежит пакет swc_ecma_ast, задающий набор структур данных, где каждый синтаксический элемент представлен отдельным типом узла с фиксированным набором полей. Архитектура AST в SWC оптимизирована под Rust-реализацию компилятора, однако концептуально сохраняет совместимость с экосистемой JavaScript.

Базовая структура узлов

Каждый узел AST в SWC имеет общую концепцию: он описывает конкретную грамматическую конструкцию и содержит метаданные позиции в исходном коде.

Ключевые элементы любого узла:

  • span — диапазон символов в исходном коде (start/end позиции)
  • тип узла (enum variant)
  • специфичные поля конструкции

Пример общей модели:

pub struct Ident {
    pub span: Span,
    pub sym: JsWord,
    pub optional: bool,
}

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

Иерархия Program и модульная структура

Корневым элементом AST является Program, который определяет тип исходного файла:

  • Script — классический JavaScript без модулей
  • Module — ECMAScript module

Внутри Module находится список ModuleItem, который может включать:

  • ModuleDecl (импорт/экспорт/декларации)
  • Stmt (операторы верхнего уровня)

Структура верхнего уровня:

Program
 ├── Module
 │    ├── ModuleItem[]
 │    │     ├── ModuleDecl
 │    │     └── Stmt
 └── Script
      └── Stmt[]

Такое разделение позволяет строго различать контексты исполнения, что важно для корректной трансформации ESM ↔︎ CJS.

Узлы выражений (Expression)

Выражения формируют одну из наиболее насыщенных частей AST. В SWC они представлены enum Expr, содержащим множество вариантов.

Основные типы выражений:

  • Ident — идентификатор
  • Lit — литералы (строки, числа, булевы значения)
  • CallExpr — вызовы функций
  • MemberExpr — доступ к свойствам объекта
  • ArrowExpr — стрелочные функции
  • FnExpr — функциональные выражения
  • ObjectLit — объектные литералы
  • ArrayLit — массивы
  • BinExpr — бинарные операции
  • UnaryExpr — унарные операции
  • CondExpr — тернарные выражения

Пример CallExpr

{
  "type": "CallExpression",
  "callee": {
    "type": "Identifier",
    "value": "parse"
  },
  "arguments": [
    {
      "type": "StringLiteral",
      "value": "code"
    }
  ]
}

CallExpr содержит:

  • callee — вызываемое выражение
  • args — список аргументов
  • type_args — generics (для TS)

Statement-уровень AST

Операторы (Statements) формируют основную структуру исполнения.

Основной enum: Stmt

Основные виды:

  • Decl — объявления (function, class, var, let, const)
  • ExprStmt — выражения как операторы
  • BlockStmt — блоки кода
  • IfStmt — условные конструкции
  • ForStmt, WhileStmt, DoWhileStmt
  • ReturnStmt
  • SwitchStmt
  • ThrowStmt
  • TryStmt

BlockStmt как контейнер

{
  "type": "BlockStatement",
  "stmts": [
    {
      "type": "ReturnStatement",
      "argument": {
        "type": "NumericLiteral",
        "value": 42
      }
    }
  ]
}

BlockStmt играет ключевую роль в нормализации управления потоком при трансформациях.

Декларации и объявления

Декларативные узлы представлены отдельной категорией Decl, включающей:

  • ClassDecl
  • FnDecl
  • VarDecl
  • TsInterfaceDecl
  • TsTypeAliasDecl
  • TsEnumDecl

VarDecl структура

{
  "type": "VariableDeclaration",
  "kind": "let",
  "decls": [
    {
      "id": {
        "type": "Identifier",
        "value": "x"
      },
      "init": {
        "type": "NumericLiteral",
        "value": 10
      }
    }
  ]
}

Ключевой особенностью является разделение kind и списка decls, что позволяет представлять множественные объявления в одном операторе.

Функции и замыкания

Функции представлены двумя основными узлами:

  • FnDecl — именованные функции
  • FnExpr — функциональные выражения
  • ArrowExpr — стрелочные функции

Структура функции

{
  "type": "FunctionDeclaration",
  "identifier": {
    "type": "Identifier",
    "value": "sum"
  },
  "params": [
    {
      "type": "Identifier",
      "value": "a"
    },
    {
      "type": "Identifier",
      "value": "b"
    }
  ],
  "body": {
    "type": "BlockStatement",
    "stmts": []
  }
}

Особое значение имеет разделение params и body, что позволяет проводить независимые трансформации параметров (например, для TypeScript или Babel-подобных преобразований).

TypeScript-расширения AST

AST SWC включает нативную поддержку TypeScript через дополнительные узлы:

  • TsType
  • TsExprWithTypeArgs
  • TsInterfaceDecl
  • TsTypeAliasDecl
  • TsEnumDecl

Пример типизированного узла

{
  "type": "TsTypeReference",
  "typeName": {
    "type": "Identifier",
    "value": "Promise"
  },
  "typeParams": {
    "params": [
      {
        "type": "TsKeywordType",
        "kind": "TsStringKeyword"
      }
    ]
  }
}

TypeScript-расширения не влияют на runtime-структуру JavaScript, но критически важны для трансформаций и удаления типов.

Управление областью видимости и идентификаторы

Идентификаторы (Ident) являются базовым строительным блоком AST.

pub struct Ident {
    pub span: Span,
    pub sym: JsWord,
    pub optional: bool,
}
  • sym — имя символа
  • optional — используется в optional chaining и параметрах

SWC AST не хранит полноценную таблицу символов внутри узлов, но структура позволяет внешним passes строить scope graph.

Литералы и примитивы

Литералы представлены через Lit:

  • StringLiteral
  • NumericLiteral
  • BooleanLiteral
  • NullLiteral
  • BigIntLiteral
  • RegexLiteral

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

Операции и выражения операторов

Бинарные и унарные операции представлены компактными структурами:

BinExpr

{
  "type": "BinaryExpression",
  "operator": "+",
  "left": { "type": "NumericLiteral", "value": 1 },
  "right": { "type": "NumericLiteral", "value": 2 }
}

Поддерживаемые операторы включают:

  • арифметические
  • логические
  • битовые
  • сравнительные

Управляющие конструкции

AST SWC моделирует управление потоком через специализированные узлы:

IfStmt

{
  "type": "IfStatement",
  "test": { "type": "BooleanLiteral", "value": true },
  "consequent": { "type": "BlockStatement", "stmts": [] },
  "alternate": null
}

ForStmt

Содержит:

  • init
  • test
  • update
  • body

Такая структура позволяет гибко преобразовывать циклы в другие формы (например, генераторы или async-итераторы).

Модули и импорт/экспорт

Модульная система AST включает:

  • ImportDecl
  • ExportDecl
  • ExportDefaultDecl
  • ExportNamedDecl

ImportDeclaration

{
  "type": "ImportDeclaration",
  "specifiers": [
    {
      "type": "ImportDefaultSpecifier",
      "local": { "value": "React" }
    }
  ],
  "source": {
    "value": "react"
  }
}

Модель импорта разделяет спецификаторы и источник, что упрощает tree-shaking и анализ зависимостей.

Принципы неизменяемости и трансформации

AST в SWC проектируется как структура, которая активно клонируется и трансформируется в ходе проходов (passes). Вместо мутирования узлов часто создаются новые экземпляры с частично изменёнными полями.

Ключевые подходы:

  • структурное клонирование узлов
  • рекурсивные visitor-паттерны
  • минимизация аллокаций через оптимизированные структуры Rust

Span и source map модель

Span является критическим компонентом всей AST-системы.

Он содержит:

  • начало
  • конец
  • дополнительную информацию о макросах/гигиене (в некоторых режимах)

Используется для:

  • генерации source maps
  • диагностики ошибок
  • точного восстановления исходного кода

Особенности оптимизации AST в SWC

Архитектура AST ориентирована на производительность:

  • использование компактных enum вместо глубоких классовых иерархий
  • минимизация heap allocations
  • строгая типизация через Rust enums
  • отсутствие динамической типизации на уровне узлов

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

Сравнительная модель представления кода

В отличие от более абстрактных AST моделей, используемых в JS-инструментах, структура SWC:

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

Это делает её особенно эффективной в задачах транспиляции и бандлинга.