Babel plugin that compiles ES2015 classes to ES5-compatible JavaScript code
npx @tessl/cli install tessl/npm-babel--plugin-transform-classes@7.28.0@babel/plugin-transform-classes is a Babel transform plugin that compiles ES2015/ES6 class declarations and expressions into ES5-compatible JavaScript code. It handles class inheritance, method definitions, static methods, and constructor functions, making modern JavaScript classes compatible with older environments.
npm install --save-dev @babel/plugin-transform-classesThis is a Babel plugin, so it's typically not imported directly in application code. Instead, it's configured in Babel configuration files:
{
"plugins": ["@babel/plugin-transform-classes"]
}For programmatic use with Babel's transform API:
import transformClasses from "@babel/plugin-transform-classes";
import { transform } from "@babel/core";CommonJS:
const transformClasses = require("@babel/plugin-transform-classes");
const { transform } = require("@babel/core");.babelrc.json:
{
"plugins": [
["@babel/plugin-transform-classes", { "loose": false }]
]
}babel.config.js:
module.exports = {
plugins: [
["@babel/plugin-transform-classes", { loose: true }]
]
};import { transform } from "@babel/core";
import transformClasses from "@babel/plugin-transform-classes";
const code = `
class MyClass extends BaseClass {
constructor(name) {
super();
this.name = name;
}
greet() {
return "Hello, " + this.name;
}
static create(name) {
return new MyClass(name);
}
}
`;
const result = transform(code, {
plugins: [[transformClasses, { loose: false }]]
});
console.log(result.code);The plugin operates through Babel's visitor pattern and relies heavily on Babel's runtime helper system:
declare() helper to create plugin configuration, integrating with Babel's assumption systemClassDeclaration, ClassExpression, and ExportDefaultDeclaration nodes during compilationtransformClass.ts converts class structures to function-based equivalents using IIFEscreateClass, inherits, callSuper, classCallCheck, etc.)setClassMethods, constantSuper, etc.) for optimized outputThe main plugin export that integrates with Babel's transform pipeline using the declare helper.
/**
* Main plugin factory function created using Babel's declare helper
* @param api - Babel API object providing utilities, version checking, and assumptions
* @param options - Plugin configuration options
* @returns Babel plugin configuration object
*/
declare function default(api: BabelAPI, options: Options): BabelPlugin;
interface Options {
/** Enable loose mode for simpler, faster output with fewer spec compliance checks */
loose?: boolean;
}
interface BabelAPI {
/** Assert minimum Babel version requirement */
assertVersion(version: number): void;
/** Get current compilation targets */
targets(): any;
/** Get assumption value with optional fallback */
assumption(name: string): boolean | undefined;
}
interface BabelPlugin {
/** Plugin identifier */
name: "transform-classes";
/** AST visitor configuration */
visitor: {
ExportDefaultDeclaration(path: NodePath<t.ExportDefaultDeclaration>): void;
ClassDeclaration(path: NodePath<t.ClassDeclaration>): void;
ClassExpression(path: NodePath<t.ClassExpression>, state: PluginState): void;
};
}
interface PluginState {
file: File;
opts: Options;
}Configuration Examples:
// Strict mode (default) - full ES6 spec compliance
module.exports = {
plugins: ["@babel/plugin-transform-classes"]
};
// Loose mode - simpler output, better performance
module.exports = {
plugins: [
["@babel/plugin-transform-classes", { loose: true }]
]
};Transforms ES6 class declarations into ES5 variable declarations with function constructors.
Input:
class MyClass {
constructor(name) {
this.name = name;
}
greet() {
return "Hello, " + this.name;
}
}Output (loose: false):
var MyClass = /*#__PURE__*/function () {
"use strict";
function MyClass(name) {
babelHelpers.classCallCheck(this, MyClass);
this.name = name;
}
var _proto = MyClass.prototype;
_proto.greet = function greet() {
return "Hello, " + this.name;
};
return babelHelpers.createClass(MyClass);
}();Transforms ES6 class expressions into immediately invoked function expressions (IIFEs).
Input:
const MyClass = class {
constructor(value) {
this.value = value;
}
};Output:
var MyClass = /*#__PURE__*/function () {
"use strict";
function _class(value) {
babelHelpers.classCallCheck(this, _class);
this.value = value;
}
return babelHelpers.createClass(_class);
}();Handles class inheritance with extends keyword and super() calls.
Input:
class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
info() {
return super.info() + ", age: " + this.age;
}
}Output:
var Child = /*#__PURE__*/function (_Parent) {
"use strict";
function Child(name, age) {
var _this;
_this = babelHelpers.callSuper(this, Child, [name]);
_this.age = age;
return babelHelpers.assertThisInitialized(_this);
}
babelHelpers.inherits(Child, _Parent);
var _proto = Child.prototype;
_proto.info = function info() {
return babelHelpers.get(babelHelpers.getPrototypeOf(Child.prototype), "info", this).call(this) + ", age: " + this.age;
};
return babelHelpers.createClass(Child);
}(Parent);Transforms static class methods into function properties.
Input:
class Utils {
static format(text) {
return text.toUpperCase();
}
}Output:
var Utils = /*#__PURE__*/function () {
"use strict";
function Utils() {
babelHelpers.classCallCheck(this, Utils);
}
Utils.format = function format(text) {
return text.toUpperCase();
};
return babelHelpers.createClass(Utils);
}();Handles exported class declarations by splitting the export and class definition.
Input:
export default class MyClass {
constructor() {}
}Output:
var MyClass = /*#__PURE__*/function () {
"use strict";
function MyClass() {
babelHelpers.classCallCheck(this, MyClass);
}
return babelHelpers.createClass(MyClass);
}();
export { MyClass as default };interface Options {
/**
* Enable loose mode for simpler output.
* - loose: true - generates simpler, faster code with fewer spec compliance checks
* - loose: false (default) - full ES6 spec compliance with runtime checks
*/
loose?: boolean;
}
interface ClassAssumptions {
/** Use simple assignments instead of Object.defineProperty for class methods */
setClassMethods: boolean;
/** Assume super class doesn't change during execution */
constantSuper: boolean;
/** Assume super constructors are callable (not just constructable) */
superIsCallableConstructor: boolean;
/** Don't add runtime checks for calling class as function */
noClassCalls: boolean;
}
interface NodePath<T = t.Node> {
/** AST node being processed */
node: T;
/** Replace current node with new node(s) */
replaceWith(node: t.Node | t.Node[]): void;
/** Split export declaration into separate export and declaration */
splitExportDeclaration?(): void;
/** Ensure function has a name */
ensureFunctionName?(supportUnicodeId: boolean): NodePath | void;
/** Check if node is of specific type */
isClassDeclaration(): this is NodePath<t.ClassDeclaration>;
isClassExpression(): this is NodePath<t.ClassExpression>;
isCallExpression(): this is NodePath<t.CallExpression>;
isArrowFunctionExpression(): this is NodePath<t.ArrowFunctionExpression>;
/** Convert arrow function to regular function expression */
arrowFunctionToExpression(): void;
/** Access parent path */
parentPath: NodePath;
/** Access child paths */
get(key: string): NodePath;
/** Access scope information */
scope: Scope;
}
interface Scope {
/** Generate unique identifier */
generateUidIdentifier(name?: string): t.Identifier;
}
interface File {
/** File metadata and state */
[key: string]: any;
}
/** Core Babel types namespace */
declare namespace t {
interface Node {
type: string;
}
interface ClassDeclaration extends Node {
type: "ClassDeclaration";
id: Identifier | null;
}
interface ClassExpression extends Node {
type: "ClassExpression";
id: Identifier | null;
}
interface ExportDefaultDeclaration extends Node {
type: "ExportDefaultDeclaration";
declaration: Node;
}
interface Identifier extends Node {
type: "Identifier";
name: string;
}
interface VariableDeclaration extends Node {
type: "VariableDeclaration";
}
interface VariableDeclarator extends Node {
type: "VariableDeclarator";
}
interface CallExpression extends Node {
type: "CallExpression";
}
interface ArrowFunctionExpression extends Node {
type: "ArrowFunctionExpression";
}
// Helper functions
function cloneNode(node: Node): Node;
function toExpression(node: Node): Node;
function variableDeclaration(kind: "var" | "let" | "const", declarations: VariableDeclarator[]): VariableDeclaration;
function variableDeclarator(id: Identifier, init: Node): VariableDeclarator;
function inherits(child: Node, parent: Node): void;
}The plugin generates code that relies on Babel runtime helpers. These helpers must be available at runtime:
/** Core helpers used by the plugin */
interface BabelHelpers {
/** Creates the class constructor function with proper prototype chain */
createClass(constructor: Function, protoMethods?: any[], staticMethods?: any[]): Function;
/** Checks that class is not called as function */
classCallCheck(instance: any, Constructor: Function): void;
/** Sets up inheritance chain between child and parent */
inherits(subClass: Function, superClass: Function): void;
/** Calls super constructor with proper binding */
callSuper(object: any, Constructor: Function, args?: any[]): any;
/** Ensures 'this' is initialized after super call */
assertThisInitialized(self: any): any;
/** Gets property from prototype chain */
get(object: any, property: string, receiver?: any): any;
/** Gets prototype of object */
getPrototypeOf(object: any): any;
}To use these helpers, you need either:
{
"plugins": [
["@babel/plugin-transform-runtime", {
"helpers": true
}],
"@babel/plugin-transform-classes"
]
}{
"presets": [["@babel/preset-env", { "useBuiltIns": "usage" }]],
"plugins": ["@babel/plugin-transform-classes"]
}The plugin integrates with Babel's assumptions system for optimization:
// babel.config.js with assumptions
module.exports = {
assumptions: {
"setClassMethods": true, // Use assignments for methods
"constantSuper": true, // Super doesn't change
"superIsCallableConstructor": true, // Super is callable
"noClassCalls": true // Classes won't be called as functions
},
plugins: ["@babel/plugin-transform-classes"]
};These assumptions enable more optimized output when you can guarantee certain usage patterns in your code.