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 usually transpile and bundle this file and its relative dependencies to CommonJS before requiring it. This 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.

You can also use other languages for your configuration files like TypeScript. To do that, install a corresponding Rollup plugin like @rollup/plugin-typescript and use the --configPlugin option:

rollup --config rollup.config.ts --configPlugin typescript

Also have a look at Config Intellisense for more ways to use TypeScript typings in your config files.

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

// rollup.config.js

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

  // advanced input options
  cache,
  onwarn,
  preserveEntrySignatures,
  strictDeprecations,

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

  // experimental
  experimentalCacheExpiry,
  perf,

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

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

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

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

You can export an array from your config file to build bundles from several 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: {...}
  }
}

Config Intellisense

Since Rollup ships with TypeScript typings, you can leverage your IDE's Intellisense with JSDoc type hints:

// rollup.config.js
/**
 * @type {import('rollup').RollupOptions}
 */
const config = {
  // ...
};

export default config;

Alternatively you can use the defineConfig helper, which should provide Intellisense without the need for JSDoc annotations:

// rollup.config.js
import { defineConfig } from 'rollup';

export default defineConfig({
  // ...
});

Besides RollupOptions and the defineConfig helper that encapsulates this type, the following types can prove useful as well:

  • OutputOptions: The output part of a config file
  • Plugin: A plugin object that provides a name and some hooks. All hooks are fully typed to aid in plugin development.
  • PluginImpl: A function that maps an options object to a plugin object. Most public Rollup plugins follow this pattern.

You can also directly write your config in TypeScript via the --configPlugin option.

Differences to the JavaScript API

While config files provide an easy way to configure Rollup, they also limit how Rollup can be invoked and configured. Especially if you are bundling Rollup into 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.autoId                Generate the AMD ID based off the chunk name
--amd.basePath <prefix>     Path to prepend to auto generated AMD ID
--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
--failAfterWarnings         Exit with an error if the build produced warnings
--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
--preserveModulesRoot       Put preserved modules under this path at root level
--preserveSymlinks          Do not follow symlinks when resolving files
--no-sanitizeFileName       Do not replace invalid characters in file names
--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
--stdin=ext                 Specify file extension used for stdin input
--no-stdin                  Do not read "-" from stdin
--no-strict                 Don't emit `"use strict";` in the generated modules
--strictDeprecations        Throw errors for deprecated features
--systemNullSetters         Replace empty SystemJS setters with `null`
--no-treeshake              Disable tree-shaking optimisations
--no-treeshake.annotations  Ignore pure call annotations
--no-treeshake.moduleSideEffects Assume modules have no side-effects
--no-treeshake.propertyReadSideEffects Ignore property access side-effects
--no-treeshake.tryCatchDeoptimization Do not turn off try-catch-tree-shaking
--no-treeshake.unknownGlobalSideEffects Assume unknown globals do not throw
--waitForBundleInput        Wait for bundle input files
--watch.buildDelay <number> Throttle watch rebuilds
--no-watch.clearScreen      Do not clear the screen when rebuilding
--watch.skipWrite           Do not write files to disk when watching
--watch.exclude <files>     Exclude files from being watched
--watch.include <files>     Limit watching to specified files
--validate                  Validate output

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 function returning a plugin 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, plugin 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}}'

--configPlugin <plugin>

Allows specifying Rollup plugins to transpile or otherwise control the parsing of your configuration file. The main benefit is that it allows you to use non-JavaScript configuration files. For instance the following will allow you to write your configuration in TypeScript, provided you have @rollup/plugin-typescript installed:

rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript

It supports the same syntax as the --plugin option i.e., you can specify the option multiple times, you can omit the @rollup/plugin- prefix and just write typescript and you can specify plugin options via ={...}.

-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 other processes. Plugins should instead check this.meta.watchMode, which is independent of the command line interface.

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

--failAfterWarnings

Exit the build with an error if any warnings occurred, once the build is complete.

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

--waitForBundleInput

This will not throw an error if one of the entry point files is not available. Instead, it will wait until all files are present before starting the build. This is useful, especially in watch mode, when Rollup is consuming the output of another process.

--stdin=ext

Specify a virtual file extension when reading content from stdin. By default, Rollup will use the virtual file name - without an extension for content read from stdin. Some plugins, however, rely on file extensions to determine if they should process a file. See also Reading a file from stdin.

--no-stdin

Do not read files from stdin. Setting this flag will prevent piping content to Rollup and make sure Rollup interprets - and -.[ext] as a regular file names instead of interpreting these 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 using a config file, 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.

As some plugins rely on file extensions to process files, you can specify a file extension for stdin via --stdin=ext where ext is the desired extension. In that case, the virtual file name will be -.ext:

echo '{"foo": 42, "bar": "ok"}' | rollup --stdin=json -p json

The JavaScript API will always treat - and -.ext as regular file names.

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.

