or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

builders.mdcloning.mdcomments.mdconstants.mdconverters.mdindex.mdmodifications.mdreact.mdretrievers.mdtraversal.mdvalidators.md
tile.json

cloning.mddocs/

Node Cloning

Utilities for duplicating AST nodes with various levels of depth and location information handling. Cloning is essential when transforming ASTs to avoid modifying original nodes.

Capabilities

Basic Cloning Functions

Clone Node

The main cloning function with configurable depth options.

/**
 * Clone an AST node with optional deep cloning
 * @param node - Node to clone
 * @param deep - Whether to perform deep clone (default: true)
 * @param withoutLoc - Whether to exclude location info (default: false)
 * @returns Cloned node
 */
function cloneNode<T extends t.Node>(
  node: T,
  deep?: boolean,
  withoutLoc?: boolean
): T;

Simple Clone

@deprecated Use t.cloneNode instead.

Alias for shallow cloning (backwards compatibility).

/**
 * Shallow clone of an AST node (alias for cloneNode with deep=false)
 * @deprecated Use t.cloneNode instead.
 * @param node - Node to clone
 * @returns Shallow cloned node
 */
function clone<T extends t.Node>(node: T): T;

Deep Cloning Functions

Deep Clone

Performs a complete deep clone including all nested nodes.

/**
 * Perform deep clone of AST node and all children
 * @param node - Node to deep clone
 * @returns Deep cloned node with all children cloned
 */
function cloneDeep<T extends t.Node>(node: T): T;

Deep Clone Without Location

Deep clones while removing all location information (useful for generated code).

/**
 * Deep clone AST node without location information
 * @param node - Node to clone
 * @returns Deep cloned node without start/end/loc properties
 */
function cloneDeepWithoutLoc<T extends t.Node>(node: T): T;

Specialized Cloning Functions

Clone Without Location

Clones node while removing location information but preserving structure.

/**
 * Clone node without location information (start, end, loc)
 * @param node - Node to clone
 * @returns Cloned node without location properties
 */
function cloneWithoutLoc<T extends t.Node>(node: T): T;

Cloning Behavior

Location Information Handling

Location information includes:

  • start: Starting position in source
  • end: Ending position in source
  • loc: Location object with line/column information
  • range: Source range array

Deep vs Shallow Cloning

Shallow Clone (clone, cloneNode(node, false)):

  • Creates new node object
  • Copies primitive properties
  • Shares references to child nodes
  • Fast but modifications affect original children

Deep Clone (cloneDeep, cloneNode(node, true)):

  • Creates new node object
  • Recursively clones all child nodes
  • No shared references
  • Slower but completely independent

Property Preservation

All cloning functions preserve:

  • Node type
  • All node-specific properties
  • Comments (leadingComments, trailingComments, innerComments)
  • Custom properties added to nodes

Usage Examples

Basic Cloning

import * as t from "@babel/types";

const original = t.identifier("myVar");
original.typeAnnotation = t.tsTypeAnnotation(t.tsStringKeyword());

// Shallow clone - shares type annotation reference
const shallow = t.clone(original);
shallow.name = "newVar";
console.log(original.name); // Still "myVar"
console.log(shallow.typeAnnotation === original.typeAnnotation); // true

// Deep clone - independent copy
const deep = t.cloneDeep(original);
deep.name = "anotherVar";
console.log(deep.typeAnnotation === original.typeAnnotation); // false

Location Information Handling

// Node with location info
const nodeWithLoc = t.identifier("test");
nodeWithLoc.start = 0;
nodeWithLoc.end = 4;
nodeWithLoc.loc = {
  start: { line: 1, column: 0 },
  end: { line: 1, column: 4 }
};

// Clone with location info preserved
const withLoc = t.cloneNode(nodeWithLoc);
console.log(withLoc.start); // 0
console.log(withLoc.loc); // Location object

// Clone without location info
const withoutLoc = t.cloneWithoutLoc(nodeWithLoc);
console.log(withoutLoc.start); // undefined
console.log(withoutLoc.loc); // undefined
console.log(withoutLoc.name); // "test" - other properties preserved

Complex Node Cloning

// Complex node with nested children
const binaryExpr = t.binaryExpression(
  "+",
  t.identifier("a"),
  t.callExpression(t.identifier("func"), [t.numericLiteral(42)])
);

// Shallow clone - children are shared
const shallowClone = t.clone(binaryExpr);
shallowClone.left.name = "modified";
console.log(binaryExpr.left.name); // "modified" - original affected!

// Deep clone - children are independent
const deepClone = t.cloneDeep(binaryExpr);
deepClone.left.name = "independent";
console.log(binaryExpr.left.name); // Still "a" - original unaffected

