Создание новых узлов

Работа с AST в SWC предполагает прямое конструирование синтаксических узлов (nodes), соответствующих внутренней структуре дерева разбора. Каждый узел представляет собой строго типизированный объект, описывающий конкретную синтаксическую конструкцию: идентификатор, литерал, выражение, объявление, оператор и так далее. Создание новых узлов является ключевым этапом трансформаций, когда необходимо генерировать код, а не только изменять существующий.


AST в SWC строится как набор вложенных объектов. Каждый узел содержит:

  • type / variant — тип конструкции (Identifier, BinaryExpression, VariableDeclaration и т.д.)
  • span — диапазон исходного кода (позиции начала и конца)
  • дополнительные поля — зависят от конкретного типа узла

Пример общей структуры:

{
  type: "Identifier",
  value: "x",
  span: { start: 0, end: 1 }
}

Поле span критично для корректной работы трансформаций, source maps и диагностики.


Создание идентификаторов

Идентификатор является базовым строительным блоком большинства выражений.

const id = {
  type: "Identifier",
  value: "myVariable",
  span: { start: 0, end: 0 }
};

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


Литералы и константные значения

Литералы описывают фиксированные значения: числа, строки, булевы значения.

Числовой литерал

const num = {
  type: "NumericLiteral",
  value: 42,
  span: { start: 0, end: 0 }
};

Строковый литерал

const str = {
  type: "StringLiteral",
  value: "hello",
  span: { start: 0, end: 0 }
};

Булевый литерал

const bool = {
  type: "BooleanLiteral",
  value: true,
  span: { start: 0, end: 0 }
};

Литералы часто используются как листья AST и редко содержат вложенные структуры.


Создание бинарных выражений

Бинарные выражения описывают операции между двумя операндами.

const expr = {
  type: "BinaryExpression",
  operator: "+",
  left: {
    type: "Identifier",
    value: "a",
    span: { start: 0, end: 0 }
  },
  right: {
    type: "NumericLiteral",
    value: 10,
    span: { start: 0, end: 0 }
  },
  span: { start: 0, end: 0 }
};

Типичные операторы:

  • +, -, *, /
  • ===, !==
  • <, >, <=, >=

Бинарные выражения активно применяются при оптимизациях и переписывании выражений.


Объявления переменных

Переменные в SWC представлены через VariableDeclaration и VariableDeclarator.

const decl = {
  type: "VariableDeclaration",
  kind: "const",
  declare: false,
  declarations: [
    {
      type: "VariableDeclarator",
      id: {
        type: "Identifier",
        value: "x",
        span: { start: 0, end: 0 }
      },
      init: {
        type: "NumericLiteral",
        value: 10,
        span: { start: 0, end: 0 }
      }
    }
  ],
  span: { start: 0, end: 0 }
};

VariableDeclarator связывает идентификатор и инициализирующее выражение.


Создание функций

Функциональные узлы включают параметры, тело и имя.

const func = {
  type: "FunctionDeclaration",
  identifier: {
    type: "Identifier",
    value: "sum",
    span: { start: 0, end: 0 }
  },
  params: [
    {
      type: "Parameter",
      pat: {
        type: "Identifier",
        value: "a",
        span: { start: 0, end: 0 }
      }
    },
    {
      type: "Parameter",
      pat: {
        type: "Identifier",
        value: "b",
        span: { start: 0, end: 0 }
      }
    }
  ],
  body: {
    type: "BlockStatement",
    stmts: [
      {
        type: "ReturnStatement",
        argument: {
          type: "BinaryExpression",
          operator: "+",
          left: {
            type: "Identifier",
            value: "a",
            span: { start: 0, end: 0 }
          },
          right: {
            type: "Identifier",
            value: "b",
            span: { start: 0, end: 0 }
          },
          span: { start: 0, end: 0 }
        }
      }
    ]
  },
  span: { start: 0, end: 0 }
};

Функции являются одними из наиболее сложных узлов из-за вложенности параметров и блоков.


Генерация блоков и операторов

Блок кода

const block = {
  type: "BlockStatement",
  stmts: []
};

Return statement

const ret = {
  type: "ReturnStatement",
  argument: {
    type: "NumericLiteral",
    value: 0,
    span: { start: 0, end: 0 }
  }
};

Условные конструкции

IfStatement

const ifStmt = {
  type: "IfStatement",
  test: {
    type: "BinaryExpression",
    operator: ">",
    left: { type: "Identifier", value: "x", span: { start: 0, end: 0 } },
    right: { type: "NumericLiteral", value: 0, span: { start: 0, end: 0 } },
    span: { start: 0, end: 0 }
  },
  consequent: {
    type: "BlockStatement",
    stmts: []
  },
  alternate: null
};

Создание объектов и свойств

Объекты строятся через ObjectExpression и KeyValueProperty.

const obj = {
  type: "ObjectExpression",
  properties: [
    {
      type: "KeyValueProperty",
      key: {
        type: "Identifier",
        value: "a",
        span: { start: 0, end: 0 }
      },
      value: {
        type: "NumericLiteral",
        value: 1,
        span: { start: 0, end: 0 }
      }
    }
  ]
};

Массивы и элементы

const arr = {
  type: "ArrayExpression",
  elements: [
    {
      expression: {
        type: "NumericLiteral",
        value: 1,
        span: { start: 0, end: 0 }
      }
    },
    {
      expression: {
        type: "NumericLiteral",
        value: 2,
        span: { start: 0, end: 0 }
      }
    }
  ]
};

Практика создания узлов в трансформациях

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

export default function transformer() {
  return {
    Program(program) {
      return {
        ...program,
        body: [
          {
            type: "VariableDeclaration",
            kind: "const",
            declare: false,
            declarations: [
              {
                type: "VariableDeclarator",
                id: {
                  type: "Identifier",
                  value: "injected",
                  span: program.span
                },
                init: {
                  type: "StringLiteral",
                  value: "generated",
                  span: program.span
                }
              }
            ],
            span: program.span
          },
          ...program.body
        ]
      };
    }
  };
}

Клонирование и модификация узлов

Создание новых узлов часто основано на копировании существующих с изменениями:

function cloneIdentifier(id, newName) {
  return {
    ...id,
    value: newName
  };
}

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


Важность корректного span

Неправильное или пустое значение span может приводить к:

  • некорректным source maps
  • ошибкам диагностики
  • сбоям при генерации кода

Часто используется переиспользование span родительского узла:

span: parent.span

Составные конструкции и вложенность

Создание сложных узлов всегда сводится к композиции базовых:

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

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