Rollup Complete Guide | ES Module Bundler

Rollup Complete Guide | ES Module Bundler

이 글의 핵심

Rollup is a module bundler for JavaScript that compiles ES modules into optimized bundles. It's the bundler of choice for libraries and powers Vite's production builds.

Introduction

Rollup is a module bundler for JavaScript that treats ES modules as first-class citizens. It’s designed for building libraries and produces highly optimized output.

Why Rollup?

Traditional bundlers include lots of runtime code:

// Webpack output (simplified)
(function(modules) {
  var installedModules = {};
  function __webpack_require__(moduleId) {
    // ... runtime code
  }
  return __webpack_require__(0);
})([/* modules */]);

Rollup output is clean and minimal:

// Just your code, optimized
function add(a, b) {
  return a + b;
}

export { add };

1. Installation

npm install --save-dev rollup

Basic config:

// rollup.config.js
export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'es'
  }
};
rollup -c

2. Output Formats

Rollup supports multiple module formats:

export default {
  input: 'src/index.js',
  output: [
    // ES Module (modern)
    {
      file: 'dist/bundle.esm.js',
      format: 'es'
    },
    // CommonJS (Node.js)
    {
      file: 'dist/bundle.cjs.js',
      format: 'cjs'
    },
    // UMD (browser)
    {
      file: 'dist/bundle.umd.js',
      format: 'umd',
      name: 'MyLibrary'
    },
    // IIFE (browser script tag)
    {
      file: 'dist/bundle.iife.js',
      format: 'iife',
      name: 'MyLibrary'
    }
  ]
};

3. Tree Shaking

Rollup pioneered tree shaking - removing unused code:

// math.js
export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

export function multiply(a, b) {
  return a * b;
}
// main.js
import { add } from './math.js';

console.log(add(1, 2));

Output (only used code):

function add(a, b) {
  return a + b;
}

console.log(add(1, 2));
// subtract and multiply are removed!

4. Essential Plugins

@rollup/plugin-node-resolve

Resolves npm packages:

npm install --save-dev @rollup/plugin-node-resolve
import resolve from '@rollup/plugin-node-resolve';

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

@rollup/plugin-commonjs

Converts CommonJS to ES modules:

npm install --save-dev @rollup/plugin-commonjs
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';

export default {
  plugins: [
    resolve(),
    commonjs() // After resolve
  ]
};

@rollup/plugin-babel

Transpiles modern JavaScript:

npm install --save-dev @rollup/plugin-babel @babel/core @babel/preset-env
import babel from '@rollup/plugin-babel';

export default {
  plugins: [
    babel({
      babelHelpers: 'bundled',
      presets: ['@babel/preset-env']
    })
  ]
};

@rollup/plugin-terser

Minifies output:

npm install --save-dev @rollup/plugin-terser
import terser from '@rollup/plugin-terser';

export default {
  plugins: [
    terser()
  ]
};

5. Building a Library

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

export default {
  input: 'src/index.js',
  output: [
    {
      file: 'dist/my-library.esm.js',
      format: 'es',
      sourcemap: true
    },
    {
      file: 'dist/my-library.cjs.js',
      format: 'cjs',
      sourcemap: true
    },
    {
      file: 'dist/my-library.min.js',
      format: 'umd',
      name: 'MyLibrary',
      sourcemap: true,
      plugins: [terser()]
    }
  ],
  plugins: [
    resolve(),
    commonjs(),
    babel({
      babelHelpers: 'bundled',
      exclude: 'node_modules/**'
    })
  ],
  external: ['react', 'react-dom'] // Don't bundle React
};

package.json:

{
  "name": "my-library",
  "main": "dist/my-library.cjs.js",
  "module": "dist/my-library.esm.js",
  "browser": "dist/my-library.min.js",
  "files": ["dist"],
  "scripts": {
    "build": "rollup -c"
  }
}

6. Code Splitting

export default {
  input: {
    main: 'src/main.js',
    admin: 'src/admin.js'
  },
  output: {
    dir: 'dist',
    format: 'es'
  }
};

Dynamic imports:

// main.js
async function loadModule() {
  const module = await import('./heavy-module.js');
  module.init();
}

7. TypeScript Support

npm install --save-dev @rollup/plugin-typescript typescript
import typescript from '@rollup/plugin-typescript';

export default {
  input: 'src/index.ts',
  output: {
    file: 'dist/bundle.js',
    format: 'es'
  },
  plugins: [
    typescript()
  ]
};

8. React Library

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';

