Authoring a JavaScript library that works everywhere using Rollup

  1. The library is written in ES6+, using import and export keywords
  2. The library can be used with a <script> tag
  3. The library can be used in a web application that uses a modern bundler.
  4. The library can be used in a node application.
<html>
<head>
<script src="scripts/my-library.min.js"></script>
</head>
<body>
<div id="app" />
<script>
myLibrary.helloWorld();
</script>
</body>
</html>
define(["my-library"], function (myLibrary) {});// ordefine(function (require) {
var myLibrary = require("my-library");
});
import { helloWorld } from "my-library";helloWorld();
const myLibrary = require("my-library");
myLibrary.helloWorld();
// orconst { helloWorld } = require("my-library");
helloWorld();
  1. UMD (Universal Module Definition): this will support the use of a script tag, and RequireJS. As the consuming app will not be transpiling or bundling the code themselves, we need to provide a version of our library that is minified and transpiled for wide browser support.
  2. ESM (ES2015 Module): this will allow bundlers to import our application, elimintate dead code and transpile it down to the level they choose. We’re still compiling the code, but just providing it in a format that’s convenient for consumers and letting them decide what to do next. This will allow the import keyword to work.
  3. CJS (CommonJS): the format of choice for Node.js. No tree-shaking needed here as code size doesn’t matter as much, this format allows the use of the require keyword in a node application.
$ mkdir my-library
$ cd my-library
$ npm init -y
$ npm install rollup --save-dev
$ npm install @babel/core @babel/preset-env --save-dev
"dependencies": {},
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/preset-env": "^7.11.5",
"@rollup/plugin-babel": "^5.2.1",
"@rollup/plugin-node-resolve": "^9.0.0",
"rollup": "^2.28.2",
"rollup-plugin-terser": "^7.0.2"
},
$ mkdir src
$ touch .babelrc.json
$ touch rollup.config.js
{
"presets": [["@babel/env", { "modules": false }]]
}
import { nodeResolve } from "@rollup/plugin-node-resolve";
import { terser } from "rollup-plugin-terser";
import babel from "@rollup/plugin-babel";
{
// UMD
input: "src/index.js",
plugins: [
nodeResolve(),
babel({
babelHelpers: "bundled",
}),
terser(),
],
output: {
file: `dist/${pkg.name}.min.js`,
format: "umd",
name: "myLibrary",
esModule: false,
exports: "named",
sourcemap: true,
},
},
{
input: ["src/index.js"],
plugins: [nodeResolve()],
output: [
{
dir: "dist/esm",
format: "esm",
exports: "named",
sourcemap: true,
},
{
dir: "dist/cjs",
format: "cjs",
exports: "named",
sourcemap: true,
},
],
},
import { nodeResolve } from "@rollup/plugin-node-resolve";
import { terser } from "rollup-plugin-terser";
import babel from "@rollup/plugin-babel";
import pkg from "./package.json";const input = ["src/index.js"];export default [
{
// UMD
input,
plugins: [
nodeResolve(),
babel({
babelHelpers: "bundled",
}),
terser(),
],
output: {
file: `dist/${pkg.name}.min.js`,
format: "umd",
name: "myLibrary", // this is the name of the global object
esModule: false,
exports: "named",
sourcemap: true,
},
},
// ESM and CJS
{
input,
plugins: [nodeResolve()],
output: [
{
dir: "dist/esm",
format: "esm",
exports: "named",
sourcemap: true,
},
{
dir: "dist/cjs",
format: "cjs",
exports: "named",
sourcemap: true,
},
],
},
];
src
├── goodbye
│ ├── goodbye.js
│ └── index.js
├── hello
│ ├── hello.js
│ └── index.js
└── index.js
// src/index.js
export { default as hello } from "./hello";
export { default as goodbye } from "./goodbye";
// src/hello/index.js
export { default } from "./hello";
// src/hello/hello.js
export default function hello() {
console.log("hello");
}
// src/goodbye/index.js
export { default } from "./goodbye";
// src/goodbye/goodbye.js
export default function goodbye() {
console.log("goodbye");
}
“scripts”: {
“build”: “rollup -c”,
“dev”: “rollup -c -w”
},
// package.json
...
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
...
files: [
"dist"
]

Testing the library

Using the script tag, simply create an HTML file, and open it in a browser. You will see the word “hello” printed in the console.

<html><head>
<script src="dist/my-library.min.js"></script>
</head>
<body>
<script>
myLibrary.hello()
</script>
</body>
</html>
www
├── index.html
└── scripts
├── app.js
├── my-library.min.js
└── require.js
<html><head>
<script
data-main="scripts/app.js"
src="scripts/require.js"
></script>
</head>
<body>
</body>
</html>
requirejs.config({
baseUrl: "scripts"
});
requirejs(["my-library.min"], function (myLibrary) {
myLibrary.hello();
});
const myLibrary = require("../my-library");
myLibrary.hello(); // hello
myLibrary.goodbye(); // goodbye
$ npx create-react-app my-library-cra
$ cd my-library-cra
"my-library": "../my-library/"
import {hello} from 'my-library';
hello();

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store