SuperAPI AI Directory

rollup

Coding & development

View repository

Updated 10-15 21:49

About this skill

Rollup

Rollup (v4) is the bundler of choice for libraries. It produces smaller, cleaner code than Webpack and pioneered Tree Shaking.

When to Use

  • Modern JavaScript/TypeScript Library Packaging: Generating optimized ESM and CommonJS bundles with tree-shaking.
  • Zero-Overhead Code Splitting: Emitting dynamic import chunks and shared modules with minimal wrapper boilerplate.
  • Design System & Component Library Distribution: Bundling React, Vue, or Web Components with externalized peer dependencies.
  • Custom Toolchain Compilation: Powering modern bundlers (such as Vite) under the hood for production build generation.

Quick Start

1. Minimal rollup.config.mjs

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import terser from "@rollup/plugin-terser";

export default {
  input: "src/index.ts",
  output: [
    { file: "dist/index.cjs", format: "cjs", sourcemap: true },
    { file: "dist/index.mjs", format: "esm", sourcemap: true },
  ],
  plugins: [
    resolve(),
    commonjs(),
    typescript({ tsconfig: "./tsconfig.json" }),
    terser(),
  ],
  external: ["react", "react-dom"],
};

2. Build via CLI

npx rollup -c --bundleConfigAsCjs

Core Concepts

Production ESM / CJS Library Configuration (rollup.config.mjs)

Building a TypeScript library with external dependencies and source maps:

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import terser from "@rollup/plugin-terser";
import peerDepsExternal from "rollup-plugin-peer-deps-external";

export default {
  input: "src/index.ts",
  output: [
    {
      file: "dist/index.mjs",
      format: "esm",
      sourcemap: true,
    },
    {
      file: "dist/index.cjs",
      format: "cjs",
      sourcemap: true,
      exports: "named",
    },
  ],
  plugins: [
    peerDepsExternal(),
    resolve({ extensions: [".ts", ".js"] }),
    commonjs(),
    typescript({
      tsconfig: "./tsconfig.json",
      declaration: true,
      declarationDir: "dist/types",
    }),
    terser({
      format: { comments: false },
      compress: { drop_console: true },
    }),
  ],
  external: ["react", "react-dom"],
};

Tree-Shaking Verification & Pure Annotations

Optimizing tree-shaking by marking side-effect-free packages and pure function calls:

// package.json
{
  "name": "my-math-lib",
  "sideEffects": false
}

// src/utils.ts
/* @__PURE__ */
export const complexCalculation = (a, b) => {
  return a * 1000 + b;
};

Multi-Entrypoint Build Automation

Bundling multiple package subpath exports (my-lib/core, my-lib/react):

export default [
  {
    input: "src/core/index.ts",
    output: { dir: "dist/core", format: "esm" },
    plugins: [typescript()],
  },
  {
    input: "src/react/index.ts",
    output: { dir: "dist/react", format: "esm" },
    plugins: [typescript()],
    external: ["react"],
  },
];

Common Patterns

Library Bundling with External Dependencies

Problem: Third-party peer dependencies (e.g. lodash, react) get bundled into output library instead of being treated as externals.
Solution: Declare externals using regular expression or peerDependencies keys.

import pkg from "./package.json" assert { type: "json" };

export default {
  input: "src/index.ts",
  external: [
    ...Object.keys(pkg.dependencies || {}),
    ...Object.keys(pkg.peerDependencies || {}),
    /^node:/, // Mark node built-ins external
  ],
  // ...
};

Preserving Directory Structure (Multi-entry Code Splitting)

Problem: Need to build a library where users can import submodules (e.g. import { helper } from 'my-lib/utils').
Solution: Set output.preserveModules = true.

export default {
  input: ["src/index.ts", "src/utils.ts"],
  output: {
    dir: "dist",
    format: "esm",
    preserveModules: true,
    preserveModulesRoot: "src",
  },
};

Best Practices

Do:

  • Externalize runtime peer dependencies (react, lodash) to avoid bundling duplicate copies into consumer packages.
  • Set "sideEffects": false in package.json to enable consumer bundlers to aggressively tree-shake unused code.
  • Emit TypeScript declaration files (.d.ts) alongside bundles using @rollup/plugin-typescript.
  • Use .mjs extension for Rollup configuration files to ensure native ESM loading in modern Node.js.

Don't:

  • Use Rollup for complex SPAs when modern bundlers like Vite or Turbopack provide superior dev server workflows.
  • Disable source maps for production library builds; consumers need them for stack trace debugging.
  • Bundle large Node.js built-ins (fs, path) into browser library targets without polyfill wrappers.

Troubleshooting

Error Cause Solution
Unresolved dependencies: '...' treated as external Package import not found by Rollup resolver Add @rollup/plugin-node-resolve to the plugins array.
Cannot find module ... or its corresponding type declarations TypeScript plugin tsconfig path mismatch Configure typescript({ tsconfig: "./tsconfig.json", declaration: true, outDir: "dist" }).
Circular dependency: a.js -> b.js -> a.js Circular imports causing runtime initialization issues Refactor circular references or suppress specific warning via onwarn(warning, warn) hook.

References