Collections of operators, keywords, and utility functions for AST manipulation. These constants provide standardized collections of JavaScript operators and language constructs.
Collections of binary operators organized by category.
/**
* All binary operators supported in expressions
*/
const BINARY_OPERATORS: string[];
/**
* Arithmetic binary operators
*/
const NUMBER_BINARY_OPERATORS: string[];
/**
* Boolean-returning binary operators
*/
const BOOLEAN_BINARY_OPERATORS: string[];
/**
* Equality comparison operators
*/
const EQUALITY_BINARY_OPERATORS: string[];
/**
* Relational comparison operators
*/
const COMPARISON_BINARY_OPERATORS: string[];
/**
* Numeric comparison operators (>, <, >=, <=)
*/
const BOOLEAN_NUMBER_BINARY_OPERATORS: string[];Collections of unary operators by result type.
/**
* All unary operators
*/
const UNARY_OPERATORS: string[];
/**
* Boolean-returning unary operators
*/
const BOOLEAN_UNARY_OPERATORS: string[];
/**
* Number-returning unary operators
*/
const NUMBER_UNARY_OPERATORS: string[];
/**
* String-returning unary operators
*/
const STRING_UNARY_OPERATORS: string[];Logical operators for boolean expressions.
/**
* Logical operators (||, &&, ??)
*/
const LOGICAL_OPERATORS: string[];All assignment operators including compound assignments.
/**
* All assignment operators (=, +=, -=, etc.)
*/
const ASSIGNMENT_OPERATORS: string[];Pre/post increment and decrement operators.
/**
* Update operators (++, --)
*/
const UPDATE_OPERATORS: string[];Property names that contain statement or block-like structures.
/**
* Property keys that can contain statements or blocks
*/
const STATEMENT_OR_BLOCK_KEYS: string[];
/**
* Property keys that can be flattened (contain arrays of statements)
*/
const FLATTENABLE_KEYS: string[];
/**
* Property keys used for loop initialization
*/
const FOR_INIT_KEYS: string[];Property names for different types of comments.
/**
* Property keys for comment arrays
*/
const COMMENT_KEYS: readonly string[];Properties that should be inherited during AST transformations.
/**
* Keys defining inheritance behavior during transformations
*/
const INHERIT_KEYS: {
readonly optional: readonly string[];
readonly force: readonly string[];
};Utility for comparing object properties shallowly.
/**
* Perform shallow equality comparison between objects
* @param actual - First object to compare
* @param expected - Second object to compare
* @returns Whether objects are shallowly equal
*/
function shallowEqual(actual: object, expected: object): boolean;Legacy symbol for block scoping (available in non-breaking builds).
/**
* Symbol used to mark variables that were block scoped
* @deprecated Available only in legacy builds
*/
const BLOCK_SCOPED_SYMBOL: symbol;
/**
* Symbol used to mark bindings that should not be considered local
* @deprecated Available only in legacy builds
*/
const NOT_LOCAL_BINDING: symbol;// Complete list of binary operators
const BINARY_OPERATORS = [
"+", // Addition/concatenation
"-", "/", "%", "*", "**", "&", "|", ">>", ">>>", "<<", "^", // Arithmetic/bitwise
"==", "===", "!=", "!==", // Equality
">", "<", ">=", "<=", // Comparison
"in", "instanceof", // Type/property checks
"|>" // Pipeline operator
];
// Number-returning binary operators
const NUMBER_BINARY_OPERATORS = [
"-", "/", "%", "*", "**", // Arithmetic
"&", "|", ">>", ">>>", "<<", "^" // Bitwise
];
// Boolean-returning comparison operators
const BOOLEAN_NUMBER_BINARY_OPERATORS = [">", "<", ">=", "<="];
const EQUALITY_BINARY_OPERATORS = ["==", "===", "!=", "!=="];
const COMPARISON_BINARY_OPERATORS = [
...EQUALITY_BINARY_OPERATORS,
"in", "instanceof"
];
const BOOLEAN_BINARY_OPERATORS = [
...COMPARISON_BINARY_OPERATORS,
...BOOLEAN_NUMBER_BINARY_OPERATORS
];// All unary operators
const UNARY_OPERATORS = [
"void", "throw", // Special operators
"delete", "!", // Boolean-returning
"+", "-", "~", // Number-returning
"typeof" // String-returning
];
const BOOLEAN_UNARY_OPERATORS = ["delete", "!"];
const NUMBER_UNARY_OPERATORS = ["+", "-", "~"];
const STRING_UNARY_OPERATORS = ["typeof"];const LOGICAL_OPERATORS = ["||", "&&", "??"];
const UPDATE_OPERATORS = ["++", "--"];
// Assignment operators (generated from other operator lists)
const ASSIGNMENT_OPERATORS = [
"=", // Basic assignment
"+=", // Addition assignment
...NUMBER_BINARY_OPERATORS.map(op => op + "="), // Arithmetic assignments
...LOGICAL_OPERATORS.map(op => op + "=") // Logical assignments
];import * as t from "@babel/types";
// Check if operator is valid for binary expressions
function isValidBinaryOperator(operator: string): boolean {
return t.BINARY_OPERATORS.includes(operator);
}
// Check if operator returns boolean
function isBooleanBinaryOperator(operator: string): boolean {
return t.BOOLEAN_BINARY_OPERATORS.includes(operator);
}
// Usage
console.log(isValidBinaryOperator("+")); // true
console.log(isValidBinaryOperator("**")); // true
console.log(isValidBinaryOperator("@")); // false
console.log(isBooleanBinaryOperator(">")); // true
console.log(isBooleanBinaryOperator("+")); // false// Infer result type of binary expression
function inferBinaryExpressionType(operator: string): "number" | "boolean" | "unknown" {
if (t.NUMBER_BINARY_OPERATORS.includes(operator)) {
return "number";
}
if (t.BOOLEAN_BINARY_OPERATORS.includes(operator)) {
return "boolean";
}
return "unknown"; // Could be string concatenation, etc.
}
// Usage
console.log(inferBinaryExpressionType("*")); // "number"
console.log(inferBinaryExpressionType(">")); // "boolean"
console.log(inferBinaryExpressionType("+")); // "unknown" (could be number or string)// Group operators by precedence (simplified example)
function getOperatorPrecedence(operator: string): number {
if (t.LOGICAL_OPERATORS.includes(operator)) {
return operator === "??" ? 3 : operator === "&&" ? 4 : 5;
}
if (t.EQUALITY_BINARY_OPERATORS.includes(operator)) {
return 8;
}
if (t.BOOLEAN_NUMBER_BINARY_OPERATORS.includes(operator)) {
return 9;
}
if (["+", "-"].includes(operator)) {
return 11;
}
if (["*", "/", "%"].includes(operator)) {
return 12;
}
return 0; // Unknown/lowest precedence
}
// Check if parentheses needed
function needsParentheses(
parentOperator: string,
childOperator: string,
isRightChild: boolean = false
): boolean {
const parentPrec = getOperatorPrecedence(parentOperator);
const childPrec = getOperatorPrecedence(childOperator);
if (childPrec < parentPrec) return true;
if (childPrec === parentPrec && isRightChild) return true;
return false;
}// Convert compound assignment to binary expression
function expandAssignment(
left: t.LVal,
operator: string,
right: t.Expression
): t.AssignmentExpression | null {
if (!t.ASSIGNMENT_OPERATORS.includes(operator)) {
return null;
}
if (operator === "=") {
return t.assignmentExpression("=", left, right);
}
// Compound assignment: a += b becomes a = a + b
const binaryOp = operator.slice(0, -1); // Remove '='
if (t.BINARY_OPERATORS.includes(binaryOp)) {
const binaryExpr = t.binaryExpression(
binaryOp as any,
left as t.Expression,
right
);
return t.assignmentExpression("=", left, binaryExpr);
}
return null;
}// Analyze unary expression effects
function analyzeUnaryExpression(operator: string) {
return {
operator,
returnsBoolean: t.BOOLEAN_UNARY_OPERATORS.includes(operator),
returnsNumber: t.NUMBER_UNARY_OPERATORS.includes(operator),
returnsString: t.STRING_UNARY_OPERATORS.includes(operator),
hasSideEffects: operator === "delete",
throwsException: operator === "throw"
};
}
// Usage
console.log(analyzeUnaryExpression("!"));
// { operator: "!", returnsBoolean: true, returnsNumber: false, ... }
console.log(analyzeUnaryExpression("typeof"));
// { operator: "typeof", returnsBoolean: false, returnsString: true, ... }// Check if property key can contain statements
function canContainStatements(key: string): boolean {
return t.STATEMENT_OR_BLOCK_KEYS.includes(key);
}
// Check if property can be flattened
function isFlatteneableProperty(key: string): boolean {
return t.FLATTENABLE_KEYS.includes(key);
}
// Check if property is used for loop initialization
function isLoopInitProperty(key: string): boolean {
return t.FOR_INIT_KEYS.includes(key);
}
// Usage with node analysis
function analyzeNodeStructure(node: t.Node) {
Object.keys(node).forEach(key => {
if (canContainStatements(key)) {
console.log(`${key} can contain statements`);
}
if (isFlatteneableProperty(key)) {
console.log(`${key} can be flattened`);
}
});
}// Check if property contains comments
function isCommentProperty(key: string): boolean {
return t.COMMENT_KEYS.includes(key);
}
// Extract all comments from a node
function extractAllComments(node: t.Node): t.Comment[] {
const comments: t.Comment[] = [];
t.COMMENT_KEYS.forEach(key => {
const commentArray = (node as any)[key];
if (Array.isArray(commentArray)) {
comments.push(...commentArray);
}
});
return comments;
}// Check if property should be inherited
function shouldInheritProperty(key: string, force: boolean = false): boolean {
if (force) {
return t.INHERIT_KEYS.force.includes(key);
}
return t.INHERIT_KEYS.optional.includes(key) || t.INHERIT_KEYS.force.includes(key);
}
// Custom inheritance with constants
function customInheritance<T extends t.Node>(child: T, parent: t.Node): T {
// Force inherit certain properties
t.INHERIT_KEYS.force.forEach(key => {
if ((parent as any)[key] !== undefined) {
(child as any)[key] = (parent as any)[key];
}
});
// Optionally inherit others
t.INHERIT_KEYS.optional.forEach(key => {
if ((parent as any)[key] !== undefined && (child as any)[key] === undefined) {
(child as any)[key] = (parent as any)[key];
}
});
return child;
}// Object comparison with shallowEqual
function compareNodeProperties(node1: t.Node, node2: t.Node): boolean {
// Compare only essential properties, ignoring location info
const props1 = { ...node1 };
const props2 = { ...node2 };
// Remove location properties
delete props1.start;
delete props1.end;
delete props1.loc;
delete props2.start;
delete props2.end;
delete props2.loc;
return t.shallowEqual(props1, props2);
}
// Property filtering
function filterNodeProperties(node: t.Node, includeComments: boolean = true): object {
const filtered: any = {};
Object.keys(node).forEach(key => {
if (!includeComments && isCommentProperty(key)) {
return; // Skip comment properties
}
if (!t.INHERIT_KEYS.force.includes(key)) {
filtered[key] = (node as any)[key];
}
});
return filtered;
}Babel Types provides 50+ automatically generated arrays containing node types organized by functionality. These are essential for type checking and AST analysis.
// Core node type collections
const EXPRESSION_TYPES: string[]; // All expression node types
const STATEMENT_TYPES: string[]; // All statement node types
const DECLARATION_TYPES: string[]; // All declaration node types
const LITERAL_TYPES: string[]; // All literal node types
const FUNCTION_TYPES: string[]; // All function-related node types
const CLASS_TYPES: string[]; // All class-related node types
const PATTERN_TYPES: string[]; // All pattern node types
const LVAL_TYPES: string[]; // All left-value node types
// Structural collections
const BLOCK_TYPES: string[]; // All block statement types
const LOOP_TYPES: string[]; // All loop statement types
const CONDITIONAL_TYPES: string[]; // All conditional statement types
const SCOPABLE_TYPES: string[]; // All scope-creating node types
const BLOCKPARENT_TYPES: string[]; // All block parent node types
const TERMINATORLESS_TYPES: string[]; // Statements that don't need semicolons
// Control flow collections
const COMPLETIONSTATEMENT_TYPES: string[]; // Completion statements
const WHILE_TYPES: string[]; // While loop types
const FOR_TYPES: string[]; // For loop types
const FORXSTATEMENT_TYPES: string[]; // For-in/of statement types
// Expression subcategories
const BINARY_TYPES: string[]; // Binary expression types
const UNARYLIKE_TYPES: string[]; // Unary-like expression types
const EXPRESSIONWRAPPER_TYPES: string[]; // Expression wrapper types
const PUREISH_TYPES: string[]; // Side-effect free expressions
const IMMUTABLE_TYPES: string[]; // Immutable value types
// Member and method collections
const METHOD_TYPES: string[]; // Method definition types
const OBJECTMEMBER_TYPES: string[]; // Object member types
const PROPERTY_TYPES: string[]; // Property-related types
const ACCESSOR_TYPES: string[]; // Accessor (getter/setter) types
const PRIVATE_TYPES: string[]; // Private member types
// Module system collections
const IMPORTOREXPORTDECLARATION_TYPES: string[]; // Import/export declarations
const EXPORTDECLARATION_TYPES: string[]; // Export declaration types
const MODULESPECIFIER_TYPES: string[]; // Module specifier types
// Type system collections (Flow)
const FLOW_TYPES: string[]; // All Flow-related types
const FLOWTYPE_TYPES: string[]; // Flow type annotation types
const FLOWBASEANNOTATION_TYPES: string[]; // Flow base annotation types
const FLOWDECLARATION_TYPES: string[]; // Flow declaration types
const FLOWPREDICATE_TYPES: string[]; // Flow predicate types
// Type system collections (TypeScript)
const TSENTITYNAME_TYPES: string[]; // TypeScript entity name types
const TSTYPE_TYPES: string[]; // TypeScript type types
const TSTYPEELEMENT_TYPES: string[]; // TypeScript type element types
const TS_TYPES: string[]; // All TypeScript-related types
// JSX collections
const JSX_TYPES: string[]; // All JSX-related types
const JSXELEMENT_TYPES: string[]; // JSX element types
// Specialized collections
const STANDARDIZED_TYPES: string[]; // Standardized node types
const FUNCTIONPARENT_TYPES: string[]; // Function parent types
const PATTERNLIKE_TYPES: string[]; // Pattern-like types
const USERWHITESPACABLE_TYPES: string[]; // User-whitespaceable typesimport * as t from "@babel/types";
function analyzeNode(node: t.Node) {
if (t.EXPRESSION_TYPES.includes(node.type)) {
console.log("This is an expression");
}
if (t.STATEMENT_TYPES.includes(node.type)) {
console.log("This is a statement");
}
if (t.FUNCTION_TYPES.includes(node.type)) {
console.log("This is a function-related node");
}
}function findNodesByCategory(ast: t.Node, category: string[]): t.Node[] {
const found: t.Node[] = [];
t.traverse(ast, (node, ancestors, state) => {
if (category.includes(node.type)) {
found.push(node);
}
});
return found;
}
// Find all expressions in an AST
const expressions = findNodesByCategory(ast, t.EXPRESSION_TYPES);
// Find all loop statements
const loops = findNodesByCategory(ast, t.LOOP_TYPES);
// Find all immutable values
const immutables = findNodesByCategory(ast, t.IMMUTABLE_TYPES);function processNodeByType(node: t.Node) {
if (t.LITERAL_TYPES.includes(node.type)) {
console.log("Processing literal value");
// Handle literals
} else if (t.BINARY_TYPES.includes(node.type)) {
console.log("Processing binary operation");
// Handle binary expressions
} else if (t.FUNCTION_TYPES.includes(node.type)) {
console.log("Processing function");
// Handle functions
}
}function filterPureExpressions(nodes: t.Node[]): t.Node[] {
return nodes.filter(node =>
t.PUREISH_TYPES.includes(node.type)
);
}
function isControlFlow(node: t.Node): boolean {
return t.CONDITIONAL_TYPES.includes(node.type) ||
t.LOOP_TYPES.includes(node.type) ||
t.COMPLETIONSTATEMENT_TYPES.includes(node.type);
}
function requiresSemicolon(node: t.Node): boolean {
return !t.TERMINATORLESS_TYPES.includes(node.type);
}function analyzeASTComplexity(ast: t.Node) {
const stats = {
expressions: 0,
statements: 0,
functions: 0,
loops: 0,
conditionals: 0
};
t.traverse(ast, (node, ancestors, state) => {
if (t.EXPRESSION_TYPES.includes(node.type)) stats.expressions++;
if (t.STATEMENT_TYPES.includes(node.type)) stats.statements++;
if (t.FUNCTION_TYPES.includes(node.type)) stats.functions++;
if (t.LOOP_TYPES.includes(node.type)) stats.loops++;
if (t.CONDITIONAL_TYPES.includes(node.type)) stats.conditionals++;
});
return stats;
}Note: These generated constants are automatically maintained and updated as Babel adds support for new JavaScript features and proposals. They provide a reliable way to categorize and work with AST nodes without hardcoding node type strings.