Skip to content

Visitor Methods API

This page documents all available visitor methods in ReluxScript.

Overview

Visitor methods are called automatically when the AST traversal encounters matching node types. Each method receives:

  • node: &mut NodeType - Mutable reference to the AST node
  • ctx: &Context - Context object with scope and utilities

Expression Visitors

visit_identifier

Called for every identifier (variable name).

reluxscript
fn visit_identifier(node: &mut Identifier, ctx: &Context) {
    // node.name: Str - The identifier name
    println!("Found identifier: {}", node.name);
}

Example matches:

  • foo
  • bar
  • myVariable

visit_call_expression

Called for function and method calls.

reluxscript
fn visit_call_expression(node: &mut CallExpression, ctx: &Context) {
    // node.callee: Expression - The function being called
    // node.arguments: Vec<Expression> - Call arguments
    // node.type_args: Option<TypeArgs> - Generic type arguments

    if matches!(node.callee, "console.log") {
        *node = Statement::empty();
    }
}

Example matches:

  • foo()
  • obj.method(arg1, arg2)
  • useState<string>()

visit_member_expression

Called for property access.

reluxscript
fn visit_member_expression(node: &mut MemberExpression, ctx: &Context) {
    // node.object: Expression - The object being accessed
    // node.property: Expression - The property name
    // node.computed: bool - true for obj[prop], false for obj.prop

    if matches!(node.object, Identifier { name: "console" }) {
        // Accessing console.*
    }
}

Example matches:

  • obj.property
  • arr[index]
  • console.log

visit_binary_expression

Called for binary operations.

reluxscript
fn visit_binary_expression(node: &mut BinaryExpression, ctx: &Context) {
    // node.left: Expression - Left operand
    // node.operator: Str - Operator (+, -, *, /, ==, etc.)
    // node.right: Expression - Right operand

    match node.operator.as_str() {
        "+" => handle_addition(),
        "-" => handle_subtraction(),
        _ => {}
    }
}

Example matches:

  • a + b
  • x * y
  • foo == bar

visit_unary_expression

Called for unary operations.

reluxscript
fn visit_unary_expression(node: &mut UnaryExpression, ctx: &Context) {
    // node.operator: Str - Operator (!, -, +, ~, typeof, etc.)
    // node.argument: Expression - Operand
    // node.prefix: bool - true for prefix, false for postfix

    if node.operator == "!" {
        // Logical NOT
    }
}

Example matches:

  • !flag
  • -num
  • typeof x

visit_arrow_function

Called for arrow functions.

reluxscript
fn visit_arrow_function(node: &mut ArrowFunctionExpression, ctx: &Context) {
    // node.params: Vec<Pattern> - Parameters
    // node.body: BlockStatement or Expression
    // node.async: bool - Is async function

    if node.async {
        // Handle async arrow function
    }
}

Example matches:

  • () => {}
  • (x) => x * 2
  • async (data) => await fetch(data)

visit_object_expression

Called for object literals.

reluxscript
fn visit_object_expression(node: &mut ObjectExpression, ctx: &Context) {
    // node.properties: Vec<ObjectProperty> - Object properties

    for prop in &node.properties {
        // Process each property
    }
}

Example matches:

  • {}
  • { a: 1, b: 2 }
  • { name, age: 30 }

visit_array_expression

Called for array literals.

reluxscript
fn visit_array_expression(node: &mut ArrayExpression, ctx: &Context) {
    // node.elements: Vec<Option<Expression>> - Array elements

    for elem in &node.elements {
        if let Some(expr) = elem {
            // Process element
        }
    }
}

Example matches:

  • []
  • [1, 2, 3]
  • [...items, newItem]

Statement Visitors

visit_function_declaration

Called for function declarations.

reluxscript
fn visit_function_declaration(node: &mut FunctionDeclaration, ctx: &Context) {
    // node.id: Identifier - Function name
    // node.params: Vec<Pattern> - Parameters
    // node.body: BlockStatement - Function body
    // node.async: bool - Is async
    // node.generator: bool - Is generator

    if node.async {
        // Handle async function
    }
}

