Transforms logical assignment operators into short-circuited assignments for JavaScript environments that don't natively support these ES2021 operators
npx @tessl/cli install tessl/npm-babel--plugin-transform-logical-assignment-operators@7.27.0This Babel plugin transforms logical assignment operators (&&=, ||=, ??=) into equivalent short-circuited assignment expressions for JavaScript environments that don't natively support these ES2021 operators. The plugin provides comprehensive transformation logic with proper memoization to handle complex assignment targets safely.
npm install @babel/plugin-transform-logical-assignment-operatorsThis is a Babel plugin used through Babel configuration. It is not imported directly in user code:
{
"plugins": ["@babel/plugin-transform-logical-assignment-operators"]
}Add the plugin to your Babel configuration to transform logical assignment operators:
{
"plugins": ["@babel/plugin-transform-logical-assignment-operators"]
}Input:
// Logical OR assignment
obj.x ||= 1;
arr[0] ||= "default";
// Logical AND assignment
user.preferences &&= { theme: "dark" };
// Nullish coalescing assignment
config.apiUrl ??= "https://api.example.com";Output:
// Logical OR assignment
obj.x || (obj.x = 1);
arr[0] || (arr[0] = "default");
// Logical AND assignment
user.preferences && (user.preferences = { theme: "dark" });
// Nullish coalescing assignment
config.apiUrl ?? (config.apiUrl = "https://api.example.com");This plugin follows Babel's standard plugin architecture:
The plugin exports a default function created using declare() from @babel/helper-plugin-utils that returns a Babel plugin object.
declare function PluginFactory(api: PluginAPI): PluginObject;
export default PluginFactory;
interface PluginAPI {
assertVersion(version: number): void;
}
interface PluginObject {
name: "transform-logical-assignment-operators";
manipulateOptions?: (opts: any, parser: { plugins: string[] }) => void;
visitor: {
AssignmentExpression(path: NodePath<AssignmentExpression>): void;
};
}This plugin accepts no configuration options and operates with fixed transformation behavior.
// Plugin options interface (empty)
interface PluginOptions {}Usage Examples:
{
"plugins": [
"@babel/plugin-transform-logical-assignment-operators"
]
}The plugin transforms three types of logical assignment operators into equivalent expressions that preserve evaluation semantics.
Transforms logical OR assignment operators into conditional assignment expressions.
Input: x ||= y
Output: x || (x = y)
// Handles expressions like:
// obj.prop ||= value
// arr[index] ||= value
// computed[key] ||= valueTransforms logical AND assignment operators into conditional assignment expressions.
Input: x &&= y
Output: x && (x = y)
// Handles expressions like:
// obj.prop &&= value
// arr[index] &&= value
// computed[key] &&= valueTransforms nullish coalescing assignment operators into logical expressions using the nullish coalescing operator with conditional assignment.
Input: x ??= y
Output: x ?? (x = y)
// Handles expressions like:
// obj.prop ??= value → obj.prop ?? (obj.prop = value)
// arr[index] ??= value → arr[index] ?? (arr[index] = value)
// computed[key] ??= value → computed[key] ?? (computed[key] = value)The plugin uses Babel's scope.maybeGenerateMemoised() to intelligently determine when memoization is needed for complex member expressions, preventing side effects during property access.
// The plugin memoizes complex object expressions and computed properties separately
// Uses t.assignmentExpression("=", memo, originalExpression) for memoizationMemoization Examples:
// Simple properties don't need memoization
// Input: obj.x ||= 1
// Output: obj.x || (obj.x = 1)
// Complex objects are memoized
// Input: deep.obj.x ||= 1
// Output: (_deep$obj = deep.obj).x || (_deep$obj.x = 1)
// Computed properties with side effects are memoized
// Input: obj[++key] ||= 1
// Output: obj[_key = ++key] || (obj[_key] = 1)
// Both object and property memoization when needed
// Input: deep.obj[++key] ||= 1
// Output: (_deep$obj = deep.obj)[_key = ++key] || (_deep$obj[_key] = 1)Conditionally adds parser support for logical assignment operators based on Babel version. The behavior differs between Babel 7 and Babel 8.
// Babel version-specific conditional logic
manipulateOptions: process.env.BABEL_8_BREAKING
? undefined
: (_, parser) => parser.plugins.push("logicalAssignment");Babel Version Behavior:
"logicalAssignment" parser plugin to enable syntax parsingundefined - logical assignment syntax is built-in to the parserImplementation Details:
process.env.BABEL_8_BREAKING environment variable for version detectionlogicalAssignment parser plugin enables recognition of ||=, &&=, and ??= operatorsThe plugin handles transformation errors gracefully within Babel's error reporting system. It does not throw custom exceptions but relies on Babel's built-in AST validation and error handling.
// Plugin version requirements
interface VersionRequirements {
babel: "^7.0.0-0"; // Peer dependency
node: ">=6.9.0"; // Minimum Node.js version
}The plugin is written in TypeScript and provides full type definitions:
src/index.tslib/index.jslib/index.d.ts@babel/typesThe plugin works with these Babel AST node types and validates operators using t.LOGICAL_OPERATORS:
interface AssignmentExpression {
type: "AssignmentExpression";
operator: string; // "||=", "&&=", or "??="
left: LVal;
right: Expression;
}
// The plugin extracts logical operator using operator.slice(0, -1)
// Then validates against t.LOGICAL_OPERATORS: ["||", "&&", "??"]
type LVal =
| Identifier
| MemberExpression
| ArrayPattern
| ObjectPattern
| RestElement
| TSParameterProperty;
// Key AST utilities used:
// - t.cloneNode() for safe node duplication
// - t.logicalExpression() for creating logical expressions
// - t.assignmentExpression() for creating assignment expressions
// - scope.maybeGenerateMemoised() for intelligent memoizationThe core transformation logic is implemented in the AssignmentExpression visitor:
interface Visitor {
AssignmentExpression(path: NodePath<AssignmentExpression>): void;
}
interface NodePath<T> {
node: T;
replaceWith(node: Expression): void;
// ... other NodePath methods
}The visitor method implementation:
operator.slice(0, -1) to extract logical part, validates with t.LOGICAL_OPERATORS.includes(operatorTrunc)t.cloneNode(left) to avoid mutation issuesMemberExpression: Uses scope.maybeGenerateMemoised(object) for object memoizationscope.maybeGenerateMemoised(property) for property memoizationt.logicalExpression() with original operator and assignment expressionpath.replaceWith() to substitute the transformed expressionSpecial Handling:
Super objects in memoizationPrivateName properties with type guards