or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

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

utilities.mddocs/

AST Utilities

Helper functions for AST node manipulation, visitor key management, and compatibility utilities.

All utility functions can be imported individually or through namespace imports:

// Individual imports
import { isNode, getVisitorKeys, nodeWith, shallowCloneNode } from "hermes-parser";

// Namespace imports
import { astNodeMutationHelpers, astArrayMutationHelpers } from "hermes-parser";

Capabilities

Node Type Checking

Utilities for validating and checking AST node types and structures.

/**
 * Type guard to check if a value is an AST node
 * @param value - Value to test  
 * @returns True if value is an AST node with type property
 */
function isNode(value: unknown): value is ESNode;

Usage Examples:

import { isNode } from "hermes-parser";

// Check various values
console.log(isNode({ type: "Identifier", name: "x" })); // true
console.log(isNode({ type: "Literal", value: 42 })); // true
console.log(isNode("not a node")); // false
console.log(isNode(null)); // false
console.log(isNode({ name: "x" })); // false (no type property)

// Use in traversal logic
function processNode(value) {
  if (isNode(value)) {
    console.log(`Processing ${value.type} node`);
    // Safe to access node properties
  } else {
    console.log("Skipping non-node value");
  }
}

Visitor Key Management

Functions for managing visitor keys that control AST traversal behavior.

/**
 * Get visitor keys for a specific AST node type
 * @param node - AST node to get traversal keys for
 * @param visitorKeys - Optional custom visitor keys mapping
 * @returns Array of property names that should be traversed
 */
function getVisitorKeys<T extends ESNode>(
  node: T,
  visitorKeys?: VisitorKeysType
): ReadonlyArray<keyof T>;

/**
 * Default Flow ESTree visitor keys mapping
 * Maps each AST node type to its traversable properties
 */
const FlowVisitorKeys: VisitorKeysType;

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

Visitor Key Examples:

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

const functionNode = {
  type: "FunctionDeclaration",
  id: { type: "Identifier", name: "test" },
  params: [],
  body: { type: "BlockStatement", body: [] }
};

// Get default visitor keys for function
const keys = getVisitorKeys(functionNode);
console.log(keys); 
// ["id", "params", "body", "typeParameters", "returnType", "predicate"]

// Check what keys exist for different node types
console.log(FlowVisitorKeys.Identifier); // []
console.log(FlowVisitorKeys.CallExpression); // ["callee", "typeArguments", "arguments"]
console.log(FlowVisitorKeys.MemberExpression); // ["object", "property"]

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

const customTraversalKeys = getVisitorKeys(functionNode, customKeys);

Prettier Compatibility

Utility for making AST compatible with Prettier formatting tools.

/**
 * Mutate ESTree AST to be compatible with Prettier formatting
 * Modifies the AST in place to work around Prettier-specific requirements
 * @param rootNode - Program node to mutate
 * @param visitorKeys - Optional visitor keys for traversal
 */
function mutateESTreeASTForPrettier(
  rootNode: Program,
  visitorKeys?: VisitorKeysType
): void;

Prettier Compatibility Examples:

import { mutateESTreeASTForPrettier, parse } from "hermes-parser";

const code = "const x = obj?.method?.();";
const ast = parse(code);

// Make AST compatible with Prettier V2
mutateESTreeASTForPrettier(ast);

// Now the AST can be formatted with Prettier
// Note: This modifies the AST in place

AST Node Cloning

Utilities for creating copies of AST nodes with proper structure preservation.

/**
 * Create shallow clone of AST node with correct parent pointers
 * @param node - AST node to clone
 * @param visitorKeys - Optional visitor keys for parent management
 * @returns Shallow clone with updated parent pointers
 */
function shallowCloneNode<T extends ESNode>(
  node: T,
  visitorKeys?: VisitorKeysType
): T;

/**
 * Create deep clone of AST node and entire subtree
 * @param node - AST node to clone  
 * @param visitorKeys - Optional visitor keys for parent management
 * @returns Deep clone with all children and correct parent pointers
 */
function deepCloneNode<T extends ESNode>(
  node: T,
  visitorKeys?: VisitorKeysType
): T;

Cloning Examples:

import { shallowCloneNode, deepCloneNode } from "hermes-parser";

const originalNode = {
  type: "FunctionDeclaration",
  id: { type: "Identifier", name: "test" },
  params: [],
  body: { type: "BlockStatement", body: [] }
};

// Shallow clone - children are still shared references
const shallowCopy = shallowCloneNode(originalNode);
console.log(shallowCopy.id === originalNode.id); // true (shared reference)