Example matches:

  • function foo() {}
  • async function getData() {}
  • function* generator() {}

visit_variable_declaration

Called for variable declarations.

reluxscript
fn visit_variable_declaration(node: &mut VariableDeclaration, ctx: &Context) {
    // node.kind: Str - "var", "let", or "const"
    // node.declarations: Vec<VariableDeclarator>

    for decl in &node.declarations {
        // Process each declarator
    }
}

Example matches:

  • let x = 1;
  • const name = "foo";
  • var a, b, c;

visit_if_statement

Called for if statements.

reluxscript
fn visit_if_statement(node: &mut IfStatement, ctx: &Context) {
    // node.test: Expression - Condition
    // node.consequent: Statement - Then branch
    // node.alternate: Option<Statement> - Else branch

    if node.alternate.is_some() {
        // Has else clause
    }
}

Example matches:

  • if (x) {}
  • if (y) {} else {}
  • if (a) {} else if (b) {}

visit_for_statement

Called for for loops.

reluxscript
fn visit_for_statement(node: &mut ForStatement, ctx: &Context) {
    // node.init: Option<ForInit> - Initializer
    // node.test: Option<Expression> - Condition
    // node.update: Option<Expression> - Update
    // node.body: Statement - Loop body
}

Example matches:

  • for (let i = 0; i < 10; i++) {}
  • for (;;) {}

visit_while_statement

Called for while loops.

reluxscript
fn visit_while_statement(node: &mut WhileStatement, ctx: &Context) {
    // node.test: Expression - Condition
    // node.body: Statement - Loop body
}

Example matches:

  • while (condition) {}
  • while (true) {}

visit_return_statement

Called for return statements.

reluxscript
fn visit_return_statement(node: &mut ReturnStatement, ctx: &Context) {
    // node.argument: Option<Expression> - Return value

    if let Some(expr) = &node.argument {
        // Has return value
    }
}

Example matches:

  • return;
  • return value;
  • return x + y;

visit_block_statement

Called for block statements (code blocks).

reluxscript
fn visit_block_statement(node: &mut BlockStatement, ctx: &Context) {
    // node.body: Vec<Statement> - Statements in block

    for stmt in &mut node.body {
        // Process each statement
    }
}

Example matches:

  • {}
  • { stmt1; stmt2; }
  • Function/loop bodies

JSX Visitors

visit_jsx_element

Called for JSX elements.

reluxscript
fn visit_jsx_element(node: &mut JSXElement, ctx: &Context) {
    // node.opening_element: JSXOpeningElement
    // node.closing_element: Option<JSXClosingElement>
    // node.children: Vec<JSXChild>

    let tag_name = &node.opening_element.name;
    // Process JSX element
}

Example matches:

  • <div />
  • <Component prop="value" />
  • <div>content</div>

visit_jsx_attribute

Called for JSX attributes.

reluxscript
fn visit_jsx_attribute(node: &mut JSXAttribute, ctx: &Context) {
    // node.name: JSXIdentifier - Attribute name
    // node.value: Option<JSXAttributeValue> - Attribute value

    if node.name.name == "key" {
        // Found key attribute
    }
}

Example matches:

  • key="id"
  • onClick={handler}
  • disabled

TypeScript Visitors

visit_ts_interface_declaration

Called for TypeScript interfaces.

reluxscript
fn visit_ts_interface_declaration(node: &mut TSInterfaceDeclaration, ctx: &Context) {
    // node.id: Identifier - Interface name
    // node.body: TSInterfaceBody - Interface members

    println!("Interface: {}", node.id.name);
}

Example matches:

  • interface User {}
  • interface Props extends BaseProps {}

visit_ts_type_annotation

Called for TypeScript type annotations.