Function and Statement Cloning

// Function declaration with body
const funcDecl = t.functionDeclaration(
  t.identifier("myFunc"),
  [t.identifier("param")],
  t.blockStatement([
    t.returnStatement(t.identifier("param"))
  ])
);

// Deep clone for transformation
const clonedFunc = t.cloneDeep(funcDecl);
clonedFunc.id.name = "clonedFunc";
clonedFunc.params[0].name = "newParam";

// Original remains unchanged
console.log(funcDecl.id.name); // "myFunc"
console.log(funcDecl.params[0].name); // "param"

Array and Object Cloning

// Array expression
const arrayExpr = t.arrayExpression([
  t.stringLiteral("hello"),
  t.numericLiteral(42),
  t.identifier("variable")
]);

// Clone array - elements are shared in shallow clone
const shallowArray = t.clone(arrayExpr);
shallowArray.elements[0].value = "modified";
console.log(arrayExpr.elements[0].value); // "modified"

// Deep clone array - elements are independent
const deepArray = t.cloneDeep(arrayExpr);
deepArray.elements[0].value = "independent";
console.log(arrayExpr.elements[0].value); // Still "hello"

// Object expression
const objExpr = t.objectExpression([
  t.objectProperty(t.identifier("key"), t.stringLiteral("value")),
  t.objectMethod(
    "method",
    t.identifier("method"),
    [],
    t.blockStatement([])
  )
]);

const clonedObj = t.cloneDeep(objExpr);
// Modify without affecting original
clonedObj.properties[0].key.name = "newKey";

Cloning with Comments

// Node with comments
const nodeWithComments = t.identifier("commented");
nodeWithComments.leadingComments = [
  { type: "CommentLine", value: " This is a comment" }
];
nodeWithComments.trailingComments = [
  { type: "CommentBlock", value: "* Another comment *" }
];

// Comments are preserved in cloning
const clonedWithComments = t.cloneDeep(nodeWithComments);
console.log(clonedWithComments.leadingComments); // Comment array
console.log(clonedWithComments.trailingComments); // Comment array

// Comments removed with location info
const clonedWithoutLoc = t.cloneDeepWithoutLoc(nodeWithComments);
console.log(clonedWithoutLoc.leadingComments); // Still present - only loc removed

Template and Generated Code

// Template for code generation
const template = t.functionExpression(
  null,
  [t.identifier("PARAM_NAME")],
  t.blockStatement([
    t.returnStatement(t.identifier("RETURN_VALUE"))
  ])
);

// Generate multiple functions from template
function generateFunction(paramName: string, returnValue: string) {
  const func = t.cloneDeep(template);
  
  // Replace placeholders
  func.params[0].name = paramName;
  const returnStmt = func.body.body[0] as t.ReturnStatement;
  (returnStmt.argument as t.Identifier).name = returnValue;
  
  return func;
}

const func1 = generateFunction("input", "output");
const func2 = generateFunction("data", "result");

// Each function is independent
console.log(func1.params[0].name); // "input"
console.log(func2.params[0].name); // "data"
console.log(template.params[0].name); // "PARAM_NAME" - unchanged

Performance Considerations

// For large ASTs, consider shallow cloning when possible
const largeAST = /* complex nested structure */;

// Fast - only clones top-level node
const quickClone = t.clone(largeAST);

// Slower - clones entire tree
const fullClone = t.cloneDeep(largeAST);

// Use shallow cloning when you only need to modify top-level properties
function renameFunction(funcDecl: t.FunctionDeclaration, newName: string) {
  const cloned = t.clone(funcDecl);
  cloned.id = t.identifier(newName);
  return cloned; // Body and params are shared but unchanged
}

// Use deep cloning when modifying nested structures
function transformFunction(funcDecl: t.FunctionDeclaration) {
  const cloned = t.cloneDeep(funcDecl);
  // Safe to modify any part of the cloned function
  cloned.body.body.push(t.returnStatement());
  return cloned;
}

Integration with Transformations

import { traverse } from "@babel/types";

// Clone nodes during transformation
traverse(ast, {
  Identifier(path) {
    // Clone before modifying to preserve original AST
    const cloned = t.cloneNode(path.node);
    cloned.name = cloned.name.toUpperCase();
    path.replaceWith(cloned);
  },
  
  BinaryExpression(path) {
    // Deep clone for complex modifications
    const cloned = t.cloneDeep(path.node);
    
    // Swap operands
    const temp = cloned.left;
    cloned.left = cloned.right;
    cloned.right = temp;
    
    path.replaceWith(cloned);
  }
});