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

<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();

--

--

--

Previously Paris and Geneva, currently New York. Can also be found at https://dev.to/arnaud

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

How to Build a Modal in Vue.Js

Using Javascript here is how you can make the “drag and drop” implementation on your Adobe XD…

The magic of Array iteration in Javascript

How to Make a Line Chart Using Javascript

Angular vs. React: Who is Winning?

React Native 9 Pros and 3 Cons

Set Up Webpack And Babel For Your WordPress Theme

Migrating to CSS in JS: Part 1

Photo of a flock of birds flying under blue sky during daytime

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
Arnaud Dostes

Arnaud Dostes

Previously Paris and Geneva, currently New York. Can also be found at https://dev.to/arnaud

More from Medium

Integrating Your New and Legacy React Applications with Asgardeo

Form Validation in React JS and React-Bootstrap without using any library

An investigative guide to React JS[DOM, Virtual DOM and JSX] Part-VIII

Conditional rendering: React