rollup.js

Introduction

Overview

Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application. It uses the new standardized format for code modules included in the ES6 revision of JavaScript, instead of previous idiosyncratic solutions such as CommonJS and AMD. ES modules let you freely and seamlessly combine the most useful individual functions from your favorite libraries. This will eventually be possible natively everywhere, but Rollup lets you do it today.

Installation

npm install --global rollup

This will make Rollup available as a global command line tool. You can also install it locally, see Installing Rollup locally.

Quick start

Rollup can be used either through a command line interface with an optional configuration file, or else through its JavaScript API. Run rollup --help to see the available options and parameters.

See rollup-starter-lib and rollup-starter-app to see example library and application projects using Rollup

These commands assume the entry point to your application is named main.js, and that you'd like all imports compiled into a single file named bundle.js.

For browsers:

# compile to a <script> containing a self-executing function ('iife')
rollup main.js --file bundle.js --format iife

For Node.js:

# compile to a CommonJS module ('cjs')
rollup main.js --file bundle.js --format cjs

For both browsers and Node.js:

# UMD format requires a bundle name
rollup main.js --file bundle.js --format umd --name "myBundle"

The Why

Developing software is usually easier if you break your project into smaller separate pieces, since that often removes unexpected interactions and dramatically reduces the complexity of the problems you'll need to solve, and simply writing smaller projects in the first place isn't necessarily the answer. Unfortunately, JavaScript has not historically included this capability as a core feature in the language.

This finally changed with the ES6 revision of JavaScript, which includes a syntax for importing and exporting functions and data so they can be shared between separate scripts. The specification is now fixed, but it is only implemented in modern browsers and not finalised in Node.js. Rollup allows you to write your code using the new module system, and will then compile it back down to existing supported formats such as CommonJS modules, AMD modules, and IIFE-style scripts. This means that you get to write future-proof code, and you also get the tremendous benefits of…

Tree-Shaking

In addition to enabling the use of ES modules, Rollup also statically analyzes the code you are importing, and will exclude anything that isn't actually used. This allows you to build on top of existing tools and modules without adding extra dependencies or bloating the size of your project.

For example, with CommonJS, the entire tool or library must be imported.

// import the entire utils object with CommonJS
const utils = require( './utils' );
const query = 'Rollup';
// use the ajax method of the utils object
utils.ajax(`https://api.example.com?search=${query}`).then(handleResponse);

With ES modules, instead of importing the whole utils object, we can just import the one ajax function we need:

// import the ajax function with an ES6 import statement
import { ajax } from './utils';
const query = 'Rollup';
// call the ajax function
ajax(`https://api.example.com?search=${query}`).then(handleResponse);

Because Rollup includes the bare minimum, it results in lighter, faster, and less complicated libraries and applications. Since this approach can utilise explicit import and export statements, it is more effective than simply running an automated minifier to detect unused variables in the compiled output code.

Compatibility

Importing CommonJS

Rollup can import existing CommonJS modules through a plugin.

Publishing ES Modules

To make sure your ES modules are immediately usable by tools that work with CommonJS such as Node.js and webpack, you can use Rollup to compile to UMD or CommonJS format, and then point to that compiled version with the main property in your package.json file. If your package.json file also has a module field, ES-module-aware tools like Rollup and webpack 2+ will import the ES module version directly.

Command Line Interface

Rollup should typically be used from the command line. You can provide an optional Rollup configuration file to simplify command line usage and enable advanced Rollup functionality.

Configuration Files

Rollup configuration files are optional, but they are powerful and convenient and thus recommended. A config file is an ES module that exports a default object with the desired options:

export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  }
};

Typically, it is called rollup.config.js and sits in the root directory of your project. Behind the scenes, Rollup will transpile and bundle this file and its relative dependencies to CommonJS before requiring it, which has the advantage that you can share code with an ES module code base while having full interoperability with the Node ecosystem.

If you want to write your config as a CommonJS module using require and module.exports, you should change the file extension to .cjs, which will prevent Rollup from trying to transpile the file. Furthermore if you are on Node 13+, changing the file extension to .mjs will also prevent Rollup from transpiling it but import the file as an ES module instead. See using untranspiled config files for more details and why you might want to do this.

Config files support the options listed below. Consult the big list of options for details on each option:

// rollup.config.js

export default { // can be an array (for multiple inputs)
  // core input options
  external,
  input, // required
  plugins,

  // advanced input options
  cache,
  inlineDynamicImports,
  manualChunks,
  onwarn,
  preserveEntrySignatures,
  preserveModules,
  strictDeprecations,

  // danger zone
  acorn,
  acornInjectPlugins,
  context,
  moduleContext,
  preserveSymlinks,
  shimMissingExports,
  treeshake,

  // experimental
  experimentalCacheExpiry,
  perf,

  output: { // required (can be an array, for multiple outputs)
    // core output options
    dir,
    file,
    format, // required
    globals,
    name,
    plugins,

    // advanced output options
    assetFileNames,
    banner,
    chunkFileNames,
    compact,
    entryFileNames,
    extend,
    footer,
    hoistTransitiveImports,
    interop,
    intro,
    minifyInternalExports,
    outro,
    paths,
    sourcemap,
    sourcemapExcludeSources,
    sourcemapFile,
    sourcemapPathTransform,

    // danger zone
    amd,
    esModule,
    exports,
    externalLiveBindings,
    freeze,
    indent,
    namespaceToStringTag,
    noConflict,
    preferConst,
    strict
  },

  watch: {
  	buildDelay,
    chokidar,
    clearScreen,
    skipWrite,
    exclude,
    include
  }
};

You can export an array from your config file to build bundles from several different unrelated inputs at once, even in watch mode. To build different bundles with the same input, you supply an array of output options for each input:

// rollup.config.js (building more than one bundle)

export default [{
  input: 'main-a.js',
  output: {
    file: 'dist/bundle-a.js',
    format: 'cjs'
  }
}, {
  input: 'main-b.js',
  output: [
    {
      file: 'dist/bundle-b1.js',
      format: 'cjs'
    },
    {
      file: 'dist/bundle-b2.js',
      format: 'es'
    }
  ]
}];

If you want to create your config asynchronously, Rollup can also handle a Promise which resolves to an object or an array.

// rollup.config.js
import fetch from 'node-fetch';
export default fetch('/some-remote-service-or-file-which-returns-actual-config');

Similarly, you can do this as well:

// rollup.config.js (Promise resolving an array)
export default Promise.all([
  fetch('get-config-1'),
  fetch('get-config-2')
])

To use Rollup with a configuration file, pass the --config or -c flags:

# pass a custom config file location to Rollup
rollup --config my.config.js

# if you do not pass a file name, Rollup will try to load
# configuration files in the following order:
# rollup.config.mjs -> rollup.config.cjs -> rollup.config.js
rollup --config

You can also export a function that returns any of the above configuration formats. This function will be passed the current command line arguments so that you can dynamically adapt your configuration to respect e.g. --silent. You can even define your own command line options if you prefix them with config:

// rollup.config.js
import defaultConfig from './rollup.default.config.js';
import debugConfig from './rollup.debug.config.js';

export default commandLineArgs => {
  if (commandLineArgs.configDebug === true) {
    return debugConfig;
  }
  return defaultConfig;
}

If you now run rollup --config --configDebug, the debug configuration will be used.

By default, command line arguments will always override the respective values exported from a config file. If you want to change this behaviour, you can make Rollup ignore command line arguments by deleting them from the commandLineArgs object:

// rollup.config.js
export default commandLineArgs => {
  const inputBase = commandLineArgs.input || 'main.js';

  // this will make Rollup ignore the CLI argument
  delete commandLineArgs.input;
  return {
    input: 'src/entries/' + inputBase,
    output: {...}
  }
}

Differences to the JavaScript API

While config files provide an easy way to configure Rollup, they also limit how Rollup can be invoked and where configuration is taken from. Especially if you are rebundling Rollup in another build tool or want to integrate it into an advanced build process, it may be better to directly invoke Rollup programmatically from your scripts.

If you want to switch from config files to using the JavaScript API at some point, there are some important differences to be aware of:

  • When using the JavaScript API, the configuration passed to rollup.rollup must be an object and cannot be wrapped in a Promise or a function.
  • You can no longer use an array of configurations. Instead, you should run rollup.rollup once for each set of inputOptions.
  • The output option will be ignored. Instead, you should run bundle.generate(outputOptions) or bundle.write(outputOptions) once for each set of outputOptions.

Loading a configuration from a Node package

For interoperability, Rollup also supports loading configuration files from packages installed into node_modules:

# this will first try to load the package "rollup-config-my-special-config";
# if that fails, it will then try to load "my-special-config"
rollup --config node:my-special-config

Using untranspiled config files

By default, Rollup will expect config files to be ES modules and bundle and transpile them and their relative imports to CommonJS before requiring them. This is a fast process and has the advantage that it is easy to share code between your configuration and an ES module code base. If you want to write your configuration as CommonJS instead, you can skip this process by using the .cjs extension:

// rollup.config.cjs
module.exports = {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  }
};

It may be pertinent if you want to use the config file not only from the command line, but also from your custom scripts programmatically.

On the other hand if you are using at least Node 13 and have "type": "module" in your package.json file, Rollup's transpilation will prevent your configuration file from importing packages that are themselves ES modules. In that case, changing your file extension to .mjs will instruct Rollup to import your configuration directly as an ES module. However note that this is specific to Node 13+; on older Node versions, .mjs is treated just like .js.

There are some potential gotchas when using .mjs on Node 13+:

  • You will only get a default export from CommonJS plugins

  • You may not be able to import JSON files such as your package.json file. There are two ways to go around this:

    • run Rollup CLI via

          node --experimental-json-modules ./node_modules/.bin/rollup --config
          
    • create a CommonJS wrapper that requires the JSON file:

          // load-package.cjs
          module.exports = require('./package.json');
          
          // rollup.config.mjs
          import pkg from './load-package.cjs';
          ...
          

Command line flags

Many options have command line equivalents. In those cases, any arguments passed here will override the config file, if you're using one. This is a list of all supported options:

-c, --config <filename>     Use this config file (if argument is used but value
                              is unspecified, defaults to rollup.config.js)
-d, --dir <dirname>         Directory for chunks (if absent, prints to stdout)
-e, --external <ids>        Comma-separate list of module IDs to exclude
-f, --format <format>       Type of output (amd, cjs, es, iife, umd, system)
-g, --globals <pairs>       Comma-separate list of `moduleID:Global` pairs
-h, --help                  Show this help message
-i, --input <filename>      Input (alternative to <entry file>)
-m, --sourcemap             Generate sourcemap (`-m inline` for inline map)
-n, --name <name>           Name for UMD export
-o, --file <output>         Single output file (if absent, prints to stdout)
-p, --plugin <plugin>       Use the plugin specified (may be repeated)
-v, --version               Show version number
-w, --watch                 Watch files in bundle and rebuild on changes
--amd.id <id>               ID for AMD module (default is anonymous)
--amd.define <name>         Function to use in place of `define`
--assetFileNames <pattern>  Name pattern for emitted assets
--banner <text>             Code to insert at top of bundle (outside wrapper)
--chunkFileNames <pattern>  Name pattern for emitted secondary chunks
--compact                   Minify wrapper code
--context <variable>        Specify top-level `this` value
--entryFileNames <pattern>  Name pattern for emitted entry chunks
--environment <values>      Settings passed to config file (see example)
--no-esModule               Do not add __esModule property
--exports <mode>            Specify export mode (auto, default, named, none)
--extend                    Extend global variable defined by --name
--no-externalLiveBindings   Do not generate code to support live bindings
--footer <text>             Code to insert at end of bundle (outside wrapper)
--no-freeze                 Do not freeze namespace objects
--no-hoistTransitiveImports Do not hoist transitive imports into entry chunks
--no-indent                 Don't indent result
--no-interop                Do not include interop block
--inlineDynamicImports      Create single bundle when using dynamic imports
--intro <text>              Code to insert at top of bundle (inside wrapper)
--minifyInternalExports     Force or disable minification of internal exports
--namespaceToStringTag      Create proper `.toString` methods for namespaces
--noConflict                Generate a noConflict method for UMD globals
--outro <text>              Code to insert at end of bundle (inside wrapper)
--preferConst               Use `const` instead of `var` for exports
--no-preserveEntrySignatures Avoid facade chunks for entry points
--preserveModules           Preserve module structure
--preserveSymlinks          Do not follow symlinks when resolving files
--shimMissingExports        Create shim variables for missing exports
--silent                    Don't print warnings
--sourcemapExcludeSources   Do not include source code in source maps
--sourcemapFile <file>      Specify bundle position for source maps
--no-stdin                  do not read "-" from stdin
--no-strict                 Don't emit `"use strict";` in the generated modules
--strictDeprecations        Throw errors for deprecated features
--no-treeshake              Disable tree-shaking optimisations
--no-treeshake.annotations  Ignore pure call annotations
--no-treeshake.no-moduleSideEffects Assume modules have no side-effects
--no-treeshake.no-propertyReadSideEffects Ignore property access side-effects
--no-treeshake.no-tryCatchDeoptimization Do not turn off try-catch-tree-shaking
--no-treeshake.no-unknownGlobalSideEffects Assume unknown globals do not throw

The flags listed below are only available via the command line interface. All other flags correspond to and override their config file equivalents, see the big list of options for details.

-h/--help

Print the help document.

-p <plugin>, --plugin <plugin>

Use the specified plugin. There are several ways to specify plugins here:

  • Via a relative path:

      rollup -i input.js -f es -p ./my-plugin.js
      

    The file should export a plugin object or a function returning such an object.

  • Via the name of a plugin that is installed in a local or global node_modules folder:

      rollup -i input.js -f es -p @rollup/plugin-node-resolve
      

    If the plugin name does not start with rollup-plugin- or @rollup/plugin-, Rollup will automatically try adding these prefixes:

      rollup -i input.js -f es -p node-resolve
      
  • Via an inline implementation:

      rollup -i input.js -f es -p '{transform: (c, i) => `/* ${JSON.stringify(i)} */\n${c}`}'
      

If you want to load more than one plugin, you can repeat the option or supply a comma-separated list of names:

rollup -i input.js -f es -p node-resolve -p commonjs,json

By default, plugins that export functions will be called with no argument to create the plugin. You can however pass a custom argument as well:

rollup -i input.js -f es -p 'terser={output: {beautify: true, indent_level: 2}}'

-v/--version

Print the installed version number.

-w/--watch

Rebuild the bundle when its source files change on disk.

Note: While in watch mode, the ROLLUP_WATCH environment variable will be set to "true" by Rollup's command line interface and can be checked by plugins and other processes.