Once you're finished with the bundle object, you should call bundle.close(), which will let plugins clean up their external processes or services via the closeBundle hook.

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
      //   implicitlyLoadedBefore: string[]; // entries that should only be loaded after this chunk
      //   imports: string[],             // external modules imported statically by the chunk
      //   importedBindings: {[imported: string]: string[]} // imported bindings per dependency
      //   isDynamicEntry: boolean,       // is this chunk a dynamic entry point
      //   isEntry: boolean,              // is this chunk a static entry point
      //   isImplicitEntry: boolean,      // should this chunk only be loaded after other chunks
      //   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
      //       code: string | null;       // remaining code in this module
      //     };
      //   },
      //   name: string                   // the name of this chunk as used in naming patterns
      //   referencedFiles: string[]      // files referenced via import.meta.ROLLUP_FILE_URL_<id>
      //   type: 'chunk',                 // signifies that this is a chunk
      // }
      console.log('Chunk', chunkOrAsset.modules);
    }
  }

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

  // closes the bundle
  await bundle.close();
}

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, // conditionally required
  plugins,

  // advanced input options
  cache,
  onwarn,
  preserveEntrySignatures,
  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,
  inlineDynamicImports,
  interop,
  intro,
  manualChunks,
  minifyInternalExports,
  outro,
  paths,
  preserveModules,
  preserveModulesRoot,
  sourcemap,
  sourcemapExcludeSources,
  sourcemapFile,
  sourcemapPathTransform,
  validate,

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

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. Note that when using watch mode via the JavaScript API, it is your responsibility to call event.result.close() in response to the BUNDLE_END event to allow plugins to clean up resources in the closeBundle hook, see below.

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
  //                  * event.input will be the input options object if present
  //                  * event.output contains an array of the "file" or
  //                    "dir" option values of the generated outputs
  //   BUNDLE_END   — finished building a bundle
  //                  * event.input will be the input options object if present
  //                  * event.output contains an array of the "file" or
  //                    "dir" option values of the generated outputs
  //                  * event.duration is the build duration in milliseconds
  //                  * event.result contains the bundle object that can be
  //                    used to generate additional outputs by calling
  //                    bundle.generate or bundle.write. This is especially
  //                    important when the watch.skipWrite option is used.
  //                  You should call "event.result.close()" once you are done
  //                  generating outputs, or if you do not generate outputs.
  //                  This will allow plugins to clean up resources via the
  //                  "closeBundle" hook.
  //   END          — finished building all bundles
  //   ERROR        — encountered an error while bundling
  //                  * event.error contains the error that was thrown
  //                  * event.result is null for build errors and contains the
  //                    bundle object for output generation errors. As with
  //                    "BUNDLE_END", you should call "event.result.close()" if
  //                    present once you are done.
});

// This will make sure that bundles are properly closed after each run
watcher.on('event', ({ result }) => {
  if (result) {
  	result.close();
  }
});

// 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 array of "inputOptions" objects with an additional "output"
    // property that contains an array of "outputOptions".
    // The following will generate all outputs for all inputs, and write them to disk the same
    // way the CLI does it:
    for (const optionsObj of options) {
      const bundle = await rollup.rollup(optionsObj);
      await Promise.all(optionsObj.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

For code splitting, there are cases where Rollup splits code into chunks automatically, like dynamic loading or multiple entry points, and there is a way to explicitly tell Rollup which modules to split into separate chunks via the output.manualChunks option.

To use the code splitting feature to achieve the lazy dynamic loading (where some imported module(s) is only loaded after executing a function), 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 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. If there is a build error, closeBundle will be called after that.

parallel
sequential
first
async
sync

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. Also, when watcher closes, the closeWatcher hook will be triggered.

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: moduleParsed, 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 takes the transformations by all options hooks into account and also contains the right default values for unset options.

closeWatcher

Type: () => 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 no new watchChange events will be triggered ever.

Notifies a plugin when watcher process closes and all open resources should be closed too. This hook cannot be used by output plugins.

load

Type: (id: string) => string | null | {code: string, map?: string | SourceMap, ast? : ESTree.Program, moduleSideEffects?: boolean | "no-treeshake" | null, syntheticNamedExports?: boolean | string | null, meta?: {[plugin: string]: any} | 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 even if the module would have side-effects. 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 "no-treeshake" is returned, treeshaking will be turned off for this module and it will also be included in one of the generated chunks even if it is empty. 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.

See synthetic named exports for the effect of the syntheticNamedExports option. If null is returned or the flag is omitted, then syntheticNamedExports will be determined by the first resolveId hook that resolved this module or eventually default to false. The transform hook can override this.

See custom module meta-data for how to use the meta option. If a meta object is returned by this hook, it will be merged shallowly with any meta object returned by the resolveId hook. If no hook returns a meta object it will default to an empty object. The transform hook can further add or replace properties of this object.

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

moduleParsed

Type: (moduleInfo: ModuleInfo) => void
Kind: async, parallel
Previous Hook: transform where the currently handled file was transformed.
NextHook: resolveId and resolveDynamicImport to resolve all discovered static and dynamic imports in parallel if present, otherwise buildEnd.

This hook is called each time a module has been fully parsed by Rollup. See this.getModuleInfo for what information is passed to this hook.

In contrast to the transform hook, this hook is never cached and can be used to get information about both cached and other modules, including the final shape of the meta property, the code and the ast.

Note that information about imported modules is not yet available in this hook, and information about importing modules may be incomplete as additional importers could be discovered later. If you need this information, use the buildEnd hook.

options

Type: (options: InputOptions) => InputOptions | null
Kind: async, 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: moduleParsed for the importing file.
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, options: {custom?: {[plugin: string]: any}) => string | false | null | {id: string, external?: boolean | "relative" | "absolute", moduleSideEffects?: boolean | "no-treeshake" | null, syntheticNamedExports?: boolean | string | null, meta?: {[plugin: string]: any} | null}
Kind: async, first
Previous Hook: buildStart if we are resolving an entry point, moduleParsed 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. Here source is the importee exactly as it is written in the import statement, i.e. for

import { foo } from '../bar.js';

the source will be "../bar.js"".

The importer is the fully resolved id of the importing module. When resolving entry points, importer will be undefined. You can use this for instance as a mechanism to define custom proxy modules for entry points.

The following plugin will only expose the default export from entry points while still keeping named exports available for internal usage:

async resolveId(source,importer) {
  if (!importer) {
    // We need to skip this plugin to avoid an infinite loop
    const resolution = await this.resolve(source, undefined, { skipSelf: true });
    // If it cannot be resolved, return `null` so that Rollup displays an error
    if (!resolution) return null;
    return `${resolution.id}?entry-proxy`;
  }
  return null;
},
load(id) {
  if (id.endsWith('?entry-proxy')) {
    const importee = id.slice(0, -'?entry-proxy'.length);
    // Note that this will throw if there is no default export
    return `export {default} from '${importee}';`;
  }
  return null;
}

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;
}

If external is true, then absolute ids will be converted to relative ids based on the user's choice for the makeAbsoluteExternalsRelative option. This choice can be overridden by passing either external: "relative" to always convert an absolute id to a relative id or external: "absolute" to keep it as an absolute id. When returning an object, relative external ids, i.e. ids starting with ./ or ../, will not be internally converted to an absolute id and converted back to a relative id in the output, but are instead included in the output unchanged. If you want relative ids to be renormalised and deduplicated instead, return an absolute file system location as id and choose external: "relative".

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 even if the module would have side-effects. 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 "no-treeshake" is returned, treeshaking will be turned off for this module and it will also be included in one of the generated chunks even if it is empty. 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.

See synthetic named exports for the effect of the syntheticNamedExports option. If null is returned or the flag is omitted, then syntheticNamedExports will default to false. The load and transform hooks can override this.

See custom module meta-data for how to use the meta option. If null is returned or the option is omitted, then meta will default to an empty object. The load and transform hooks can add or replace properties of this object.

When triggering this hook from a plugin via this.resolve(source, importer, options), it is possible to pass a custom options object to this hook. While this object will be passed unmodified, plugins should follow the convention of adding a custom property with an object where the keys correspond to the names of the plugins that the options are intended for. For details see custom resolver options.

transform

Type: (code: string, id: string) => string | null | {code?: string, map?: string | SourceMap, ast? : ESTree.Program, moduleSideEffects?: boolean | "no-treeshake" | null, syntheticNamedExports?: boolean | string | null, meta?: {[plugin: string]: any} | null}
Kind: async, sequential
Previous Hook: load where the currently handled file was loaded.
NextHook: moduleParsed once the file has been processed and parsed.

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.

You can also use the object form of the return value to configure additional properties of the module. Note that it's possible to return only properties and no code transformations.

If false is returned for moduleSideEffects and no other module imports anything from this module, then this module will not be included even if the module would have side-effects.

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 "no-treeshake" is returned, treeshaking will be turned off for this module and it will also be included in one of the generated chunks even if it is empty.

If null is returned or the flag is omitted, then moduleSideEffects will be determined by the load hook that loaded this module, the first resolveId hook that resolved this module, the treeshake.moduleSideEffects option, or eventually default to true.

See synthetic named exports for the effect of the syntheticNamedExports option. If null is returned or the flag is omitted, then syntheticNamedExports will be determined by the load hook that loaded this module, the first resolveId hook that resolved this module, the treeshake.moduleSideEffects option, or eventually default to false.

See custom module meta-data for how to use the meta option. If null is returned or the option is omitted, then meta will be determined by the load hook that loaded this module, the first resolveId hook that resolved this module or eventually default to an empty object.

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

watchChange

Type: watchChange: (id: string, change: {event: 'create' | 'update' | 'delete'}) => 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. Second argument contains additional details of change event.

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.

parallel
sequential
first
async
sync

Additionally, closeBundle can be called as the very last hook, but it is the responsibility of the User to manually call bundle.close() to trigger this. The CLI will always make sure this is the case.

augmentChunkHash

Type: (chunkInfo: ChunkInfo) => 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 chunkInfo is a reduced version of the one in generateBundle without properties that rely on file names.

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.

closeBundle

Type: closeBundle: () => Promise<void> | void
Kind: async, parallel
Previous Hook: buildEnd if there was a build error, otherwise when bundle.close() is called, in which case this would be the last hook to be triggered.

Can be used to clean up any external service that may be running. Rollup's CLI will make sure this hook is called after each run, but it is the responsibility of users of the JavaScript API to manually call bundle.close() once they are done generating bundles. For that reason, any plugin relying on this feature should carefully mention this in its documentation.

If a plugin wants to retain resources across builds in watch mode, they can check for this.meta.watchMode in this hook and perform the necessary cleanup for watch mode in closeWatcher.

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,
  name?: string,
  source: string | Uint8Array,
  type: 'asset',
}

// ChunkInfo
{
  code: string,
  dynamicImports: string[],
  exports: string[],
  facadeModuleId: string | null,
  fileName: string,
  implicitlyLoadedBefore: string[],
  imports: string[],
  importedBindings: {[imported: string]: string[]},
  isDynamicEntry: boolean,
  isEntry: boolean,
  isImplicitEntry: boolean,
  map: SourceMap | null,
  modules: {
    [id: string]: {
      renderedExports: string[],
      removedExports: string[],
      renderedLength: number,
      originalLength: number,
      code: string | null
    },
  },
  name: string,
  referencedFiles: 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: ')'
      };
    }
  }
};

