Helper utilities for class initialization, computed key extraction, and AST manipulation.
Injects initialization code into class constructors.
/**
* Injects initialization code into a class constructor
* Creates constructor if none exists, or augments existing constructor
* @param path - Class path to modify
* @param constructor - Existing constructor path (undefined if none)
* @param instanceNodes - Initialization expressions to inject
* @param referenceVisitor - Callback for handling property references
* @param lastInstanceNodeReturnsThis - Whether last node returns 'this'
*/
function injectInitialization(
path: NodePath<t.Class>,
constructor: NodePath<t.ClassMethod> | undefined,
instanceNodes: t.ExpressionStatement[],
referenceVisitor: (visitor: Visitor, state: any) => void,
lastInstanceNodeReturnsThis: boolean
): void;
type Visitor = {
[key: string]: (path: NodePath, state: any) => void;
};Usage Examples:
import { injectInitialization } from "@babel/helper-create-class-features-plugin";
// Inject field initialization into constructor
injectInitialization(
classPath,
constructorPath, // may be undefined
[
t.expressionStatement(
t.assignmentExpression("=",
t.memberExpression(t.thisExpression(), t.identifier("field")),
t.stringLiteral("value")
)
)
],
(referenceVisitor, state) => {
// Handle property references during injection
for (const prop of props) {
if (!prop.node.static) {
prop.traverse(referenceVisitor, state);
}
}
},
false // last node doesn't return this
);Extracts and memoizes computed property keys from class members.
/**
* Extracts computed keys from class methods and properties
* Hoists computed key expressions to avoid re-evaluation
* @param path - Class path containing computed keys
* @param computedPaths - Paths with computed keys to extract
* @param file - Babel file instance
* @returns Array of statement nodes for hoisted key variables
*/
function extractComputedKeys(
path: NodePath<t.Class>,
computedPaths: NodePath<t.ClassProperty | t.ClassMethod>[],
file: File
): t.Statement[];Usage Examples:
import { extractComputedKeys } from "@babel/helper-create-class-features-plugin";
// Find computed properties
const computedPaths: NodePath<t.ClassProperty | t.ClassMethod>[] = [];
for (const memberPath of classPath.get("body.body")) {
if ((memberPath.isClassProperty() || memberPath.isClassMethod()) &&
memberPath.node.computed) {
computedPaths.push(memberPath);
}
}
// Extract computed keys
const keyNodes = extractComputedKeys(classPath, computedPaths, file);
// Insert hoisted key variables before class
classPath.insertBefore(keyNodes);Builds a right-hand side expression for private property 'in' checks with proper validation.
/**
* Builds a right-hand side expression for private property 'in' checks
* Wraps expressions with helper when available for proper validation
* @param rhs - The right-hand side expression to check
* @param file - Babel file instance for helper access
* @param inRHSIsObject - Whether RHS is known to be an object
* @returns Enhanced expression with proper validation
*/
function buildCheckInRHS(
rhs: t.Expression,
file: File,
inRHSIsObject?: boolean
): t.Expression;Usage Examples:
import { buildCheckInRHS } from "@babel/helper-create-class-features-plugin";
// Build RHS check for private 'in' operation
const rhs = t.identifier("obj");
const checkedRHS = buildCheckInRHS(rhs, file);
// Use in private property 'in' check
const privateInCheck = t.binaryExpression(
"in",
privateNameId,
checkedRHS
);
// With known object type (skips validation)
const checkedRHSOptimized = buildCheckInRHS(rhs, file, true);When no constructor exists, one is created:
// Before transformation
class Example {
field = "value";
}
// After initialization injection
class Example {
constructor() {
this.field = "value";
}
}When constructor exists, initialization is injected:
// Before transformation
class Example {
field = "value";
constructor(param) {
console.log("constructor");
}
}
// After initialization injection
class Example {
constructor(param) {
this.field = "value";
console.log("constructor");
}
}Initialization respects super call requirements:
// Before transformation
class Child extends Parent {
field = "value";
constructor() {
super();
console.log("after super");
}
}
// After initialization injection
class Child extends Parent {
constructor() {
super();
this.field = "value";
console.log("after super");
}
}// Before extraction
class Example {
[computeKey()] = "value";
[expensive.calculation] = "value2";
}
// After extraction
const _computeKey = computeKey();
const _expensive$calculation = expensive.calculation;
class Example {
[_computeKey] = "value";
[_expensive$calculation] = "value2";
}// Before extraction
class Example {
[prefix + Math.random()] = "value";
[getKey() + suffix] = "value2";
}
// After extraction
const _prefix$Math$random = prefix + Math.random();
const _getKey$suffix = getKey() + suffix;
class Example {
[_prefix$Math$random] = "value";
[_getKey$suffix] = "value2";
}The reference visitor pattern handles property references during initialization:
const referenceVisitor: Visitor = {
ThisExpression(path) {
// Handle 'this' references in initializers
},
Super(path) {
// Handle 'super' references in initializers
},
PrivateName(path) {
// Handle private name references
}
};
// Used in injectInitialization callback
(visitor, state) => {
for (const prop of instanceProps) {
prop.traverse(visitor, state);
}
}Utilities properly handle scope and binding: