Babel plugin to ensure function declarations at the block level are block scoped
npx @tessl/cli install tessl/npm-babel--plugin-transform-block-scoped-functions@7.27.0@babel/plugin-transform-block-scoped-functions is a Babel plugin that transforms function declarations within block statements to ensure proper block-level scoping semantics. It converts function declarations inside blocks (like if statements, loops, or switch cases) into let-bound variable declarations containing function expressions, preventing function hoisting issues and ensuring ES6+ block scoping behavior.
npm install --save-dev @babel/plugin-transform-block-scoped-functions// CommonJS
const plugin = require("@babel/plugin-transform-block-scoped-functions");
// ES modules (for Babel configuration)
import plugin from "@babel/plugin-transform-block-scoped-functions";{
"plugins": ["@babel/plugin-transform-block-scoped-functions"]
}// babel.config.js
module.exports = {
plugins: ["@babel/plugin-transform-block-scoped-functions"]
};Input:
if (true) {
function f() {
return 1;
}
console.log(f()); // 1
}Output:
if (true) {
let f = function() {
return 1;
};
console.log(f()); // 1
}The main plugin export that integrates with Babel's transformation pipeline. The plugin enforces Babel 7+ compatibility through version assertion.
/**
* Main Babel plugin function created using declare() from @babel/helper-plugin-utils
* Internally calls api.assertVersion(REQUIRED_VERSION(7)) to ensure Babel 7+ compatibility
* @param api - Babel API object containing helper functions and version checks
* @returns Babel plugin object with visitor methods
*/
export default function(api: any): BabelPlugin;
interface BabelPlugin {
/** Plugin identifier name */
name: string;
/** AST visitor methods for transformation */
visitor: PluginVisitor;
}
interface PluginVisitor {
/** Transforms function declarations within block statements */
BlockStatement(path: NodePath<t.BlockStatement>): void;
/** Transforms function declarations within switch case consequents */
SwitchCase(path: NodePath<t.SwitchCase>): void;
}Handles function declarations within block statements, converting them to let-bound variable declarations.
/**
* Transforms function declarations within block statements
* @param path - NodePath for the BlockStatement node
*/
BlockStatement(path: NodePath<t.BlockStatement>): void;Behavior:
transformStatementListHandles function declarations within switch case consequents.
/**
* Transforms function declarations within switch case consequents
* @param path - NodePath for the SwitchCase node
*/
SwitchCase(path: NodePath<t.SwitchCase>): void;Behavior:
transformStatementListIn all Babel versions, the plugin transforms function declarations within blocks:
// Before transformation
{
function myFunc() {
return "hello";
}
}
// After transformation
{
let myFunc = function() {
return "hello";
};
}In Babel 8, additional rules apply based on strict mode and function characteristics:
Transformed variable declarations receive special hoisting treatment:
// The generated variable declaration gets _blockHoist = 2
let myFunc = function() { /* ... */ };
// This ensures proper ordering within the blockThe plugin operates at the AST transformation level and does not throw runtime errors. Potential issues:
declare() function for plugin creation// From @babel/core
interface NodePath<T = Node> {
node: T;
parent: Node;
isInStrictMode(): boolean;
isFunctionDeclaration(): boolean;
get(key: string): NodePath | NodePath[];
replaceWith(node: Node): void;
getData(key: string): any;
}
// From @babel/types
interface BlockStatement extends Node {
body: Statement[];
}
interface SwitchCase extends Node {
consequent: Statement[];
}
interface FunctionDeclaration extends Node {
id: Identifier | null;
async: boolean;
generator: boolean;
}
interface VariableDeclaration extends Node {
kind: "var" | "let" | "const";
declarations: VariableDeclarator[];
_blockHoist?: number;
}