You can wrap your esbuild output in an IIFE (Immediately Invoked Function Expression) format, which is a common pattern for browser-based JavaScript to create a private scope and avoid polluting the global namespace.
Here’s how you can achieve this with esbuild:
Let’s say you have a simple src/index.js file:
// src/index.js
const greeting = "Hello from the IIFE!";
console.log(greeting);
function privateHelper() {
console.log("This is a private function.");
}
privateHelper();
And you want to bundle it into a single file, wrapped in an IIFE.
You can use esbuild’s banner option to prepend the IIFE wrapper.
First, create a file named iife-wrapper.js that contains your IIFE structure:
// iife-wrapper.js
(function() {
// Your bundled code will go here
})();
Now, you can use the esbuild command-line interface:
esbuild src/index.js --bundle --outfile=dist/bundle.js --banner:js='(function() {
' --footer:js='
})();'
Let’s break down what’s happening here:
esbuild src/index.js: This is your entry point file.--bundle: This tells esbuild to bundle all your dependencies into a single output file.--outfile=dist/bundle.js: This specifies the output file name and path.--banner:js='(function() {: This prepends the string(function() {to your output file. We use--banner:jsto ensure it’s treated as JavaScript.--footer:js='})();': This appends the string})();to your output file. Similarly,--footer:jsensures it’s JavaScript.
After running this command, your dist/bundle.js will look something like this:
// dist/bundle.js
(function() {
const greeting = "Hello from the IIFE!";
console.log(greeting);
function privateHelper() {
console.log("This is a private function.");
}
privateHelper();
})();
When you include this bundle.js in an HTML file:
<!DOCTYPE html>
<html>
<head>
<title>IIFE Example</title>
</head>
<body>
<script src="dist/bundle.js"></script>
</body>
</html>
The greeting and privateHelper function are now scoped within the IIFE and do not exist in the global scope. If you were to try and access greeting or privateHelper directly in another script tag or in the browser’s console after the bundle has loaded, you would get a ReferenceError.
This pattern is particularly useful when you’re building libraries or scripts intended to be dropped into a page without interfering with other existing JavaScript.
The core problem this solves is the "global namespace pollution" issue. In older JavaScript, or in simple scripts, variables and functions declared at the top level become properties of the global window object. As your project grows, or when integrating with third-party scripts, this can lead to naming collisions where two different scripts try to declare a variable with the same name, causing unexpected behavior or overwriting each other’s code. The IIFE creates a function scope that is immediately executed. Any variables or functions declared inside this function are local to that scope and are not exposed globally.
You can also achieve this programmatically using esbuild’s JavaScript API. This is useful if you’re integrating esbuild into a build process or a custom tool.
// build.js
const esbuild = require('esbuild');
esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
banner: { js: '(function() { /* Start of IIFE */' },
footer: { js: '/* End of IIFE */ })();' },
}).catch(() => process.exit(1));
Running node build.js will produce the same dist/bundle.js file as the command-line example. The banner and footer options accept an object with a js property for JavaScript code, allowing for more structured insertion.
The most surprising thing about this approach is how little actual transformation esbuild performs. It’s not intelligently wrapping your code; it’s simply prepending and appending raw text. The magic is in the JavaScript syntax you provide.
The next concept you’ll likely encounter when dealing with browser scripts and bundling is module resolution and how esbuild handles import and export statements, especially when you want to expose specific parts of your bundled code to the global scope or to other modules.