Babel plugin that transforms ES2022 class static blocks into compatible code for older JavaScript environments
npx @tessl/cli install tessl/npm-babel--plugin-transform-class-static-block@7.28.0A Babel plugin that transforms ES2022 class static blocks into compatible code for older JavaScript environments. The plugin handles complex transformations while maintaining proper initialization order and scoping semantics.
npm install --save-dev @babel/plugin-transform-class-static-block./lib/index.js./lib/index.d.ts// CommonJS - default plugin export
const plugin = require("@babel/plugin-transform-class-static-block");
// ESM - default plugin export
import plugin from "@babel/plugin-transform-class-static-block";
// Plugin internal imports (from source)
import { declare } from "@babel/helper-plugin-utils";
import type { NodePath, Scope, types as t } from "@babel/core";
import {
buildNamedEvaluationVisitor,
enableFeature,
FEATURES,
} from "@babel/helper-create-class-features-plugin";Add the plugin to your Babel configuration:
// babel.config.js
module.exports = {
plugins: ["@babel/plugin-transform-class-static-block"]
};
// or with explicit plugin import
module.exports = {
plugins: [require("@babel/plugin-transform-class-static-block")]
};Example transformation:
// Input (ES2022 class static block)
class Foo {
static bar = 42;
static {
this.foo = this.bar;
}
}
// Output (transformed for older environments)
var _Foo;
class Foo {}
_Foo = Foo;
babelHelpers.defineProperty(Foo, "bar", 42);
_Foo.foo = _Foo.bar;The main export is the plugin function that integrates with Babel's transformation pipeline.
/**
* Babel plugin that transforms class static blocks
* Uses declare() from @babel/helper-plugin-utils to create the plugin
* @param api - Babel API object containing types, template, traverse, and assertVersion
* @returns Babel plugin configuration object
*/
declare function plugin(api: {
types: typeof t;
template: any;
traverse: any;
assertVersion: (version: string) => void;
}): BabelPlugin;
export default plugin;
interface BabelPlugin {
/** Plugin identifier */
name: "transform-class-static-block";
/** Parser configuration function (Babel 7 only) */
manipulateOptions?: (opts: any, parser: { plugins: string[] }) => void;
/** Pre-transformation lifecycle method */
pre?(): void;
/** AST visitor configuration */
visitor: {
ClassBody(path: NodePath<t.ClassBody>): void;
};
}The plugin provides comprehensive transformation of class static blocks:
static { ... } blocks into immediately invoked function expressions or inlined expressionsbuildNamedEvaluationVisitor@babel/helper-create-class-features-pluginMultiple Static Blocks:
// Input
class Foo {
static #bar = 21;
static {
this.foo = this.#bar;
this.qux1 = this.qux;
}
static qux = 21;
static {
this.qux2 = this.qux;
}
}
// Output (when using private fields plugin)
var _Foo;
class Foo {}
_Foo = Foo;
var _bar = {
_: 21
};
(() => {
_Foo.foo = babelHelpers.assertClassBrand(_Foo, _Foo, _bar)._;
_Foo.qux1 = _Foo.qux;
})();
babelHelpers.defineProperty(Foo, "qux", 21);
_Foo.qux2 = _Foo.qux;Static Block without Static Properties:
// Input
class Foo {
static {
this.foo = 42;
}
}
// Output - creates private property to hold the static block
var _staticBlock;
class Foo {
static #_ = _staticBlock = () => this.foo = 42;
}
_staticBlock();The plugin handles several complex scenarios:
Class Expressions with Named Evaluation:
// Input - anonymous class expression
const MyClass = class {
static {
this.name = "MyClass";
}
};
// Output - adds named evaluation support
const MyClass = class {
static {
setFunctionName(this, "MyClass");
this.name = "MyClass";
}
};Integration with Class Features Plugin:
/**
* Enables static block support in the class features plugin system
* This allows coordination with other class transformation plugins
*/
enableFeature(file: any, feature: FEATURES.staticBlocks, loose: boolean): void;
/**
* Builds visitor for handling named evaluation of anonymous class expressions
* @param predicate - Function to determine if named evaluation is needed
* @param visitor - Function to perform the named evaluation transformation
*/
buildNamedEvaluationVisitor(
predicate: (path: NodePath) => boolean,
visitor: (classPath: NodePath<t.ClassExpression>, state: any, name: string | t.Expression) => void
): TraversalVisitor;Private Property Generation:
/**
* Generates unique identifier names avoiding conflicts with existing private properties
* @param scope - Babel scope for generating unique identifiers
* @param denyList - Set of existing private property names to avoid
* @returns Unique identifier name
*/
function generateUid(scope: Scope, denyList: Set<string>): string;Transformation Algorithm:
The plugin follows this transformation algorithm:
/**
* Converts static block AST nodes to executable expressions
* Optimizes single expression blocks to avoid unnecessary IIFEs
*/
const blocksToExpressions = (blocks: Array<t.StaticBlock>) => t.Expression[];
/**
* Prepends expressions to a static property initializer using sequence expressions
*/
const prependToInitializer = (
prop: t.ClassProperty | t.ClassPrivateProperty,
expressions: t.Expression[]
) => void;
/**
* Creates a sequence expression or returns single expression if only one
*/
const maybeSequenceExpression = (expressions: t.Expression[]) => t.Expression;/**
* Core Babel types and utilities used by the plugin
*/
import type { NodePath, Scope, types as t } from "@babel/core";
import { declare } from "@babel/helper-plugin-utils";
import {
buildNamedEvaluationVisitor,
enableFeature,
FEATURES,
} from "@babel/helper-create-class-features-plugin";
interface PluginConfiguration {
/** Plugin name identifier */
name: "transform-class-static-block";
/** Parser plugin registration (Babel 7 only) */
manipulateOptions?: (opts: any, parser: { plugins: string[] }) => void;
/** Enable static block features in class features plugin */
pre(): void;
/** AST transformation visitor */
visitor: {
ClassBody(path: NodePath<t.ClassBody>): void;
};
}Peer Dependencies:
@babel/core (^7.12.0) - Required Babel transformation coreRuntime Dependencies:
@babel/helper-create-class-features-plugin - Class feature transformation utilities@babel/helper-plugin-utils - Plugin declaration utilitiesclassStaticBlock parser plugin (Babel 7 only)Basic Babel Configuration:
// babel.config.js
module.exports = {
presets: ["@babel/preset-env"],
plugins: ["@babel/plugin-transform-class-static-block"]
};With Class Properties Plugin:
module.exports = {
plugins: [
"@babel/plugin-transform-class-properties",
"@babel/plugin-transform-class-static-block"
]
};Programmatic Usage:
const babel = require("@babel/core");
const plugin = require("@babel/plugin-transform-class-static-block");
const result = babel.transformSync(code, {
plugins: [plugin]
});/**
* Babel AST node types used by the plugin
*/
interface t {
isStaticBlock(node: any): node is t.StaticBlock;
isClassProperty(node: any): node is t.ClassProperty;
isClassPrivateProperty(node: any): node is t.ClassPrivateProperty;
isExpressionStatement(node: any): node is t.ExpressionStatement;
staticBlock(body: t.Statement[]): t.StaticBlock;
privateName(id: t.Identifier): t.PrivateName;
classPrivateProperty(
key: t.PrivateName,
value: t.Expression | null,
decorators: any[],
static: boolean
): t.ClassPrivateProperty;
sequenceExpression(expressions: t.Expression[]): t.SequenceExpression;
unaryExpression(operator: string, argument: t.Expression): t.UnaryExpression;
callExpression(callee: t.Expression, arguments: t.Expression[]): t.CallExpression;
thisExpression(): t.ThisExpression;
returnStatement(argument: t.Expression | null): t.ReturnStatement;
blockStatement(body: t.Statement[]): t.BlockStatement;
inheritsComments<T extends t.Node>(child: T, parent: t.Node): T;
cloneNode<T extends t.Node>(node: T): T;
}
interface NodePath<T = t.Node> {
node: T;
parent: t.Node;
parentPath: NodePath;
scope: Scope;
get(key: string): NodePath;
isClassExpression(): boolean;
isStaticBlock(): boolean;
isClassProperty(opts?: { static?: boolean }): boolean;
isClassPrivateProperty(opts?: { static?: boolean }): boolean;
isPrivate(): boolean;
isExpression(): boolean;
isStatement(): boolean;
remove(): void;
replaceWith(node: t.Node): void;
insertAfter(nodes: t.Node | t.Node[]): void;
unshiftContainer(key: string, nodes: t.Node | t.Node[]): void;
pushContainer(key: string, nodes: t.Node | t.Node[]): void;
}
interface Scope {
generateDeclaredUidIdentifier(name?: string): t.Identifier;
}
interface t.StaticBlock {
type: "StaticBlock";
body: t.Statement[];
}
interface t.ClassBody {
type: "ClassBody";
body: Array<t.ClassMethod | t.ClassProperty | t.ClassPrivateProperty | t.StaticBlock>;
}