Note that when this hook rewrites dynamic imports in non-ES formats, no interop code to make sure that e.g. the default export is available as .default is generated. It is the responsibility of the plugin to make sure the rewritten dynamic import returns a Promise that resolves to a proper namespace object.

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 takes the transformations by all outputOptions hooks into account and also contains the right default values for unset options. 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,
  name?: string,
  fileName?: string,
  implicitlyLoadedAfterOneOf?: string[],
  importer?: string,
  preserveSignature?: 'strict' | 'allow-extension' | 'exports-only' | false,
}

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

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.

By default, Rollup assumes that emitted chunks are executed independent of other entry points, possibly even before any other code is executed. This means that if an emitted chunk shares a dependency with an existing entry point, Rollup will create an additional chunk for dependencies that are shared between those entry points. Providing a non-empty array of module ids for implicitlyLoadedAfterOneOf will change that behaviour by giving Rollup additional information to prevent this in some cases. Those ids will be resolved the same way as the id property, respecting the importer property if it is provided. Rollup will now assume that the emitted chunk is only executed if at least one of the entry points that lead to one of the ids in implicitlyLoadedAfterOneOf being loaded has already been executed, creating the same chunks as if the newly emitted chunk was only reachable via dynamic import from the modules in implicitlyLoadedAfterOneOf. Here is an example that uses this to create a simple HTML file with several scripts, creating optimized chunks to respect their execution order:

// rollup.config.js
function generateHtml() {
  let ref1, ref2, ref3;
  return {
    buildStart() {
      ref1 = this.emitFile({
        type: 'chunk',
        id: 'src/entry1'
      });
      ref2 = this.emitFile({
        type: 'chunk',
        id: 'src/entry2',
        implicitlyLoadedAfterOneOf: ['src/entry1']
      });
      ref3 = this.emitFile({
        type: 'chunk',
        id: 'src/entry3',
        implicitlyLoadedAfterOneOf: ['src/entry2']
      });
    },
    generateBundle() {
      this.emitFile({
        type: 'asset',
        fileName: 'index.html',
        source: `
        <!DOCTYPE html>
        <html>
        <head>
          <meta charset="UTF-8">
          <title>Title</title>
         </head>
        <body>
          <script src="${this.getFileName(ref1)}" type="module"></script>
          <script src="${this.getFileName(ref2)}" type="module"></script>
          <script src="${this.getFileName(ref3)}" type="module"></script>
        </body>
        </html>`
      });
    }
  };
}

