@babel/plugin-proposal-explicit-resource-management is a Babel plugin that transforms JavaScript's explicit resource management syntax (using and await using declarations) into ES2015-compatible code. It implements the ECMAScript Explicit Resource Management proposal by converting resource acquisition declarations into try-catch-finally blocks with proper disposal patterns.
npm install --save-dev @babel/plugin-proposal-explicit-resource-managementThe plugin is not imported directly in application code but configured in Babel configuration:
{
"plugins": ["@babel/plugin-proposal-explicit-resource-management"]
}For plugin development and understanding the source code:
import { declare } from "@babel/helper-plugin-utils";
import { unshiftForXStatementBody } from "@babel/plugin-transform-destructuring";
import { types as t, template, traverse } from "@babel/core";
import type { NodePath, Visitor, PluginPass } from "@babel/core";Note: REQUIRED_VERSION is a build-time macro available in Babel plugin development, not a regular import. It's transformed during the build process to version strings.
This plugin transforms explicit resource management syntax from:
{
using resource = acquireResource();
await using asyncResource = acquireAsyncResource();
// Resources automatically disposed when leaving scope
}Into ES2015-compatible code with proper disposal handling:
{
try {
var _usingCtx = _usingCtx();
const resource = _usingCtx.u(acquireResource());
const asyncResource = _usingCtx.a(acquireAsyncResource());
// Original code here
} catch (_) {
_usingCtx.e = _;
} finally {
await _usingCtx.d();
}
}The plugin operates as a standard Babel transform plugin with the following key components:
explicitResourceManagement parser pluginCreates the Babel plugin object that transforms explicit resource management syntax.
/**
* Main plugin factory function exported as default
* Uses declare helper to create the plugin with version assertion
*/
declare((api: PluginAPI) => {
api.assertVersion(REQUIRED_VERSION("^7.22.0"));
return {
name: "proposal-explicit-resource-management";
manipulateOptions: (_: any, p: { plugins: string[] }) => p.plugins.push("explicitResourceManagement");
visitor: Visitor<PluginPass>;
};
});
interface PluginObject {
/** Plugin identifier name */
name: "proposal-explicit-resource-management";
/** Function to enable explicitResourceManagement parser plugin */
manipulateOptions: (_: any, parserOpts: { plugins: string[] }) => void;
/** AST visitor object for transformations merged from multiple visitors */
visitor: Visitor<PluginPass>;
}The plugin uses specific visitor methods to transform different AST node types:
/**
* Main transformation visitor that handles using declarations
*/
interface TransformUsingDeclarationsVisitor {
/** Transforms using declarations in for-of statements by creating temporary variables */
ForOfStatement(path: NodePath<t.ForOfStatement>): void;
/** Transforms using declarations in block statements and static blocks with try-catch-finally */
"BlockStatement|StaticBlock"(path: NodePath<t.BlockStatement | t.StaticBlock>, state: PluginPass): void;
}
/**
* Complete plugin visitor merged from multiple visitors
*/
interface CompletePluginVisitor extends TransformUsingDeclarationsVisitor {
/** Handles top-level using declarations by wrapping module body and hoisting exports */
Program(path: NodePath<t.Program>): void;
/** Pre-processes async functions with using declarations before async-to-generator transform */
Function(path: NodePath<t.Function>, state: PluginPass): void;
}
/**
* Visitor that skips function processing for specific transformation phases
*/
interface SkipFunctionVisitor extends TransformUsingDeclarationsVisitor {
/** Skips function body traversal */
Function(path: NodePath<t.Function>): void;
}The plugin transforms the following syntax patterns:
Transforms using and await using declarations in block statements and static blocks.
// Input syntax
using resource = acquireResource();
await using asyncResource = acquireAsyncResource();
// Transforms to try-catch-finally with disposalHandles using declarations at module scope by wrapping the module body.
// Module-level using declarations
using moduleResource = acquireResource();
export { someValue };
// Transforms entire module body into block with disposalTransforms using declarations in for-of loop iteration variables.
// Input syntax
for (using item of items) {
// item is disposed after each iteration
}
// Transforms to proper disposal patternSpecial handling for await using in async functions before async-to-generator transformation.
async function example() {
await using resource = acquireAsyncResource();
// Proper async disposal handling
}The plugin implements dual transformation strategies based on Babel version and helper availability:
/**
* Babel 8 transformation strategy using usingCtx helper
* Generated when state.availableHelper("usingCtx") returns true
*/
interface Babel8TransformStrategy {
/** Creates disposal context variable */
contextId: t.Identifier;
/** Wraps using declarations in try-catch-finally with context calls */
transformPattern: "try { var ctx = usingCtx(); const resource = ctx.u(acquire()); } catch(_) { ctx.e = _; } finally { await ctx.d(); }";
}
/**
* Legacy transformation strategy using using/dispose helpers
* Used when usingCtx helper is not available
*/
interface LegacyTransformStrategy {
/** Creates disposal stack array */
stackId: t.Identifier;
/** Uses stack-based disposal with using() and dispose() helpers */
transformPattern: "try { var stack = []; const resource = using(stack, acquire()); } catch(_) { var error = _; var hasError = true; } finally { dispose(stack, error, hasError); }";
}The plugin integrates with Babel's helper system for runtime functionality:
/**
* Runtime helpers used by the plugin
*/
interface BabelHelpers {
/** Sets function name for anonymous functions */
setFunctionName(fn: Function, name: string): Function;
/** Creates disposal context (Babel 8 mode) */
usingCtx(): DisposalContext;
/** Registers resource for disposal (legacy mode) */
using(stack: any[], resource: any, isAsync?: boolean): any;
/** Performs resource disposal (legacy mode) */
dispose(stack: any[], error: any, hasError: boolean): Promise<void> | void;
}
interface DisposalContext {
/** Add synchronous disposable resource */
u(resource: any): any;
/** Add asynchronous disposable resource */
a(resource: any): any;
/** Dispose all resources */
d(): Promise<void>;
/** Error to be rethrown after disposal */
e?: any;
}The plugin has specific requirements for proper operation:
interface PluginRequirements {
/** Minimum Babel version required (checked via REQUIRED_VERSION macro) */
babelVersion: "^7.22.0";
/** Parser plugin automatically enabled by manipulateOptions */
parserPlugin: "explicitResourceManagement";
/** Peer dependency requirement from package.json */
babelCore: "^7.0.0-0";
/** Node.js version requirement */
nodeVersion: ">=6.9.0";
/** Helper availability determines transformation strategy */
helperAvailability: {
/** Babel 8 mode: uses usingCtx() helper */
modern: "state.availableHelper('usingCtx')";
/** Legacy mode: uses using() and dispose() helpers */
legacy: "!state.availableHelper('usingCtx')";
};
}
/**
* REQUIRED_VERSION macro usage in plugin
* This is a compile-time macro that gets replaced with version strings
*/
interface RequiredVersionMacro {
/** Macro function signature (not a real function) */
REQUIRED_VERSION(version: "^7.22.0"): string;
/** Transformed at build time to actual version requirement */
transformedTo: "^7.22.0" | "^7.22.0 || >8.0.0-alpha <8.0.0-beta";
}The plugin generates robust error handling patterns:
Complete list of syntax patterns the plugin handles:
/**
* Supported syntax transformations
*/
interface SupportedSyntax {
/** Synchronous resource management */
using: "using resource = acquireResource();";
/** Asynchronous resource management */
awaitUsing: "await using resource = acquireAsyncResource();";
/** Block-scoped declarations */
blockScope: "{ using resource = acquire(); }";
/** Module-scoped declarations */
moduleScope: "using moduleResource = acquire(); export default value;";
/** For-of loop declarations */
forOfLoop: "for (using item of items) { }";
/** Anonymous function name setting */
anonymousFunction: "using fn = class { };";
}/**
* Internal enums and types used by the plugin
*/
enum USING_KIND {
NORMAL = 0,
AWAIT = 1
}
/**
* Babel API types used by the plugin
*/
import type {
NodePath,
Visitor,
PluginPass,
PluginAPI,
PluginObject
} from "@babel/core";
import type { types as t } from "@babel/core";
/**
* Internal helper functions used by the plugin
*/
interface InternalHelpers {
/** Checks if node is an anonymous function definition (class expression without id) */
isAnonymousFunctionDefinition(node: t.Node): node is t.ClassExpression;
/** Checks if node is a using declaration (using or await using) */
isUsingDeclaration(node: t.Node): node is t.VariableDeclaration;
/** Creates a call to setFunctionName helper for anonymous functions */
emitSetFunctionNameCall(state: PluginPass, expression: t.Expression, name: string): t.CallExpression;
}
/**
* Top-level using declaration tracking map used internally
* Maps nodes to their using kind (NORMAL or AWAIT)
*/
const TOP_LEVEL_USING: Map<t.Node, USING_KIND>;
/**
* Transform visitor that skips function bodies for specific transformations
*/
const transformUsingDeclarationsVisitorSkipFn: Visitor<PluginPass>;
/**
* Main transformation visitor for using declarations
*/
const transformUsingDeclarationsVisitor: Visitor<PluginPass>;
/**
* Helper function from @babel/plugin-transform-destructuring
* Used for transforming for-of statements with using declarations
*/
function unshiftForXStatementBody(
path: NodePath<t.ForOfStatement>,
nodes: t.Statement[]
): void;