// Deep clone - entire subtree is copied
const deepCopy = deepCloneNode(originalNode);
console.log(deepCopy.id === originalNode.id); // false (separate copy)
console.log(deepCopy.id.name === originalNode.id.name); // true (same value)

// Parent pointers are correctly set in both cases
console.log(deepCopy.id.parent === deepCopy); // true

Parent Pointer Management

Utilities for managing parent relationships in AST nodes.

/**
 * Set parent pointers on direct children of a node
 * @param node - Parent node to update children for
 * @param visitorKeys - Optional visitor keys for traversal
 */
function setParentPointersInDirectChildren(
  node: ESNode,
  visitorKeys?: VisitorKeysType
): void;

/**
 * Update all parent pointers in entire subtree
 * @param node - Root node of subtree to update
 * @param visitorKeys - Optional visitor keys for traversal
 */
function updateAllParentPointers(
  node: ESNode,
  visitorKeys?: VisitorKeysType
): void;

Parent Pointer Examples:

import { 
  setParentPointersInDirectChildren, 
  updateAllParentPointers 
} from "hermes-parser";

const ast = parse("function test() { return x + y; }");

// Update only direct children
setParentPointersInDirectChildren(ast);

// Update entire subtree (recommended after manual AST construction)
updateAllParentPointers(ast);

// Verify parent pointers are set
function verifyParents(node, expectedParent = null) {
  console.log(`${node.type} parent:`, node.parent?.type || 'null');
  
  // Check each child has correct parent
  const keys = getVisitorKeys(node);
  keys.forEach(key => {
    const child = node[key];
    if (Array.isArray(child)) {
      child.forEach(item => {
        if (isNode(item)) {
          verifyParents(item, node);
        }
      });
    } else if (isNode(child)) {
      verifyParents(child, node);
    }
  });
}

verifyParents(ast);

Type Definitions

Core type definitions used throughout the utility functions.

/**
 * Base interface for all AST nodes
 */
interface ESNode {
  type: string;
  range?: [number, number];
  loc?: SourceLocation;
  parent?: ESNode;
}

/**
 * Source location information
 */
interface SourceLocation {
  start: Position;
  end: Position;
}

interface Position {
  line: number;
  column: number;
}

/**
 * Visitor keys mapping type
 */
type VisitorKeysType = {
  [nodeType: string]: ReadonlyArray<string>;
};

/**
 * Program AST node type
 */
interface Program extends ESNode {
  type: "Program";
  body: Statement[];
  sourceType: "script" | "module";
  comments?: Comment[];
  tokens?: Token[];
}

Utility Patterns

Common patterns for using AST utilities in real applications.

Safe Node Property Access:

function safeGetProperty(node, path) {
  const parts = path.split('.');
  let current = node;
  
  for (const part of parts) {
    if (!isNode(current) && typeof current !== 'object') {
      return undefined;
    }
    current = current[part];
  }
  
  return current;
}

// Usage
const memberName = safeGetProperty(callExpression, 'callee.property.name');

Custom Visitor Key Creation:

function createCustomVisitorKeys(additionalKeys = {}) {
  return {
    ...FlowVisitorKeys,
    ...additionalKeys,
    // Add custom traversal for specific nodes
    CustomWrapper: ['wrappedNode', 'metadata'],
    EnhancedIdentifier: ['name', 'typeAnnotation', 'customData']
  };
}

const customKeys = createCustomVisitorKeys({
  SpecialNode: ['specialProp1', 'specialProp2']
});

AST Structure Validation:

function validateASTStructure(node, depth = 0) {
  if (depth > 100) {
    throw new Error('AST too deep - possible circular reference');
  }
  
  if (!isNode(node)) {
    throw new Error(`Invalid node at depth ${depth}: ${typeof node}`);
  }
  
  const keys = getVisitorKeys(node);
  for (const key of keys) {
    const child = node[key];
    
    if (Array.isArray(child)) {
      child.forEach((item, index) => {
        if (isNode(item)) {
          if (item.parent !== node) {
            console.warn(`Parent pointer mismatch at ${key}[${index}]`);
          }
          validateASTStructure(item, depth + 1);
        }
      });
    } else if (isNode(child)) {
      if (child.parent !== node) {
        console.warn(`Parent pointer mismatch at ${key}`);
      }
      validateASTStructure(child, depth + 1);
    }
  }
}

// Usage
try {
  validateASTStructure(ast);
  console.log('AST structure is valid');
} catch (error) {
  console.error('AST validation failed:', error.message);
}