webpack – How To Generate an ES Module bundle

In this post I will detail how you can use webpack to output a library (bundle) file that exposes the ECMAscript module (ESM) interface – the new JavaScript module standard. From my research, I found that webpack does not yet have a libraryTarget for ESM and my searching also did not surface much on the topic, so I created a new webpack plugin to produce such output.

For the last few weeks, as time allows it, I been in the process of upgrading my base set of tools, which I use on most side projects, to use the latest versions of their underlying 3rd party libraries. As part of those tools, I also maintain a set of webpack configurations that my projects use via npm run scripts to output different types of distribution files – for example: output minified and non-minfied bundles. A few weeks ago I saw this post on Twitter:

Which reminded me: one of the things I been looking add is a webpack configuration that allows me to produce a ES module.  I thought for sure this would be just a matter of turning a nob in the webpack configuration file, but to my surprise, it is not yet supported.  I did find a thread in github that seems to be tracking this feature request, so I’m sure its just a matter of time before it is introduced.  Other build tools, like Rollup, do support outputting an ES module, but I don’t want to learn yet another bundling tool – I’m committed to webpack for the time being, so what am I to do? Create a webpack plugin that outputs the type of bundle I want! 🙂 That’s one of the powerful things about webpack – it has very fine grained APIs that allows you to do just about anything.

The Plugin

The new plugin is named esm-webpack-plugin and it adds back the export statements to the bottom of the built bundle that comes out of a normal webpack build.  The setup is similar to other webpack plugin, so this is how you would use it:

Install it:

npm i -D @purtuga/esm-webpack-plugin

Setup your webpack.config.js:

const EsmWebpackPlugin = require("esm-webpack-plugin");
module.exports = {
    mode: "development",
    entry: "index.js",
    output: {
        filename: "consoleUtils.js",
        library: "LIB",
        libraryTarget: "var"
    plugins: [
        new EsmWebpackPlugin()

And that is it. The output bundle should now have export statements that allows you to consume it as a ESM.

In generating libraries, you are likely familiar with setting the output.libraryTarget to umd, amd or commonjs , but in order to avoid having webpack insert unnecessary loading code into the bundle, this plugin expects that you set the libraryTarget to either var (default) or assign. The name you provide to ouptput.library is also important and should not conflict with the names that will be exported.



Lets take a look at what the sample output will look like when this plugin is used. Let say you have this main.js file:

export function writeRed(msg) {
    console.log('%c ' + msg + ' ', 'color: white;background:red');

export default writeRed;

The above code, after being built with webpack using the esm-webpack-plugin, will produce a bundle similar to the following:

var LIB = (/******/ (function(modules){/* webpack bundle code */}));
export const writeRed = LIB['writeRed'];
export default LIB['default'];

Which, can now be consumed from a browser as:

import {writeRed} from "consoleUtils.js";



At the moment, I can only think of the following limitations with the solution I detailed above for generating ES modules:

  1. The value provided in output.libraryType is expected to be either var or assign
  2. The value of output.library must be set to a value that does match the name of a exported module member
  3. When consuming the module produced from other projects that themselves will generate a built package, note that I doubt that webpack will be able to apply its tree-shaking functionality to a bundle produced with this plugin – thus your bundle would likely include unused/dead code.



The above plugin now allows me to generate an ES module out of my libraries (in addition to other formats), thus targeting the JavaScript standard library format.  I’m sure that webpack will add support for this in the future, along with efficiencies that goes well beyond what this plugin does, but in the mean time, this seems to do the job.




Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s