or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdparsing.mdtransformation.mdtraversal.mdutilities.md
tile.json

traversal.mddocs/

AST Traversal

Tools for walking and analyzing AST structures with flexible visitor patterns and control flow mechanisms.

Capabilities

SimpleTraverser Class

Main class for traversing AST nodes with enter/leave callbacks and flexible visitor key configuration.

/**
 * A simple traverser class for walking AST trees
 */
class SimpleTraverser {
  /**
   * Traverse AST tree with callbacks (static method)
   * @param node - Root AST node to traverse
   * @param options - Traversal configuration with callbacks
   */
  static traverse(node: ESNode, options: TraverserOptions): void;
  
  /**
   * Traverse AST tree with callbacks (instance method)
   * @param node - Root AST node to traverse  
   * @param options - Traversal configuration with callbacks
   */
  traverse(node: ESNode, options: TraverserOptions): void;
  
  /** Error constant for skipping subtree traversal */
  static Skip: Error;
  
  /** Error constant for stopping traversal completely */
  static Break: Error;
}

interface TraverserOptions {
  /** Callback invoked when entering each node */
  enter: (node: ESNode, parent?: ESNode) => void;
  
  /** Callback invoked when leaving each node */
  leave: (node: ESNode, parent?: ESNode) => void;
  
  /** Custom visitor keys for traversal (optional) */
  visitorKeys?: VisitorKeysType;
}

type TraverserCallback = (node: ESNode, parent?: ESNode) => void;

Usage Examples:

import { SimpleTraverser } from "hermes-parser";

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

// Basic traversal with enter/leave callbacks
SimpleTraverser.traverse(ast, {
  enter(node, parent) {
    console.log(`Entering ${node.type}`);
    if (parent) {
      console.log(`  Parent: ${parent.type}`);
    }
  },
  leave(node, parent) {
    console.log(`Leaving ${node.type}`);
  }
});

// Collect all function names
const functionNames = [];
SimpleTraverser.traverse(ast, {
  enter(node) {
    if (node.type === "FunctionDeclaration" && node.id) {
      functionNames.push(node.id.name);
    }
  },
  leave() {} // Required but can be empty
});

// Instance-based traversal
const traverser = new SimpleTraverser();
traverser.traverse(ast, {
  enter: (node) => console.log(node.type),
  leave: () => {}
});

Traversal Control

Control traversal flow using error-based mechanisms for skipping and stopping.

/** Skip traversal of current node's children */
const SimpleTraverserSkip: Error;

/** Stop traversal entirely */  
const SimpleTraverserBreak: Error;

Control Flow Examples:

SimpleTraverser.traverse(ast, {
  enter(node) {
    // Skip function bodies to only analyze declarations
    if (node.type === "BlockStatement") {
      throw SimpleTraverser.Skip;
    }
    
    // Stop traversal when finding specific pattern
    if (node.type === "CallExpression" && 
        node.callee.name === "stopHere") {
      throw SimpleTraverser.Break;
    }
    
    console.log(`Processing ${node.type}`);
  },
  leave() {}
});

// Alternative: Use static properties
SimpleTraverser.traverse(ast, {
  enter(node) {
    if (node.type === "FunctionExpression") {
      throw SimpleTraverser.Skip; // Skip function body
    }
  },
  leave() {}
});

Visitor Keys

Functions for managing and using visitor keys that define which properties to traverse on each node type.

/**
 * Get visitor keys for a specific AST node type
 * @param node - AST node to get keys for
 * @param visitorKeys - Optional custom visitor keys mapping
 * @returns Array of property names to traverse
 */
function getVisitorKeys(node: ESNode, visitorKeys?: VisitorKeysType): ReadonlyArray<string>;

/**
 * Check if a value is an AST node
 * @param value - Value to test
 * @returns True if value is an AST node object
 */
function isNode(value: unknown): value is ESNode;

/** Default Flow ESTree visitor keys */
const FlowVisitorKeys: VisitorKeysType;

type VisitorKeysType = {
  [nodeType: string]: ReadonlyArray<string>;
};

Visitor Keys Examples:

import { getVisitorKeys, isNode, FlowVisitorKeys } from "hermes-parser";

// Get visitor keys for a node
const functionNode = { type: "FunctionDeclaration", id: null, params: [], body: null };
const keys = getVisitorKeys(functionNode);
console.log(keys); // ["id", "params", "body", "typeParameters", "returnType", "predicate"]

// Check if value is AST node
console.log(isNode({ type: "Identifier", name: "x" })); // true
console.log(isNode("not a node")); // false

// Use custom visitor keys
const customKeys = {
  ...FlowVisitorKeys,
  CustomNode: ["customProp1", "customProp2"]
};

SimpleTraverser.traverse(ast, {
  enter(node) {
    const keys = getVisitorKeys(node, customKeys);
    console.log(`${node.type} keys:`, keys);
  },
  leave() {},
  visitorKeys: customKeys
});

Advanced Traversal Patterns

Common patterns for AST analysis and manipulation.

Finding Specific Nodes:

function findNodes(ast, predicate) {
  const results = [];
  SimpleTraverser.traverse(ast, {
    enter(node) {
      if (predicate(node)) {
        results.push(node);
      }
    },
    leave() {}
  });
  return results;
}

// Find all variable declarations
const variables = findNodes(ast, node => 
  node.type === "VariableDeclarator"
);

// Find all function calls
const calls = findNodes(ast, node =>
  node.type === "CallExpression"
);

Collecting Node Statistics:

function analyzeAST(ast) {
  const stats = {};
  
  SimpleTraverser.traverse(ast, {
    enter(node) {
      stats[node.type] = (stats[node.type] || 0) + 1;
    },
    leave() {}
  });
  
  return stats;
}

const analysis = analyzeAST(ast);
console.log(analysis);
// { Program: 1, FunctionDeclaration: 2, Identifier: 8, ... }

Parent-Child Relationship Analysis:

function buildParentMap(ast) {
  const parentMap = new WeakMap();
  
  SimpleTraverser.traverse(ast, {
    enter(node, parent) {
      if (parent) {
        parentMap.set(node, parent);
      }
    },
    leave() {}
  });
  
  return parentMap;
}

const parents = buildParentMap(ast);
// Later: const parent = parents.get(someNode);

Error Context

When errors occur during traversal, contextual information is automatically attached.

interface TraversalError extends Error {
  currentNode?: {
    type: string;
    range?: [number, number];
    loc?: SourceLocation;
  };
}

Error Handling:

SimpleTraverser.traverse(ast, {
  enter(node) {
    if (node.type === "CallExpression") {
      // This error will include currentNode context
      throw new Error("Processing failed");
    }
  },
  leave() {}
});