babel-plugin-transform-decorators-legacy is a Babel 6 plugin that transforms decorator syntax to provide backward compatibility with Babel 5's decorator behavior. It implements the original decorator proposal specification with proper evaluation order and handles edge cases around static property initialization that were problematic in Babel 5.
npm install --save-dev babel-plugin-transform-decorators-legacyThis is a Babel plugin, so it doesn't have traditional imports. It's configured in your Babel configuration:
{
"plugins": ["transform-decorators-legacy"]
}The plugin can also be imported programmatically for advanced usage:
const transformDecoratorsLegacy = require("babel-plugin-transform-decorators-legacy");Configure the plugin in your .babelrc file or Babel configuration:
{
"plugins": ["transform-decorators-legacy"]
}Important: Order matters! If you're using transform-class-properties, make sure transform-decorators-legacy comes before it:
{
"plugins": [
"transform-decorators-legacy",
"transform-class-properties"
]
}The plugin transforms decorator syntax in your code:
// Input: Decorator syntax
@deprecated
@logged
class MyClass {
@readonly
@validate
method() {
return "hello";
}
@configurable(false)
property = "value";
}
// Output: Legacy decorator behavior compatible with Babel 5The plugin is built around several key components:
Main plugin export that integrates with Babel's plugin system.
/**
* Main Babel plugin function for transform-decorators-legacy
* @param {Object} api - Babel plugin API containing types and utilities
* @param {Object} api.types - Babel types (t) for AST manipulation
* @returns {Object} Plugin configuration with inherits and visitor
*/
export default function({types: t}): BabelPlugin;
interface BabelPlugin {
/** Inherits babel-plugin-syntax-decorators for decorator parsing */
inherits: any;
/** AST visitor configuration for transforming decorator nodes */
visitor: VisitorConfig;
}
interface VisitorConfig {
/** Handles export default class declarations with decorators */
ExportDefaultDeclaration(path: NodePath): void;
/** Transforms class declarations to variable declarations for decorator processing */
ClassDeclaration(path: NodePath): void;
/** Main visitor for processing class expressions with decorators */
ClassExpression(path: NodePath, state: PluginState): void;
/** Processes object expressions with decorated properties/methods */
ObjectExpression(path: NodePath, state: PluginState): void;
/** Handles class property assignments for decorator initialization */
AssignmentExpression(path: NodePath, state: PluginState): void;
}Internal helper functions for generating runtime code and managing plugin state.
/**
* Ensures the applyDecoratedDescriptor helper function exists in the program
* @param {NodePath} path - Current AST path
* @param {PluginState} state - Plugin state object
* @returns {Identifier} Identifier for the helper function
*/
function ensureApplyDecoratedDescriptorHelper(path: NodePath, state: PluginState): Identifier;
/**
* Ensures the initializerDefineProp helper function exists in the program
* @param {NodePath} path - Current AST path
* @param {PluginState} state - Plugin state object
* @returns {Identifier} Identifier for the helper function
*/
function ensureInitializerDefineProp(path: NodePath, state: PluginState): Identifier;
/**
* Ensures the initializerWarning helper function exists in the program
* @param {NodePath} path - Current AST path
* @param {PluginState} state - Plugin state object
* @returns {Identifier} Identifier for the helper function
*/
function ensureInitializerWarning(path: NodePath, state: PluginState): Identifier;
/**
* Applies proper evaluation ordering for decorator expressions
* @param {NodePath} path - Class or object path with decorators
* @returns {Expression} Sequence expression with ordered evaluations
*/
function applyEnsureOrdering(path: NodePath): Expression | null;
/**
* Applies class-level decorators to a class expression
* @param {NodePath} classPath - Class expression path
* @param {PluginState} state - Plugin state object
* @returns {Expression} Decorated class expression
*/
function applyClassDecorators(classPath: NodePath, state: PluginState): Expression | null;
/**
* Applies method decorators to class methods
* @param {NodePath} path - Class expression path
* @param {PluginState} state - Plugin state object
* @returns {Expression} Class expression with decorated methods
*/
function applyMethodDecorators(path: NodePath, state: PluginState): Expression | null;
/**
* Applies decorators to object expression properties
* @param {NodePath} path - Object expression path
* @param {PluginState} state - Plugin state object
* @returns {Expression} Object expression with decorated properties
*/
function applyObjectDecorators(path: NodePath, state: PluginState): Expression | null;Code generation templates used internally by the plugin to create consistent runtime helpers.
/**
* Template for applying a class decorator
* Generates: DECORATOR(CLASS_REF = INNER) || CLASS_REF;
*/
const buildClassDecorator: Template;
/**
* Template for accessing class prototype
* Generates: CLASS_REF.prototype;
*/
const buildClassPrototype: Template;
/**
* Template for getting property descriptor
* Generates: Object.getOwnPropertyDescriptor(TARGET, PROPERTY);
*/
const buildGetDescriptor: Template;
/**
* Template for getting object property initializer descriptor
* Creates descriptor with initializer function for object properties
*/
const buildGetObjectInitializer: Template;
/**
* Template for initializer warning helper function
* Throws error when class properties aren't properly configured
*/
const buildInitializerWarningHelper: Template;
/**
* Template for initializer property definition helper
* Defines properties with initializer values at runtime
*/
const buildInitializerDefineProperty: Template;
/**
* Template for applying decorated descriptor helper function
* Main runtime helper that applies decorators to property descriptors
*/
const buildApplyDecoratedDescriptor: Template;Transforms class-level decorators that modify or replace the constructor.
Behavior: Class decorators are functions that receive the class constructor and can return a new constructor or modify the existing one.
Evaluation Order: Decorator expressions are evaluated top-to-bottom, but decorators are applied bottom-to-top (closest to class first).
// Decorator function signature
function classDecorator(constructor) {
// Can return new constructor or modify existing one
return constructor;
}
@classDecorator
class MyClass {}Transforms method decorators that modify property descriptors.
Behavior: Method decorators receive (target, property, descriptor) following the legacy decorator proposal.
// Method decorator function signature
function methodDecorator(target, property, descriptor) {
// Can return new descriptor or modify existing one
return descriptor;
}
class MyClass {
@methodDecorator
method() {}
@methodDecorator
static staticMethod() {}
}Transforms property decorators including class properties with initializers.
Behavior: Property decorators receive a descriptor with an initializer function for properties with default values.
// Property decorator function signature
function propertyDecorator(target, property, descriptor) {
// descriptor.initializer contains the property's initial value function
return descriptor;
}
class MyClass {
@propertyDecorator
property = "default value";
@propertyDecorator
static staticProperty = 42;
}Transforms decorators on object literal properties and methods.
Behavior: Works the same as class decorators but applies to object expressions.
const obj = {
@methodDecorator
method() {},
@propertyDecorator
property: "value"
};Ensures proper decorator evaluation and execution order.
Evaluation: Decorator expressions are evaluated top-to-bottom (as written) Execution: Decorators are executed bottom-to-top (closest to target first)
function dec(id) {
console.log('evaluated', id);
return function(target, property, descriptor) {
console.log('executed', id);
return descriptor;
};
}
class Example {
@dec(1) // Evaluated first
@dec(2) // Evaluated second, but executed first
method() {}
}
// Output: evaluated 1, evaluated 2, executed 2, executed 1The plugin generates helpful error messages for common issues:
/** Babel plugin state object passed between visitors */
interface PluginState {
/** Helper function identifier for applying decorated descriptors */
applyDecoratedDescriptor?: Identifier;
/** Helper function identifier for defining properties with initializers */
initializerDefineProp?: Identifier;
/** Helper function identifier for initializer warning */
initializerWarningHelper?: Identifier;
}
/** Babel template interface for code generation */
interface Template {
/** Generate AST nodes from template with placeholders */
(opts: {[key: string]: any}): {expression?: Expression, statement?: Statement};
}
/** Babel AST node path interface */
interface NodePath {
/** The AST node */
node: Node;
/** Scope information */
scope: Scope;
/** Replace this node with another */
replaceWith(node: Node): void;
/** Insert a node after this one */
insertAfter(node: Node): void;
/** Get a child path */
get(key: string): NodePath;
/** Check if this path represents a class */
isClass(): boolean;
/** Check if this path represents a class declaration */
isClassDeclaration(): boolean;
/** Build error with code frame */
buildCodeFrameError(message: string): Error;
}
/** Babel scope interface */
interface Scope {
/** Generate unique identifier */
generateUidIdentifier(name: string): Identifier;
/** Generate declared unique identifier */
generateDeclaredUidIdentifier(name: string): Identifier;
/** Get the program parent scope */
getProgramParent(): Scope;
/** Scope path */
path: NodePath;
}
/** Babel AST Expression node */
interface Expression {
type: string;
}
/** Babel AST Statement node */
interface Statement {
type: string;
}
/** Babel AST Identifier node */
interface Identifier {
type: "Identifier";
name: string;
}
/** Babel AST Node interface */
interface Node {
type: string;
decorators?: Decorator[];
}
/** Decorator AST node */
interface Decorator {
expression: Expression;
}
/** Decorator function signature for legacy decorators */
type DecoratorFunction = (
target: any,
property: string | symbol,
descriptor: PropertyDescriptor
) => PropertyDescriptor | void;
/** Class decorator function signature */
type ClassDecoratorFunction = (constructor: Function) => Function | void;transform-class-properties in plugin arraytransform-class-properties for proper class property handling@babel/plugin-proposal-decorators with legacy: true for Babel 7+)The plugin provides a smooth migration path from Babel 5 decorator behavior:
(target, property, descriptor)