Babel plugin to ensure function declarations at the block level are block scoped
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Babel plugin to ensure function declarations at the block level are block scoped. This plugin transforms ES2015 block-scoped function declarations to prevent hoisting issues and maintain proper block scope semantics by converting function declarations within block statements to let-scoped variable declarations with function expressions.
npm install --save-dev babel-plugin-transform-es2015-block-scoped-functionsThe plugin exports a single default function that creates the Babel plugin configuration:
const plugin = require("babel-plugin-transform-es2015-block-scoped-functions");For use in Babel configuration:
// Via require (CommonJS)
require("babel-core").transform("code", {
plugins: ["transform-es2015-block-scoped-functions"]
});.babelrc
{
"plugins": ["transform-es2015-block-scoped-functions"]
}babel --plugins transform-es2015-block-scoped-functions script.jsrequire("babel-core").transform("code", {
plugins: ["transform-es2015-block-scoped-functions"]
});The plugin transforms function declarations within block statements to prevent ES5-style hoisting behavior:
Input:
{
function foo() {
return "block scoped";
}
if (condition) {
function bar() {
return "conditional";
}
}
}Output:
{
let foo = function () {
return "block scoped";
};
if (condition) {
let bar = function () {
return "conditional";
};
}
}The main export that creates the Babel plugin configuration.
/**
* Creates a Babel plugin for transforming block-scoped function declarations
* @param {Object} context - Babel plugin context
* @param {Object} context.types - Babel types utility object (destructured as 't')
* @returns {PluginConfig} Babel plugin configuration with visitor methods
*/
export default function ({ types: t }) {
return {
visitor: {
BlockStatement(path) { /* transforms function declarations in block statements */ },
SwitchCase(path) { /* transforms function declarations in switch cases */ }
}
};
}The object returned by the plugin factory function containing AST visitor methods.
interface PluginConfig {
visitor: {
/** Handles function declarations within block statements */
BlockStatement(path: NodePath<BlockStatement>): void;
/** Handles function declarations within switch case statements */
SwitchCase(path: NodePath<SwitchCase>): void;
};
}Transforms function declarations within block statements, excluding function bodies and export declarations.
/**
* Processes BlockStatement nodes to transform function declarations
* @param {NodePath} path - Babel AST path for the BlockStatement node
*/
function BlockStatement(path) {
const { node, parent } = path;
// Skip if parent is a function body or export declaration
if (t.isFunction(parent, { body: node }) || t.isExportDeclaration(parent)) {
return;
}
// Transform function declarations in the body
statementList("body", path);
}Transforms function declarations within switch case consequent statements.
/**
* Processes SwitchCase nodes to transform function declarations
* @param {NodePath} path - Babel AST path for the SwitchCase node
*/
function SwitchCase(path) {
// Transform function declarations in the consequent
statementList("consequent", path);
}The internal helper function that performs the actual transformation:
/**
* Internal helper that transforms function declarations in statement arrays
* @param {string} key - Property name containing the statement array
* @param {NodePath} path - Babel AST path object
*/
function statementList(key, path) {
const paths = path.get(key);
for (const path of paths) {
const func = path.node;
if (!path.isFunctionDeclaration()) continue;
// Create let variable declaration with function expression
const declar = t.variableDeclaration("let", [
t.variableDeclarator(func.id, t.toExpression(func))
]);
// Set hoisting priority
declar._blockHoist = 2;
// Remove original function name
func.id = null;
// Replace the function declaration
path.replaceWith(declar);
}
}interface BabelContext {
/** Babel types utility object for AST manipulation */
types: BabelTypes;
}
interface BabelTypes {
/** Check if node is a function */
isFunction(node: Node, opts?: Object): boolean;
/** Check if node is an export declaration */
isExportDeclaration(node: Node): boolean;
/** Create a variable declaration node */
variableDeclaration(kind: string, declarations: VariableDeclarator[]): VariableDeclaration;
/** Create a variable declarator node */
variableDeclarator(id: Identifier, init: Expression): VariableDeclarator;
/** Convert a function declaration to function expression */
toExpression(node: Node): Expression;
}interface NodePath<T = Node> {
/** The AST node at this path */
node: T;
/** Parent node */
parent: Node;
/** Get child paths for a property */
get(key: string): NodePath[];
/** Check if node is a function declaration */
isFunctionDeclaration(): boolean;
/** Replace this node with another node */
replaceWith(node: Node): void;
}The plugin specifically processes these AST node types:
The plugin ensures proper ES2015 block scoping by:
let variable declarations_blockHoist = 2)