Helper function to infer and assign names to anonymous function expressions and class expressions in JavaScript/TypeScript AST nodes
npx @tessl/cli install tessl/npm-babel--helper-function-name@7.24.0@babel/helper-function-name is a utility function that intelligently infers and assigns names to anonymous function expressions and class expressions in JavaScript/TypeScript AST nodes. It's designed to work with Babel's AST transformation pipeline to improve debugging and code readability by ensuring functions have meaningful names in the compiled output.
npm install @babel/helper-function-nameimport helperFunctionName from "@babel/helper-function-name";For CommonJS:
const helperFunctionName = require("@babel/helper-function-name");import helperFunctionName from "@babel/helper-function-name";
import { traverse } from "@babel/traverse";
import * as t from "@babel/types";
// Example: Adding names to anonymous functions during AST transformation
traverse(ast, {
FunctionExpression(path) {
if (!path.node.id) {
// Use the helper to infer and assign a name
const result = helperFunctionName(path);
if (result && result !== path.node) {
// Replace with wrapped function if needed
path.replaceWith(result);
}
}
}
});
// Example: Usage in a Babel plugin (simplified)
export default function myPlugin() {
return {
visitor: {
FunctionExpression(path) {
const replacement = helperFunctionName(path);
if (replacement) path.replaceWith(replacement);
},
ObjectProperty(path) {
const value = path.get("value");
if (value.isFunction()) {
const newNode = helperFunctionName(value, false, true);
if (newNode) value.replaceWith(newNode);
}
}
}
};
}The helper uses several key strategies to infer function names:
The primary capability that infers and assigns names to anonymous function expressions and class expressions.
/**
* Add id to function/class expression inferred from the AST
* @param nodePathLike - The NodePath-like input containing node, parent, scope, and optional id
* @param localBinding - Whether a name could shadow a self-reference (default: false)
* @param supportUnicodeId - Whether the compilation target supports Unicode identifiers (default: false)
* @returns Modified node, IIFE wrapper, or void
*/
export default function <N extends t.FunctionExpression | t.Class>(
nodePathLike: {
node: N;
parent?: NodePath<N>["parent"];
scope: Scope;
id?:
| t.AssignmentExpression["left"]
| t.StringLiteral
| t.NumericLiteral
| t.BigIntLiteral;
} | NodePath<N>,
localBinding?: boolean,
supportUnicodeId?: boolean,
): N | t.CallExpression | void;Parameters:
nodePathLike (required): Either a NodePath object or an object containing:
node: The function expression or class expression AST node to nameparent (optional): The parent AST node for contextual name inferencescope: Babel traverse scope object for analyzing bindings and referencesid (optional): Fallback naming source when name cannot be inferred from AST contextlocalBinding (optional, default: false): Boolean indicating if the name could shadow a self-reference (relevant when converting arrow functions)supportUnicodeId (optional, default: false): Boolean indicating whether the compilation target supports Unicode/non-BMP characters in identifiersReturn Values:
t.CallExpression): When the node contains bindings that would shadow the inferred function nameUsage Examples:
import helperFunctionName from "@babel/helper-function-name";
import { traverse } from "@babel/traverse";
import * as t from "@babel/types";
// Usage with NodePath (most common in Babel plugins)
traverse(ast, {
FunctionExpression(path) {
const result = helperFunctionName(path);
if (result && result !== path.node) {
path.replaceWith(result);
}
}
});
// Usage with manual object construction
const functionExpr = t.functionExpression(null, [], t.blockStatement([]));
const objectProperty = t.objectProperty(t.identifier("foo"), functionExpr);
const result = helperFunctionName({
node: functionExpr,
parent: objectProperty,
scope: currentScope
});
// Result: function expression with id "foo"
// Usage with variable declarator
const variableDeclarator = t.variableDeclarator(
t.identifier("myFunction"),
t.functionExpression(null, [], t.blockStatement([]))
);
const result2 = helperFunctionName({
node: variableDeclarator.init as t.FunctionExpression,
parent: variableDeclarator,
scope: currentScope
});
// Result: function expression with id "myFunction"
// Usage with assignment expression
const assignment = t.assignmentExpression(
"=",
t.identifier("handler"),
t.functionExpression(null, [], t.blockStatement([]))
);
const result3 = helperFunctionName({
node: assignment.right as t.FunctionExpression,
parent: assignment,
scope: currentScope
});
// Result: function expression with id "handler"
// Usage with Unicode support
const result4 = helperFunctionName({
node: functionWithUnicodeContext,
parent: parentWithUnicodeName,
scope: currentScope
}, false, true); // Enable Unicode supportThe helper can infer names from various AST contexts:
Object Properties and Methods:
// { foo: function() {} } -> function foo() {}
// { foo() {} } -> function foo() {}Variable Declarations:
// let foo = function() {} -> let foo = function foo() {}
// const bar = class {} -> const bar = class bar {}Assignment Expressions:
// obj.method = function() {} -> obj.method = function method() {}
// handler = function() {} -> handler = function handler() {}Literal Keys:
// { "my-function": function() {} } -> function myFunction() {}
// { 123: function() {} } -> function _123() {}When functions reference themselves by name, the helper intelligently handles the situation:
// Case 1: Local scope rename (preferred)
let foo = function() { return foo(); };
// Becomes: let foo = function foo() { return foo(); };
// Case 2: IIFE wrapper (when scope conflicts exist)
let foo = function(foo) { return foo(); };
// Becomes: let foo = (function(key) {
// function foo() { return key.apply(this, arguments); }
// foo.toString = function() { return key.toString(); };
// return foo;
// })(function(foo) { return foo(); });// Re-exported from @babel/types and @babel/traverse
import type * as t from "@babel/types";
import type { NodePath, Scope } from "@babel/traverse";
// Internal state type used during analysis
interface State {
name: string;
outerDeclar: t.Identifier;
selfAssignment: boolean;
selfReference: boolean;
}The helper gracefully handles various edge cases:
void when name cannot be inferrednode.id already existsThis package requires:
@babel/template: For creating AST wrapper templates@babel/types: For AST node type checking and creation@babel/traverse: For scope analysis (peer dependency)