reluxscript
fn visit_ts_type_annotation(node: &mut TSTypeAnnotation, ctx: &Context) {
    // node.type_annotation: TSType - The type

    // Process type annotation
}

Example matches:

  • : string
  • : number[]
  • : { name: string }

Program Hooks

pre (or init)

Called before traversal begins. Used for initialization.

reluxscript
plugin MyPlugin {
    fn pre(file: &File) {
        // Initialize state
        file.metadata.originalCode = file.code;
    }
}

Alternative name: init()


exit (or finish)

Called after traversal completes. Used for finalization.

reluxscript
plugin MyPlugin {
    fn exit(program: &mut Program, state: &PluginState) {
        // Generate output, write files, etc.
        generate_report(state);
    }
}

Alternative name: finish()


Context API

The Context object provides utilities and information:

ctx.scope

Scope information (expensive in SWC, requires pre-pass).

reluxscript
// Check if name is bound in scope
if ctx.scope.has_binding("foo") {
    // 'foo' is declared in this scope
}

// Get binding information
let binding = ctx.scope.get_binding("foo");

ctx.generate_uid(hint)

Generate a unique identifier.

reluxscript
let temp_name = ctx.generate_uid("temp");
// Returns: "_temp", "_temp2", etc.

ctx.filename

Current source filename.

reluxscript
println!("Processing: {}", ctx.filename);

Node Replacement

Replace nodes using the dereference operator:

reluxscript
// Replace with empty statement
*node = Statement::empty();

// Replace with new node
*node = CallExpression {
    callee: Identifier::new("newFunc"),
    arguments: vec![],
};

// Replace with cloned node
*node = other_node.clone();

Traversal Control

Skip Children

Don't call visit_children() to skip traversing child nodes:

reluxscript
fn visit_function_declaration(node: &mut FunctionDeclaration, ctx: &Context) {
    // Don't traverse function body
    // (omit node.visit_children(self))
}

Explicit Traversal

Manually traverse children:

reluxscript
fn visit_block_statement(node: &mut BlockStatement, ctx: &Context) {
    for stmt in &mut node.body {
        stmt.visit_with(self);
    }
}

Nested Visitors

Use traverse for scoped traversal with different rules:

reluxscript
fn visit_function_declaration(func: &mut FunctionDeclaration, ctx: &Context) {
    traverse(&mut func.body) {
        let mut count = 0;

        fn visit_return_statement(ret: &mut ReturnStatement, ctx: &Context) {
            self.count += 1;
        }
    }
}

Complete Node Type List

Visitor MethodNode TypeExample
visit_identifierIdentifierfoo
visit_call_expressionCallExpressionfn()
visit_member_expressionMemberExpressionobj.prop
visit_binary_expressionBinaryExpressiona + b
visit_unary_expressionUnaryExpression!x
visit_arrow_functionArrowFunctionExpression() => {}
visit_function_expressionFunctionExpressionfunction() {}
visit_function_declarationFunctionDeclarationfunction foo() {}
visit_variable_declarationVariableDeclarationlet x = 1
visit_variable_declaratorVariableDeclaratorx = 1
visit_if_statementIfStatementif (x) {}
visit_for_statementForStatementfor(;;) {}
visit_while_statementWhileStatementwhile(x) {}
visit_return_statementReturnStatementreturn x
visit_block_statementBlockStatement{ }
visit_expression_statementExpressionStatementexpr;
visit_object_expressionObjectExpression{ a: 1 }
visit_array_expressionArrayExpression[1, 2]
visit_string_literalStringLiteral"text"
visit_numeric_literalNumericLiteral42
visit_boolean_literalBooleanLiteraltrue
visit_jsx_elementJSXElement<div />
visit_jsx_attributeJSXAttributekey="x"
visit_ts_interface_declarationTSInterfaceDeclarationinterface I {}
visit_ts_type_annotationTSTypeAnnotation: string

Next Steps

Released under the MIT License.