Skip to content
On this page

Frequently Asked Questions

Why are ES modules better than CommonJS Modules?

ES modules are an official standard and the clear path forward for JavaScript code structure, whereas CommonJS modules are an idiosyncratic legacy format that served as a stopgap solution before ES modules had been proposed. ES modules allow static analysis that helps with optimizations like tree-shaking and scope-hoisting, and provide advanced features like circular references and live bindings.

What Is "tree-shaking?"

Tree-shaking, also known as "live code inclusion", is Rollup's process of eliminating code that is not actually used in a given project. It is a form of dead code elimination but can be much more efficient than other approaches with regard to output size. The name is derived from the abstract syntax tree of the modules (not the module graph). The algorithm first marks all relevant statements and then "shakes the syntax tree" to remove all dead code. It is similar in idea to the mark-and-sweep garbage collection algorithm. Even though this algorithm is not restricted to ES modules, they make it much more efficient as they allow Rollup to treat all modules together as a big abstract syntax tree with shared bindings.

How do I use Rollup in Node.js with CommonJS modules?

Rollup strives to implement the specification for ES modules, not necessarily the behaviors of Node.js, NPM, require(), and CommonJS. Consequently, loading of CommonJS modules and use of Node's module location resolution logic are both implemented as optional plugins, not included by default in the Rollup core. Just npm install the commonjs and node-resolve plugins and then enable them using a rollup.config.js file and you should be all set. If the modules import JSON files, you will also need the json plugin.

Why isn't node-resolve a built-in feature?

There are two primary reasons:

  1. Philosophically, it's because Rollup is essentially a polyfill of sorts for native module loaders in both Node and browsers. In a browser, import foo from 'foo' won't work, because browsers don't use Node's resolution algorithm.

  2. On a practical level, it's just much easier to develop software if these concerns are neatly separated with a good API. Rollup's core is quite large, and everything that stops it getting larger is a good thing. Meanwhile, it's easier to fix bugs and add features. By keeping Rollup lean, the potential for technical debt is small.

Please see this issue for a more verbose explanation.

Why do additional imports turn up in my entry chunks when code-splitting?

By default, when creating multiple chunks, imports of dependencies of entry chunks will be added as empty imports to the entry chunks themselves. Example:

js
// input
// main.js
import value from './other-entry.js';
console.log(value);

// other-entry.js
import externalValue from 'external';
export default 2 * externalValue;

// output
// main.js
import 'external'; // this import has been hoisted from other-entry.js
import value from './other-entry.js';
console.log(value);

// other-entry.js
import externalValue from 'external';
var value = 2 * externalValue;
export default value;

This does not affect code execution order or behaviour, but it will speed up how your code is loaded and parsed. Without this optimization, a JavaScript engine needs to perform the following steps to run main.js:

  1. Load and parse main.js. At the end, an import to other-entry.js will be discovered.
  2. Load and parse other-entry.js. At the end, an import to external will be discovered.
  3. Load and parse external.
  4. Execute main.js.

With this optimization, a JavaScript engine will discover all transitive dependencies after parsing an entry module, avoiding the waterfall:

  1. Load and parse main.js. At the end, imports to other-entry.js and external will be discovered.
  2. Load and parse other-entry.js and external. The import of external from other-entry.js is already loaded and parsed.
  3. Execute main.js.

There may be situations where this optimization is not desired, in which case you can turn it off via the output.hoistTransitiveImports option. This optimization is also never applied when using the output.preserveModules option.

How do I add polyfills to a Rollup bundle?

Even though Rollup will usually try to maintain exact module execution order when bundling, there are two situations when this is not always the case: code-splitting and external dependencies. The problem is most obvious with external dependencies, see the following example:

js
// main.js
import './polyfill.js';
import 'external';
console.log('main');

// polyfill.js
console.log('polyfill');

Here the execution order is polyfill.jsexternalmain.js. Now when you bundle the code, you will get

js
import 'external';
console.log('polyfill');
console.log('main');

with the execution order externalpolyfill.jsmain.js. This is not a problem caused by Rollup putting the import at the top of the bundle—imports are always executed first, no matter where they are located in the file. This problem can be solved by creating more chunks: If polyfill.js ends up in a different chunk than main.js, correct execution order will be preserved. However, there is not yet an automatic way to do this in Rollup. For code-splitting, the situation is similar as Rollup is trying to create as few chunks as possible while making sure no code is executed that is not needed.

For most code this is not a problem, because Rollup can guarantee:

If module A imports module B and there are no circular imports, then B will always be executed before A.

This is however a problem for polyfills, as those usually need to be executed first but it is usually not desired to place an import of the polyfill in every single module. Luckily, this is not needed:

  1. If there are no external dependencies that depend on the polyfill, it is enough to add an import of the polyfill as first statement to each static entry point.
  2. Otherwise, additionally making the polyfill a separate entry or manual chunk will always make sure it is executed first.

Is Rollup meant for building libraries or applications?

Rollup is already used by many major JavaScript libraries, and can also be used to build the vast majority of applications. However, if you want to use code-splitting or dynamic imports with older browsers, you will need an additional runtime to handle loading missing chunks. We recommend using the SystemJS Production Build as it integrates nicely with Rollup's system format output and is capable of properly handling all the ES module live bindings and re-export edge cases. Alternatively, an AMD loader can be used as well.

How do I run Rollup itself in a browser

While the regular Rollup build relies on some NodeJS features, there is also a browser build available that only uses browser APIs. You can install it via

shell
npm install @rollup/browser

and in your script, import it via

js
import { rollup } from '@rollup/browser';

Alternatively, you can import from a CDN, e.g. for the ESM build

js
import * as rollup from 'https://unpkg.com/@rollup/browser/dist/es/rollup.browser.js';

and for the UMD build

html
<script src="https://unpkg.com/@rollup/browser/dist/rollup.browser.js"></script>

which will create a global variable window.rollup. As the browser build cannot access the file system, you need to provide plugins that resolve and load all modules you want to bundle. Here is a contrived example that does this:

js
const modules = {
	'main.js': "import foo from 'foo.js'; console.log(foo);",
	'foo.js': 'export default 42;'
};

rollup
	.rollup({
		input: 'main.js',
		plugins: [
			{
				name: 'loader',
				resolveId(source) {
					if (modules.hasOwnProperty(source)) {
						return source;
					}
				},
				load(id) {
					if (modules.hasOwnProperty(id)) {
						return modules[id];
					}
				}
			}
		]
	})
	.then(bundle => bundle.generate({ format: 'es' }))
	.then(({ output }) => console.log(output[0].code));

This example only supports two imports, "main.js" and "foo.js", and no relative imports. Here is another example that uses absolute URLs as entry points and supports relative imports. In that case, we are just re-bundling Rollup itself, but it could be used on any other URL that exposes an ES module:

js
rollup
	.rollup({
		input: 'https://unpkg.com/rollup/dist/es/rollup.js',
		plugins: [
			{
				name: 'url-resolver',
				resolveId(source, importer) {
					if (source[0] !== '.') {
						try {
							new URL(source);
							// If it is a valid URL, return it
							return source;
						} catch {
							// Otherwise make it external
							return { id: source, external: true };
						}
					}
					return new URL(source, importer).href;
				},
				async load(id) {
					const response = await fetch(id);
					return response.text();
				}
			}
		]
	})
	.then(bundle => bundle.generate({ format: 'es' }))
	.then(({ output }) => console.log(output));

Who made the Rollup logo? It's lovely.

Julian Lloyd!

Released under the MIT License.