Babel plugin that transforms ES2015 computed properties in object literals to ES5-compatible code. It provides two transformation modes: loose mode for simpler assignment-based transformations and spec-compliant mode using Object.defineProperty for proper property descriptor handling.
npm install --save-dev @babel/plugin-transform-computed-propertiesimport transformComputedProperties from "@babel/plugin-transform-computed-properties";For CommonJS:
const transformComputedProperties = require("@babel/plugin-transform-computed-properties");With type definitions:
import transformComputedProperties, { type Options } from "@babel/plugin-transform-computed-properties";Internal dependencies (used by the plugin):
import { declare } from "@babel/helper-plugin-utils";
import template from "@babel/template";
import { types as t } from "@babel/core";module.exports = {
plugins: [
"@babel/plugin-transform-computed-properties" // Default (spec mode)
]
};module.exports = {
plugins: [
["@babel/plugin-transform-computed-properties", { loose: true }]
]
};Input (ES2015 computed properties):
const key = "dynamicKey";
const obj = {
[key]: "value",
[expression()]: "computed",
[`prefix_${variable}`]: "template"
};Output (ES5-compatible, spec mode):
var _obj;
var key = "dynamicKey";
var obj = (_obj = {}, Object.defineProperty(_obj, key, {
value: "value",
enumerable: true,
writable: true,
configurable: true
}), Object.defineProperty(_obj, expression(), {
value: "computed",
enumerable: true,
writable: true,
configurable: true
}), Object.defineProperty(_obj, "prefix_" + variable, {
value: "template",
enumerable: true,
writable: true,
configurable: true
}), _obj);Output (ES5-compatible, loose mode):
var _obj;
var key = "dynamicKey";
var obj = (_obj = {}, _obj[key] = "value", _obj[expression()] = "computed", _obj["prefix_" + variable] = "template", _obj);Main Babel plugin created using the declare helper from @babel/helper-plugin-utils.
/**
* Default export: Babel plugin created via declare() helper
* @param api - Babel API object with helper methods and version assertion
* @param options - Plugin configuration options
* @returns Babel plugin object with visitor pattern
*/
const transformComputedProperties = declare((api: BabelApi, options: Options) => {
api.assertVersion(REQUIRED_VERSION(7)); // Ensures Babel 7+ compatibility
// Plugin implementation
return BabelPlugin;
});
export default transformComputedProperties;
interface BabelPlugin {
name: "transform-computed-properties";
visitor: {
ObjectExpression: {
exit(path: NodePath<t.ObjectExpression>, state: PluginPass): void;
};
};
}Plugin configuration interface for customizing transformation behavior.
interface Options {
/**
* Enable loose transformation mode.
* When true, uses simple assignment instead of Object.defineProperty.
* Note: This option is overridden by Babel's "setComputedProperties" assumption if present.
* Priority: api.assumption("setComputedProperties") ?? options.loose
* @default false
*/
loose?: boolean;
}Object.defineProperty() for property creationobj[key] = value)loose: true option or Babel's setComputedProperties assumptionThe plugin properly handles computed getter and setter methods:
Input:
const obj = {
get [computedKey]() { return this._value; },
set [computedKey](value) { this._value = value; }
};Output (spec mode):
var obj = (function() {
var _obj = {};
return defineAccessor("get", _obj, computedKey, function() {
return this._value;
}), defineAccessor("set", _obj, computedKey, function(value) {
this._value = value;
}), _obj;
})();Computed regular methods are converted to function expressions:
Input:
const obj = {
[methodName]() { return "hello"; }
};Output:
var _obj = {};
Object.defineProperty(_obj, methodName, {
value: function() { return "hello"; },
enumerable: true,
writable: true,
configurable: true
});The plugin generates calls to Babel runtime helpers:
state.addHelper("defineProperty")state.addHelper("defineAccessor")For compatibility with older Babel versions (@babel/helpers <= 7.20.6), the plugin includes a fallback DefineAccessorHelper created using template.expression.ast.
These helpers are automatically added by Babel's plugin system when needed.
This plugin enables computed property syntax to work in environments that don't support ES2015, including:
// Internal types used by the plugin (not part of public API)
type PropertyInfo = {
scope: Scope;
objId: t.Identifier;
body: t.Statement[];
computedProps: t.ObjectMember[];
initPropExpression: t.ObjectExpression;
state: PluginPass;
};
// Babel core types used in the plugin
interface BabelApi {
assertVersion(version: number | string): void;
assumption(name: string): boolean | undefined;
}
interface PluginPass {
addHelper(name: string): t.Identifier;
availableHelper(name: string): boolean;
file: BabelFile;
}
interface BabelFile {
scope: Scope;
get(key: string): any;
set(key: string, value: any): void;
}
interface Scope {
generateUidIdentifier(name: string): t.Identifier;
generateUidIdentifierBasedOnNode(node: t.Node): t.Identifier;
push(opts: { id: t.Identifier; init?: t.Expression }): void;
}
interface NodePath<T = t.Node> {
node: T;
parent: t.Node;
scope: Scope;
replaceWith(replacement: t.Node): void;
replaceWithMultiple(nodes: t.Node[]): void;
}
// Constants used internally
const CHUNK_LENGTH_CAP = 10; // Prevents deeply nested AST structures