export default {
  input: [],
  preserveEntrySignatures: false,
  plugins: [generateHtml()],
  output: {
    format: 'es',
    dir: 'dist'
  }
};

If there are no dynamic imports, this will create exactly three chunks where the first chunk contains all dependencies of src/entry1, the second chunk contains only the dependencies of src/entry2 that are not contained in the first chunk, importing those from the first chunk, and again the same for the third chunk.

Note that even though any module id can be used in implicitlyLoadedAfterOneOf, Rollup will throw an error if such an id cannot be uniquely associated with a chunk, e.g. because the id cannot be reached implicitly or explicitly from the existing static entry points, or because the file is completely tree-shaken. Using only entry points, either defined by the user or of previously emitted chunks, will always work, though.

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(referenceId, 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 | null)

Returns additional information about the module in question in the form

{
  id: string, // the id of the module, for convenience
  code: string | null, // the source code of the module, `null` if external or not yet available
  ast: ESTree.Program, // the parsed abstract syntax tree if available
  isEntry: boolean, // is this a user- or plugin-defined entry point
  isExternal: boolean, // for external modules that are referenced but 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()
  implicitlyLoadedAfterOneOf: string[], // implicit relationships, declared via this.emitChunk
  implicitlyLoadedBefore: string[], // implicit relationships, declared via this.emitChunk
  hasModuleSideEffects: boolean | "no-treeshake" // are imports of this module included if nothing is imported from it
  meta: {[plugin: string]: any} // custom module meta-data
  syntheticNamedExports: boolean | string // final value of synthetic named exports
}

During the build, this object represents currently available information about the module. Before the buildEnd hook, this information may be incomplete as e.g. the importedIds are not yet resolved or additional importers are discovered.

Returns null if the module id cannot be found.

this.getWatchFiles() => string[]

Get ids of the files which has been watched previously. Include both files added by plugins with this.addWatchFile and files added implicitly by rollup during the build.

this.meta: {rollupVersion: string, watchMode: boolean}

An object containing potentially useful Rollup metadata:

  • rollupVersion: The currently running version of Rollup as define in package.json.
  • watchMode: true if Rollup was started via rollup.watch(...) or from the command line with --watch, false otherwise.

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, custom?: {[plugin: string]: any}}) => Promise<{id: string, external: boolean | "absolute", moduleSideEffects: boolean | 'no-treeshake', syntheticNamedExports: boolean | string, meta: {[plugin: string]: any}} | 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 an absolute external id is returned that should remain absolute in the output either via the makeAbsoluteExternalsRelative option or by explicit plugin choice in the resolveId hook, external will be "absolute" instead of true.

If you pass skipSelf: true, then the resolveId hook of the plugin from which this.resolve is called will be skipped when resolving. When other plugins themselves also call this.resolve in their resolveId hooks with the exact same source and importer while handling the original this.resolve call, then the resolveId hook of the original plugin will be skipped for those calls as well. The rationale here is that the plugin already stated that it "does not know" how to resolve this particular combination of source and importer at this point in time. If you do not want this behaviour, do not use skipSelf but implement your own infinite loop prevention mechanism if necessary.

You can also pass an object of plugin-specific options via the custom option, see custom resolver options for details.

