CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-recast

JavaScript syntax tree transformer, nondestructive pretty-printer, and automatic source map generator

Pending
Overview
Eval results
Files

ast-manipulation.mddocs/

AST Manipulation

Tools for traversing and modifying Abstract Syntax Trees with type safety using the ast-types library integration.

Capabilities

Visit Function

Traverse and potentially modify an abstract syntax tree using a convenient visitor syntax.

/**
 * Traverse and modify AST nodes using visitor pattern
 * @param ast - The AST node to traverse
 * @param visitor - Visitor object with node-specific methods
 * @returns The modified AST
 */
function visit(ast: types.ASTNode, visitor: Visitor): types.ASTNode;

interface Visitor {
  [methodName: string]: (path: NodePath) => any;
}

interface NodePath {
  /** The current AST node */
  value: any;
  /** Parent path in the traversal */
  parent: NodePath | null;
  /** Name of the property this node represents */
  name: string | number | null;
  /** Continue traversing child nodes */
  traverse(path?: NodePath): void;
  /** Replace current node with new node */
  replace(node: any): void;
  /** Insert nodes before current node */
  insertBefore(...nodes: any[]): void;
  /** Insert nodes after current node */
  insertAfter(...nodes: any[]): void;
  /** Remove current node */
  prune(): void;
}

Usage Examples:

import { parse, visit, print } from "recast";

const ast = parse(sourceCode);

// Transform all identifier names to uppercase
visit(ast, {
  visitIdentifier(path) {
    const node = path.value;
    node.name = node.name.toUpperCase();
    this.traverse(path);
  }
});

// Replace function declarations with arrow functions
visit(ast, {
  visitFunctionDeclaration(path) {
    const node = path.value;
    const arrowFunction = types.builders.variableDeclaration("const", [
      types.builders.variableDeclarator(
        node.id,
        types.builders.arrowFunctionExpression(node.params, node.body)
      )
    ]);
    path.replace(arrowFunction);
    return false; // Skip traversing children
  }
});

AST Types Integration

Access to the complete ast-types library for AST construction and validation.

/**
 * AST types namespace providing builders and validators
 */
namespace types {
  /** AST node constructor functions */
  const builders: Builders;
  /** Type checking and assertion functions */
  const namedTypes: NamedTypes;
  /** Built-in type validators */
  const builtInTypes: BuiltInTypes;
}

interface Builders {
  /** Create identifier node */
  identifier(name: string): Identifier;
  /** Create literal node */
  literal(value: any): Literal;
  /** Create function expression */
  functionExpression(
    id: Identifier | null,
    params: Pattern[],
    body: BlockStatement
  ): FunctionExpression;
  /** Create variable declaration */
  variableDeclaration(
    kind: "var" | "let" | "const",
    declarations: VariableDeclarator[]
  ): VariableDeclaration;
  /** Create variable declarator */
  variableDeclarator(
    id: Pattern,
    init?: Expression | null
  ): VariableDeclarator;
  /** Create arrow function expression */
  arrowFunctionExpression(
    params: Pattern[],
    body: BlockStatement | Expression
  ): ArrowFunctionExpression;
  /** Create member expression */
  memberExpression(
    object: Expression,
    property: Expression | Identifier,
    computed?: boolean
  ): MemberExpression;
  /** Create call expression */
  callExpression(
    callee: Expression,
    arguments: Array<Expression | SpreadElement>
  ): CallExpression;
  // ... many more builder functions
}

interface NamedTypes {
  /** Assert node is an Identifier */
  Identifier: TypeChecker<Identifier>;
  /** Assert node is a FunctionDeclaration */
  FunctionDeclaration: TypeChecker<FunctionDeclaration>;
  /** Assert node is a VariableDeclaration */
  VariableDeclaration: TypeChecker<VariableDeclaration>;
  // ... many more type checkers
}

interface TypeChecker<T> {
  /** Check if value is of this type */
  check(value: any): value is T;
  /** Assert value is of this type (throws if not) */
  assert(value: any): asserts value is T;
  /** Get the type definition */
  def: TypeDefinition;
}

Usage Examples:

import { parse, types, visit, print } from "recast";

const b = types.builders;
const n = types.namedTypes;

const ast = parse("function add(a, b) { return a + b; }");

visit(ast, {
  visitFunctionDeclaration(path) {
    const node = path.value;
    
    // Type checking
    if (n.FunctionDeclaration.check(node)) {
      // Convert to arrow function
      const arrowFunc = b.variableDeclaration("const", [
        b.variableDeclarator(
          node.id,
          b.arrowFunctionExpression(node.params, node.body)
        )
      ]);
      path.replace(arrowFunc);
    }
    return false;
  }
});

Path-based Traversal

Advanced path manipulation for complex AST transformations.

interface NodePath {
  /** Get the value at this path */
  value: any;
  /** Get parent path */
  parent: NodePath | null;
  /** Get property name/index */
  name: string | number | null;
  /** Get scope information */
  scope: Scope;
  
  /** Navigate to parent path */
  parentPath: NodePath | null;
  /** Get child paths */
  get(...names: Array<string | number>): NodePath;
  /** Check if path exists */
  has(...names: Array<string | number>): boolean;
  
  /** Replace node at this path */
  replace(node: any): void;
  /** Insert nodes before current node */
  insertBefore(...nodes: any[]): void;
  /** Insert nodes after current node */
  insertAfter(...nodes: any[]): void;
  /** Remove node at this path */
  prune(): void;
  
  /** Continue traversal */
  traverse(path?: NodePath): void;
}

Usage Examples:

import { parse, visit } from "recast";

visit(ast, {
  visitCallExpression(path) {
    const node = path.value;
    
    // Access function name through path navigation
    const calleePath = path.get("callee");
    if (calleePath.value.name === "console") {
      const propertyPath = path.get("property");
      if (propertyPath.value.name === "log") {
        // Replace console.log with console.warn
        propertyPath.replace(types.builders.identifier("warn"));
      }
    }
    
    this.traverse(path);
  }
});

Scope Analysis

Access to scope information for variable binding analysis.

interface Scope {
  /** Parent scope */
  parent: Scope | null;
  /** Declared bindings in this scope */
  bindings: { [name: string]: Binding[] };
  /** Scope type */
  type: "function" | "block" | "catch" | "with";
  
  /** Check if identifier is bound in this scope */
  declares(name: string): boolean;
  /** Look up binding in scope chain */
  lookup(name: string): Scope | null;
  /** Get all names declared in this scope */
  getBindingNames(): string[];
}

interface Binding {
  /** Identifier node that declares this binding */
  identifier: Identifier;
  /** Scope where this binding is declared */
  scope: Scope;
  /** AST path to the binding */
  path: NodePath;
}

Usage Examples:

import { parse, visit } from "recast";

visit(ast, {
  visitIdentifier(path) {
    const node = path.value;
    const scope = path.scope;
    
    // Check if identifier is bound in current scope
    if (scope.declares(node.name)) {
      console.log(`${node.name} is declared locally`);
    } else {
      console.log(`${node.name} is from outer scope`);
    }
    
    this.traverse(path);
  }
});

Common AST Transformation Patterns

Function Conversion

Converting between different function syntaxes.

// Function declaration to arrow function
visit(ast, {
  visitFunctionDeclaration(path) {
    const node = path.value;
    const arrow = types.builders.variableDeclaration("const", [
      types.builders.variableDeclarator(
        node.id,
        types.builders.arrowFunctionExpression(node.params, node.body)
      )
    ]);
    path.replace(arrow);
    return false;
  }
});

Variable Renaming

Systematically renaming variables throughout the AST.

const renameMap = { oldName: "newName" };

visit(ast, {
  visitIdentifier(path) {
    const node = path.value;
    if (renameMap[node.name]) {
      node.name = renameMap[node.name];
    }
    this.traverse(path);
  }
});

Code Injection

Adding new statements or expressions to existing code.

visit(ast, {
  visitFunctionDeclaration(path) {
    const node = path.value;
    
    // Add console.log at start of function
    const logStatement = types.builders.expressionStatement(
      types.builders.callExpression(
        types.builders.memberExpression(
          types.builders.identifier("console"),
          types.builders.identifier("log")
        ),
        [types.builders.literal(`Entering function ${node.id.name}`)]
      )
    );
    
    node.body.body.unshift(logStatement);
    this.traverse(path);
  }
});

Install with Tessl CLI

npx tessl i tessl/npm-recast

docs

ast-manipulation.md

cli-interface.md

core-operations.md

index.md

parser-configuration.md

source-maps.md

tile.json