Utilities for modifying and transforming existing AST nodes. These functions help manipulate node properties, inheritance, and structure while maintaining AST validity.
Removes specified properties from AST nodes, useful for cleaning or normalizing ASTs.
/**
* Remove specified properties from an AST node
* @param node - Node to modify
* @param options - Configuration for property removal
*/
function removeProperties<T extends t.Node>(
node: T,
options?: RemovePropertiesOptions
): void;
/**
* Options for removeProperties function
*/
interface RemovePropertiesOptions {
/** Properties to preserve (whitelist approach) */
preserveComments?: boolean;
/** Custom properties to remove */
[key: string]: any;
}Recursively removes properties from a node and all its children.
/**
* Recursively remove properties from node and all children
* @param node - Root node to start removal from
* @param options - Configuration for property removal
*/
function removePropertiesDeep<T extends t.Node>(
node: T,
options?: RemovePropertiesOptions
): T;Transfers properties from a parent node to a child node.
/**
* Transfer properties from parent node to child node
* @param child - Node to receive properties
* @param parent - Node to copy properties from
* @returns The child node with inherited properties
*/
function inherits<T extends t.Node>(child: T, parent: t.Node): T;Adds property access to the end of a member expression chain.
/**
* Append property access to member expression
* @param member - Base member expression
* @param append - Property to append
* @param computed - Whether to use computed access (default: false)
* @returns Extended member expression
*/
function appendToMemberExpression(
member: t.MemberExpression,
append: t.Identifier,
computed?: boolean
): t.MemberExpression;Adds property access to the beginning of a member expression chain.
/**
* Prepend property access to member expression
* @param member - Base member expression
* @param prepend - Property to prepend
* @returns Extended member expression with prepended access
*/
function prependToMemberExpression(
member: t.MemberExpression,
prepend: t.Identifier
): t.MemberExpression;Removes duplicate types from Flow union types.
/**
* Remove duplicate types from Flow union type
* @param types - Array of Flow types
* @returns Array with duplicates removed
*/
function removeTypeDuplicates(types: Array<t.FlowType>): Array<t.FlowType>;import * as t from "@babel/types";
// Create node with extra properties
const node = t.identifier("myVar");
node.start = 0;
node.end = 5;
node.loc = { start: { line: 1, column: 0 }, end: { line: 1, column: 5 } };
(node as any).customProperty = "custom value";
// Remove location properties
t.removeProperties(node, { preserveComments: true });
console.log(node.start); // undefined
console.log(node.end); // undefined
console.log(node.loc); // undefined
console.log(node.name); // "myVar" - core properties preserved
console.log((node as any).customProperty); // undefined - custom properties removed// Complex nested structure
const complexNode = t.functionDeclaration(
t.identifier("myFunc"),
[t.identifier("param")],
t.blockStatement([
t.returnStatement(t.identifier("param"))
])
);
// Add location info to all nodes
const addLocationInfo = (node: t.Node) => {
node.start = 0;
node.end = 10;
node.loc = { start: { line: 1, column: 0 }, end: { line: 1, column: 10 } };
};
t.traverse(complexNode, {
enter(path) {
addLocationInfo(path.node);
}
});
// Remove all location info deeply
const cleaned = t.removePropertiesDeep(complexNode);
// Verify all location info is removed throughout the tree
t.traverse(cleaned, {
enter(path) {
console.log(path.node.start); // undefined for all nodes
console.log(path.node.end); // undefined for all nodes
console.log(path.node.loc); // undefined for all nodes
}
});// Parent node with metadata
const parent = t.identifier("parent");
parent.leadingComments = [{ type: "CommentLine", value: " Important comment" }];
parent.loc = { start: { line: 1, column: 0 }, end: { line: 1, column: 6 } };
(parent as any).sourceFile = "input.js";
// Child node to inherit properties
const child = t.identifier("child");
// Transfer properties from parent to child
t.inherits(child, parent);
console.log(child.leadingComments); // Comment from parent
console.log(child.loc); // Location from parent
console.log((child as any).sourceFile); // Custom property from parent
console.log(child.name); // "child" - own properties preserved// Base member expression: obj.prop
const base = t.memberExpression(
t.identifier("obj"),
t.identifier("prop")
);
// Append property: obj.prop.newProp
const appended = t.appendToMemberExpression(
base,
t.identifier("newProp")
);
console.log(appended); // MemberExpression: obj.prop.newProp
// Append with computed access: obj.prop["computed"]
const computed = t.appendToMemberExpression(
base,
t.identifier("computed"),
true
);
console.log(computed); // MemberExpression: obj.prop[computed]
// Prepend property: newObj.obj.prop
const prepended = t.prependToMemberExpression(
base,
t.identifier("newObj")
);
console.log(prepended); // MemberExpression: newObj.obj.prop// Build: window.document.getElementById
let chain = t.memberExpression(
t.identifier("window"),
t.identifier("document")
);
chain = t.appendToMemberExpression(
chain,
t.identifier("getElementById")
);
console.log(chain); // window.document.getElementById
// Build: this.state.user.name
let stateChain = t.memberExpression(
t.thisExpression(),
t.identifier("state")
);
stateChain = t.appendToMemberExpression(stateChain, t.identifier("user"));
stateChain = t.appendToMemberExpression(stateChain, t.identifier("name"));
console.log(stateChain); // this.state.user.name// Build dynamic property access
const obj = t.identifier("data");
const dynamicKey = t.binaryExpression(
"+",
t.stringLiteral("prop_"),
t.identifier("index")
);
// Create: data[prop_ + index]
const dynamicAccess = t.memberExpression(obj, dynamicKey, true);
// Append more properties: data[prop_ + index].value
const extended = t.appendToMemberExpression(
dynamicAccess,
t.identifier("value")
);
console.log(extended); // data[prop_ + index].value// Flow union type with duplicates
const types = [
t.stringTypeAnnotation(),
t.numberTypeAnnotation(),
t.stringTypeAnnotation(), // duplicate
t.booleanTypeAnnotation(),
t.numberTypeAnnotation(), // duplicate
];
// Remove duplicates
const uniqueTypes = t.removeTypeDuplicates(types);
console.log(uniqueTypes.length); // 3 instead of 5
// Create union type without duplicates
const unionType = t.unionTypeAnnotation(uniqueTypes);// Clean nodes for code generation (remove all metadata)
function cleanForGeneration<T extends t.Node>(node: T): T {
const cleaned = t.cloneDeep(node);
// Remove all location and debugging info
t.removePropertiesDeep(cleaned, {
preserveComments: false // Remove comments too for clean generation
});
return cleaned;
}
// Usage with complex AST
const sourceAST = /* parsed from source file with locations */;
const cleanedAST = cleanForGeneration(sourceAST);
// Generate clean code without source mapping info
const generated = generate(cleanedAST);// Transfer properties during AST transformation
function transformWithMetadata<T extends t.Node>(
original: t.Node,
transformed: T
): T {
// Inherit location and comments from original
t.inherits(transformed, original);
// Transfer custom metadata
if ((original as any).sourceMap) {
(transformed as any).sourceMap = (original as any).sourceMap;
}
if ((original as any).transformationId) {
(transformed as any).transformationId = (original as any).transformationId;
}
return transformed;
}
// Usage in transformation
const originalExpr = t.binaryExpression("+", t.identifier("a"), t.identifier("b"));
originalExpr.loc = /* location info */;
originalExpr.leadingComments = /* comments */;
const transformedExpr = t.callExpression(
t.identifier("add"),
[t.identifier("a"), t.identifier("b")]
);
const result = transformWithMetadata(originalExpr, transformedExpr);
// result now has location info and comments from original// Remove only specific properties while preserving others
function removeLocationOnly<T extends t.Node>(node: T): T {
const cleaned = t.cloneNode(node);
// Remove only location properties, preserve everything else
delete cleaned.start;
delete cleaned.end;
delete cleaned.loc;
delete cleaned.range;
return cleaned;
}
// Remove custom debugging properties
function removeDebugInfo<T extends t.Node>(node: T): T {
const cleaned = t.cloneNode(node);
// Remove debug-specific properties
delete (cleaned as any).debugId;
delete (cleaned as any).sourceFile;
delete (cleaned as any).transformHistory;
return cleaned;
}// Apply modifications to multiple nodes
function batchModify(nodes: t.Node[], modifications: (node: t.Node) => void) {
nodes.forEach(node => {
modifications(node);
// Also apply to all children
t.traverse(node, {
enter(path) {
modifications(path.node);
}
});
});
}
// Usage
const nodes = [/* array of AST nodes */];
batchModify(nodes, (node) => {
// Remove all location info
t.removeProperties(node);
// Add custom tracking
(node as any).processed = true;
(node as any).timestamp = Date.now();
});