this.setAssetSource(referenceId: 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 a referenceId that can be used to reference the emitted file. You can defer setting the source if you provide it later via this.setAssetSource(referenceId, 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_referenceId.

    The generated code that replaces import.meta.ROLLUP_ASSET_URL_referenceId can be customized via the resolveFileUrl plugin hook. Once the asset has been finalized during generate, you can also use this.getFileName(referenceId) 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 referenceId 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_referenceId.

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

  • this.getAssetFileName(referenceId: 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(referenceId: 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 github.com/rollup/awesome!

Synthetic named exports

It is possible to designate a fallback export for missing exports by setting the syntheticNamedExports option for a module in the resolveId, load or transform hook. If a string value is used for syntheticNamedExports, this module will fallback the resolution of any missing named exports to properties of the named export of the given name:

dep.js: ({syntheticNamedExports: '__synthetic'})

export const foo = 'explicit';
export const __synthetic = {
  foo: 'foo',
  bar: 'bar'
}

main.js:

import { foo, bar, baz, __synthetic } from './dep.js';

// logs "explicit" as non-synthetic exports take precedence
console.log(foo);

// logs "bar", picking the property from __synthetic
console.log(bar);

// logs "undefined"
console.log(baz);

// logs "{foo:'foo',bar:'bar'}"
console.log(__synthetic);

When used as an entry point, only explicit exports will be exposed. The synthetic fallback export, i.e. __synthetic in the example, will not be exposed for string values of syntheticNamedExports. However if the value is true, the default export will be exposed. This is the only notable difference between syntheticNamedExports: true and syntheticNamedExports: 'default'.

Inter-plugin communication

At some point when using many dedicated plugins, there may be the need for unrelated plugins to be able to exchange information during the build. There are several mechanisms through which Rollup makes this possible.

Custom resolver options

Assume you have a plugin that should resolve a module to different ids depending on how it is imported. One way to achieve this would be to use special proxy ids when importing this module, e.g. a transpiled import via require("foo") could be denoted with an id foo?require=true so that a resolver plugin knows this.

The problem here, however, is that this proxy id may or may not cause unintended side-effects when passed to other resolvers. Moreover, if the id is created by plugin A and the resolution happens in plugin B, it creates a dependency between these plugins so that one A is not usable without B.

Custom resolver option offer a solution here by allowing to pass additional options for plugins when manually resolving a module. This happens without changing the id and thus without impairing the ability for other plugins to resolve the module correctly if the intended target plugin is not present.

function requestingPlugin() {
  return {
    name: 'requesting',
    async buildStart() {
      const resolution = await this.resolve('foo', undefined, {
        custom: { resolving: { specialResolution: true } }
      });
      console.log(resolution.id); // "special"
    }
  };
}

function resolvingPlugin() {
  return {
    name: 'resolving',
    resolveId(id, importer, { custom }) {
      if (custom.resolving?.specialResolution) {
        return 'special';
      }
      return null;
    }
  };
}

Note the convention that custom options should be added using a property corresponding to the plugin name of the resolving plugin. It is responsibility of the resolving plugin to specify which options it respects.

Custom module meta-data

Plugins can annotate modules with custom meta-data which can be accessed by themselves and other plugins via the resolveId, load, and transform hooks. This meta-data should always be JSON.stringifyable and will be persisted in the cache e.g. in watch mode.

function annotatingPlugin() {
  return {
    name: 'annotating',
    transform(code, id) {
      if (thisModuleIsSpecial(code, id)) {
        return { meta: { annotating: { special: true } } };
      }
    }
  };
}

function readingPlugin() {
  let parentApi;
  return {
    name: 'reading',
    buildEnd() {
      const specialModules = Array.from(this.getModuleIds()).filter(
        id => this.getModuleInfo(id).meta.annotating?.special
      );
      // do something with this list
    }
  };
}

Note the convention that plugins that add or modify data should use a property corresponding to the plugin name, in this case annotating. On the other hand, any plugin can read all meta-data from other plugins via this.getModuleInfo.

If several plugins add meta-data or meta-data is added in different hooks, then these meta objects will be merged shallowly. That means if plugin first adds {meta: {first: {resolved: "first"}}} in the resolveId hook and {meta: {first: {loaded: "first"}}} in the load hook while plugin second adds {meta: {second: {transformed: "second"}}} in the transform hook, then the resulting meta object will be {first: {loaded: "first"}, second: {transformed: "second"}}. Here the result of the resolveId hook will be overwritten by the result of the load hook as the plugin was both storing them under its first top-level property. The transform data of the other plugin on the other hand will be placed next to it.

Direct plugin communication

For any other kind of inter-plugin communication, we recommend the pattern below. Note that api will never conflict with any upcoming plugin hooks.

function parentPlugin() {
  return {
    name: 'parent',
    api: {
      //...methods and properties exposed for other plugins
      doSomething(...args) {
        // do something interesting
      }
    }
    // ...plugin hooks
  }
}

function dependentPlugin() {
  let parentApi;
  return {
    name: 'dependent',
    buildStart({ plugins }) {
      const parentName = 'parent';
      const parentPlugin = options.plugins
        .find(plugin => plugin.name === parentName);
      if (!parentPlugin) {
        // or handle this silently if it is optional
        throw new Error(`This plugin depends on the "${parentName}" plugin.`);
      }
      // now you can access the API methods in subsequent hooks
      parentApi = parentPlugin.api;
    }
    transform(code, id) {
      if (thereIsAReasonToDoSomething(id)) {
        parentApi.doSomething(id);
      }
    }
  }
}

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 external from other-entry.js is already loaded and parsed.
  3. Execute main.js.

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

How do I add polyfills to a Rollup bundle?

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

// 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 most of the times @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. An exception for this rule is the Babel plugin, if you're using it then place it before the commonjs one.

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"]
}

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 Discord or posting a question to Stackoverflow. 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-polyfill-node.

Error: "EMFILE: too many open files"

For large projects, you may run into an EMFILE error when running Rollup in watch mode on macOS. If you experience this, disabling FSEvents may eliminate the problem:

// rollup.config.js
export default {
  ...,
  watch: {
    chokidar: {
      useFsEvents: false
    }
  }
};

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 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'
  }
};

The option can be omitted if some plugin emits at least one chunk (using this.emitFile) by the end of the buildStart hook.

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>
Default: "es"

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.). "iife" stands for "immediately-invoked Function Expression"
  • 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
  });

makeAbsoluteExternalsRelative

Type: boolean | "ifRelativeSource"
CLI: --makeAbsoluteExternalsRelative/--no-makeAbsoluteExternalsRelative
Default: true

Determines if absolute external paths should be converted to relative paths in the output. This does not only apply to paths that are absolute in the source but also to paths that are resolved to an absolute path by either a plugin or Rollup core.

For true, an external import like import "/Users/Rollup/project/relative.js" would be converted to a relative path. When converting an absolute path to a relative path, Rollup does not take the file or dir options into account, because those may not be present e.g. for builds using the JavaScript API. Instead, it assumes that the root of the generated bundle is located at the common shared parent directory of all modules that were included in the bundle. Assuming that the common parent directory of all modules is "/Users/Rollup/project", the import from above would likely be converted to import "./relative.js" in the output. If the output chunk is itself nested in a sub-directory by choosing e.g. chunkFileNames: "chunks/[name].js", the import would be "../relative.js".

As stated before, this would also apply to originally relative imports like import "./relative.js" that are resolved to an absolute path before they are marked as external by the external option.

One common problem is that this mechanism will also apply to imports like import "/absolute.js'", resulting in unexpected relative paths in the output.

For this case, choosing "ifRelativeSource" will check if the original import was a relative import and only then convert it to a relative import in the output. Choosing false will keep all paths as absolute paths in the output.

Note that when a relative path is directly marked as "external" using the external option, then it will be the same relative path in the output. When it is resolved first via a plugin or Rollup core and then marked as external, the above logic will apply.

maxParallelFileReads

Type: number
CLI: --maxParallelFileReads <number>
Default: 20

Limits the number of files rollup will open in parallel when reading modules. Without a limit or with a high enough value, builds can fail with an "EMFILE: too many open files". This dependes on how many open file handles the os allows.

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 | ((assetInfo: AssetInfo) => string)
CLI: --assetFileNames <pattern>
Default: "assets/[name]-[hash][extname]"

The pattern to use for naming custom emitted assets to include in the build output, or a function that is called per asset to return such a pattern. Patterns support 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. When using a function, assetInfo is a reduced version of the one in generateBundle without the fileName. 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 | ((chunkInfo: ChunkInfo) => string)
CLI: --chunkFileNames <pattern>
Default: "[name]-[hash].js"

