Like building applications, webpack can be used to build libraries. In this post, you will learn how to use webpack and babel to build your own library.
Demo Library
Assuming having the following project, a small library called even-numbers
with exported isEven
and isOdd
methods.
Files
.
├── src
│ └── index.js
└── package.json
src/index.js
export const isEven = (number) => {
if (isNaN(number)) {
throw new TypeError("The first argument must be a number");
}
return number % 2 === 0;
};
export const isOdd = (number) => !isEven(number);
Goals
-
I want my library to be compatible with all the long-term support nodejs versions.
-
I want my library to be compatible with all the browsers with a market share bigger than
1%
. -
Different developers have different javascript stacks, and I would like my library to be consumed in various ways as well.
ES Module import
import { isEven, isOdd } from "even-numbers";
CommonJS Module require
const { isEven, isOdd } = require("even-numbers");
AMD Module require
require("even-numbers");
require(["evenNumbers"], function (evenNumbers) {
const { isEven, isOdd } = evenNumbers;
});
Directly in the browser
<html>
<script src="https://unpkg.com/even-numbers"></script>
<script>
const { isEven, isOdd } = window.evenNumbers;
</script>
</html>
Step-1 Installing babel
Install @babel/core
and @babel/preset-env
as development dependencies.
@babel/preset-env
is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s).
yarn add -D @babel/core @babel/preset-env
# or using npm
npm install --save-dev @babel/core @babel/preset-env
Create a .babelrc
file in your project root, with the following content:
{
"presets": [
[
"@babel/env",
{
"targets": "> 1%, maintained node versions"
}
]
]
}
Those configurations would make sure our build will be compatible with:
> 1%
- All the browsers with a market share bigger than1%
.maintained node versions
- All the currently maintained nodejs versions, see: https://nodejs.org/en/about/releases/
Step-2 Installing webpack
Install webpack
and babel-loader
as development dependencies.
babel-loader
is a webpack loader; it will allow webpack to use babel
to build javascript files.
yarn add -D webpack babel-loader
Create a webpack.config.js
file in your project root, with the following content:
const path = require("path");
module.exports = {
entry: path.resolve(__dirname, "./src/index.js"),
output: {
path: path.resolve(__dirname, "./dist"),
filename: "index.js",
globalObject: "this",
libraryTarget: "umd",
library: "evenNumbers",
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
},
],
},
};
The first part of the config is the entry
field, and this is the entry point of the library:
entry: path.resolve(__dirname, './src/index.js'),
The second field is the output
, and this will configure what output webpack should produce.
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index.js',
library: 'evenNumbers',
globalObject: 'this',
libraryTarget: 'umd',
},
-
path
together withfilename
- will be resolved to the build result destination, in this case,./dist/index.js
. -
library
- the library name. Will be used to consume the library. -
globalObject: 'this'
- will make the library publicly available by usingglobal.evenNumbers
in node orwindow.evenNumbers
in the browser. -
libraryTarget: 'umd'
- will make the library available when using AMD or CommonJS
The last part is the module
filed where we add the babel-loader
so webpack will build the javascript using babel
together with the configurations in the .babelrc
file.
This will add the compatibility for all platforms.
Step-3 Make the library available by the library name when using AMD or CommonJS
Add dist/index.js
to the main
field in the package.json
:
{
"main": "dist/index.js"
}
This will make the library available to import/require while using the library name.
import { isEven, isOdd } from "even-numbers";
Last steps - Create the build script and use it
Add build
and develop
scripts to the package.json
:
{
"scripts": {
"build": "webpack --mode production",
"develop": "webpack --mode development --watch"
}
}
To build the library, run:
yarn build
# or using npm
npm run build
This will produce the following output:
$ webpack --mode production
Hash: 187393c09f5eb7fe7a4e
Version: webpack 4.41.4
Time: 241ms
Built at: 12/28/2019 5:07:55 PM
Asset Size Chunks Chunk Names
index.js 1.49 KiB 0 [emitted] main
Entrypoint main = index.js
[0] ./src/index.js 1.05 KiB {0} [built]
Done in 0.66s.
And will create the ./dist/index.js
file with the library, ready to be consumed.
To build the library in watch mode, run:
yarn develop
# or using npm
npm run develop
Optimization Bonus - Using common libraries
Many libraries use other common and popular libraries behind the scense. Such an example can be a library that exports react components.
In this case, you would want to avoid building react
into your library bundle and instead trust the consumer to provide react
and require it before the library.
We can tell webpack not build react
into the library target by adding the externals
field to the webpack config.
module.exports = {
...
externals : {
react: 'react'
},
...
};
If the library uses jquery
and lodash
, those can be added as well:
module.exports = {
...
externals : {
react: 'react',
jquery: 'jQuery',
lodash : {
commonjs: 'lodash',
amd: 'lodash',
root: '_' // indicates global variable
},
},
...
};
Happy Hacking!!!
Useful
- node-mdl-starter - A starter project for open sourced JavaScript libraries.
- generator-node-mdl - Generate an open source JavaScript libraries.