Functions to convert between different AST node types and representations. These utilities help transform nodes while maintaining semantic correctness and proper AST structure.
Converts statements to expressions where possible.
/**
* Convert a statement or expression to an expression
* @param node - Statement or expression to convert
* @returns Expression node
* @throws Error if statement cannot be converted to expression
*/
function toExpression(node: t.Statement | t.Expression): t.Expression;Converts expressions to statements by wrapping in expression statements.
/**
* Convert an expression or statement to a statement
* @param node - Expression or statement to convert
* @returns Statement node (wraps expressions in ExpressionStatement)
*/
function toStatement(node: t.Statement | t.Expression): t.Statement;Ensures a statement is wrapped in a block statement.
/**
* Ensure a statement is wrapped in a block statement
* @param node - Statement to wrap
* @returns BlockStatement containing the node
*/
function ensureBlock(node: t.Statement): t.BlockStatement;Converts a statement to a block statement.
/**
* Convert a statement to a block statement
* @param node - Statement to convert
* @returns BlockStatement (wraps non-block statements)
*/
function toBlock(node: t.Statement): t.BlockStatement;Converts strings or existing identifiers to identifier nodes.
/**
* Convert string or identifier to identifier node
* @param name - String name or existing identifier
* @returns Identifier node with valid name
*/
function toIdentifier(name: string | t.Identifier): t.Identifier;Converts a string to a valid binding identifier name.
/**
* Convert string to valid binding identifier name
* @param name - String to convert
* @returns Valid identifier name for variable bindings
*/
function toBindingIdentifierName(name: string): string;Converts property keys to string aliases for object property access.
/**
* Convert object property key to string alias
* @param key - Property key node
* @returns String representation of the key
*/
function toKeyAlias(key: t.Expression): string;Converts property keys to computed property format when needed.
/**
* Convert property key to computed key format if needed
* @param key - Property key node
* @returns Key in computed format or original if not needed
*/
function toComputedKey(key: t.Expression): t.Expression;Converts JavaScript values to appropriate AST literal nodes.
/**
* Convert JavaScript value to appropriate AST literal node
* @param value - JavaScript value to convert
* @returns Appropriate literal node or complex structure for objects/arrays
*/
function valueToNode(value: any): t.Expression;Legacy function for creating sequence expressions (conditionally available).
/**
* Convert array of expressions to sequence expression (legacy)
* @param expressions - Array of expressions
* @returns SequenceExpression or single expression if array has one element
* @deprecated Available only in non-breaking builds
*/
function toSequenceExpression(expressions: t.Expression[]): t.Expression;import * as t from "@babel/types";
// Convert statement to expression
const returnStmt = t.returnStatement(t.numericLiteral(42));
try {
const expr = t.toExpression(returnStmt);
console.log(expr); // NumericLiteral(42) - return statement stripped
} catch (error) {
console.log("Cannot convert to expression");
}
// Convert expression to statement
const expr = t.binaryExpression("+", t.identifier("a"), t.identifier("b"));
const stmt = t.toStatement(expr);
console.log(stmt.type); // "ExpressionStatement"
console.log((stmt as t.ExpressionStatement).expression === expr); // true// Ensure statement is in block
const ifStmt = t.ifStatement(
t.identifier("condition"),
t.expressionStatement(t.callExpression(t.identifier("fn"), []))
);
const blockStmt = t.ensureBlock(ifStmt.consequent);
console.log(blockStmt.type); // "BlockStatement"
console.log(blockStmt.body.length); // 1
// Convert single statement to block
const singleStmt = t.returnStatement(t.identifier("value"));
const block = t.toBlock(singleStmt);
console.log(block.body.length); // 1
console.log(block.body[0] === singleStmt); // true
// Already a block - returns as-is
const existingBlock = t.blockStatement([singleStmt]);
const unchangedBlock = t.toBlock(existingBlock);
console.log(unchangedBlock === existingBlock); // true// String to identifier
const id1 = t.toIdentifier("myVariable");
console.log(id1.type); // "Identifier"
console.log(id1.name); // "myVariable"
// Already identifier - returns as-is
const existingId = t.identifier("existing");
const id2 = t.toIdentifier(existingId);
console.log(id2 === existingId); // true
// Invalid identifier names are cleaned
const cleanId = t.toIdentifier("123invalid-name!");
console.log(cleanId.name); // Valid identifier name// Clean names for variable bindings
const validName1 = t.toBindingIdentifierName("valid_name");
console.log(validName1); // "valid_name"
const validName2 = t.toBindingIdentifierName("123-invalid");
console.log(validName2); // Cleaned to valid identifier
const validName3 = t.toBindingIdentifierName("class");
console.log(validName3); // Escaped reserved word// Property key to alias
const stringKey = t.stringLiteral("myProperty");
const alias1 = t.toKeyAlias(stringKey);
console.log(alias1); // "myProperty"
const identifierKey = t.identifier("propertyName");
const alias2 = t.toKeyAlias(identifierKey);
console.log(alias2); // "propertyName"
const numericKey = t.numericLiteral(42);
const alias3 = t.toKeyAlias(numericKey);
console.log(alias3); // "42"
// Computed key conversion
const simpleKey = t.identifier("simple");
const computed1 = t.toComputedKey(simpleKey);
console.log(computed1 === simpleKey); // true - doesn't need computation
const complexKey = t.binaryExpression("+", t.stringLiteral("prefix"), t.identifier("suffix"));
const computed2 = t.toComputedKey(complexKey);
console.log(computed2 === complexKey); // true - already computed expression// Primitive values
const stringNode = t.valueToNode("hello world");
console.log(stringNode.type); // "StringLiteral"
console.log((stringNode as t.StringLiteral).value); // "hello world"
const numberNode = t.valueToNode(42);
console.log(numberNode.type); // "NumericLiteral"
const booleanNode = t.valueToNode(true);
console.log(booleanNode.type); // "BooleanLiteral"
const nullNode = t.valueToNode(null);
console.log(nullNode.type); // "NullLiteral"
const undefinedNode = t.valueToNode(undefined);
console.log(undefinedNode.type); // "Identifier" with name "undefined"
// Array conversion
const arrayNode = t.valueToNode([1, "two", true]);
console.log(arrayNode.type); // "ArrayExpression"
const arrayExpr = arrayNode as t.ArrayExpression;
console.log(arrayExpr.elements.length); // 3
console.log((arrayExpr.elements[0] as t.NumericLiteral).value); // 1
console.log((arrayExpr.elements[1] as t.StringLiteral).value); // "two"
// Object conversion
const objectNode = t.valueToNode({
name: "John",
age: 30,
active: true
});
console.log(objectNode.type); // "ObjectExpression"
const objExpr = objectNode as t.ObjectExpression;
console.log(objExpr.properties.length); // 3
// Nested structures
const nestedNode = t.valueToNode({
users: [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" }
],
config: {
debug: true,
version: "1.0.0"
}
});
console.log(nestedNode.type); // "ObjectExpression" with nested structures// Ensure if statements have block bodies
const ifStmt = t.ifStatement(
t.identifier("condition"),
t.expressionStatement(t.callExpression(t.identifier("action"), []))
);
// Convert consequent to block
const blockConsequent = t.ensureBlock(ifStmt.consequent);
ifStmt.consequent = blockConsequent;
// Now safe to add multiple statements
blockConsequent.body.push(
t.expressionStatement(t.callExpression(t.identifier("cleanup"), []))
);
// Ensure else clause is also a block
if (ifStmt.alternate) {
ifStmt.alternate = t.ensureBlock(ifStmt.alternate);
}// Convert expression to function body
const arrowFunction = t.arrowFunctionExpression(
[t.identifier("x")],
t.binaryExpression("*", t.identifier("x"), t.numericLiteral(2))
);
// Convert to regular function with block body
const functionExpr = t.functionExpression(
null,
arrowFunction.params,
t.toBlock(t.returnStatement(arrowFunction.body as t.Expression))
);
console.log(functionExpr.body.type); // "BlockStatement"
console.log(functionExpr.body.body.length); // 1 - return statement// Ensure switch cases have block statements
const switchStmt = t.switchStatement(
t.identifier("value"),
[
t.switchCase(
t.stringLiteral("case1"),
[t.expressionStatement(t.callExpression(t.identifier("handle1"), []))]
),
t.switchCase(
t.stringLiteral("case2"),
[t.expressionStatement(t.callExpression(t.identifier("handle2"), []))]
)
]
);
// Convert each case to have block statements
switchStmt.cases.forEach(caseNode => {
if (caseNode.consequent.length === 1 && !t.isBlockStatement(caseNode.consequent[0])) {
const block = t.toBlock(caseNode.consequent[0]);
caseNode.consequent = [block];
}
});// Generate code with proper conversions
function createGetter(propertyName: string, returnType: "expression" | "statement") {
const propertyAccess = t.memberExpression(
t.thisExpression(),
t.toIdentifier(propertyName)
);
const returnNode = t.returnStatement(propertyAccess);
if (returnType === "expression") {
return t.toExpression(returnNode); // Just the property access
} else {
return t.toStatement(returnNode); // Return statement
}
}
function createMethod(methodName: string, body: t.Statement[]) {
return t.objectMethod(
"method",
t.toIdentifier(methodName),
[],
t.blockStatement(body.map(stmt => t.toStatement(stmt)))
);
}