or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

builders.mdcloning.mdcomments.mdconstants.mdconverters.mdindex.mdmodifications.mdreact.mdretrievers.mdtraversal.mdvalidators.md
tile.json

traversal.mddocs/

AST Traversal

Simple AST traversal utilities for walking through Abstract Syntax Trees. These functions provide lightweight traversal with visitor functions and ancestry tracking.

Capabilities

Basic Traversal

Main Traverse Function

A simple traversal function that walks through an AST and calls visitor functions for each node, providing ancestry information.

/**
 * Traverse an AST with visitor functions and ancestry tracking
 * @param node - Root node to start traversal from
 * @param handlers - Visitor functions (enter/exit) or single handler function
 * @param state - Optional state object passed to all handlers
 */
function traverse<T>(
  node: t.Node,
  handlers: TraversalHandler<T> | TraversalHandlers<T>,
  state?: T,
): void;

/**
 * Single handler function signature
 */
type TraversalHandler<T> = (
  node: t.Node,
  parent: TraversalAncestors,
  state: T,
) => void;

/**
 * Handler configuration object with enter/exit functions
 */
interface TraversalHandlers<T> {
  enter?: TraversalHandler<T>;
  exit?: TraversalHandler<T>;
}

/**
 * Array of ancestor information providing context during traversal
 */
type TraversalAncestors = Array<{
  node: t.Node;
  key: string;
  index?: number;
}>;

Fast Traversal

Lightweight Traversal Function

A simplified traversal function for performance-critical operations where full visitor capabilities aren't needed.

/**
 * Fast traversal with simple enter function
 * @param node - Root node to traverse (can be null/undefined)
 * @param enter - Function called for each node during traversal
 * @param opts - Optional options object passed to enter function
 * @returns true if traversal was stopped, false otherwise
 */
function traverseFast<Options = object>(
  node: t.Node | null | undefined,
  enter: (
    node: t.Node,
    opts?: Options,
  ) => void | typeof traverseFast.skip | typeof traverseFast.stop,
  opts?: Options,
): boolean;

// Control symbols for traversal flow
traverseFast.skip: symbol; // Skip current node's children
traverseFast.stop: symbol; // Stop entire traversal

Usage Examples

Basic Traversal with Single Handler

import * as t from "@babel/types";
import { traverse } from "@babel/types";
import { parse } from "@babel/parser";

// Parse some code
const code = `
  function add(a, b) {
    return a + b;
  }
  add(1, 2);
`;
const ast = parse(code);

// Simple traversal with single handler
traverse(ast, (node, ancestors, state) => {
  console.log(`Visiting ${node.type}`);
  
  // Access parent information
  if (ancestors.length > 0) {
    const parent = ancestors[ancestors.length - 1];
    console.log(`  Parent: ${parent.node.type} (key: ${parent.key})`);
  }
});

Traversal with Enter/Exit Handlers

// Traversal with both enter and exit handlers
traverse(ast, {
  enter(node, ancestors, state) {
    console.log(`Entering ${node.type}`);
  },
  exit(node, ancestors, state) {
    console.log(`Exiting ${node.type}`);
  }
});

Using State Object

interface CollectorState {
  identifiers: string[];
  functions: string[];
}

const state: CollectorState = {
  identifiers: [],
  functions: []
};

traverse(ast, (node, ancestors, state) => {
  if (t.isIdentifier(node)) {
    state.identifiers.push(node.name);
  }
  
  if (t.isFunctionDeclaration(node) && node.id) {
    state.functions.push(node.id.name);
  }
}, state);

console.log("Identifiers:", state.identifiers);
console.log("Functions:", state.functions);

Analyzing Node Context with Ancestors

traverse(ast, (node, ancestors, state) => {
  if (t.isIdentifier(node)) {
    // Find if this identifier is inside a function
    const functionAncestor = ancestors.find(ancestor => 
      t.isFunctionDeclaration(ancestor.node) || 
      t.isFunctionExpression(ancestor.node)
    );
    
    if (functionAncestor) {
      console.log(`Identifier "${node.name}" is inside a function`);
    } else {
      console.log(`Identifier "${node.name}" is at module level`);
    }
  }
});

Fast Traversal for Simple Operations

import { traverseFast } from "@babel/types";

// Count all nodes quickly
let nodeCount = 0;
traverseFast(ast, (node) => {
  nodeCount++;
});
console.log(`Total nodes: ${nodeCount}`);

// Collect all string literals quickly
const strings: string[] = [];
traverseFast(ast, (node) => {
  if (t.isStringLiteral(node)) {
    strings.push(node.value);
  }
});
console.log("String literals:", strings);

Controlling Fast Traversal Flow

// Skip function bodies during traversal
traverseFast(ast, (node, opts) => {
  console.log(`Visiting ${node.type}`);
  
  // Skip traversing inside function declarations
  if (t.isFunctionDeclaration(node)) {
    console.log("Skipping function body");
    return traverseFast.skip;
  }
  
  // Stop traversal entirely if we find an error
  if (t.isThrowStatement(node)) {
    console.log("Found throw statement, stopping traversal");
    return traverseFast.stop;
  }
});

Finding Specific Patterns

// Find all binary expressions inside functions
traverse(ast, (node, ancestors, state) => {
  if (t.isBinaryExpression(node)) {
    // Check if we're inside a function
    const inFunction = ancestors.some(ancestor => 
      t.isFunctionDeclaration(ancestor.node) || 
      t.isFunctionExpression(ancestor.node) ||
      t.isArrowFunctionExpression(ancestor.node)
    );
    
    if (inFunction) {
      console.log(`Found binary expression: ${node.operator}`);
    }
  }
});

Array Index Tracking

traverse(ast, (node, ancestors, state) => {
  if (t.isVariableDeclarator(node)) {
    // Find which declarator in the declaration this is
    const parent = ancestors[ancestors.length - 1];
    if (parent && parent.index !== undefined) {
      console.log(`Variable declarator at index ${parent.index}`);
    }
  }
});

Performance Comparison

const startTime = Date.now();

// Fast traversal - better for simple operations
let fastCount = 0;
traverseFast(ast, (node) => {
  fastCount++;
});

const fastTime = Date.now() - startTime;

const start2 = Date.now();

// Regular traversal - better when you need ancestry info
let regularCount = 0;
traverse(ast, (node, ancestors, state) => {
  regularCount++;
});

const regularTime = Date.now() - start2;

console.log(`Fast traversal: ${fastCount} nodes in ${fastTime}ms`);
console.log(`Regular traversal: ${regularCount} nodes in ${regularTime}ms`);