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

modifications.mddocs/

AST Modifications

Utilities for modifying and transforming existing AST nodes. These functions help manipulate node properties, inheritance, and structure while maintaining AST validity.

Capabilities

Property Removal

Remove Properties

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;
}

Remove Properties Deep

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;

Inheritance and Property Transfer

Inherits

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;

Member Expression Manipulation

Append to Member Expression

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;

Prepend to Member Expression

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;

Type-Specific Modifications

Remove Type Duplicates (Flow)

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>;

Usage Examples

Property Removal

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

Deep Property Removal

// 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
  }
});

Property Inheritance

// 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

Member Expression Manipulation

// 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

Building Complex Member Chains

// 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

Dynamic Property Access

// 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 Type Deduplication

// 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);

Node Cleaning for Code Generation

// 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);

Property Transfer for Transformations

// 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

Selective Property Removal

// 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;
}

Batch Modifications

// 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();
});