Utilities for modifying and transforming AST nodes with immutable patterns and parent pointer management.
Main class for transforming AST nodes with callback-based mutation and automatic parent pointer management.
/**
* A simple class for recursively transforming AST trees
*/
class SimpleTransform {
/**
* Transform AST tree with callback (static method)
* @param node - Root AST node to transform
* @param options - Transformation configuration
* @returns Transformed node or null if removed
*/
static transform(node: ESNode, options: TransformOptions): ESNode | null;
/**
* Transform Program node specifically
* @param program - Program AST node to transform
* @param options - Transformation configuration
* @returns Transformed Program node
*/
static transformProgram(program: Program, options: TransformOptions): Program;
/**
* Create new node with property overrides
* @param node - Base AST node to modify
* @param overrideProps - Properties to override
* @param visitorKeys - Optional visitor keys for parent pointer management
* @returns New node with overrides or original if unchanged
*/
static nodeWith<T extends ESNode>(
node: T,
overrideProps: Partial<T>,
visitorKeys?: VisitorKeysType
): T;
/**
* Transform AST tree with callback (instance method)
* @param rootNode - Root AST node to transform
* @param options - Transformation configuration
* @returns Transformed node or null if removed
*/
transform(rootNode: ESNode, options: TransformOptions): ESNode | null;
}
interface TransformOptions {
/** Callback invoked for each node to perform transformation */
transform: TransformCallback;
/** Custom visitor keys for traversal (optional) */
visitorKeys?: VisitorKeysType;
}
/**
* Transform callback function
* @param node - Current AST node being visited
* @returns Modified node, original node, or null to remove
*/
type TransformCallback = (node: ESNode) => ESNode | null;Usage Examples:
import { SimpleTransform } from "hermes-parser";
const ast = parse("const x = 1; const y = 2;");
// Remove all const declarations
const transformed = SimpleTransform.transform(ast, {
transform(node) {
if (node.type === "VariableDeclaration" && node.kind === "const") {
return null; // Remove this node
}
return node; // Keep unchanged
}
});
// Transform literals to double their value
const doubled = SimpleTransform.transform(ast, {
transform(node) {
if (node.type === "Literal" && typeof node.value === "number") {
return {
...node,
value: node.value * 2,
raw: String(node.value * 2)
};
}
return node;
}
});
// Use transformProgram for program-specific transforms
const program = SimpleTransform.transformProgram(ast, {
transform(node) {
// Add "use strict" directive at the beginning
if (node.type === "Program") {
return {
...node,
body: [
{
type: "ExpressionStatement",
expression: {
type: "Literal",
value: "use strict",
raw: '"use strict"'
}
},
...node.body
]
};
}
return node;
}
});Helper functions for creating and modifying AST nodes with proper parent pointer management.
/**
* Create new node with property overrides, maintaining referential equality when possible
* @param node - Base AST node
* @param overrideProps - Properties to override
* @param visitorKeys - Optional visitor keys for parent management
* @returns New node with overrides or original if unchanged
*/
function nodeWith<T extends ESNode>(
node: T,
overrideProps: Partial<T>,
visitorKeys?: VisitorKeysType
): T;
/**
* Shallow clone an AST node with correct parent pointers
* @param node - AST node to clone
* @param visitorKeys - Optional visitor keys for parent management
* @returns Shallow clone of the node
*/
function shallowCloneNode<T extends ESNode>(
node: T,
visitorKeys?: VisitorKeysType
): T;
/**
* Deep clone an AST node and entire subtree
* @param node - AST node to clone
* @param visitorKeys - Optional visitor keys for parent management
* @returns Deep clone of node and all children
*/
function deepCloneNode<T extends ESNode>(
node: T,
visitorKeys?: VisitorKeysType
): T;Node Modification Examples:
import { SimpleTransform } from "hermes-parser";
// Efficient node modification with nodeWith
const updatedNode = SimpleTransform.nodeWith(identifierNode, {
name: "newName"
});
// Only creates new object if properties actually changed
const sameNode = SimpleTransform.nodeWith(identifierNode, {
name: identifierNode.name // No change, returns same reference
});
// Manual node modification in transform
SimpleTransform.transform(ast, {
transform(node) {
if (node.type === "Identifier" && node.name === "oldName") {
return SimpleTransform.nodeWith(node, { name: "newName" });
}
return node;
}
});Low-level utilities for directly manipulating AST nodes and their relationships.
/**
* Replace a node with new node in its parent
* @param originalNode - Node to replace
* @param originalNodeParent - Parent containing the node
* @param nodeToReplaceWith - New node to insert
* @param visitorKeys - Optional visitor keys
*/
function replaceNodeOnParent(
originalNode: ESNode,
originalNodeParent: ESNode,
nodeToReplaceWith: ESNode,
visitorKeys?: VisitorKeysType
): void;
/**
* Remove a node from its parent
* @param originalNode - Node to remove
* @param originalNodeParent - Parent containing the node
* @param visitorKeys - Optional visitor keys
*/
function removeNodeOnParent(
originalNode: ESNode,
originalNodeParent: ESNode,
visitorKeys?: VisitorKeysType
): void;
/**
* Set parent pointers on direct children of a node
* @param node - Parent node
* @param visitorKeys - Optional visitor keys
*/
function setParentPointersInDirectChildren(
node: ESNode,
visitorKeys?: VisitorKeysType
): void;
/**
* Update all parent pointers in an entire subtree
* @param node - Root node of subtree
* @param visitorKeys - Optional visitor keys
*/
function updateAllParentPointers(
node: ESNode,
visitorKeys?: VisitorKeysType
): void;Utilities for manipulating arrays within AST nodes during transformations.
/**
* Check if two arrays are equal by reference and content
* @param a1 - First array
* @param a2 - Second array
* @returns True if arrays are equal
*/
function arrayIsEqual(a1: ReadonlyArray<unknown>, a2: ReadonlyArray<unknown>): boolean;
/**
* Insert elements at a specific index in array
* @param array - Source array
* @param index - Index to insert at
* @param elements - Elements to insert
* @returns New array with elements inserted
*/
function insertInArray<T>(
array: ReadonlyArray<T>,
index: number,
elements: ReadonlyArray<T>
): T[];
/**
* Remove element at specific index from array
* @param array - Source array
* @param index - Index to remove
* @returns New array with element removed
*/
function removeFromArray<T>(array: ReadonlyArray<T>, index: number): T[];
/**
* Replace element at index with new elements
* @param array - Source array
* @param index - Index to replace
* @param elements - New elements to insert
* @returns New array with replacement
*/
function replaceInArray<T>(
array: ReadonlyArray<T>,
index: number,
elements: ReadonlyArray<T>
): T[];Array Manipulation Examples:
import { insertInArray, removeFromArray, replaceInArray } from "hermes-parser";
const statements = [stmt1, stmt2, stmt3];
// Insert new statements
const withInserted = insertInArray(statements, 1, [newStmt1, newStmt2]);
// Result: [stmt1, newStmt1, newStmt2, stmt2, stmt3]
// Remove statement
const withRemoved = removeFromArray(statements, 1);
// Result: [stmt1, stmt3]
// Replace statement
const withReplaced = replaceInArray(statements, 1, [replacementStmt]);
// Result: [stmt1, replacementStmt, stmt3]Ready-to-use transformation functions for common AST modifications.
/**
* Collection of pre-built AST transformation functions
*/
const Transforms: {
/** Transform experimental match syntax */
transformMatchSyntax: (program: Program, options: ParserOptions) => Program;
/** Strip experimental component syntax */
stripComponentSyntax: (program: Program, options: ParserOptions) => Program;
/** Strip Flow types for Babel compatibility */
stripFlowTypesForBabel: (program: Program, options: ParserOptions) => Program;
/** Strip Flow types for ESTree output */
stripFlowTypes: (program: Program, options: ParserOptions) => Program;
};Pre-built Transform Examples:
import { Transforms, parse } from "hermes-parser";
const flowCode = "function add(x: number, y: number): number { return x + y; }";
const ast = parse(flowCode, { flow: "all" });
// Strip Flow types for plain JavaScript
const cleanAst = Transforms.stripFlowTypes(ast, { flow: "all" });
// Chain multiple transforms
const processedAst = [
Transforms.transformMatchSyntax,
Transforms.stripComponentSyntax,
Transforms.stripFlowTypesForBabel
].reduce((ast, transform) => transform(ast, { flow: "all", babel: true }), ast);Common patterns for AST transformation workflows.
Conditional Node Replacement:
SimpleTransform.transform(ast, {
transform(node) {
// Replace console.log with custom logger
if (node.type === "CallExpression" &&
node.callee.type === "MemberExpression" &&
node.callee.object.name === "console" &&
node.callee.property.name === "log") {
return {
...node,
callee: {
type: "Identifier",
name: "customLogger"
}
};
}
return node;
}
});Node Removal with Conditions:
SimpleTransform.transform(ast, {
transform(node) {
// Remove all debugger statements
if (node.type === "DebuggerStatement") {
return null;
}
// Remove empty statements
if (node.type === "EmptyStatement") {
return null;
}
return node;
}
});Complex Node Restructuring:
SimpleTransform.transform(ast, {
transform(node) {
// Convert arrow functions to regular functions
if (node.type === "ArrowFunctionExpression") {
return {
type: "FunctionExpression",
id: null,
params: node.params,
body: node.body.type === "BlockStatement"
? node.body
: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: node.body
}]
},
generator: false,
async: node.async
};
}
return node;
}
});