The pattern to use for naming shared chunks created when code-splitting, or a function that is called per chunk to return such a pattern. Patterns support 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 output.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. When using a function, chunkInfo is a reduced version of the one in generateBundle without properties that depend on file names. 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 | ((chunkInfo: ChunkInfo) => string)
CLI: --entryFileNames <pattern>
Default: "[name].js"

The pattern to use for chunks created from entry points, or a function that is called per entry chunk to return such a pattern. Patterns support 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. When using a function, chunkInfo is a reduced version of the one in generateBundle without properties that depend on file names. See also output.assetFileNames, output.chunkFileNames.

This pattern will also be used when setting the output.preserveModules option. Here a different set of placeholders is 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.
  • [assetExtname]: The extension of the file, prefixed by . if it is not empty and it is not one of js, jsx, ts or tsx.

output.extend

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

Whether 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 output.preserveModules option as here, imports will never be hoisted.

output.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. Note that this will change the execution order: A module that is only imported dynamically will be executed immediately if the dynamic import is inlined.

output.interop

Type: "auto" | "esModule" | "default" | "defaultOnly" | boolean | ((id: string) => "auto" | "esModule" | "default" | "defaultOnly" | boolean)
CLI: --interop <value>
Default: true

Controls how Rollup handles default, namespace and dynamic imports from external dependencies in formats like CommonJS that do not natively support these concepts. Note that even though true is the current default value, this value is deprecated and will be replaced by "auto" in the next major version of Rollup. In the examples, we will be using the CommonJS format, but the interop similarly applies to AMD, IIFE and UMD targets as well.

To understand the different values, assume we are bundling the following code for a cjs target:

import ext_default, * as external from 'external1';
console.log(ext_default, external.bar, external);
import('external2').then(console.log);

Keep in mind that for Rollup, import * as ext_namespace from 'external'; console.log(ext_namespace.bar); is completely equivalent to import {bar} from 'external'; console.log(bar); and will produce the same code. In the example above however, the namespace object itself is passed to a global function as well, which means we need it as a properly formed object.

  • "esModule" assumes that required modules are transpiled ES modules where the required value corresponds to the module namespace, and the default export is the .default property of the exported object:

      var external = require('external1');
      console.log(external['default'], external.bar, external);
      Promise.resolve()
        .then(function () {
          return require('external2');
        })
        .then(console.log);
      

    When esModule is used, Rollup adds no additional interop helpers and also supports live-bindings for default exports.

  • "default" assumes that the required value should be treated as the default export of the imported module, just like when importing CommonJS from an ES module context in Node. In contrast to Node, though, named imports are supported as well which are treated as properties of the default import. To create the namespace object, Rollup injects helpers:

      var external = require('external1');
    
      function _interopNamespaceDefault(e) {
        var n = Object.create(null);
        if (e) {
          Object.keys(e).forEach(function (k) {
            if (k !== 'default') {
              var d = Object.getOwnPropertyDescriptor(e, k);
              Object.defineProperty(
                n,
                k,
                d.get
                  ? d
                  : {
                      enumerable: true,
                      get: function () {
                        return e[k];
                      }
                    }
              );
            }
          });
        }
        n['default'] = e;
        return Object.freeze(n);
      }
    
      var external__namespace = /*#__PURE__*/ _interopNamespaceDefault(external);
      console.log(external, external.bar, external__namespace);
      Promise.resolve()
        .then(function () {
          return /*#__PURE__*/ _interopNamespaceDefault(require('external2'));
        })
        .then(console.log);
      
  • "auto" combines both "esModule" and "default" by injecting helpers that contain code that detects at runtime if the required value contains the __esModule property. Adding this property is a standard implemented by Rollup, Babel and many other tools to signify that the required value is the namespace of a transpiled ES module:

      var external = require('external1');
    
      function _interopNamespace(e) {
        if (e && e.__esModule) {
          return e;
        } else {
          var n = Object.create(null);
          if (e) {
            Object.keys(e).forEach(function (k) {
              if (k !== 'default') {
                var d = Object.getOwnPropertyDescriptor(e, k);
                Object.defineProperty(
                  n,
                  k,
                  d.get
                    ? d
                    : {
                        enumerable: true,
                        get: function () {
                          return e[k];
                        }
                      }
                );
              }
            });
          }
          n['default'] = e;
          return Object.freeze(n);
        }
      }
    
      var external__namespace = /*#__PURE__*/ _interopNamespace(external);
      console.log(external__namespace['default'], external.bar, external__namespace);
      Promise.resolve()
        .then(function () {
          return /*#__PURE__*/ _interopNamespace(require('external2'));
        })
        .then(console.log);
      

    Note how Rollup is reusing the created namespace object to get the default export. If the namespace object is not needed, Rollup will use a simpler helper:

      // input
      import ext_default from 'external';
      console.log(ext_default);
    
      // output
      var ext_default = require('external');
    
      function _interopDefault(e) {
        return e && e.__esModule ? e : { default: e };
      }
    
      var ext_default__default = /*#__PURE__*/ _interopDefault(ext_default);
      console.log(ext_default__default['default']);
      
  • "defaultOnly" is similar to "default" except for the following:

    • Named imports are forbidden. If such an import is encountered, Rollup throws an error even in es and system formats. That way it is ensures that the es version of the code is able to import non-builtin CommonJS modules in Node correctly.
    • While namespace reexports export * from 'external'; are not prohibited, they are ignored and will cause Rollup to display a warning because they would not have an effect if there are no named exports.
    • When a namespace object is generated, Rollup uses a much simpler helper.

    Here is what Rollup will create from the example code. Note that we removed external.bar from the code as otherwise, Rollup would have thrown an error because, as stated before, this is equivalent to a named import.

      var ext_default = require('external1');
    
      function _interopNamespaceDefaultOnly(e) {
        return Object.freeze({ __proto__: null, default: e });
      }
    
      var ext_default__namespace = /*#__PURE__*/ _interopNamespaceDefaultOnly(ext_default);
      console.log(ext_default, ext_default__namespace);
      Promise.resolve()
        .then(function () {
          return /*#__PURE__*/ _interopNamespaceDefaultOnly(require('external2'));
        })
        .then(console.log);
      
  • When a function is supplied, Rollup will pass each external id to this function once to control the interop type per dependency.

    As an example if all dependencies are CommonJs, the following config will ensure that named imports are only permitted from Node builtins:

      // rollup.config.js
      import builtins from 'builtins';
      const nodeBuiltins = new Set(builtins());
    
      export default {
        // ...
        output: {
          // ...
          interop(id) {
            if (nodeBuiltins.has(id)) {
              return 'default';
            }
            return 'defaultOnly';
          }
        }
      };
      
  • true is equivalent to "auto" except that it uses a slightly different helper for the default export that checks for the presence of a default property instead of the __esModule property.

    ☢️ This value is deprecated and will be removed in a future Rollup version.

  • false is equivalent to using default when importing a default export and esModule when importing a namespace.

    ☢️ This value is deprecated and will be removed in a future Rollup version.

