tooling.report

feature

CommonJS

Can CommonJS bundles be generated?

Person in shorts with blue hair walking left

Introduction

Prior to the introduction of ECMAScript Modules, the most widely-used module format in JavaScript was CommonJS. The majority of modules on npm are published as CommonJS due to its longstanding support in Node.js, so it's important that build tools can consume CommonJS modules for compatibility reasons. The same is true for JavaScript produced by build tools: many applications need to run in environments like Node.js, which often requires generating bundles in the CommonJS format.

The Test

This test checks to see if each build tool is capable of generating JavaScript bundles that use the CommonJS format. It bundles an entry module that depends on a utils module and a third dynamically-imported exclaim module. The dynamic import can be approximated in CommonJS using an inline require() call.

index.js

import { logCaps } from './utils.js';

// example dynamic import:
async function main() {
  const { exclaim } = await import('./exclaim.js');
  logCaps(exclaim('This is index'));
}
main();

utils.js

export function logCaps(msg) {
  console.log(msg.toUpperCase());
}

exclaim.js

export function exclaim(msg) {
  return msg + '!';
}

The result of bundling index.js should be two JavaScript bundles in CommonJS format: one containing the code from index.js and utils.js, and another with the contents of exclaim.js. The main() function in the first bundle should now contain a dynamic require() call that loads and executes the second bundle.

Conclusion

browserify

Browserify expects source files to be CommonJS modules, though its default output is a custom registry. Setting the node option to true instructs Browserify to produce CommonJS bundles. These bundles are Browserify-specific, though the node target does use require() to for code loading.

parcel

Parcel can build for different "targets", which are defined in package.json. Configuring a target with either { "context": "node" } or { "outputFormat": "commonjs" } will produce CommonJS output that uses require() and module.exports to load code-splitted bundles.

rollup

Rollup produces bundles that are idiomatic CommonJS modules when output.format is set to cjs. Code-splitted bundles, such as those resulting from the use of Dynamic Import, are loaded via require(). Unlike other bundlers, the generated output is clean CommonJS, and doesn't require a particular loader.

webpack

Webpack supports CommonJS output by setting the target configuration option to "node".

It's worth noting that Webpack's CommonJS output does not directly use require() or module.exports. Rather, the files are standard Webpack bundles that use require() for script loading and module.exports in place of the global Webpack registry. From the outside, this appears as standard CommonJS, but any code-splitted or internal modules can only be consumed by Webpack-based entry modules.