Frontend Development 14 min read

Why Rollup Beats Webpack for Building a Simple JS Library

This article compares Webpack and Rollup, explains why Rollup’s tree‑shaking and ES6 module support make it better for a lightweight JavaScript utility library, and provides a step‑by‑step guide to set up the project with TypeScript, ESLint, Prettier, Husky, Commitlint and Jest testing.

37 Mobile Game Tech Team
37 Mobile Game Tech Team
37 Mobile Game Tech Team
Why Rollup Beats Webpack for Building a Simple JS Library

Preface

Recently I was writing a frontend utility library. I started with Webpack, but then discovered Rollup, which better fits my needs. This article introduces building a simple JavaScript utility library with Rollup.

Requirements

Code level 1. Write code using ES6 syntax (including async) with specific parameter requirements. 2. Follow coding standards when committing. 3. Unit testing. 4. Generate documentation comments. Function level 1. Support mainstream browsers (e.g., Chrome, Firefox). 2. Provide common JS methods. 3. May depend on other libraries.

Webpack VS Rollup

Entry files Both Webpack and Rollup require a configuration file to specify entry, output, plugins, etc.

Relative path support: Webpack does not support it (requires

path.resolve

); Rollup supports it.

This is just one simple difference; there are several more.

Tree‑shaking (dead code elimination)

Tree‑shaking reduces the bundle size by removing unused code, which shortens page load time. It works by statically analyzing ES6 modules.

tree shaking illustration
tree shaking illustration

Comparing the same code bundled by Webpack and Rollup:

Execution time: Webpack 71 ms, Rollup 17 ms.

File size: Webpack 389 KB, Rollup 262 KB.

The main reason is that Rollup uses tree‑shaking, leveraging ES6 module features for static analysis and removing dead code during the uglify phase.

ES6 Module Rules

Only top‑level statements (import/export) are allowed.

Import module specifiers must be string literals.

Imported modules cannot be reassigned.

<code>// Case 1
let str = '只能作为模块顶层的语句出现';
import { sum } from 'util';

// Case 2
import { 's' + 'um' } from 'util';

// Case 3
import { sum } from 'util'
sum = 1; // Syntax Error: 'sum' is read‑only
</code>

Real‑time Reloading

Webpack uses

webpack-dev-server

. Rollup uses

rollup-plugin-serve

+

rollup-plugin-livereload

. Webpack offers more customizable middleware options.

Summary

Webpack - Rich ecosystem, complete documentation, many plugins. - Code splitting and on‑demand loading with tree‑shaking (Webpack 2+). - Generates additional code, larger bundles, slower execution, lower readability. - Suitable for projects involving CSS/HTML, complex code splitting, or full applications.

Rollup - Smaller plugin ecosystem. - Bundles everything together, uses tree‑shaking to reduce size. - Usually produces no extra code, faster execution, better readability. - Ideal for building libraries.

Project Setup

Initialize project 1. Create folder

rollup-demo

. 2. Run

npm init -y

. 3. Install Rollup and a plugin to clear the

dist

directory:

npm i rollup rollup-plugin-clear -D

. 4. Create entry file

src/main.js

.

<code>function fun1(){
  function fun2(){
    return 'no'
  }
  return 'yes'
}
fun1()
console.log(fun1())
function Useless(){
  console.log(1111)
}
</code>

Create

rollup.config.js

in the project root:

<code>'use strict';
import clear from 'rollup-plugin-clear';
export default {
  input: 'src/main.ts',
  output: {
    file: 'dist/bundle.js',
    format: 'umd' // bundle format
  },
  plugins: [
    clear({ targets: ['dist'] }) // clean dist folder
  ]
};
</code>

Add a build script to

package.json

:

<code>"build": "rollup -c rollup.config.js"
</code>

Run

npm run build

to complete a simple Rollup build.

Using TypeScript

TypeScript provides static typing, better IDE support, and compile‑time checks, making code easier to read and refactor. Install the necessary packages:

<code>npm i rollup-plugin-typescript2 typescript -D
</code>

Update

rollup.config.js

to use the TypeScript plugin:

<code>import ts from "rollup-plugin-typescript2";
export default {
  input: "./src/main.ts",
  plugins: [
    ts({ useTsconfigDeclarationDir: true })
  ]
};
</code>

Example

tsconfig.json

:

<code>{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "lib": ["es2015", "es2016", "es2017"],
    "strict": true,
    "sourceMap": true,
    "strictNullChecks": true,
    "declaration": true,
    "declarationDir": "dist/types",
    "noUnusedLocals": true,
    "outDir": "./dist",
    "typeRoots": ["node_modules/@types"]
  }
}
</code>

ESLint & Prettier

ESLint enforces code quality rules; Prettier formats code. Install the following:

<code>npm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin prettier eslint-config-prettier eslint-plugin-prettier -D
</code>

Create

.eslintrc.js

:

<code>module.exports = {
  root: true,
  parser: '@typescript-eslint/parser',
  parserOptions: { ecmaVersion: 2019, sourceType: 'module' },
  extends: [
    'plugin:@typescript-eslint/recommended',
    'prettier/@typescript-eslint',
    'plugin:prettier/recommended'
  ],
  env: { es6: true, node: true },
  rules: {
    'no-undef': 'error',
    'eqeqeq': 'error',
    'no-console': 'error'
  }
};
</code>

Create

.prettierrc.js

:

<code>module.exports = {
  arrowParens: 'avoid',
  bracketSpacing: false,
  endOfLine: 'lf',
  jsxBracketSameLine: false,
  jsxSingleQuote: false,
  printWidth: 100,
  proseWrap: 'preserve',
  semi: true,
  singleQuote: true,
  useTabs: false,
  trailingComma: 'es5'
};
</code>

Commit Code Standards (Husky, lint‑staged, Commitlint)

Install:

<code>npm i husky lint-staged -D
npm i @commitlint/cli @commitlint/config-conventional -D
</code>

Create

huskyrc.js

:

<code>module.exports = {
  hooks: {
    'commit-msg': 'commitlint -e $HUSKY_GIT_PARAMS',
    'pre-commit': 'lint-staged'
  }
};
</code>

Create

lint-staged.config.js

:

<code>module.exports = {
  '{src,test}/**/*.ts': ['npm run lint', 'git add']
};
</code>

Create

commitlint.config.js

:

<code>module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    'subject-case': [2, 'always', ['upper-case']]
  }
};
</code>

Jest Testing

Jest provides fast parallel test execution, powerful mocking, built‑in coverage, DOM testing via JSDOM, and works with TypeScript via

ts-jest

. Install:

<code>npm i jest @types/jest ts-jest -D
</code>

Create

jest.config.js

:

<code>module.exports = {
  roots: ['<rootDir>/test'],
  transform: { '.(ts|tsx)': 'ts-jest' },
  testEnvironment: 'node',
  testRegex: '(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$',
  collectCoverage: true,
  coveragePathIgnorePatterns: ['/node_modules/', '/test/'],
  coverageThreshold: {
    global: { branches: 90, functions: 95, lines: 95, statements: 95 }
  }
};
</code>

Create a sample test

test/main.test.ts

:

<code>import Sum from '../src/main';

test('sum is right', () => {
  expect(Sum(1, 2)).toBe(10);
});
</code>

Add a test script to

package.json

:

<code>"test": "jest"
</code>

Conclusion

This article explained the differences between Webpack and Rollup, demonstrated how to build a simple JavaScript library with Rollup, and incorporated TypeScript, ESLint, Prettier, Husky, Commitlint, and Jest to ensure code quality. The complete project is available on GitHub: https://github.com/turning1998/baselib .

TypeScriptjavascriptWebpackJestRollupeslintprettier
37 Mobile Game Tech Team
Written by

37 Mobile Game Tech Team

37 Mobile Game Tech Team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.