Helper function to replace certain member expressions with function calls
npx @tessl/cli install tessl/npm-babel--helper-member-expression-to-functions@7.27.0@babel/helper-member-expression-to-functions is a specialized Babel helper that transforms member expressions into function calls for complex scenarios. It handles optional chaining, private properties, computed property access, and provides essential infrastructure for Babel plugins that need to replace property access patterns with more complex runtime behavior.
npm install @babel/helper-member-expression-to-functionsimport memberExpressionToFunctions, { Handler, HandlerState, willPathCastToBoolean } from "@babel/helper-member-expression-to-functions";For CommonJS:
const memberExpressionToFunctions = require("@babel/helper-member-expression-to-functions").default;
const { Handler, HandlerState, willPathCastToBoolean } = require("@babel/helper-member-expression-to-functions");import traverse from "@babel/traverse";
import memberExpressionToFunctions, { Handler, willPathCastToBoolean } from "@babel/helper-member-expression-to-functions";
import * as t from "@babel/types";
// Example handler implementation
const handler: Handler<{}> = {
get(member) {
return t.callExpression(t.identifier("_get"), [member.node]);
},
set(member, value) {
return t.callExpression(t.identifier("_set"), [member.node, value]);
},
call(member, args) {
return t.callExpression(t.identifier("_call"), [member.node, ...args]);
},
optionalCall(member, args) {
return t.callExpression(t.identifier("_optionalCall"), [member.node, ...args]);
},
delete(member) {
return t.callExpression(t.identifier("_delete"), [member.node]);
},
destructureSet(member) {
return t.callExpression(t.identifier("_destructureSet"), [member.node]);
},
boundGet(member) {
return t.callExpression(
t.memberExpression(this.get(member), t.identifier("bind")),
[t.thisExpression()]
);
}
};
// Transform member expressions in AST
memberExpressionToFunctions(path, {
MemberExpression(path) {
this.handle(path);
},
OptionalMemberExpression(path) {
this.handle(path);
}
}, handler);
// Alternative: Handle all member expressions with single visitor
memberExpressionToFunctions(path, {
"MemberExpression|OptionalMemberExpression"(path) {
this.handle(path);
}
}, handler);
// Conditional transformation based on context
memberExpressionToFunctions(path, {
MemberExpression(path) {
// Only transform private property access
if (path.get("property").isPrivateName()) {
this.handle(path);
}
}
}, handler);@babel/helper-member-expression-to-functions is built around a visitor-based transformation system with several key components:
willPathCastToBoolean for analyzing AST contextThe system handles complex scenarios including optional chaining, private properties, computed access, and ensures proper context preservation for method calls.
Core transformation function that converts member expressions into function calls with custom handlers.
/**
* Transform member expressions into function calls using custom handler functions
* @param path - Babel AST node path to traverse
* @param visitor - Babel visitor object defining which node types to transform and their handlers
* @param state - Combined object containing Handler interface methods and optional custom state
*/
export default function memberExpressionToFunctions<State extends object>(
path: NodePath,
visitor: Visitor<HandlerState<State>>,
state: Handler<State> & State
): void;
interface Visitor<State> {
[key: string]: (path: NodePath, state: State) => void;
}The transformation system requires implementing the Handler interface to define how member expressions are converted.
interface Handler<State> {
/** Optional memoization method called when member is self-referential */
memoise?(
this: HandlerState<State> & State,
member: Member,
count: number
): void;
/** Handle destructuring assignments like { prop } = obj or [elem] = arr */
destructureSet(
this: HandlerState<State> & State,
member: Member
): t.Expression;
/** Handle bound member access for tagged templates */
boundGet(
this: HandlerState<State> & State,
member: Member
): t.Expression;
/** Optional simple set operation for update expressions */
simpleSet?(
this: HandlerState<State> & State,
member: Member
): t.Expression;
/** Handle member get operations like obj.prop */
get(
this: HandlerState<State> & State,
member: Member
): t.Expression;
/** Handle member set operations like obj.prop = value */
set(
this: HandlerState<State> & State,
member: Member,
value: t.Expression
): t.Expression;
/** Handle method calls like obj.method(args) */
call(
this: HandlerState<State> & State,
member: Member,
args: t.CallExpression["arguments"]
): t.Expression;
/** Handle optional method calls like obj?.method?.(args) */
optionalCall(
this: HandlerState<State> & State,
member: Member,
args: t.OptionalCallExpression["arguments"]
): t.Expression;
/** Handle member deletion like delete obj.prop */
delete(
this: HandlerState<State> & State,
member: Member
): t.Expression;
}Extended handler interface provided by the transformation system.
interface HandlerState<State = object> extends Handler<State> {
/** Core transformation handler method */
handle(
this: HandlerState<State> & State,
member: Member,
noDocumentAll?: boolean
): void;
/** Assignment memoization utility */
memoiser: AssignmentMemoiser;
}Utility function to determine if a path will be cast to boolean in its evaluation context.
/**
* Test if a NodePath will be cast to boolean when evaluated
* @param path - The AST node path to analyze
* @returns true if the path will be cast to boolean, false otherwise
*/
export function willPathCastToBoolean(path: NodePath): boolean;Usage Examples:
import { willPathCastToBoolean } from "@babel/helper-member-expression-to-functions";
// Returns true - used in if condition
const testPath = getNodePath("if (a?.#b) {}").get("test");
willPathCastToBoolean(testPath); // true
// Returns false - standalone expression
const standalonePath = getNodePath("a?.#b");
willPathCastToBoolean(standalonePath); // false
// Returns true - used in logical expression
const logicalPath = getNodePath("a?.#b && c").get("left");
willPathCastToBoolean(logicalPath); // true/** Type alias for member expression node paths */
type Member = NodePath<t.OptionalMemberExpression | t.MemberExpression>;
/** Assignment memoization utility class that optimizes repeated member expression access */
class AssignmentMemoiser {
/** Check if expression has been memoized for reuse */
has(key: t.Expression): boolean;
/**
* Get memoized value and decrement usage count
* Returns assignment expression on final usage to perform the memoization
*/
get(key: t.Expression): t.Expression | undefined;
/**
* Set memoized value with expected usage count
* @param key - Expression to memoize
* @param value - Identifier to store the memoized value
* @param count - Number of times this expression will be accessed
*/
set(key: t.Expression, value: t.Identifier, count: number): void;
}The helper automatically handles the following member expression patterns:
obj.prop → _get(obj.prop)obj?.prop → conditional with null checksobj[key] → _get(obj[key])obj.method(args) → _call(obj.method, args)obj?.method?.(args) → _optionalCall(obj.method, args)obj.prop = value → _set(obj.prop, value)obj.prop += value → _set(obj.prop, _get(obj.prop) + value)++obj.prop → _set(obj.prop, ++_get(obj.prop)){prop} = obj → {prop: _destructureSet(obj.prop)} = objdelete obj.prop → _delete(obj.prop)obj.method\template`→_get(obj.method).bind(obj)`The transformation system throws specific errors for unsupported operations:
delete obj?.#propThe helper implements sophisticated memoization to avoid repeated evaluation of complex expressions:
// Handler can implement memoise method
memoise(member, count) {
// Called when member needs to be referenced multiple times
// count indicates how many times the member will be accessed
}Proper handling of optional chaining with null/undefined checks:
obj?.prop to conditional expressions with appropriate null checksobj?.a?.b?.cEnsures proper this binding for method calls:
this binding preservation in transformations