--silent

Don't print warnings to the console. If your configuration file contains an onwarn handler, this handler will still be called. To manually prevent that, you can access the command line options in your configuration file as described at the end of Configuration Files.

--environment <values>

Pass additional settings to the config file via process.ENV.

rollup -c --environment INCLUDE_DEPS,BUILD:production

will set process.env.INCLUDE_DEPS === 'true' and process.env.BUILD === 'production'. You can use this option several times. In that case, subsequently set variables will overwrite previous definitions. This enables you for instance to overwrite environment variables in package.json scripts:

// in package.json
{
  "scripts": {
    "build": "rollup -c --environment INCLUDE_DEPS,BUILD:production"
  }
}

If you call this script via:

npm run build -- --environment BUILD:development

then the config file will receive process.env.INCLUDE_DEPS === 'true' and process.env.BUILD === 'development'.

--no-stdin

Do not read files from stdin. Setting this flag will prevent piping content to Rollup and make sure Rollup interprets - as a regular file name instead of interpreting this as the name of stdin. See also Reading a file from stdin.

Reading a file from stdin

When using the command line interface, Rollup can also read content from stdin:

echo "export const foo = 42;" | rollup --format cjs --file out.js

When this file contains imports, Rollup will try to resolve them relative to the current working directory. When a config file is used, Rollup will only use stdin as an entry point if the file name of the entry point is -. To read a non-entry-point file from stdin, just call it -, which is the file name that is used internally to reference stdin. I.e.

import foo from "-";

in any file will prompt Rollup to try to read the imported file from stdin and assign the default export to foo. You can pass the --no-stdin CLI flag to Rollup to treat - as a regular file name instead.

The JavaScript API will always treat - as a regular file name.

JavaScript API

Rollup provides a JavaScript API which is usable from Node.js. You will rarely need to use this, and should probably be using the command line API unless you are extending Rollup itself or using it for something esoteric, such as generating bundles programmatically.

rollup.rollup

The rollup.rollup function receives an input options object as parameter and returns a Promise that resolves to a bundle object with various properties and methods as shown below. During this step, Rollup will build the module graph and perform tree-shaking, but will not generate any output.

On a bundle object, you can call bundle.generate multiple times with different output options objects to generate different bundles in-memory. If you directly want to write them to disk, use bundle.write instead.

const rollup = require('rollup');

// see below for details on the options
const inputOptions = {...};
const outputOptions = {...};

async function build() {
  // create a bundle
  const bundle = await rollup.rollup(inputOptions);

  console.log(bundle.watchFiles); // an array of file names this bundle depends on

  // generate output specific code in-memory
  // you can call this function multiple times on the same bundle object
  const { output } = await bundle.generate(outputOptions);

  for (const chunkOrAsset of output) {
    if (chunkOrAsset.type === 'asset') {
      // For assets, this contains
      // {
      //   fileName: string,              // the asset file name
      //   source: string | Uint8Array    // the asset source
      //   type: 'asset'                  // signifies that this is an asset
      // }
      console.log('Asset', chunkOrAsset);
    } else {
      // For chunks, this contains
      // {
      //   code: string,                  // the generated JS code
      //   dynamicImports: string[],      // external modules imported dynamically by the chunk
      //   exports: string[],             // exported variable names
      //   facadeModuleId: string | null, // the id of a module that this chunk corresponds to
      //   fileName: string,              // the chunk file name
      //   imports: string[],             // external modules imported statically by the chunk
      //   isDynamicEntry: boolean,       // is this chunk a dynamic entry point
      //   isEntry: boolean,              // is this chunk a static entry point
      //   map: string | null,            // sourcemaps if present
      //   modules: {                     // information about the modules in this chunk
      //     [id: string]: {
      //       renderedExports: string[]; // exported variable names that were included
      //       removedExports: string[];  // exported variable names that were removed
      //       renderedLength: number;    // the length of the remaining code in this module
      //       originalLength: number;    // the original length of the code in this module
      //     };
      //   },
      //   name: string                   // the name of this chunk as used in naming patterns
      //   type: 'chunk',                 // signifies that this is a chunk
      // }
      console.log('Chunk', chunkOrAsset.modules);
    }
  }

  // or write the bundle to disk
  await bundle.write(outputOptions);
}

build();

inputOptions object

The inputOptions object can contain the following properties (see the big list of options for full details on these):

const inputOptions = {
  // core input options
  external,
  input, // required
  plugins,

  // advanced input options
  cache,
  inlineDynamicImports,
  manualChunks,
  onwarn,
  preserveEntrySignatures,
  preserveModules,
  strictDeprecations,

  // danger zone
  acorn,
  acornInjectPlugins,
  context,
  moduleContext,
  preserveSymlinks,
  shimMissingExports,
  treeshake,

  // experimental
  experimentalCacheExpiry,
  perf
};

outputOptions object

The outputOptions object can contain the following properties (see the big list of options for full details on these):

const outputOptions = {
  // core output options
  dir,
  file,
  format, // required
  globals,
  name,
  plugins,

  // advanced output options
  assetFileNames,
  banner,
  chunkFileNames,
  compact,
  entryFileNames,
  extend,
  externalLiveBindings,
  footer,
  hoistTransitiveImports,
  interop,
  intro,
  minifyInternalExports,
  outro,
  paths,
  sourcemap,
  sourcemapExcludeSources,
  sourcemapFile,
  sourcemapPathTransform,

  // danger zone
  amd,
  esModule,
  exports,
  freeze,
  indent,
  namespaceToStringTag,
  noConflict,
  preferConst,
  strict
};

rollup.watch

Rollup also provides a rollup.watch function that rebuilds your bundle when it detects that the individual modules have changed on disk. It is used internally when you run Rollup from the command line with the --watch flag.

const rollup = require('rollup');

const watchOptions = {...};
const watcher = rollup.watch(watchOptions);

watcher.on('event', event => {
  // event.code can be one of:
  //   START        — the watcher is (re)starting
  //   BUNDLE_START — building an individual bundle
  //   BUNDLE_END   — finished building a bundle
  //   END          — finished building all bundles
  //   ERROR        — encountered an error while bundling
});

// stop watching
watcher.close();

watchOptions

The watchOptions argument is a config (or an array of configs) that you would export from a config file.

const watchOptions = {
  ...inputOptions,
  output: [outputOptions],
  watch: {
    buildDelay,
    chokidar,
    clearScreen,
    skipWrite,
    exclude,
    include
  }
};

See above for details on inputOptions and outputOptions, or consult the big list of options for info on chokidar, include and exclude.

Programmatically loading a config file

In order to aid in generating such a config, rollup exposes the helper it uses to load config files in its command line interface via a separate entry-point. This helper receives a resolved fileName and optionally an object containing command line parameters:

const loadConfigFile = require('rollup/dist/loadConfigFile');
const path = require('path');
const rollup = require('rollup');

// load the config file next to the current script;
// the provided config object has the same effect as passing "--format es"
// on the command line and will override the format of all outputs
loadConfigFile(path.resolve(__dirname, 'rollup.config.js'), { format: 'es' })
  .then(async ({options, warnings}) => {
    // "warnings" wraps the default `onwarn` handler passed by the CLI.
    // This prints all warnings up to this point:
    console.log(`We currently have ${warnings.count} warnings`);

    // This prints all deferred warnings
    warnings.flush();
    
    // options is an "inputOptions" object with an additional "output"
    // property that contains an array of "outputOptions".
    // The following will generate all outputs and write them to disk the same
    // way the CLI does it:
    const bundle = await rollup.rollup(options);
    await Promise.all(options.output.map(bundle.write));
 
    // You can also pass this directly to "rollup.watch"
    rollup.watch(options);
  })

ES Module Syntax

The following is intended as a lightweight reference for the module behaviors defined in the ES2015 specification, since a proper understanding of the import and export statements are essential to the successful use of Rollup.

Importing

Imported values cannot be reassigned, though imported objects and arrays can be mutated (and the exporting module, and any other importers, will be affected by the mutation). In that way, they behave similarly to const declarations.

Named Imports

Import a specific item from a source module, with its original name.

import { something } from './module.js';

Import a specific item from a source module, with a custom name assigned upon import.

import { something as somethingElse } from './module.js';

Namespace Imports

Import everything from the source module as an object which exposes all the source module's named exports as properties and methods.

import * as module from './module.js'

The something example from above would then be attached to the imported object as a property, e.g. module.something. If present, the default export can be accessed via module.default.

Default Import

Import the default export of the source module.

import something from './module.js';

Empty Import

Load the module code, but don't make any new objects available.

import './module.js';

This is useful for polyfills, or when the primary purpose of the imported code is to muck about with prototypes.

Dynamic Import

Import modules using the dynamic import API.

import('./modules.js').then(({ default: DefaultExport, NamedExport })=> {
  // do something with modules.
})

This is useful for code-splitting applications and using modules on-the-fly.

Exporting

Named exports

Export a value that has been previously declared:

const something = true;
export { something };

Rename on export:

export { something as somethingElse };

Export a value immediately upon declaration:

// this works with `var`, `let`, `const`, `class`, and `function`
export const something = true;

Default Export

Export a single value as the source module's default export:

export default something;

This practice is only recommended if your source module only has one export.

It is bad practice to mix default and named exports in the same module, though it is allowed by the specification.

How bindings work

ES modules export live bindings, not values, so values can be changed after they are initially imported as per this demo:

// incrementer.js
export let count = 0;

export function increment() {
  count += 1;
}
// main.js
import { count, increment } from './incrementer.js';

console.log(count); // 0
increment();
console.log(count); // 1

count += 1; // Error — only incrementer.js can change this

Tutorial

Creating Your First Bundle

Before we begin, you'll need to have Node.js installed so that you can use NPM. You'll also need to know how to access the command line on your machine.

The easiest way to use Rollup is via the Command Line Interface (or CLI). For now, we'll install it globally (later on we'll learn how to install it locally to your project so that your build process is portable, but don't worry about that yet). Type this into the command line:

npm install rollup --global
# or `npm i rollup -g` for short

You can now run the rollup command. Try it!

rollup

Because no arguments were passed, Rollup prints usage instructions. This is the same as running rollup --help, or rollup -h.

Let's create a simple project:

mkdir -p my-rollup-project/src
cd my-rollup-project

First, we need an entry point. Paste this into a new file called src/main.js:

// src/main.js
import foo from './foo.js';
export default function () {
  console.log(foo);
}

Then, let's create the foo.js module that our entry point imports:

// src/foo.js
export default 'hello world!';

Now we're ready to create a bundle:

rollup src/main.js -f cjs

The -f option (short for --format) specifies what kind of bundle we're creating — in this case, CommonJS (which will run in Node.js). Because we didn't specify an output file, it will be printed straight to stdout:

'use strict';

const foo = 'hello world!';

const main = function () {
  console.log(foo);
};

module.exports = main;

You can save the bundle as a file like so:

rollup src/main.js -o bundle.js -f cjs

(You could also do rollup src/main.js -f cjs > bundle.js, but as we'll see later, this is less flexible if you're generating sourcemaps.)

Try running the code:

node
> var myBundle = require('./bundle.js');
> myBundle();
'hello world!'

Congratulations! You've created your first bundle with Rollup.

Using Config Files

So far, so good, but as we start adding more options it becomes a bit of a nuisance to type out the command.

To save repeating ourselves, we can create a config file containing all the options we need. A config file is written in JavaScript and is more flexible than the raw CLI.

Create a file in the project root called rollup.config.js, and add the following code:

// rollup.config.js
export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  }
};

(Note that you can use CJS modules and therefore module.exports = {/* config */})

To use the config file, we use the --config or -c flag:

rm bundle.js # so we can check the command works!
rollup -c

You can override any of the options in the config file with the equivalent command line options:

rollup -c -o bundle-2.js # `-o` is equivalent to `--file` (formerly "output")

Note: Rollup itself processes the config file, which is why we're able to use export default syntax – the code isn't being transpiled with Babel or anything similar, so you can only use ES2015 features that are supported in the version of Node.js that you're running.

You can, if you like, specify a different config file from the default rollup.config.js:

rollup --config rollup.config.dev.js
rollup --config rollup.config.prod.js

Installing Rollup locally

When working within teams or distributed environments it can be wise to add Rollup as a local dependency. Installing Rollup locally prevents the requirement that multiple contributors install Rollup separately as an extra step, and ensures that all contributors are using the same version of Rollup.

To install Rollup locally with NPM:

npm install rollup --save-dev

Or with Yarn:

yarn -D add rollup

After installing, Rollup can be run within the root directory of your project:

npx rollup --config

Or with Yarn:

yarn rollup --config

Once installed, it's common practice to add a single build script to package.json, providing a convenient command for all contributors. e.g.

{
  "scripts": {
    "build": "rollup --config"
  }
}

Note: Once installed locally, both NPM and Yarn will resolve the dependency's bin file and execute Rollup when called from a package script.

Using plugins

So far, we've created a simple bundle from an entry point and a module imported via a relative path. As you build more complex bundles, you'll often need more flexibility – importing modules installed with NPM, compiling code with Babel, working with JSON files and so on.

For that, we use plugins, which change the behaviour of Rollup at key points in the bundling process. A list of awesome plugins is maintained on the Rollup Awesome List.

For this tutorial, we'll use @rollup/plugin-json, which allows Rollup to import data from a JSON file.

Create a file in the project root called package.json, and add the following content:

{
  "name": "rollup-tutorial",
  "version": "1.0.0",
  "scripts": {
    "build": "rollup -c"
  }
}

Install @rollup/plugin-json as a development dependency:

npm install --save-dev @rollup/plugin-json

(We're using --save-dev rather than --save because our code doesn't actually depend on the plugin when it runs – only when we're building the bundle.)

Update your src/main.js file so that it imports from your package.json instead of src/foo.js:

// src/main.js
import { version } from '../package.json';

export default function () {
  console.log('version ' + version);
}

Edit your rollup.config.js file to include the JSON plugin:

// rollup.config.js
import json from '@rollup/plugin-json';

export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  plugins: [ json() ]
};

Run Rollup with npm run build. The result should look like this:

'use strict';

var version = "1.0.0";

function main () {
  console.log('version ' + version);
}

module.exports = main;

Note: Only the data we actually need gets imported – name and devDependencies and other parts of package.json are ignored. That's tree-shaking in action.

Using output plugins

Some plugins can also be applied specifically to some outputs. See plugin hooks for the technical details of what output-specific plugins can do. In a nut-shell, those plugins can only modify code after the main analysis of Rollup has completed. Rollup will warn if an incompatible plugin is used as an output-specific plugin. One possible use-case is minification of bundles to be consumed in a browser.

Let us extend the previous example to provide a minified build together with the non-minified one. To that end, we install rollup-plugin-terser:

npm install --save-dev rollup-plugin-terser

Edit your rollup.config.js file to add a second minified output. As format, we choose iife. This format wraps the code so that it can be consumed via a script tag in the browser while avoiding unwanted interactions with other code. As we have an export, we need to provide the name of a global variable that will be created by our bundle so that other code can access our export via this variable.

// rollup.config.js
import json from '@rollup/plugin-json';
import {terser} from 'rollup-plugin-terser';

export default {
  input: 'src/main.js',
  output: [
    {
      file: 'bundle.js',
      format: 'cjs'
    },
    {
      file: 'bundle.min.js',
      format: 'iife',
      name: 'version',
      plugins: [terser()]
    }
  ],
  plugins: [ json() ]
};

Besides bundle.js, Rollup will now create a second file bundle.min.js:

var version=function(){"use strict";var n="1.0.0";return function(){console.log("version "+n)}}();

Code Splitting

To use the code splitting feature, we go back to the original example and modify src/main.js to load src/foo.js dynamically instead of statically:

// src/main.js
export default function () {
  import('./foo.js').then(({ default: foo }) => console.log(foo));
}

Rollup will use the dynamic import to create a separate chunk that is only loaded on demand. In order for Rollup to know where to place the second chunk, instead of passing the --file option we set a folder to output to with the --dir option:

rollup src/main.js -f cjs -d dist

This will create a folder dist containing two files, main.js and chunk-[hash].js, where [hash] is a content based hash string. You can supply your own naming patterns by specifying the output.chunkFileNames and output.entryFileNames options.

You can still run your code as before with the same output, albeit a little slower as loading and parsing of ./foo.js will only commence once we call the exported function for the first time.

node -e "require('./dist/main.js')()"

If we do not use the --dir option, Rollup will again print the chunks to stdout, adding comments to highlight the chunk boundaries:

//→ main.js:
'use strict';

function main () {
  Promise.resolve(require('./chunk-b8774ea3.js')).then(({ default: foo }) => console.log(foo));
}

module.exports = main;

//→ chunk-b8774ea3.js:
'use strict';

var foo = 'hello world!';

exports.default = foo;

This is useful if you want to load and parse expensive features only once they are used.

A different use for code-splitting is the ability to specify several entry points that share some dependencies. Again we extend our example to add a second entry point src/main2.js that statically imports src/foo.js just like we did in the original example:

// src/main2.js
import foo from './foo.js';
export default function () {
  console.log(foo);
}

If we supply both entry points to rollup, three chunks are created:

rollup src/main.js src/main2.js -f cjs

will output

//→ main.js:
'use strict';

function main () {
  Promise.resolve(require('./chunk-b8774ea3.js')).then(({ default: foo }) => console.log(foo));
}

module.exports = main;

//→ main2.js:
'use strict';

var foo_js = require('./chunk-b8774ea3.js');

function main2 () {
  console.log(foo_js.default);
}

module.exports = main2;

//→ chunk-b8774ea3.js:
'use strict';

var foo = 'hello world!';

exports.default = foo;

Notice how both entry points import the same shared chunk. Rollup will never duplicate code and instead create additional chunks to only ever load the bare minimum necessary. Again, passing the --dir option will write the files to disk.

You can build the same code for the browser via native ES modules, an AMD loader or SystemJS.

For example, with -f es for native modules:

rollup src/main.js src/main2.js -f es -d dist
<!doctype html>
<script type="module">
  import main2 from './dist/main2.js';
  main2();
</script>

Or alternatively, for SystemJS with -f system:

rollup src/main.js src/main2.js -f system -d dist

Install SystemJS via

npm install --save-dev systemjs

And then load either or both entry points in an HTML page as needed:

<!doctype html>
<script src="node_modules/systemjs/dist/s.min.js"></script>
<script>
  System.import('./dist/main2.js')
  .then(({ default: main }) => main());
</script>

See rollup-starter-code-splitting for an example on how to set up a web app that uses native ES modules on browsers that support them with a fallback to SystemJS if necessary.

Plugin Development

Plugins Overview

A Rollup plugin is an object with one or more of the properties, build hooks, and output generation hooks described below, and which follows our conventions. A plugin should be distributed as a package which exports a function that can be called with plugin specific options and returns such an object.

Plugins allow you to customise Rollup's behaviour by, for example, transpiling code before bundling, or finding third-party modules in your node_modules folder. For an example on how to use them, see Using plugins.

A List of Plugins may be found at https://github.com/rollup/awesome. If you would like to make a suggestion for a plugin, please submit a Pull Request.

A Simple Example

The following plugin will intercept any imports of virtual-module without accessing the file system. This is for instance necessary if you want to use Rollup in a browser. It can even be used to replace entry points as shown in the example.

// rollup-plugin-my-example.js
export default function myExample () {
  return {
    name: 'my-example', // this name will show up in warnings and errors
    resolveId ( source ) {
      if (source === 'virtual-module') {
        return source; // this signals that rollup should not ask other plugins or check the file system to find this id
      }
      return null; // other ids should be handled as usually
    },
    load ( id ) {
      if (id === 'virtual-module') {
        return 'export default "This is virtual!"'; // the source code for "virtual-module"
      }
      return null; // other ids should be handled as usually
    }
  };
}

// rollup.config.js
import myExample from './rollup-plugin-my-example.js';
export default ({
  input: 'virtual-module', // resolved by our plugin
  plugins: [myExample()],
  output: [{
    file: 'bundle.js',
    format: 'es'
  }]
});

Conventions

  • Plugins should have a clear name with rollup-plugin- prefix.
  • Include rollup-plugin keyword in package.json.
  • Plugins should be tested. We recommend mocha or ava which support promises out of the box.
  • Use asynchronous methods when it is possible.
  • Document your plugin in English.
  • Make sure your plugin outputs correct source mappings if appropriate.
  • If your plugin uses 'virtual modules' (e.g. for helper functions), prefix the module ID with \0. This prevents other plugins from trying to process it.

Properties

name

Type: string

The name of the plugin, for use in error messages and warnings.

Build Hooks

To interact with the build process, your plugin object includes 'hooks'. Hooks are functions which are called at various stages of the build. Hooks can affect how a build is run, provide information about a build, or modify a build once complete. There are different kinds of hooks:

  • async: The hook may also return a promise resolving to the same type of value; otherwise, the hook is marked as sync.
  • first: If several plugins implement this hook, the hooks are run sequentially until a hook returns a value other than null or undefined.
  • sequential: If several plugins implement this hook, all of them will be run in the specified plugin order. If a hook is async, subsequent hooks of this kind will wait until the current hook is resolved.
  • parallel: If several plugins implement this hook, all of them will be run in the specified plugin order. If a hook is async, subsequent hooks of this kind will be run in parallel and not wait for the current hook.

Build hooks are run during the build phase, which is triggered by rollup.rollup(inputOptions). They are mainly concerned with locating, providing and transforming input files before they are processed by Rollup. The first hook of the build phase is options, the last one is always buildEnd. Additionally in watch mode, the watchChange hook can be triggered at any time to notify a new run will be triggered once the current run has generated its outputs.

See Output Generation Hooks for hooks that run during the output generation phase to modify the generated output.

buildEnd

Type: (error?: Error) => void
Kind: async, parallel
Previous Hook: transform, resolveId or resolveDynamicImport.
Next Hook: outputOptions in the output generation phase as this is the last hook of the build phase.

Called when rollup has finished bundling, but before generate or write is called; you can also return a Promise. If an error occurred during the build, it is passed on to this hook.

buildStart

Type: (options: InputOptions) => void
Kind: async, parallel
Previous Hook: options
Next Hook: resolveId to resolve each entry point in parallel.

Called on each rollup.rollup build. This is the recommended hook to use when you need access to the options passed to rollup.rollup() as it will take the transformations by all options hooks into account.

load

Type: (id: string) => string | null | { code: string, map?: string | SourceMap, ast? : ESTree.Program, moduleSideEffects?: boolean | null, syntheticNamedExports?: boolean | null }
Kind: async, first
Previous Hook: resolveId or resolveDynamicImport where the loaded id was resolved.
Next Hook: transform to transform the loaded file.

Defines a custom loader. Returning null defers to other load functions (and eventually the default behavior of loading from the file system). To prevent additional parsing overhead in case e.g. this hook already used this.parse to generate an AST for some reason, this hook can optionally return a { code, ast, map } object. The ast must be a standard ESTree AST with start and end properties for each node. If the transformation does not move code, you can preserve existing sourcemaps by setting map to null. Otherwise you might need to generate the source map. See the section on source code transformations.

If false is returned for moduleSideEffects and no other module imports anything from this module, then this module will not be included in the bundle without checking for actual side-effects inside the module. If true is returned, Rollup will use its default algorithm to include all statements in the module that have side-effects (such as modifying a global or exported variable). If null is returned or the flag is omitted, then moduleSideEffects will be determined by the first resolveId hook that resolved this module, the treeshake.moduleSideEffects option, or eventually default to true. The transform hook can override this.

If true is returned for syntheticNamedExports, this module will fallback the resolution of any missing named export to properties of the default export. The transform hook can override this. This option allows to have dynamic named exports that might not be declared in the module, such as in this example:

dep.js: ({syntheticNamedExports: true})

export default {
  foo: 42,
  bar: 'hello'
}

main.js: (entry point)

import { foo, bar } from './dep.js'
console.log(foo, bar);

You can use this.getModuleInfo to find out the previous values of moduleSideEffects and syntheticNamedExports inside this hook.

options

Type: (options: InputOptions) => InputOptions | null
Kind: sync, sequential
Previous Hook: This is the first hook of the build phase.
Next Hook: buildStart

Replaces or manipulates the options object passed to rollup.rollup. Returning null does not replace anything. If you just need to read the options, it is recommended to use the buildStart hook as that hook has access to the options after the transformations from all options hooks have been taken into account.

This is the only hook that does not have access to most plugin context utility functions as it is run before rollup is fully configured.

resolveDynamicImport

Type: (specifier: string | ESTree.Node, importer: string) => string | false | null | {id: string, external?: boolean}
Kind: async, first
Previous Hook: transform where the importing file was transformed.
Next Hook: load if the hook resolved with an id that has not yet been loaded, resolveId if the dynamic import contains a string and was not resolved by the hook, otherwise buildEnd.

Defines a custom resolver for dynamic imports. Returning false signals that the import should be kept as it is and not be passed to other resolvers thus making it external. Similar to the resolveId hook, you can also return an object to resolve the import to a different id while marking it as external at the same time.

In case a dynamic import is passed a string as argument, a string returned from this hook will be interpreted as an existing module id while returning null will defer to other resolvers and eventually to resolveId .

In case a dynamic import is not passed a string as argument, this hook gets access to the raw AST nodes to analyze and behaves slightly different in the following ways:

  • If all plugins return null, the import is treated as external without a warning.
  • If a string is returned, this string is not interpreted as a module id but is instead used as a replacement for the import argument. It is the responsibility of the plugin to make sure the generated code is valid.
  • To resolve such an import to an existing module, you can still return an object {id, external}.

Note that the return value of this hook will not be passed to resolveId afterwards; if you need access to the static resolution algorithm, you can use this.resolve(source, importer) on the plugin context.

resolveId

Type: (source: string, importer: string | undefined) => string | false | null | {id: string, external?: boolean, moduleSideEffects?: boolean | null, syntheticNamedExports?: boolean | null}
Kind: async, first
Previous Hook: buildStart if we are resolving an entry point, transform if we are resolving an import, or as fallback for resolveDynamicImport. Additionally this hook can be triggered during the build phase from plugin hooks by calling this.emitFile to emit an entry point or at any time by calling this.resolve to manually resolve an id.
Next Hook: load if the resolved id that has not yet been loaded, otherwise buildEnd.

Defines a custom resolver. A resolver can be useful for e.g. locating third-party dependencies. Returning null defers to other resolveId functions and eventually the default resolution behavior; returning false signals that source should be treated as an external module and not included in the bundle. If this happens for a relative import, the id will be renormalized the same way as when the external option is used.

If you return an object, then it is possible to resolve an import to a different id while excluding it from the bundle at the same time. This allows you to replace dependencies with external dependencies without the need for the user to mark them as "external" manually via the external option:

resolveId(source) {
  if (source === 'my-dependency') {
    return {id: 'my-dependency-develop', external: true};
  }
  return null;
}

Relative ids, i.e. starting with ./ or ../, will not be renormalized when returning an object. If you want this behaviour, return an absolute file system location as id instead.

If false is returned for moduleSideEffects in the first hook that resolves a module id and no other module imports anything from this module, then this module will not be included without checking for actual side-effects inside the module. If true is returned, Rollup will use its default algorithm to include all statements in the module that have side-effects (such as modifying a global or exported variable). If null is returned or the flag is omitted, then moduleSideEffects will be determined by the treeshake.moduleSideEffects option or default to true. The load and transform hooks can override this.

If true is returned for syntheticNamedExports, this module will fallback the resolution of any missing named export to properties of the default export. The load and transform hooks can override this. This option allows to have dynamic named exports that might not be declared in the module, such as in this example:

dep.js: ({syntheticNamedExports: true})

export default {
  foo: 42,
  bar: 'hello'
}

main.js: (entry point)

import { foo, bar } from './dep.js'
console.log(foo, bar);

transform

Type: (code: string, id: string) => string | null | { code: string, map?: string | SourceMap, ast? : ESTree.Program, moduleSideEffects?: boolean | null, syntheticNamedExports?: boolean | null }
Kind: async, sequential
Previous Hook: load where the currently handled file was loaded.
NextHook: resolveId and resolveDynamicImport to resolve all discovered static and dynamic imports in parallel if present, otherwise buildEnd.

Can be used to transform individual modules. To prevent additional parsing overhead in case e.g. this hook already used this.parse to generate an AST for some reason, this hook can optionally return a { code, ast, map } object. The ast must be a standard ESTree AST with start and end properties for each node. If the transformation does not move code, you can preserve existing sourcemaps by setting map to null. Otherwise you might need to generate the source map. See the section on source code transformations.

Note that in watch mode, the result of this hook is cached when rebuilding and the hook is only triggered again for a module id if either the code of the module has changed or a file has changed that was added via this.addWatchFile the last time the hook was triggered for this module.

If false is returned for moduleSideEffects and no other module imports anything from this module, then this module will not be included without checking for actual side-effects inside the module. If true is returned, Rollup will use its default algorithm to include all statements in the module that have side-effects (such as modifying a global or exported variable). If null is returned or the flag is omitted, then moduleSideEffects will be determined by the first resolveId hook that resolved this module, the treeshake.moduleSideEffects option, or eventually default to true.

If true is returned for syntheticNamedExports, this module will fallback the resolution of any missing named export to properties of the default export. This option allows to have dynamic named exports that might not be declared in the module, such as in this example:

dep.js: ({syntheticNamedExports: true})

export default {
  foo: 42,
  bar: 'hello'
}

main.js: (entry point)

import { foo, bar } from './dep.js'
console.log(foo, bar);

You can use this.getModuleInfo to find out the previous value of moduleSideEffects inside this hook.

watchChange

Type: (id: string) => void
Kind: sync, sequential
Previous/Next Hook: This hook can be triggered at any time both during the build and the output generation phases. If that is the case, the current build will still proceed but a new build will be scheduled to start once the current build has completed, starting again with options.

Notifies a plugin whenever rollup has detected a change to a monitored file in --watch mode. This hook cannot be used by output plugins.

Output Generation Hooks

Output generation hooks can provide information about a generated bundle and modify a build once complete. They work the same way and have the same types as Build Hooks but are called separately for each call to bundle.generate(outputOptions) or bundle.write(outputOptions). Plugins that only use output generation hooks can also be passed in via the output options and therefore run only for certain outputs.

The first hook of the output generation phase is outputOptions, the last one is either generateBundle if the output was successfully generated via bundle.generate(...), writeBundle if the output was successfully generated via bundle.write(...), or renderError if an error occurred at any time during the output generation.

augmentChunkHash

Type: (preRenderedChunk: PreRenderedChunk) => string
Kind: sync, sequential
Previous Hook: renderDynamicImport for each dynamic import expression.
Next Hook: resolveFileUrl for each use of import.meta.ROLLUP_FILE_URL_referenceId and resolveImportMeta for all other accesses to import.meta.

Can be used to augment the hash of individual chunks. Called for each Rollup output chunk. Returning a falsy value will not modify the hash. Truthy values will be passed to hash.update.

The following plugin will invalidate the hash of chunk foo with the timestamp of the last build:

// rollup.config.js
augmentChunkHash(chunkInfo) {
  if(chunkInfo.name === 'foo') {
    return Date.now().toString();
  }
}

Type: string | (() => string)
Kind: async, parallel
Previous Hook: renderStart
Next Hook: renderDynamicImport for each dynamic import expression.

Cf. output.banner/output.footer.

Type: string | (() => string)
Kind: async, parallel
Previous Hook: renderStart
Next Hook: renderDynamicImport for each dynamic import expression.

Cf. output.banner/output.footer.

generateBundle

Type: (options: OutputOptions, bundle: { [fileName: string]: AssetInfo | ChunkInfo }, isWrite: boolean) => void
Kind: async, sequential
Previous Hook: renderChunk for each chunk.
Next Hook: writeBundle if the output was generated via bundle.write(...), otherwise this is the last hook of the output generation phase and may again be followed by outputOptions if another output is generated.

Called at the end of bundle.generate() or immediately before the files are written in bundle.write(). To modify the files after they have been written, use the writeBundle hook. bundle provides the full list of files being written or generated along with their details:

// AssetInfo
{
  fileName: string,
  source: string | Uint8Array,
  type: 'asset',
}

// ChunkInfo
{
  code: string,
  dynamicImports: string[],
  exports: string[],
  facadeModuleId: string | null,
  fileName: string,
  imports: string[],
  isDynamicEntry: boolean,
  isEntry: boolean,
  map: SourceMap | null,
  modules: {
    [id: string]: {
      renderedExports: string[],
      removedExports: string[],
      renderedLength: number,
      originalLength: number
    },
  },
  name: string,
  type: 'chunk',
}

You can prevent files from being emitted by deleting them from the bundle object in this hook. To emit additional files, use the this.emitFile plugin context function.

intro

Type: string | (() => string)
Kind: async, parallel
Previous Hook: renderStart
Next Hook: renderDynamicImport for each dynamic import expression.

Cf. output.intro/output.outro.

outputOptions

Type: (outputOptions: OutputOptions) => OutputOptions | null
Kind: sync, sequential
Previous Hook: buildEnd if this is the first time an output is generated, otherwise either generateBundle, writeBundle or renderError depending on the previously generated output. This is the first hook of the output generation phase.
Next Hook: renderStart.

Replaces or manipulates the output options object passed to bundle.generate() or bundle.write(). Returning null does not replace anything. If you just need to read the output options, it is recommended to use the renderStart hook as this hook has access to the output options after the transformations from all outputOptions hooks have been taken into account.

outro

Type: string | (() => string)
Kind: async, parallel
Previous Hook: renderStart
Next Hook: renderDynamicImport for each dynamic import expression.

Cf. output.intro/output.outro.

renderChunk

Type: (code: string, chunk: ChunkInfo, options: OutputOptions) => string | { code: string, map: SourceMap } | null
Kind: async, sequential
Previous Hook: resolveFileUrl for each use of import.meta.ROLLUP_FILE_URL_referenceId and resolveImportMeta for all other accesses to import.meta.
Next Hook: generateBundle.

Can be used to transform individual chunks. Called for each Rollup output chunk file. Returning null will apply no transformations.

renderDynamicImport

Type: ({format: string, moduleId: string, targetModuleId: string | null, customResolution: string | null}) => {left: string, right: string} | null
Kind: sync, first
Previous Hook: banner, footer, intro, outro.
Next Hook: augmentChunkHash for each chunk that would contain a hash in the file name.

This hook provides fine-grained control over how dynamic imports are rendered by providing replacements for the code to the left (import() and right ()) of the argument of the import expression. Returning null defers to other hooks of this type and ultimately renders a format-specific default.

format is the rendered output format, moduleId the id of the module performing the dynamic import. If the import could be resolved to an internal or external id, then targetModuleId will be set to this id, otherwise it will be null. If the dynamic import contained a non-string expression that was resolved by a resolveDynamicImport hook to a replacement string, then customResolution will contain that string.

The following code will replace all dynamic imports with a custom handler, adding import.meta.url as a second argument to allow the handler to resolve relative imports correctly:

// plugin
const plugin = {
  name: 'dynamic-import-polyfill',
  renderDynamicImport() {
    return {
      left: 'dynamicImportPolyfill(',
      right: ', import.meta.url)'
    }
  }
};

// input
import('./lib.js');

// output
dynamicImportPolyfill('./lib.js', import.meta.url);

The next plugin will make sure all dynamic imports of esm-lib are marked as external and retained as import expressions to e.g. allow a CommonJS build to import an ES module in Node 13+, cf. how to import ES modules from CommonJS in the Node documentation.

// plugin
const plugin = {
  name: 'retain-import-expression',
  resolveDynamicImport(specifier) {
    if (specifier === 'esm-lib') return false;
    return null;
  },
  renderDynamicImport({targetModuleId}) {
    if (targetModuleId === 'esm-lib')
    return {
      left: 'import(',
      right: ')'
    }
  }
};

renderError

Type: (error: Error) => void
Kind: async, parallel
Previous Hook: Any hook from renderStart to renderChunk.
Next Hook: If it is called, this is the last hook of the output generation phase and may again be followed by outputOptions if another output is generated.

Called when rollup encounters an error during bundle.generate() or bundle.write(). The error is passed to this hook. To get notified when generation completes successfully, use the generateBundle hook.

renderStart

Type: (outputOptions: OutputOptions, inputOptions: InputOptions) => void
Kind: async, parallel
Previous Hook: outputOptions
Next Hook: banner, footer, intro and outro run in parallel.

Called initially each time bundle.generate() or bundle.write() is called. To get notified when generation has completed, use the generateBundle and renderError hooks. This is the recommended hook to use when you need access to the output options passed to bundle.generate() or bundle.write() as it will take the transformations by all outputOptions hooks into account. It also receives the input options passed to rollup.rollup() so that plugins that can be used as output plugins, i.e. plugins that only use generate phase hooks, can get access to them.

resolveFileUrl

Type: ({chunkId: string, fileName: string, format: string, moduleId: string, referenceId: string, relativePath: string}) => string | null
Kind: sync, first
Previous Hook: augmentChunkHash for each chunk that would contain a hash in the file name.
Next Hook: renderChunk for each chunk.

Allows to customize how Rollup resolves URLs of files that were emitted by plugins via this.emitFile. By default, Rollup will generate code for import.meta.ROLLUP_FILE_URL_referenceId that should correctly generate absolute URLs of emitted files independent of the output format and the host system where the code is deployed.

For that, all formats except CommonJS and UMD assume that they run in a browser environment where URL and document are available. In case that fails or to generate more optimized code, this hook can be used to customize this behaviour. To do that, the following information is available:

  • chunkId: The id of the chunk this file is referenced from.
  • fileName: The path and file name of the emitted asset, relative to output.dir without a leading ./.
  • format: The rendered output format.
  • moduleId: The id of the original module this file is referenced from. Useful for conditionally resolving certain assets differently.
  • referenceId: The reference id of the file.
  • relativePath: The path and file name of the emitted file, relative to the chunk the file is referenced from. This will path will contain no leading ./ but may contain a leading ../.

Note that since this hook has access to the filename of the current chunk, its return value will not be considered when generating the hash of this chunk.

The following plugin will always resolve all files relative to the current document:

// rollup.config.js
resolveFileUrl({fileName}) {
  return `new URL('${fileName}', document.baseURI).href`;
}

resolveImportMeta

Type: (property: string | null, {chunkId: string, moduleId: string, format: string}) => string | null
Kind: sync, first
Previous Hook: augmentChunkHash for each chunk that would contain a hash in the file name.
Next Hook: renderChunk for each chunk.

Allows to customize how Rollup handles import.meta and import.meta.someProperty, in particular import.meta.url. In ES modules, import.meta is an object and import.meta.url contains the URL of the current module, e.g. http://server.net/bundle.js for browsers or file:///path/to/bundle.js in Node.

By default for formats other than ES modules, Rollup replaces import.meta.url with code that attempts to match this behaviour by returning the dynamic URL of the current chunk. Note that all formats except CommonJS and UMD assume that they run in a browser environment where URL and document are available. For other properties, import.meta.someProperty is replaced with undefined while import.meta is replaced with an object containing a url property.

This behaviour can be changed—also for ES modules—via this hook. For each occurrence of import.meta<.someProperty>, this hook is called with the name of the property or null if import.meta is accessed directly. For example, the following code will resolve import.meta.url using the relative path of the original module to the current working directory and again resolve this path against the base URL of the current document at runtime:

// rollup.config.js
resolveImportMeta(property, {moduleId}) {
  if (property === 'url') {
    return `new URL('${path.relative(process.cwd(), moduleId)}', document.baseURI).href`;
  }
  return null;
}

Note that since this hook has access to the filename of the current chunk, its return value will not be considered when generating the hash of this chunk.

writeBundle

Type: (options: OutputOptions, bundle: { [fileName: string]: AssetInfo | ChunkInfo }) => void
Kind: async, parallel
Previous Hook: generateBundle
Next Hook: If it is called, this is the last hook of the output generation phase and may again be followed by outputOptions if another output is generated.

Called only at the end of bundle.write() once all files have been written. Similar to the generateBundle hook, bundle provides the full list of files being written along with their details.

Deprecated Hooks

☢️ These hooks have been deprecated and may be removed in a future Rollup version.

  • resolveAssetUrl - Use resolveFileUrl - Function hook that allows to customize the generated code for asset URLs.

More properties may be supported in the future, as and when they prove necessary.

Plugin Context

A number of utility functions and informational bits can be accessed from within most hooks via this:

this.addWatchFile(id: string) => void

Adds additional files to be monitored in watch mode so that changes to these files will trigger rebuilds. id can be an absolute path to a file or directory or a path relative to the current working directory. This context function can only be used in hooks during the build phase, i.e. in buildStart, load, resolveId, and transform.

Note: Usually in watch mode to improve rebuild speed, the transform hook will only be triggered for a given module if its contents actually changed. Using this.addWatchFile from within the transform hook will make sure the transform hook is also reevaluated for this module if the watched file changes.

In general, it is recommended to use this.addWatchFile from within the hook that depends on the watched file.

this.emitFile(emittedFile: EmittedChunk | EmittedAsset) => string

Emits a new file that is included in the build output and returns a referenceId that can be used in various places to reference the emitted file. emittedFile can have one of two forms:

// EmittedChunk
{
  type: 'chunk',
  id: string,
  importer?: string,
  preserveSignature?: 'strict' | 'allow-extension' | false,
  name?: string,
  fileName?: string
}

// EmittedAsset
{
  type: 'asset',
  source?: string | Uint8Array,
  name?: string,
  fileName?: string
}

In both cases, either a name or a fileName can be supplied. If a fileName is provided, it will be used unmodified as the name of the generated file, throwing an error if this causes a conflict. Otherwise if a name is supplied, this will be used as substitution for [name] in the corresponding output.chunkFileNames or output.assetFileNames pattern, possibly adding a unique number to the end of the file name to avoid conflicts. If neither a name nor fileName is supplied, a default name will be used.

You can reference the URL of an emitted file in any code returned by a load or transform plugin hook via import.meta.ROLLUP_FILE_URL_referenceId. See File URLs for more details and an example.

The generated code that replaces import.meta.ROLLUP_FILE_URL_referenceId can be customized via the resolveFileUrl plugin hook. You can also use this.getFileName(referenceId) to determine the file name as soon as it is available

If the type is chunk, then this emits a new chunk with the given module id as entry point. To resolve it, the id will be passed through build hooks just like regular entry points, starting with resolveId. If an importer is provided, this acts as the second parameter of resolveId and is important to properly resolve relative paths. If it is not provided, paths will be resolved relative to the current working directory. If a value for preserveSignature is provided, this will override preserveEntrySignatures for this particular chunk.

This will not result in duplicate modules in the graph, instead if necessary, existing chunks will be split or a facade chunk with reexports will be created. Chunks with a specified fileName will always generate separate chunks while other emitted chunks may be deduplicated with existing chunks even if the name does not match. If such a chunk is not deduplicated, the output.chunkFileNames name pattern will be used.

If the type is asset, then this emits an arbitrary new file with the given source as content. It is possible to defer setting the source via this.setAssetSource(assetReferenceId, source) to a later time to be able to reference a file during the build phase while setting the source separately for each output during the generate phase. Assets with a specified fileName will always generate separate files while other emitted assets may be deduplicated with existing assets if they have the same source even if the name does not match. If such an asset is not deduplicated, the output.assetFileNames name pattern will be used.

this.error(error: string | Error, position?: number | { column: number; line: number }) => never

Structurally equivalent to this.warn, except that it will also abort the bundling process.

this.getCombinedSourcemap() => SourceMap

Get the combined source maps of all previous plugins. This context function can only be used in the transform plugin hook.

this.getFileName(referenceId: string) => string

Get the file name of a chunk or asset that has been emitted via this.emitFile. The file name will be relative to outputOptions.dir.

this.getModuleIds() => IterableIterator<string>

Returns an Iterator that gives access to all module ids in the current graph. It can be iterated via

for (const moduleId of this.getModuleIds()) { /* ... */ }

or converted into an Array via Array.from(this.getModuleIds()).

this.getModuleInfo(moduleId: string) => ModuleInfo

Returns additional information about the module in question in the form

{
  id: string, // the id of the module, for convenience
  isEntry: boolean, // is this a user- or plugin-defined entry point
  isExternal: boolean, // for external modules that are not included in the graph
  importedIds: string[], // the module ids statically imported by this module
  importers: string[], // the ids of all modules that statically import this module
  dynamicallyImportedIds: string[], // the module ids imported by this module via dynamic import()
  dynamicImporters: string[], // the ids of all modules that import this module via dynamic import()
  hasModuleSideEffects: boolean // are imports of this module included if nothing is imported from it
}

If the module id cannot be found, an error is thrown.

this.meta: {rollupVersion: string}

An Object containing potentially useful Rollup metadata. meta is the only context property accessible from the options hook.

this.parse(code: string, acornOptions?: AcornOptions) => ESTree.Program

Use Rollup's internal acorn instance to parse code to an AST.

this.resolve(source: string, importer?: string, options?: {skipSelf: boolean}) => Promise<{id: string, external: boolean} | null>

Resolve imports to module ids (i.e. file names) using the same plugins that Rollup uses, and determine if an import should be external. If null is returned, the import could not be resolved by Rollup or any plugin but was not explicitly marked as external by the user.

If you pass skipSelf: true, then the resolveId hook of the plugin from which this.resolve is called will be skipped when resolving.

this.setAssetSource(assetReferenceId: string, source: string | Uint8Array) => void

Set the deferred source of an asset. Note that you can also pass a Node Buffer as source as it is a sub-class of Uint8Array.

this.warn(warning: string | RollupWarning, position?: number | { column: number; line: number }) => void

Using this method will queue warnings for a build. These warnings will be printed by the CLI just like internally generated warnings (except with the plugin name), or captured by custom onwarn handlers.

The warning argument can be a string or an object with (at minimum) a message property:

this.warn( 'hmm...' );
// is equivalent to
this.warn({ message: 'hmm...' });

Use the second form if you need to add additional properties to your warning object. Rollup will augment the warning object with a plugin property containing the plugin name, code (PLUGIN_WARNING) and id (the file being transformed) properties.

The position argument is a character index where the warning was raised. If present, Rollup will augment the warning object with pos, loc (a standard { file, line, column } object) and frame (a snippet of code showing the error).

Deprecated Context Functions

☢️ These context utility functions have been deprecated and may be removed in a future Rollup version.

  • this.emitAsset(assetName: string, source: string) => string - Use this.emitFile - Emits a custom file that is included in the build output, returning an assetReferenceId that can be used to reference the emitted file. You can defer setting the source if you provide it later via this.setAssetSource(assetReferenceId, source). A string or Uint8Array/Buffer source must be set for each asset through either method or an error will be thrown on generate completion.

    Emitted assets will follow the output.assetFileNames naming scheme. You can reference the URL of the file in any code returned by a load or transform plugin hook via import.meta.ROLLUP_ASSET_URL_assetReferenceId.

    The generated code that replaces import.meta.ROLLUP_ASSET_URL_assetReferenceId can be customized via the resolveFileUrl plugin hook. Once the asset has been finalized during generate, you can also use this.getFileName(assetReferenceId) to determine the file name.

  • this.emitChunk(moduleId: string, options?: {name?: string}) => string - Use this.emitFile - Emits a new chunk with the given module as entry point. This will not result in duplicate modules in the graph, instead if necessary, existing chunks will be split. It returns a chunkReferenceId that can be used to later access the generated file name of the chunk.

    Emitted chunks will follow the output.chunkFileNames, output.entryFileNames naming scheme. If a name is provided, this will be used for the [name] file name placeholder, otherwise the name will be derived from the file name. If a name is provided, this name must not conflict with any other entry point names unless the entry points reference the same entry module. You can reference the URL of the emitted chunk in any code returned by a load or transform plugin hook via import.meta.ROLLUP_CHUNK_URL_chunkReferenceId.

    The generated code that replaces import.meta.ROLLUP_CHUNK_URL_chunkReferenceId can be customized via the resolveFileUrl plugin hook. Once the chunk has been rendered during generate, you can also use this.getFileName(chunkReferenceId) to determine the file name.

  • this.getAssetFileName(assetReferenceId: string) => string - Use this.getFileName - Get the file name of an asset, according to the assetFileNames output option pattern. The file name will be relative to outputOptions.dir.

  • this.getChunkFileName(chunkReferenceId: string) => string - Use this.getFileName - Get the file name of an emitted chunk. The file name will be relative to outputOptions.dir.

  • this.isExternal(id: string, importer: string | undefined, isResolved: boolean) => boolean - Use this.resolve - Determine if a given module ID is external when imported by importer. When isResolved is false, Rollup will try to resolve the id before testing if it is external.

  • this.moduleIds: IterableIterator<string> - Use this.getModuleIds - An Iterator that gives access to all module ids in the current graph. It can be iterated via

      for (const moduleId of this.moduleIds) { /* ... */ }
      

    or converted into an Array via Array.from(this.moduleIds).

  • this.resolveId(source: string, importer?: string) => Promise<string | null> - Use this.resolve - Resolve imports to module ids (i.e. file names) using the same plugins that Rollup uses. Returns null if an id cannot be resolved.

File URLs

To reference a file URL reference from within JS code, use the import.meta.ROLLUP_FILE_URL_referenceId replacement. This will generate code that depends on the output format and generates a URL that points to the emitted file in the target environment. Note that all formats except CommonJS and UMD assume that they run in a browser environment where URL and document are available.

The following example will detect imports of .svg files, emit the imported files as assets, and return their URLs to be used e.g. as the src attribute of an img tag:

// plugin
export default function svgResolverPlugin () {
  return ({
    resolveId(source, importer) {
      if (source.endsWith('.svg')) {
        return path.resolve(path.dirname(importer), source);
      }
    },
    load(id) {
      if (id.endsWith('.svg')) {
      	const referenceId = this.emitFile({
          type: 'asset',
          name: path.basename(id),
          source: fs.readFileSync(id)
        });
        return `export default import.meta.ROLLUP_FILE_URL_${referenceId};`;
      }
    }
  });
}

Usage:

import logo from '../images/logo.svg';
const image = document.createElement('img');
image.src = logo;
document.body.appendChild(image);

Similar to assets, emitted chunks can be referenced from within JS code via import.meta.ROLLUP_FILE_URL_referenceId as well.

The following example will detect imports prefixed with register-paint-worklet: and generate the necessary code and separate chunk to generate a CSS paint worklet. Note that this will only work in modern browsers and will only work if the output format is set to es.

// plugin
const REGISTER_WORKLET = 'register-paint-worklet:';
export default function paintWorkletPlugin () {
  return ({
    load(id) {
      if (id.startsWith(REGISTER_WORKLET)) {
        return `CSS.paintWorklet.addModule(import.meta.ROLLUP_FILE_URL_${this.emitFile({
          type: 'chunk',
          id: id.slice(REGISTER_WORKLET.length)
        })});`;
      }
    },
    resolveId(source, importer) {
      // We remove the prefix, resolve everything to absolute ids and add the prefix again
      // This makes sure that you can use relative imports to define worklets
    if (source.startsWith(REGISTER_WORKLET)) {
      return this.resolve(source.slice(REGISTER_WORKLET.length), importer).then(
        resolvedId => REGISTER_WORKLET + resolvedId.id
      );
    }
    return null;
    }
  });
}

Usage:

// main.js
import 'register-paint-worklet:./worklet.js';
import { color, size } from './config.js';
document.body.innerHTML += `<h1 style="background-image: paint(vertical-lines);">color: ${color}, size: ${size}</h1>`;

// worklet.js
import { color, size } from './config.js';
registerPaint(
  'vertical-lines',
  class {
    paint(ctx, geom) {
      for (let x = 0; x < geom.width / size; x++) {
        ctx.beginPath();
        ctx.fillStyle = color;
        ctx.rect(x * size, 0, 2, geom.height);
        ctx.fill();
      }
    }
  }
);

// config.js
export const color = 'greenyellow';
export const size = 6;

If you build this code, both the main chunk and the worklet will share the code from config.js via a shared chunk. This enables us to make use of the browser cache to reduce transmitted data and speed up loading the worklet.

Transformers

Transformer plugins (i.e. those that return a transform function for e.g. transpiling non-JS files) should support options.include and options.exclude, both of which can be a minimatch pattern or an array of minimatch patterns. If options.include is omitted or of zero length, files should be included by default; otherwise they should only be included if the ID matches one of the patterns.

The transform hook, if returning an object, can also include an ast property. Only use this feature if you know what you're doing. Note that only the last AST in a chain of transforms will be used (and if there are transforms, any ASTs generated by the load hook will be discarded for the transformed modules.)

Example Transformer

(Use @rollup/pluginutils for commonly needed functions, and to implement a transformer in the recommended manner.)

import { createFilter } from '@rollup/pluginutils';

export default function myPlugin ( options = {} ) {
  const filter = createFilter( options.include, options.exclude );

  return {
    transform ( code, id ) {
      if ( !filter( id ) ) return;

      // proceed with the transformation...
      return {
        code: generatedCode,
        map: generatedSourceMap
      };
    }
  };
}

Source Code Transformations

If a plugin transforms source code, it should generate a sourcemap automatically, unless there's a specific sourceMap: false option. Rollup only cares about the mappings property (everything else is handled automatically). If it doesn't make sense to generate a sourcemap, (e.g. rollup-plugin-string), return an empty sourcemap:

return {
  code: transformedCode,
  map: { mappings: '' }
};

If the transformation does not move code, you can preserve existing sourcemaps by returning null:

return {
  code: transformedCode,
  map: null
};

If you create a plugin that you think would be useful to others, please publish it to NPM and add submit it to https://github.com/rollup/awesome!

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:

// 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 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 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:

// 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

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 dep.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.

Who made the Rollup logo? It's lovely.

Julian Lloyd!

Integrating Rollup With Other Tools

With NPM Packages

At some point, it's likely that your project will depend on packages installed from NPM into your node_modules folder. Unlike other bundlers such as Webpack and Browserify, Rollup doesn't know "out of the box" how to handle these dependencies - we need to add some configuration.

Let's add a simple dependency called the-answer, which exports the answer to the question of life, the universe and everything:

npm install the-answer
# or `npm i the-answer`

If we update our src/main.js file…

// src/main.js
import answer from 'the-answer';

export default function () {
  console.log('the answer is ' + answer);
}

…and run Rollup…

npm run build

…we'll see a warning like this:

(!) Unresolved dependencies
https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency
the-answer (imported by main.js)

The resulting bundle.js will still work in Node.js, because the import declaration gets turned into a CommonJS require statement, but the-answer does not get included in the bundle. For that, we need a plugin.

@rollup/plugin-node-resolve

The @rollup/plugin-node-resolve plugin teaches Rollup how to find external modules. Install it…

npm install --save-dev @rollup/plugin-node-resolve

…and add it to your config file:

// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';

export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  plugins: [ resolve() ]
};

This time, when you npm run build, no warning is emitted — the bundle contains the imported module.

@rollup/plugin-commonjs

Some libraries expose ES modules that you can import as-is — the-answer is one such module. But at the moment, the majority of packages on NPM are exposed as CommonJS modules instead. Until that changes, we need to convert CommonJS to ES2015 before Rollup can process them.

The @rollup/plugin-commonjs plugin does exactly that.

Note that @rollup/plugin-commonjs should go before other plugins that transform your modules — this is to prevent other plugins from making changes that break the CommonJS detection.

Peer dependencies

Let's say that you're building a library that has a peer dependency, such as React or Lodash. If you set up externals as described above, your rollup will bundle all imports:

import answer from 'the-answer';
import _ from 'lodash';

You can finely tune which imports are bundled and which are treated as external. For this example, we'll treat lodash as external, but not the-answer.

Here is the config file:

// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';

export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  plugins: [resolve({
    // pass custom options to the resolve plugin
    customResolveOptions: {
      moduleDirectory: 'node_modules'
    }
  })],
  // indicate which modules should be treated as external
  external: ['lodash']
};

Voilà, lodash will now be treated as external, and not be bundled with your library.

The external key accepts either an array of module names, or a function which takes the module name and returns true if it should be treated as external. For example:

export default {
  // ...
  external: id => /lodash/.test(id)
}

You might use this form if you're using babel-plugin-lodash to cherry-pick lodash modules. In this case, Babel will convert your import statements to look like this:

import _merge from 'lodash/merge';

The array form of external does not handle wildcards, so this import will only be treated as external in the functional form.

Babel

Many developers use Babel in their projects in order to use the latest JavaScript features that aren't yet supported by browsers and Node.js.

The easiest way to use both Babel and Rollup is with @rollup/plugin-babel. First, install the plugin:

npm i -D @rollup/plugin-babel @rollup/plugin-node-resolve

Add it to rollup.config.js:

// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import babel from '@rollup/plugin-babel';

export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  plugins: [
    resolve(),
    babel({ babelHelpers: 'bundled' })
  ]
};

Before Babel will actually compile your code, it needs to be configured. Create a new file, src/.babelrc.json:

{
  "presets": [
    ["@babel/env", {"modules": false}]
  ]
}

There are a few unusual elements to this setup. First, we're setting "modules": false, otherwise Babel will convert our modules to CommonJS before Rollup gets a chance to do its thing, causing it to fail.

Secondly, we're putting our .babelrc.json file in src, rather than the project root. This allows us to have a different .babelrc.json for things like tests, if we need that later – See the Babel documentation for more information on both project wide and file relative configuration.

Now, before we run rollup, we need to install babel-core and the env preset:

npm i -D @babel/core @babel/preset-env

Running Rollup now will create a bundle - except we're not actually using any ES2015 features. Let's change that by editing src/main.js:

// src/main.js
import answer from 'the-answer';

export default () => {
  console.log(`the answer is ${answer}`);
}

Run Rollup with npm run build, and check the bundle:

'use strict';

var index = 42;

var main = (function () {
  console.log('the answer is ' + index);
});

module.exports = main;

Gulp

Rollup returns Promises which are understood by gulp so integration is relatively painless.

The syntax is very similar to the configuration file, but the properties are split across two different operations corresponding to the JavaScript API:

const gulp = require('gulp');
const rollup = require('rollup');
const rollupTypescript = require('@rollup/plugin-typescript');

gulp.task('build', () => {
  return rollup.rollup({
    input: './src/main.ts',
    plugins: [
      rollupTypescript()
    ]
  }).then(bundle => {
    return bundle.write({
      file: './dist/library.js',
      format: 'umd',
      name: 'library',
      sourcemap: true
    });
  });
});

You may also use the async/await syntax:

const gulp = require('gulp');
const rollup = require('rollup');
const rollupTypescript = require('@rollup/plugin-typescript');

gulp.task('build', async function () {
  const bundle = await rollup.rollup({
    input: './src/main.ts',
    plugins: [
      rollupTypescript()
    ]
  });

  await bundle.write({
    file: './dist/library.js',
    format: 'umd',
    name: 'library',
    sourcemap: true
  });
});

Troubleshooting

If you get stuck, please try discussing the issue on the Rollup Gitter or posting a question to https://stackoverflow.com/questions/tagged/rollupjs. If you've found a bug, or Rollup can't meet your needs, please try raising an issue. Lastly, you may try contacting @RollupJS on Twitter.

Avoiding eval

You probably already know that 'eval is evil', at least according to some people. But it's particularly harmful with Rollup, because of how it works – unlike other module bundlers, which wrap each module in a function, Rollup puts all your code in the same scope.

That's more efficient, but it means that the shared scope is 'polluted' whenever you use eval, whereas with a different bundler, modules that didn't use eval would not be polluted. A minifier can't mangle variable names in polluted code, because it can't guarantee that the code to be evaluated doesn't reference those variable names.

Furthermore, it poses a security risk in that a malicious module could access another module's private variables with eval('SUPER_SEKRIT').

Luckily, unless you really do intend for the evaluated code to have access to local variables (in which case you're probably doing something wrong!), you can achieve the same effect in one of two ways:

eval2 = eval

Simply 'copying' eval provides you with a function that does exactly the same thing, but which runs in the global scope rather than the local one:

var eval2 = eval;

(function () {
  var foo = 42;
  eval('console.log("with eval:",foo)');  // logs 'with eval: 42'
  eval2('console.log("with eval2:",foo)'); // throws ReferenceError
})();

new Function

Using the Function constructor generates a function from the supplied string. Again, it runs in the global scope. If you need to call the function repeatedly, this is much, much faster than using eval.

Tree-shaking Doesn't Seem to be Working

Sometimes, you'll end up with code in your bundle that doesn't seem like it should be there. For example, if you import a utility from lodash-es, you might expect that you'll get the bare minimum of code necessary for that utility to work.

But Rollup has to be conservative about what code it removes in order to guarantee that the end result will run correctly. If an imported module appears to have side-effects, either on bits of the module that you're using or on the global environment, Rollup plays it safe and includes those side-effects.

Because static analysis in a dynamic language like JavaScript is hard, there will occasionally be false positives. Lodash is a good example of a module that looks like it has lots of side-effects, even in places that it doesn't. You can often mitigate those false positives by importing submodules (e.g. import map from 'lodash-es/map' rather than import { map } from 'lodash-es').

Rollup's static analysis will improve over time, but it will never be perfect in all cases – that's just JavaScript.

Error: "[name] is not exported by [module]"

Occasionally you will see an error message like this:

'foo' is not exported by bar.js (imported by baz.js)

Import declarations must have corresponding export declarations in the imported module. For example, if you have import a from './a.js' in a module, and a.js doesn't have an export default declaration, or import {foo} from './b.js', and b.js doesn't export foo, Rollup cannot bundle the code.

This error frequently occurs with CommonJS modules converted by @rollup/plugin-commonjs, which makes a reasonable attempt to generate named exports from the CommonJS code but won't always succeed, because the freewheeling nature of CommonJS is at odds with the rigorous approach we benefit from in JavaScript modules. It can be solved by using the namedExports option, which allows you to manually fill in the information gaps.

Error: "this is undefined"

In a JavaScript module, this is undefined at the top level (i.e., outside functions). Because of that, Rollup will rewrite any this references to undefined so that the resulting behaviour matches what will happen when modules are natively supported.

There are occasional valid reasons for this to mean something else. If you're getting errors in your bundle, you can use options.context and options.moduleContext to change this behaviour.

Warning: "Sourcemap is likely to be incorrect"

You'll see this warning if you generate a sourcemap with your bundle (sourceMap: true or sourceMap: 'inline') but you're using one or more plugins that transformed code without generating a sourcemap for the transformation.

Usually, a plugin will only omit the sourcemap if it (the plugin, not the bundle) was configured with sourceMap: false – so all you need to do is change that. If the plugin doesn't generate a sourcemap, consider raising an issue with the plugin author.

Warning: "Treating [module] as external dependency"

Rollup will only resolve relative module IDs by default. This means that an import statement like this…

import moment from 'moment';

…won't result in moment being included in your bundle – instead, it will be an external dependency that is required at runtime. If that's what you want, you can suppress this warning with the external option, which makes your intentions explicit:

// rollup.config.js
export default {
  entry: 'src/index.js',
  dest: 'bundle.js',
  format: 'cjs',
  external: [ 'moment' ] // <-- suppresses the warning
};

If you do want to include the module in your bundle, you need to tell Rollup how to find it. In most cases, this is a question of using @rollup/plugin-node-resolve.

Some modules, like events or util, are built in to Node.js. If you want to include those (for example, so that your bundle runs in the browser), you may need to include rollup-plugin-node-polyfills.

Big list of options

Core functionality

external

Type: (string | RegExp)[] | RegExp | string | (id: string, parentId: string, isResolved: boolean) => boolean
CLI: -e/--external <external-id,another-external-id,...>

Either a function that takes an id and returns true (external) or false (not external), or an Array of module IDs, or regular expressions to match module IDs, that should remain external to the bundle. Can also be just a single ID or regular expression. The matched IDs should be either:

  1. the name of an external dependency, exactly the way it is written in the import statement. I.e. to mark import "dependency.js" as external, use "dependency.js" while to mark import "dependency" as external, use "dependency".
  2. a resolved ID (like an absolute path to a file).
// rollup.config.js
import path from 'path';

export default {
  ...,
  external: [
    'some-externally-required-library',
    path.resolve( __dirname, 'src/some-local-file-that-should-not-be-bundled.js' ),
    /node_modules/
  ]
};

Note that if you want to filter out package imports, e.g. import {rollup} from 'rollup', via a /node_modules/ regular expression, you need something like @rollup/plugin-node-resolve to resolve the imports to node_modules first.

When given as a command line argument, it should be a comma-separated list of IDs:

rollup -i src/main.js ... -e foo,bar,baz

When providing a function, it is actually called with three parameters (id, parent, isResolved) that can give you more fine-grained control:

  • id is the id of the module in question
  • parent is the id of the module doing the import
  • isResolved signals whether the id has been resolved by e.g. plugins

When creating an iife or umd bundle, you will need to provide global variable names to replace your external imports via the output.globals option.

If a relative import, i.e. starting with ./ or ../, is marked as "external", rollup will internally resolve the id to an absolute file system location so that different imports of the external module can be merged. When the resulting bundle is written, the import will again be converted to a relative import. Example:

// input
// src/main.js (entry point)
import x from '../external.js';
import './nested/nested.js';
console.log(x);

// src/nested/nested.js
// the import would point to the same file if it existed
import x from '../../external.js';
console.log(x);

// output
// the different imports are merged
import x from '../external.js';

console.log(x);

console.log(x);

The conversion back to a relative import is done as if output.file or output.dir were in the same location as the entry point or the common base directory of all entry points if there is more than one.

input

Type: string | string [] | { [entryName: string]: string }
CLI: -i/--input <filename>

The bundle's entry point(s) (e.g. your main.js or app.js or index.js). If you provide an array of entry points or an object mapping names to entry points, they will be bundled to separate output chunks. Unless the output.file option is used, generated chunk names will follow the output.entryFileNames option. When using the object form, the [name] portion of the file name will be the name of the object property while for the array form, it will be the file name of the entry point.

Note that it is possible when using the object form to put entry points into different sub-folders by adding a / to the name. The following will generate at least two entry chunks with the names entry-a.js and entry-b/index.js, i.e. the file index.js is placed in the folder entry-b:

// rollup.config.js
export default {
  ...,
  input: {
    a: 'src/main-a.js',
    'b/index': 'src/main-b.js'
  },
  output: {
    ...,
    entryFileNames: 'entry-[name].js'
  }
};

When using the command line interface, multiple inputs can be provided by using the option multiple times. When provided as the first options, it is equivalent to not prefix them with --input:

rollup --format es --input src/entry1.js --input src/entry2.js
# is equivalent to
rollup src/entry1.js src/entry2.js --format es

Chunks can be named by adding an = to the provided value:

rollup main=src/entry1.js other=src/entry2.js --format es

File names containing spaces can be specified by using quotes:

rollup "main entry"="src/entry 1.js" "src/other entry.js" --format es

output.dir

Type: string
CLI: -d/--dir <dirname>

The directory in which all generated chunks are placed. This option is required if more than one chunk is generated. Otherwise, the file option can be used instead.

output.file

Type: string
CLI: -o/--file <filename>

The file to write to. Will also be used to generate sourcemaps, if applicable. Can only be used if not more than one chunk is generated.

output.format

Type: string
CLI: -f/--format <formatspecifier>

Specifies the format of the generated bundle. One of the following:

  • amd – Asynchronous Module Definition, used with module loaders like RequireJS
  • cjs – CommonJS, suitable for Node and other bundlers (alias: commonjs)
  • es – Keep the bundle as an ES module file, suitable for other bundlers and inclusion as a <script type=module> tag in modern browsers (alias: esm, module)
  • iife – A self-executing function, suitable for inclusion as a <script> tag. (If you want to create a bundle for your application, you probably want to use this.)
  • umd – Universal Module Definition, works as amd, cjs and iife all in one
  • system – Native format of the SystemJS loader (alias: systemjs)

output.globals

Type: { [id: string]: string } | ((id: string) => string)
CLI: -g/--globals <external-id:variableName,another-external-id:anotherVariableName,...>

Specifies id: variableName pairs necessary for external imports in umd/iife bundles. For example, in a case like this…

import $ from 'jquery';

…we want to tell Rollup that jquery is external and the jquery module ID equates to the global $ variable:

// rollup.config.js
export default {
  ...,
  external: ['jquery'],
  output: {
    format: 'iife',
    name: 'MyBundle',
    globals: {
      jquery: '$'
    }
  }
};

/*
var MyBundle = (function ($) {
  // code goes here
}($));
*/

Alternatively, supply a function that will turn an external module ID into a global variable name.

When given as a command line argument, it should be a comma-separated list of id:variableName pairs:

rollup -i src/main.js ... -g jquery:$,underscore:_

To tell Rollup that a local file should be replaced by a global variable, use an absolute id:

// rollup.config.js
import path from 'path';
const externalId = path.resolve( __dirname, 'src/some-local-file-that-should-not-be-bundled.js' );

export default {
  ...,
  external: [externalId],
  output: {
    format: 'iife',
    name: 'MyBundle',
    globals: {
      [externalId]: 'globalVariable'
    }
  }
};

output.name

Type: string
CLI: -n/--name <variableName>

Necessary for iife/umd bundles that exports values in which case it is the global variable name representing your bundle. Other scripts on the same page can use this variable name to access the exports of your bundle.

// rollup.config.js
export default {
  ...,
  output: {
    file: 'bundle.js',
    format: 'iife',
    name: 'MyBundle'
  }
};

// var MyBundle = (function () {...

Namespaces are supported i.e. your name can contain dots. The resulting bundle will contain the setup necessary for the namespacing.

rollup -n "a.b.c"

/* ->
this.a = this.a || {};
this.a.b = this.a.b || {};
this.a.b.c = ...
*/

output.plugins

Type: OutputPlugin | (OutputPlugin | void)[]

Adds a plugin just to this output. See Using output plugins for more information on how to use output-specific plugins and Plugins on how to write your own. For plugins imported from packages, remember to call the imported plugin function (i.e. commonjs(), not just commonjs). Falsy plugins will be ignored, which can be used to easily activate or deactivate plugins.

Not every plugin can be used here. output.plugins is limited to plugins that only use hooks that run during bundle.generate() or bundle.write(), i.e. after Rollup's main analysis is complete. If you are a plugin author, see output generation hooks to find out which hooks can be used.

The following will add minification to one of the outputs:

// rollup.config.js
import {terser} from 'rollup-plugin-terser';

export default {
  input: 'main.js',
  output: [
    {
      file: 'bundle.js',
      format: 'es'
    },
    {
      file: 'bundle.min.js',
      format: 'es',
      plugins: [terser()]
    }
  ]
};

plugins

Type: Plugin | (Plugin | void)[]

See Using plugins for more information on how to use plugins and Plugins on how to write your own (try it out, it's not as difficult as it may sound and very much extends what you can do with Rollup). For plugins imported from packages, remember to call the imported plugin function (i.e. commonjs(), not just commonjs). Falsy plugins will be ignored, which can be used to easily activate or deactivate plugins.

// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';

const isProduction = process.env.NODE_ENV === 'production';

export default (async () => ({
  input: 'main.js',
  plugins: [
    resolve(),
    commonjs(),
    isProduction && (await import('rollup-plugin-terser')).terser()
  ],
  output: {
    file: 'bundle.js',
    format: 'cjs'
  }
}))();

(This example also demonstrates how to use an async IIFE and dynamic imports to avoid unnecessary module loading, which can be surprisingly slow.)

Advanced functionality

cache

Type: RollupCache | false

The cache property of a previous bundle. Use it to speed up subsequent builds in watch mode — Rollup will only reanalyse the modules that have changed. Setting this option explicitly to false will prevent generating the cache property on the bundle and also deactivate caching for plugins.

const rollup = require('rollup');
let cache;

async function buildWithCache() {
  const bundle = await rollup.rollup({
    cache, // is ignored if falsy
    // ... other input options
  });
  cache = bundle.cache; // store the cache object of the previous build
  return bundle;
}

buildWithCache()
  .then(bundle => {
    // ... do something with the bundle
  })
  .then(() => buildWithCache()) // will use the cache of the previous build
  .then(bundle => {
    // ... do something with the bundle
  })

inlineDynamicImports

Type: boolean
CLI: --inlineDynamicImports/--no-inlineDynamicImports Default: false

This will inline dynamic imports instead of creating new chunks to create a single bundle. Only possible if a single input is provided.

manualChunks

Type: { [chunkAlias: string]: string[] } | ((id: string, {getModuleInfo, getModuleIds}) => string | void)

Allows the creation of custom shared common chunks. When using the object form, each property represents a chunk that contains the listed modules and all their dependencies if they are part of the module graph unless they are already in another manual chunk. The name of the chunk will be determined by the property key.

Note that it is not necessary for the listed modules themselves to be be part of the module graph, which is useful if you are working with @rollup/plugin-node-resolve and use deep imports from packages. For instance

manualChunks: {
  lodash: ['lodash']
}

will put all lodash modules into a manual chunk even if you are only using imports of the form import get from 'lodash/get'.

When using the function form, each resolved module id will be passed to the function. If a string is returned, the module and all its dependency will be added to the manual chunk with the given name. For instance this will create a vendor chunk containing all dependencies inside node_modules:

manualChunks(id) {
  if (id.includes('node_modules')) {
    return 'vendor';
  }
}

Be aware that manual chunks can change the behaviour of the application if side-effects are triggered before the corresponding modules are actually used.

When using the function form, manualChunks will be passed an object as second parameter containing the functions getModuleInfo and getModuleIds that work the same way as this.getModuleInfo and this.getModuleIds on the plugin context.

This can be used to dynamically determine into which manual chunk a module should be placed depending on its position in the module graph. For instance consider a scenario where you have a set of components, each of which dynamically imports a set of translated strings, i.e.

// Inside the "foo" component

function getTranslatedStrings(currentLanguage) {
  switch (currentLanguage) {
    case 'en': return import('./foo.strings.en.js');
    case 'de': return import('./foo.strings.de.js');
    // ...
  }
}

If a lot of such components are used together, this will result in a lot of dynamic imports of very small chunks: Even though we known that all language files of the same language that are imported by the same chunk will always be used together, Rollup does not have this information.

The following code will merge all files of the same language that are only used by a single entry point:

manualChunks(id, { getModuleInfo }) {
  const match = /.*\.strings\.(\w+)\.js/.exec(id);
  if (match) {
    const language = match[1]; // e.g. "en"
    const dependentEntryPoints = [];

    // we use a Set here so we handle each module at most once. This
    // prevents infinite loops in case of circular dependencies
    const idsToHandle = new Set(getModuleInfo(id).dynamicImporters);

    for (const moduleId of idsToHandle) {
      const { isEntry, dynamicImporters, importers } = getModuleInfo(moduleId);
      if (isEntry || dynamicImporters.length > 0) dependentEntryPoints.push(moduleId);

      // The Set iterator is intelligent enough to iterate over elements that
      // are added during iteration
      for (const importerId of importers) idsToHandle.add(importerId);
    }

    // If there is a unique entry, we put it into into a chunk based on the entry name
    if (dependentEntryPoints.length === 1) {
      return `${dependentEntryPoints[0].split('/').slice(-1)[0].split('.')[0]}.strings.${language}`;
    }
    // For multiple entries, we put it into a "shared" chunk
    if (dependentEntryPoints.length > 1) {
      return `shared.strings.${language}`;
    }
  }
}

onwarn

Type: (warning: RollupWarning, defaultHandler: (warning: string | RollupWarning) => void) => void;

A function that will intercept warning messages. If not supplied, warnings will be deduplicated and printed to the console. When using the --silent CLI option, this handler is the only way to get notified about warnings.

The function receives two arguments: the warning object and the default handler. Warnings objects have, at a minimum, a code and a message property, allowing you to control how different kinds of warnings are handled. Other properties are added depending on the type of warning.

// rollup.config.js
export default {
  ...,
  onwarn (warning, warn) {
    // skip certain warnings
    if (warning.code === 'UNUSED_EXTERNAL_IMPORT') return;

    // throw on others
    if (warning.code === 'NON_EXISTENT_EXPORT') throw new Error(warning.message);

    // Use default for everything else
    warn(warning);
  }
};

Many warnings also have a loc property and a frame allowing you to locate the source of the warning:

// rollup.config.js
export default {
  ...,
  onwarn ({ loc, frame, message }) {
    if (loc) {
      console.warn(`${loc.file} (${loc.line}:${loc.column}) ${message}`);
      if (frame) console.warn(frame);
    } else {
      console.warn(message);
    }
  }
};

output.assetFileNames

Type: string
CLI: --assetFileNames <pattern>
Default: "assets/[name]-[hash][extname]"

The pattern to use for naming custom emitted assets to include in the build output. Pattern supports the following placeholders:

  • [extname]: The file extension of the asset including a leading dot, e.g. .css.
  • [ext]: The file extension without a leading dot, e.g. css.
  • [hash]: A hash based on the name and content of the asset.
  • [name]: The file name of the asset excluding any extension.

Forward slashes / can be used to place files in sub-directories. See also output.chunkFileNames, output.entryFileNames.

output.banner/output.footer

Type: string | (() => string | Promise<string>)
CLI: --banner/--footer <text>

A string to prepend/append to the bundle. You can also supply a function that returns a Promise that resolves to a string to generate it asynchronously (Note: banner and footer options will not break sourcemaps).

// rollup.config.js
export default {
  ...,
  output: {
    ...,
    banner: '/* my-library version ' + version + ' */',
    footer: '/* follow me on Twitter! @rich_harris */'
  }
};

See also output.intro/output.outro.

output.chunkFileNames

Type: string
CLI: --chunkFileNames <pattern>
Default: "[name]-[hash].js"

The pattern to use for naming shared chunks created when code-splitting. Pattern supports the following placeholders:

  • [format]: The rendering format defined in the output options, e.g. es or cjs.
  • [hash]: A hash based on the content of the chunk and the content of all its dependencies.
  • [name]: The name of the chunk. This can be explicitly set via the manualChunks option or when the chunk is created by a plugin via this.emitFile. Otherwise it will be derived from the chunk contents.

Forward slashes / can be used to place files in sub-directories. See also output.assetFileNames, output.entryFileNames.

output.compact

Type: boolean
CLI: --compact/--no-compact
Default: false

This will minify the wrapper code generated by rollup. Note that this does not affect code written by the user. This option is useful when bundling pre-minified code.

output.entryFileNames

Type: string
CLI: --entryFileNames <pattern>
Default: "[name].js"

The pattern to use for chunks created from entry points. Pattern supports the following placeholders:

  • [format]: The rendering format defined in the output options, e.g. es or cjs.
  • [hash]: A hash based on the content of the entry point and the content of all its dependencies.
  • [name]: The file name (without extension) of the entry point, unless the object form of input was used to define a different name.

Forward slashes / can be used to place files in sub-directories. See also output.assetFileNames, output.chunkFileNames.

This pattern will also be used when using the preserveModules option. Here there is a different set of placeholders available, though:

  • [format]: The rendering format defined in the output options.
  • [name]: The file name (without extension) of the file.
  • [ext]: The extension of the file.
  • [extname]: The extension of the file, prefixed by . if it is not empty.

output.extend

Type: boolean
CLI: --extend/--no-extend
Default: false

Whether or not to extend the global variable defined by the name option in umd or iife formats. When true, the global variable will be defined as (global.name = global.name || {}). When false, the global defined by name will be overwritten like (global.name = {}).

output.hoistTransitiveImports

Type: boolean
CLI: --hoistTransitiveImports/--no-hoistTransitiveImports
Default: true

By default when creating multiple chunks, transitive imports of entry chunks will be added as empty imports to the entry chunks. See "Why do additional imports turn up in my entry chunks when code-splitting?" for details and background. Setting this option to false will disable this behaviour. This option is ignored when using the preserveModules option as here, imports will never be hoisted.

output.interop

Type: boolean
CLI: --interop/--no-interop
Default: true

Whether or not to add an 'interop block'. By default (interop: true), for safety's sake, Rollup will assign any external dependencies' default exports to a separate variable if it is necessary to distinguish between default and named exports. This generally only applies if your external dependencies were transpiled (for example with Babel) – if you are sure you do not need it, you can save a few bytes with interop: false.

output.intro/output.outro

Type: string | (() => string | Promise<string>)
CLI: --intro/--outro <text>

Similar to output.banner/output.footer, except that the code goes inside any format-specific wrapper.

export default {
  ...,
  output: {
    ...,
    intro: 'const ENVIRONMENT = "production";'
  }
};

output.minifyInternalExports

Type: boolean
CLI: --minifyInternalExports/--no-minifyInternalExports
Default: true for formats es and system or if output.compact is true, false otherwise

By default for formats es and system or if output.compact is true, Rollup will try to export internal variables as single letter variables to allow for better minification.

Example
Input:

// main.js
import './lib.js';

// lib.js
import('./dynamic.js');
export const value = 42;

// dynamic.js
import {value} from './lib.js';
console.log(value);

Output with output.minifyInternalExports: true:

// main.js
import './main-5532def0.js';

// main-5532def0.js
import('./dynamic-402de2f0.js');
const importantValue = 42;

export { importantValue as i };

// dynamic-402de2f0.js
import { i as importantValue } from './main-5532def0.js';

console.log(importantValue);

Output with output.minifyInternalExports: false:

// main.js
import './main-5532def0.js';

// main-5532def0.js
import('./dynamic-402de2f0.js');
const importantValue = 42;

export { importantValue };

// dynamic-402de2f0.js
import { importantValue } from './main-5532def0.js';

console.log(importantValue);

Even though it appears that setting this option to true makes the output larger, it actually makes it smaller if a minifier is used. In this case, export { importantValue as i } can become e.g. export{a as i} or even export{i}, while otherwise it would produce export{ a as importantValue } because a minifier usually will not change export signatures.

output.paths

Type: { [id: string]: string } | ((id: string) => string)

Maps external module IDs to paths. External ids are ids that cannot be resolved or ids explicitly provided by the external option. Paths supplied by output.paths will be used in the generated bundle instead of the module ID, allowing you to, for example, load dependencies from a CDN:

// app.js
import { selectAll } from 'd3';
selectAll('p').style('color', 'purple');
// ...

// rollup.config.js
export default {
  input: 'app.js',
  external: ['d3'],
  output: {
    file: 'bundle.js',
    format: 'amd',
    paths: {
      d3: 'https://d3js.org/d3.v4.min'
    }
  }
};

// bundle.js
define(['https://d3js.org/d3.v4.min'], function (d3) {

  d3.selectAll('p').style('color', 'purple');
  // ...

});

output.sourcemap

Type: boolean | 'inline' | 'hidden'
CLI: -m/--sourcemap/--no-sourcemap
Default: false

If true, a separate sourcemap file will be created. If "inline", the sourcemap will be appended to the resulting output file as a data URI. "hidden" works like true except that the corresponding sourcemap comments in the bundled files are suppressed.

output.sourcemapExcludeSources

Type: boolean
CLI: --sourcemapExcludeSources/--no-sourcemapExcludeSources
Default: false

If true, the actual code of the sources will not be added to the sourcemaps making them considerably smaller.

output.sourcemapFile

Type: string
CLI: --sourcemapFile <file-name-with-path>

The location of the generated bundle. If this is an absolute path, all the sources paths in the sourcemap will be relative to it. The map.file property is the basename of sourcemapFile, as the location of the sourcemap is assumed to be adjacent to the bundle.

sourcemapFile is not required if output is specified, in which case an output filename will be inferred by adding ".map" to the output filename for the bundle.

output.sourcemapPathTransform

Type: (sourcePath: string) => string

A transformation to apply to each path in a sourcemap. For instance the following will change all paths to be relative to the src directory.

import path from 'path';
export default ({
  input: 'src/main',
  output: [{
    file: 'bundle.js',
    sourcemapPathTransform: relativePath => {
      // will transform e.g. "src/main.js" -> "main.js"
      return path.relative('src', relativePath)
    },
    format: 'es',
    sourcemap: true
  }]
});

preserveEntrySignatures

Type: "strict" | "allow-extension" | false
CLI: --preserveEntrySignatures <strict|allow-extension>/--no-preserveEntrySignatures
Default: "strict"

Controls if Rollup tries to ensure that entry chunks have the same exports as the underlying entry module.

  • If set to "strict", Rollup will create exactly the same exports in the entry chunk as there are in the corresponding entry module. If this is not possible because additional internal exports need to be added to a chunk, Rollup will instead create a "facade" entry chunk that reexports just the necessary bindings from other chunks but contains no code otherwise. This is the recommended setting for libraries.
  • "allow-extension" will create all exports of the entry module in the entry chunk but may also add additional exports if necessary, avoiding a "facade" entry chunk. This setting makes sense for libraries where a strict signature is not required.
  • false will not add any exports of an entry module to the corresponding chunk and does not even include the corresponding code unless those exports are used elsewhere in the bundle. Internal exports may be added to entry chunks, though. This is the recommended setting for web apps where the entry chunks are to be placed in script tags as it may reduce both the number of chunks and possibly the bundle size.

Example
Input:

// main.js
import { shared } from './lib.js';
export const value = `value: ${shared}`;
import('./dynamic.js');

// lib.js
export const shared = 'shared';

// dynamic.js
import { shared } from './lib.js';
console.log(shared);

Output for preserveEntrySignatures: "strict":

// main.js
export { v as value } from './main-50a71bb6.js';

// main-50a71bb6.js
const shared = 'shared';

const value = `value: ${shared}`;
import('./dynamic-cd23645f.js');

export { shared as s, value as v };

// dynamic-cd23645f.js
import { s as shared } from './main-50a71bb6.js';

console.log(shared);

Output for preserveEntrySignatures: "allow-extension":

// main.js
const shared = 'shared';

const value = `value: ${shared}`;
import('./dynamic-298476ec.js');

export { shared as s, value };

// dynamic-298476ec.js
import { s as shared } from './main.js';

console.log(shared);

Output for preserveEntrySignatures: false:

// main.js
import('./dynamic-39821cef.js');

// dynamic-39821cef.js
const shared = 'shared';

console.log(shared);

At the moment, the only way to override this setting for individual entry chunks is to use the plugin API and emit those chunks via this.emitFile instead of using the input option.

preserveModules

Type: boolean
CLI: --preserveModules/--no-preserveModules
Default: false

Instead of creating as few chunks as possible, this mode will create separate chunks for all modules using the original module names as file names. Requires the output.dir option. Tree-shaking will still be applied, suppressing files that are not used by the provided entry points or do not have side-effects when executed. This mode can be used to transform a file structure to a different module format.

Note that when transforming to cjs or amd format, each file will by default be treated as an entry point with output.exports set to auto. This means that e.g. for cjs, a file that only contains a default export will be rendered as

// input main.js
export default 42;

// output main.js
'use strict';

var main = 42;

module.exports = main;

assigning the value directly to module.exports. If someone imports this file, they will get access to the default export via

const main = require('./main.js');
console.log(main); // 42

As with regular entry points, files that mix default and named exports will produce warnings. You can avoid the warnings by forcing all files to use named export mode via output.exports: "named". In that case, the default export needs to be accessed via the .default property of the export:

// input main.js
export default 42;

// output main.js
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var main = 42;

exports.default = main;

// consuming file
const main = require('./main.js');
console.log(main.default); // 42

strictDeprecations

Type: boolean
CLI: --strictDeprecations/--no-strictDeprecations
Default: false

When this flag is enabled, Rollup will throw an error instead of showing a warning when a deprecated feature is used. Furthermore, features that are marked to receive a deprecation warning with the next major version will also throw an error when used.

This flag is intended to be used by e.g. plugin authors to be able to adjust their plugins for upcoming major releases as early as possible.

Danger zone

You probably don't need to use these options unless you know what you are doing!

acorn

Type: AcornOptions

Any options that should be passed through to Acorn's parse function, such as allowReserved: true. Cf. the Acorn documentation for more available options.

acornInjectPlugins

Type: AcornPluginFunction | AcornPluginFunction[]

A single plugin or an array of plugins to be injected into Acorn. For instance to use JSX syntax, you can specify

import jsx from 'acorn-jsx';

export default {
    // … other options …
    acornInjectPlugins: [
        jsx()
    ]
};

in your rollup configuration. Note that this is different from using Babel in that the generated output will still contain JSX while Babel will replace it with valid JavaScript.

context

Type: string
CLI: --context <contextVariable>
Default: undefined

By default, the context of a module – i.e., the value of this at the top level – is undefined. In rare cases you might need to change this to something else, like 'window'.

moduleContext

Type: ((id: string) => string) | { [id: string]: string }

Same as context, but per-module – can either be an object of id: context pairs, or an id => context function.

output.amd

Type: { id?: string, define?: string}

An object that can contain the following properties:

output.amd.id
Type: string
CLI: --amd.id <amdId>

An ID to use for AMD/UMD bundles:

// rollup.config.js
export default {
  ...,
  format: 'amd',
  amd: {
    id: 'my-bundle'
  }
};

// -> define('my-bundle', ['dependency'], ...

output.amd.define
Type: string
CLI: --amd.define <defineFunctionName>

A function name to use instead of define:

// rollup.config.js
export default {
  ...,
  format: 'amd',
  amd: {
    define: 'def'
  }
};

// -> def(['dependency'],...

output.esModule

Type: boolean
CLI: --esModule/--no-esModule
Default: true

Whether or not to add a __esModule: true property when generating exports for non-ES formats.

output.exports

Type: string
CLI: --exports <exportMode>
Default: 'auto'

What export mode to use. Defaults to auto, which guesses your intentions based on what the input module exports:

  • default – suitable if you are only exporting one thing using export default ...
  • named – suitable if you are exporting more than one thing
  • none – suitable if you are not exporting anything (e.g. you are building an app, not a library)

The difference between default and named affects how other people can consume your bundle. If you use default, a CommonJS user could do this, for example:

const yourLib = require( 'your-lib' );

With named, a user would do this instead:

const yourMethod = require( 'your-lib' ).yourMethod;

The wrinkle is that if you use named exports but also have a default export, a user would have to do something like this to use the default export:

const yourMethod = require( 'your-lib' ).yourMethod;
const yourLib = require( 'your-lib' ).default;

output.externalLiveBindings

Type: boolean
CLI: --externalLiveBindings/--no-externalLiveBindings
Default: true

When set to false, Rollup will not generate code to support live bindings for external imports but instead assume that exports do not change over time. This will enable Rollup to generate more optimized code. Note that this can cause issues when there are circular dependencies involving an external dependency.

This will avoid most cases where Rollup generates getters in the code and can therefore be used to make code IE8 compatible in many cases.

Example:

// input
export {x} from 'external';

// CJS output with externalLiveBindings: true
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var external = require('external');

Object.defineProperty(exports, 'x', {
  enumerable: true,
  get: function () {
    return external.x;
  }
});

// CJS output with externalLiveBindings: false
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var external = require('external');

exports.x = external.x;

output.freeze

Type: boolean
CLI: --freeze/--no-freeze
Default: true

Whether to Object.freeze() namespace import objects (i.e. import * as namespaceImportObject from...) that are accessed dynamically.

output.indent

Type: boolean | string
CLI: --indent/--no-indent
Default: true

The indent string to use, for formats that require code to be indented (amd, iife, umd, system). Can also be false (no indent), or true (the default – auto-indent)

// rollup.config.js
export default {
  ...,
  output: {
    ...,
    indent: false
  }
};

output.namespaceToStringTag

Type: boolean
CLI: --namespaceToStringTag/--no-namespaceToStringTag
Default: false

Whether to add spec compliant .toString() tags to namespace objects. If this option is set,

import * as namespace from './file.js';
console.log(String(namespace));

will always log [object Module];

output.noConflict

Type: boolean
CLI: --noConflict/--no-noConflict
Default: false

This will generate an additional noConflict export to UMD bundles. When called in an IIFE scenario, this method will return the bundle exports while restoring the corresponding global variable to its previous value.

output.preferConst

Type: boolean
CLI: --preferConst/--no-preferConst
Default: false

Generate const declarations for exports rather than var declarations.

output.strict

Type: boolean
CLI: --strict/--no-strict
Default: true

Whether to include the 'use strict' pragma at the top of generated non-ES bundles. Strictly speaking, ES modules are always in strict mode, so you shouldn't disable this without good reason.

Type: boolean
CLI: --preserveSymlinks
Default: false

When set to false, symbolic links are followed when resolving a file. When set to true, instead of being followed, symbolic links are treated as if the file is where the link is. To illustrate, consider the following situation:

// /main.js
import {x} from './linked.js';
console.log(x);

// /linked.js
// this is a symbolic link to /nested/file.js

// /nested/file.js
export {x} from './dep.js';

// /dep.js
export const x = 'next to linked';

// /nested/dep.js
export const x = 'next to original';

If preserveSymlinks is false, then the bundle created from /main.js will log "next to original" as it will use the location of the symbolically linked file to resolve its dependencies. If preserveSymlinks is true, however, it will log "next to linked" as the symbolic link will not be resolved.

shimMissingExports

Type: boolean
CLI: --shimMissingExports/--no-shimMissingExports
Default: false

If this option is provided, bundling will not fail if bindings are imported from a file that does not define these bindings. Instead, new variables will be created for these bindings with the value undefined.

treeshake

Type: boolean | { annotations?: boolean, moduleSideEffects?: ModuleSideEffectsOption, propertyReadSideEffects?: boolean, tryCatchDeoptimization?: boolean, unknownGlobalSideEffects?: boolean }
CLI: --treeshake/--no-treeshake
Default: true

Whether or not to apply tree-shaking and to fine-tune the tree-shaking process. Setting this option to false will produce bigger bundles but may improve build performance. If you discover a bug caused by the tree-shaking algorithm, please file an issue! Setting this option to an object implies tree-shaking is enabled and grants the following additional options:

treeshake.annotations
Type: boolean
CLI: --treeshake.annotations/--no-treeshake.annotations
Default: true

If false, ignore hints from pure annotations, i.e. comments containing @__PURE__ or #__PURE__, when determining side-effects of function calls and constructor invocations. These annotations need to immediately precede the call invocation to take effect. The following code will be completely removed unless this option is set to false, in which case it will remain unchanged.

/*@__PURE__*/console.log('side-effect');

class Impure {
  constructor() {
    console.log('side-effect')
  }
}

/*@__PURE__*/new Impure();

treeshake.moduleSideEffects
Type: boolean | "no-external" | string[] | (id: string, external: boolean) => boolean
CLI: --treeshake.moduleSideEffects/--no-treeshake.moduleSideEffects
Default: true

If false, assume modules and external dependencies from which nothing is imported do not have other side-effects like mutating global variables or logging without checking. For external dependencies, this will suppress empty imports:

// input file
import {unused} from 'external-a';
import 'external-b';
console.log(42);
// output with treeshake.moduleSideEffects === true
import 'external-a';
import 'external-b';
console.log(42);
// output with treeshake.moduleSideEffects === false
console.log(42);

For non-external modules, false will not include any statements from a module unless at least one import from this module is included:

// input file a.js
import {unused} from './b.js';
console.log(42);

// input file b.js
console.log('side-effect');
// output with treeshake.moduleSideEffects === true
console.log('side-effect');

console.log(42);
// output with treeshake.moduleSideEffects === false
console.log(42);

You can also supply a list of modules with side-effects or a function to determine it for each module individually. The value "no-external" will only remove external imports if possible and is equivalent to the function (id, external) => !external;

treeshake.propertyReadSideEffects Type: boolean
CLI: --treeshake.propertyReadSideEffects/--no-treeshake.propertyReadSideEffects
Default: true

If false, assume reading a property of an object never has side-effects. Depending on your code, disabling this option can significantly reduce bundle size but can potentially break functionality if you rely on getters or errors from illegal property access.

// Will be removed if treeshake.propertyReadSideEffects === false
const foo = {
  get bar() {
    console.log('effect');
    return 'bar';
  }
}
const result = foo.bar;
const illegalAccess = foo.quux.tooDeep;

treeshake.tryCatchDeoptimization Type: boolean
CLI: --treeshake.tryCatchDeoptimization/--no-treeshake.tryCatchDeoptimization
Default: true

By default, Rollup assumes that many builtin globals of the runtime behave according to the latest specs when tree-shaking and do not throw unexpected errors. In order to support e.g. feature detection workflows that rely on those errors being thrown, Rollup will by default deactivate tree-shaking inside try-statements. If a function parameter is called from within a try-statement, this parameter will be deoptimized as well. Set treeshake.tryCatchDeoptimization to false if you do not need this feature and want to have tree-shaking inside try-statements.

function otherFn() {
  // even though this function is called from a try-statement, the next line
  // will be removed as side-effect-free
  Object.create(null);
}

function test(callback) {
  try {
  	// calls to otherwise side-effect-free global functions are retained
  	// inside try-statements for tryCatchDeoptimization: true
    Object.create(null);

  	// calls to other function are retained as well but the body of this
  	// function may again be subject to tree-shaking
    otherFn();

    // if a parameter is called, then all arguments passed to that function
    // parameter will be deoptimized
    callback();
  } catch {}
}

test(() => {
  // will be ratained
  Object.create(null)
});

// call will be retained but again, otherFn is not deoptimized
test(otherFn);

treeshake.unknownGlobalSideEffects Type: boolean
CLI: --treeshake.unknownGlobalSideEffects/--no-treeshake.unknownGlobalSideEffects
Default: true

Since accessing a non-existing global variable will throw an error, Rollup does by default retain any accesses to non-builtin global variables. Set this option to false to avoid this check. This is probably safe for most code-bases.

// input
const jQuery = $;
const requestTimeout = setTimeout;
const element = angular.element;

// output with unknownGlobalSideEffects == true
const jQuery = $;
const element = angular.element;

// output with unknownGlobalSideEffects == false
const element = angular.element;

In the example, the last line is always retained as accessing the element property could also throw an error if angular is e.g. null. To avoid this check, set treeshake.propertyReadSideEffects to false as well.

Experimental options

These options reflect new features that have not yet been fully finalized. Availability, behaviour and usage may therefore be subject to change between minor versions.

experimentalCacheExpiry

Type: number
CLI: --experimentalCacheExpiry <numberOfRuns>
Default: 10

Determines after how many runs cached assets that are no longer used by plugins should be removed.

perf

Type: boolean
CLI: --perf/--no-perf
Default: false

Whether to collect performance timings. When used from the command line or a configuration file, detailed measurements about the current bundling process will be displayed. When used from the JavaScript API, the returned bundle object will contain an additional getTimings() function that can be called at any time to retrieve all accumulated measurements.

getTimings() returns an object of the following form:

{
  "# BUILD": [ 698.020877, 33979632, 45328080 ],
  "## parse modules": [ 537.509342, 16295024, 27660296 ],
  "load modules": [ 33.253778999999994, 2277104, 38204152 ],
  ...
}

For each key, the first number represents the elapsed time while the second represents the change in memory consumption and the third represents the total memory consumption after this step. The order of these steps is the order used by Object.keys. Top level keys start with # and contain the timings of nested steps, i.e. in the example above, the 698ms of the # BUILD step include the 538ms of the ## parse modules step.

Watch options

These options only take effect when running Rollup with the --watch flag, or using rollup.watch.

watch.buildDelay

Type: number
Default: 0

Configures how long Rollup will wait for further changes until it triggers a rebuild in milliseconds. By default, Rollup does not wait but there is a small debounce timeout configured in the chokidar instance. Setting this to a value greater than 0 will mean that Rollup will only triger a rebuild if there was no change for the configured number of milliseconds. If several configurations are watched, Rollup will use the largest configured build delay.

watch.chokidar

Type: ChokidarOptions

An optional object of watch options that will be passed to the bundled chokidar instance. See the chokidar documentation to find out what options are available.

watch.clearScreen

Type: boolean
Default: true

Whether to clear the screen when a rebuild is triggered.

watch.skipWrite

Type: boolean
Default: false

Whether to skip the bundle.write() step when a rebuild is triggered.

watch.exclude

Type: string

Prevent files from being watched:

// rollup.config.js
export default {
  ...,
  watch: {
    exclude: 'node_modules/**'
  }
};

watch.include

Type: string

Limit the file-watching to certain files:

// rollup.config.js
export default {
  ...,
  watch: {
    include: 'src/**'
  }
};

Deprecated options

☢️ These options have been deprecated and may be removed in a future Rollup version.

treeshake.pureExternalModules

Use treeshake.moduleSideEffects: 'no-external' instead.
Type: boolean | string[] | (id: string) => boolean | null
CLI: --treeshake.pureExternalModules/--no-treeshake.pureExternalModules
Default: false

If true, assume external dependencies from which nothing is imported do not have other side-effects like mutating global variables or logging.

// input file
import {unused} from 'external-a';
import 'external-b';
console.log(42);
// output with treeshake.pureExternalModules === false
import 'external-a';
import 'external-b';
console.log(42);
// output with treeshake.pureExternalModules === true
console.log(42);

You can also supply a list of external ids to be considered pure or a function that is called whenever an external import could be removed.

output.dynamicImportFunction

Use the renderDynamicImport plugin hook instead.
Type: string
CLI: --dynamicImportFunction <name>
Default: import

This will rename the dynamic import function to the chosen name when outputting ES bundles. This is useful for generating code that uses a dynamic import polyfill such as this one.