There are some additional options that have an effect on the generated interop code:

  • Setting output.externalLiveBindings to false will generate simplified namespace helpers as well as simplified code for extracted default imports.
  • Setting output.freeze to false will prevent generated interop namespace objects from being frozen.

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.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 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}`;
    }
  }
}

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

output.preserveModulesRoot

Type: string
CLI: --preserveModulesRoot <directory-name>

A directory path to input modules that should be stripped away from output.dir path while output.preserveModules is true.

For example, given the following configuration:

export default {
  input: ['src/module.js', `src/another/module.js`],
  output: [
    {
      format: 'es',
      dir: 'dist',
      preserveModules: true,
      preserveModulesRoot: 'src'
    }
  ]
};

The preserveModulesRoot setting ensures that the input modules will be output to the paths dist/module.js and dist/another/module.js.

This option is particularly useful while using plugins such as @rollup/plugin-node-resolve, which may cause changes in the output directory structure. This can happen when third-party modules are not marked external, or while developing in a monorepo of multiple packages that rely on one another and are not marked external.

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: (relativeSourcePath: string, sourcemapPath: string) => string

A transformation to apply to each path in a sourcemap. relativeSourcePath is a relative path from the generated .map file to the corresponding source file while sourcemapPath is the fully resolved path of the generated sourcemap file.

import path from 'path';
export default {
  input: 'src/main',
  output: [
    {
      file: 'bundle.js',
      sourcemapPathTransform: (relativeSourcePath, sourcemapPath) => {
        // will replace relative paths with absolute paths
        return path.resolve(path.dirname(sourcemapPath), relativeSourcePath);
      },
      format: 'es',
      sourcemap: true
    }
  ]
};

output.validate

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

Re-parses each generated chunk to detect if the generated code is valid JavaScript. This can be useful to debug output generated by plugins that use the renderChunk hook to transform code.

If the code is invalid, a warning will be issued. Note that no error is thrown so that you can still inspect the generated output. To promote this warning to an error, you can watch for it in an onwarn handler.

preserveEntrySignatures

Type: "strict" | "allow-extension" | "exports-only" | 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.
  • "exports-only" behaves like "strict" if the entry module has exports, otherwise it behaves like "allow-extension".
  • 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.

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, autoId?: boolean, basePath?: string, define?: string }

Note id can only be used for single-file builds, and cannot be combined with autoId/basePath.

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.autoId
Type: boolean
CLI: --amd.autoId

Set the ID to the chunk ID (with the '.js' extension removed).

// rollup.config.js
export default {
  ...,
  format: 'amd',
  amd: {
    autoId: true
  }
};

// -> define('main', ['dependency'], ...
// -> define('dynamic-chunk', ['dependency'], ...

output.amd.basePath
Type: string
CLI: --amd.basePath

The path that will be prepended to the auto generated ID. This is useful if the build is going to be placed inside another AMD project, and is not at the root.

Only valid with output.amd.autoId.

// rollup.config.js
export default {
  ...,
  format: 'amd',
  amd: {
    autoId: true
    basePath: 'some/where'
  }
};

// -> define('some/where/main', ['dependency'], ...
// -> define('some/where/dynamic-chunk', ['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 to add a __esModule: true property when generating exports for non-ES formats. This property signifies that the exported value is the namespace of an ES module and that the default export of this module corresponds to the .default property of the exported object. By default, Rollup adds this property when using named exports mode for a chunk. See also output.interop.

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 – if you are only exporting one thing using export default ...; note that this can cause issues when generating CommonJS output that is meant to be interchangeable with ESM output, see below
  • named – if you are using named exports
  • none – if you are not exporting anything (e.g. you are building an app, not a library)

As this is only an output transformation, you can only choose default if a default export is the only export for all entry chunks. Likewise, you can only choose none if there are no exports, otherwise Rollup will throw an error.

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:

// your-lib package entry
export default 'Hello world';

// a CommonJS consumer
/* require( "your-lib" ) returns "Hello World" */
const hello = require('your-lib');

With named, a user would do this instead:

// your-lib package entry
export const hello = 'Hello world';

// a CommonJS consumer
/* require( "your-lib" ) returns {hello: "Hello World"} */
const hello = require('your-lib').hello;
/* or using destructuring */
const { hello } = require('your-lib');

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:

// your-lib package entry
export default 'foo';
export const bar = 'bar';

// a CommonJS consumer
/* require( "your-lib" ) returns {default: "foo", bar: "bar"} */
const foo = require('your-lib').default;
const bar = require('your-lib').bar;
/* or using destructuring */
const { default: foo, bar } = require('your-lib');

Note: There are some tools such as Babel, TypeScript, Webpack, and @rollup/plugin-commonjs that are capable of resolving a CommonJS require(...) call with an ES module. If you are generating CommonJS output that is meant to be interchangeable with ESM output for those tools, you should always use named export mode. The reason is that most of those tools will by default return the namespace of an ES module on require where the default export is the .default property.

In other words for those tools, you cannot create a package interface where const lib = require("your-lib") yields the same as import lib from "your-lib". With named export mode however, const {lib} = require("your-lib") will be equivalent to import {lib} from "your-lib".

To alert you to this, Rollup will generate a warning when you encounter such a situation and did not select an explicit value for output.exports.

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

Type: boolean | (string) => string
CLI: --sanitizeFileName/no-sanitizeFileName Default: true

Set to false to disable all chunk name sanitizations (removal of \0, ? and * characters).

Alternatively set to a function to allow custom chunk name sanitization.

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.

output.systemNullSetters

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

When outputting the system module format, this will replace empty setter functions with null as an output simplification. This is only supported in SystemJS 6.3.3 and above.

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 | "smallest" | "safest" | "recommended" | { annotations?: boolean, correctVarValueBeforeDeclaration?: boolean, moduleSideEffects?: ModuleSideEffectsOption, preset?: "smallest" | "safest" | "recommended", propertyReadSideEffects?: boolean | 'always', tryCatchDeoptimization?: boolean, unknownGlobalSideEffects?: boolean }
CLI: --treeshake/--no-treeshake
Default: true

Whether 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. You may also choose one of three presets that will automatically be updated if new options are added:

  • "smallest" will choose option values for you to minimize output size as much as possible. This should work for most code bases as long as you do not rely on certain patterns, which are currently:
    • getters with side effects will only be retained if the return value is used (treeshake.propertyReadSideEffects: false)
    • code from imported modules will only be retained if at least one exported value is used (treeshake.moduleSideEffects: false)
    • you should not bundle polyfills that rely on detecting broken builtins (treeshake.tryCatchDeoptimization: false)
    • some semantic issues may be swallowed (treeshake.unknownGlobalSideEffects: false, treeshake.correctVarValueBeforeDeclaration: false)
  • "recommended" should work well for most usage patterns. Some semantic issues may be swallowed, though (treeshake.unknownGlobalSideEffects: false, treeshake.correctVarValueBeforeDeclaration: false)
  • "safest" tries to be as spec compliant as possible while still providing some basic tree-shaking capabilities.
  • true is equivalent to not specifying the option and will always choose the default value (see below).

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.correctVarValueBeforeDeclaration
Type: boolean
CLI: --treeshake.correctVarValueBeforeDeclaration/--no-treeshake.correctVarValueBeforeDeclaration
Default: false

In some edge cases if a variable is accessed before its declaration assignment and is not reassigned, then Rollup may incorrectly assume that variable is constant throughout the program, as in the example below. This is not true if the variable is declared with var, however, as those variables can be accessed before their declaration where they will evaluate to undefined. Choosing true will make sure Rollup does not make any assumptions about the value of variables declared with var. Note though that this can have a noticeable negative impact on tree-shaking results.

// everything will be tree-shaken unless treeshake.correctVarValueBeforeDeclaration === true
let logBeforeDeclaration = false;

function logIfEnabled() {
  if (logBeforeDeclaration) {
    log();
  }

  var value = true;

  function log() {
    if (!value) {
      console.log('should be retained, value is undefined');
    }
  }
}

logIfEnabled(); // could be removed
logBeforeDeclaration = true;
logIfEnabled(); // needs to be retained as it displays a log

treeshake.moduleSideEffects
Type: boolean | "no-external" | string[] | (id: string, external: boolean) => boolean
CLI: --treeshake.moduleSideEffects/--no-treeshake.moduleSideEffects/--treeshake.moduleSideEffects no-external
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');
const ignored = 'will still be removed';
// 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;

If a module that has this flag set to false reexports a variable from another module and this variable is used, the question if the reexporting module is scanned for side effects depends on how the variable is reexported:

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

// input file b.js
// direct reexports will ignore side effects
export { foo } from './c.js';
console.log('this side-effect is ignored');

// input file c.js
// indirect reexports will include side effects
import { foo } from './d.js';
foo.mutated = true;
console.log('this side-effect and the mutation are retained');
export { foo };

// input file d.js
export const foo = 42;
// output with treeshake.moduleSideEffects === false
const foo = 42;

foo.mutated = true;
console.log('this side-effect and the mutation are retained');

console.log(foo);

Note that despite the name, this option does not "add" side effects to modules that do not have side effects. If it is important that e.g. an empty module is "included" in the bundle because you need this for dependency tracking, the plugin interface allows you to designate modules as being excluded from tree-shaking via the resolveId, load or transform hook.

treeshake.preset
Type: "smallest" | "safest" | "recommended"
CLI: --treeshake <value>

Allows choosing one of the presets listed above while overriding some of the options.

export default {
  treeshake: {
    preset: 'smallest',
    propertyReadSideEffects: true
  }
  // ...
};

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

If true, retain unused property reads that Rollup can determine to have side-effects. This includes accessing properties of null or undefined or triggering explicit getters via property access. Note that this does not cover destructuring assignment or getters on objects passed as function parameters.

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.

If 'always', assume all member property accesses, including destructuring, have side effects. This setting is recommended for code relying on getters with side effects. It typically results in larger bundle size, but smaller than disabling treeshake altogether.

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

Type: { buildDelay?: number, chokidar?: ChokidarOptions, clearScreen?: boolean, exclude?: string, include?: string, skipWrite?: boolean } | false
Default: {}

Specify options for watch mode or prevent this configuration from being watched. Specifying false is only really useful when an array of configurations is used. In that case, this configuration will not be built or rebuilt on change in watch mode, but it will be built when running Rollup regularly:

// rollup.config.js
export default [
  {
    input: 'main.js',
    output: { file: 'bundle.cjs.js', format: 'cjs' }
  },
  {
    input: 'main.js',
    watch: false,
    output: { file: 'bundle.es.js', format: 'es' }
  }
];

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

watch.buildDelay

Type: number
CLI: --watch.buildDelay <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 trigger 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
CLI: --watch.clearScreen/--no-watch.clearScreen
Default: true

Whether to clear the screen when a rebuild is triggered.

watch.exclude

Type: string | RegExp | (string | RegExp)[]
CLI: --watch.exclude <files>

Prevent files from being watched:

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

watch.include

Type: string | RegExp | (string | RegExp)[]
CLI: --watch.include <files>

Limit the file-watching to certain files. Note that this only filters the module graph but does not allow adding additional watch files:

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

watch.skipWrite

Type: boolean
CLI: --watch.skipWrite/--no-watch.skipWrite
Default: false

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

Deprecated options

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

inlineDynamicImports

Use the output.inlineDynamicImports output option instead, which has the same signature.

manualChunks

Use the output.manualChunks output option instead, which has the same signature.

preserveModules

Use the output.preserveModules output option instead, which has the same signature.

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.

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.