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";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");
}
}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);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 placeUtilities 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); // trueUtilities 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);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[];
}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);
}