export default {
  input: 'src/index.jsx',
  output: [
    {
      file: 'dist/index.js',
      format: 'cjs',
      sourcemap: true
    },
    {
      file: 'dist/index.esm.js',
      format: 'es',
      sourcemap: true
    }
  ],
  plugins: [
    peerDepsExternal(), // Externalize peer dependencies
    resolve(),
    babel({
      babelHelpers: 'bundled',
      presets: [
        '@babel/preset-env',
        ['@babel/preset-react', { runtime: 'automatic' }]
      ],
      exclude: 'node_modules/**'
    }),
    commonjs()
  ],
  external: ['react', 'react-dom']
};

9. CSS and Assets

PostCSS

npm install --save-dev rollup-plugin-postcss
import postcss from 'rollup-plugin-postcss';

export default {
  plugins: [
    postcss({
      extract: true,
      minimize: true
    })
  ]
};

Images

npm install --save-dev @rollup/plugin-image
import image from '@rollup/plugin-image';

export default {
  plugins: [
    image()
  ]
};

10. Development Workflow

npm install --save-dev rollup-plugin-serve rollup-plugin-livereload
import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';

const dev = process.env.NODE_ENV !== 'production';

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: dev
  },
  plugins: [
    // ... other plugins
    dev && serve({
      open: true,
      contentBase: ['dist', 'public'],
      port: 3000
    }),
    dev && livereload('dist')
  ].filter(Boolean)
};
{
  "scripts": {
    "dev": "rollup -c -w",
    "build": "NODE_ENV=production rollup -c"
  }
}

11. Advanced Configuration

Multiple Configs

// rollup.config.js
import dev from './rollup.config.dev.js';
import prod from './rollup.config.prod.js';

export default process.env.NODE_ENV === 'production' ? prod : dev;

Conditional Plugins

const production = !process.env.ROLLUP_WATCH;

export default {
  plugins: [
    resolve(),
    commonjs(),
    production && terser()
  ].filter(Boolean)
};

12. Optimizations

External Dependencies

export default {
  external: [
    'react',
    'react-dom',
    /^lodash/ // All lodash packages
  ]
};

Mangled Props (Advanced)

import terser from '@rollup/plugin-terser';

export default {
  plugins: [
    terser({
      mangle: {
        properties: {
          regex: /^_/ // Mangle properties starting with _
        }
      }
    })
  ]
};

13. Watch Mode

rollup -c -w
// rollup.config.js
export default {
  watch: {
    include: 'src/**',
    exclude: 'node_modules/**',
    clearScreen: false
  }
};

14. Real-World Example

Complete library build:

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';
import postcss from 'rollup-plugin-postcss';

const production = !process.env.ROLLUP_WATCH;

export default {
  input: 'src/index.ts',
  output: [
    {
      file: 'dist/index.js',
      format: 'cjs',
      sourcemap: true,
      exports: 'named'
    },
    {
      file: 'dist/index.esm.js',
      format: 'es',
      sourcemap: true,
      exports: 'named'
    },
    {
      file: 'dist/index.umd.js',
      format: 'umd',
      name: 'MyLib',
      sourcemap: true,
      exports: 'named',
      globals: {
        react: 'React',
        'react-dom': 'ReactDOM'
      }
    }
  ],
  plugins: [
    peerDepsExternal(),
    resolve({
      extensions: ['.js', '.jsx', '.ts', '.tsx']
    }),
    commonjs(),
    typescript({
      tsconfig: './tsconfig.json',
      declaration: true,
      declarationDir: 'dist'
    }),
    postcss({
      extract: 'styles.css',
      minimize: production
    }),
    production && terser()
  ].filter(Boolean),
  external: ['react', 'react-dom']
};

15. Performance Tips

1. Use Cache

Rollup caches by default in watch mode.

2. Parallelize Builds

npm install --save-dev npm-run-all
{
  "scripts": {
    "build:esm": "rollup -c rollup.config.esm.js",
    "build:cjs": "rollup -c rollup.config.cjs.js",
    "build": "npm-run-all --parallel build:*"
  }
}

3. Minimize Plugin Work

plugins: [
  resolve({
    mainFields: ['module', 'main'],
    extensions: ['.js'] // Only what you need
  })
]

Summary

Rollup excels at building optimized libraries:

  • Tree shaking pioneer
  • Clean output with minimal runtime
  • ES module first design
  • Plugin ecosystem for everything
  • Powers Vite production builds

Key Takeaways:

  1. Best for library builds
  2. Excellent tree shaking
  3. Multiple output formats
  4. Clean, readable output
  5. Plugin-based architecture

Next Steps:

Resources: