CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-hermes-parser

A JavaScript parser built from the Hermes engine that supports ES6, Flow, and JSX syntax

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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() {}
});

docs

index.md

parsing.md

transformation.md

traversal.md

utilities.md

tile.json