How to Build a JavaScript Library with Webpack and ES6
This tutorial explains how to define a JavaScript library, set up a clean project structure, use Babel to transpile ES6, configure Webpack for development and production builds (including UMD output), add npm scripts, run Mocha/Chai tests, and manage related configuration files.
Definition of a JavaScript library
A library is a piece of code that provides a specific function, does one thing well, and ideally has no dependencies on other libraries or frameworks. Examples include jQuery, React, and Vue.
Required characteristics
Usable in a browser via a <script> tag.
Installable through npm.
Compatible with ES6 (ES2015) modules, CommonJS and AMD.
Project layout
+-- lib
| +-- library.js
| +-- library.min.js
+-- src
| +-- index.js
+-- testThe src directory holds source files; the lib directory holds the compiled output. The library’s entry point must be placed in lib, not in src.
Build process
Transpiling with Babel
Babel converts ES6 files to ES5 but does not resolve import or require statements, so a bundler is required.
+-- src
+-- index.js (es6)
+-- helpers.js (es6)After Babel the directory looks like:
+-- lib
| +-- index.js (es5)
| +-- helpers.js (es5)
+-- src
+-- index.js (es6)
+-- helpers.js (es6)npm scripts
npm run build– generates the final compressed library file. npm run dev – generates an uncompressed development bundle and starts a watch process. npm run test – runs the test suite.
Development build with Webpack
npm run devinvokes Webpack using the following configuration:
var webpack = require('webpack');
var path = require('path');
var libraryName = 'library';
var outputFile = libraryName + '.js';
var config = {
entry: __dirname + '/src/index.js',
devtool: 'source-map',
output: {
path: __dirname + '/lib',
filename: outputFile,
library: libraryName,
libraryTarget: 'umd',
umdNamedDefine: true
},
module: {
loaders: [
{ test: /(\.jsx|\.js)$/, loader: 'babel', exclude: /(node_modules|bower_components)/ },
{ test: /(\.jsx|\.js)$/, loader: 'eslint-loader', exclude: /node_modules/ }
]
},
resolve: {
root: path.resolve('./src'),
extensions: ['', '.js']
}
};
module.exports = config;The configuration defines the entry point ( src/index.js), the output location ( lib/library.js), and applies Babel and ESLint loaders. Omitting library, libraryTarget and umdNamedDefine results in a bundle that cannot be consumed as a library.
When those fields are present, Webpack injects a UMD wrapper similar to:
(function webpackUniversalModuleDefinition(root, factory) {
if (typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if (typeof define === 'function' && define.amd)
define('library', [], factory);
else if (typeof exports === 'object')
exports['library'] = factory();
else
root['library'] = factory();
})(this, function() { /* ... */ });Production build
The only difference between development and production builds is minification. Adding UglifyJsPlugin to the plugins array and setting the environment variable WEBPACK_ENV=build produces library.min.js:
var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
var env = process.env.WEBPACK_ENV;
var libraryName = 'library';
var plugins = [], outputFile;
if (env === 'build') {
plugins.push(new UglifyJsPlugin({ minimize: true }));
outputFile = libraryName + '.min.js';
} else {
outputFile = libraryName + '.js';
}
var config = {
entry: __dirname + '/src/index.js',
devtool: 'source-map',
output: { /* same as development */ },
module: { /* same as development */ },
resolve: { /* same as development */ },
plugins: plugins
};
module.exports = config;The --watch flag in the dev script enables continuous rebuilding during development.
Testing
Mocha and Chai are used for tests. Because test files are written in ES6, Babel must compile them first. The test script is:
"test": "mocha --compilers js:babel-core/register --colors -w ./test/*.spec.js"The --compilers option tells Mocha to preprocess each test file with Babel before execution.
Additional configuration files
.babelrc
{
"presets": ["es2015"],
"plugins": ["babel-plugin-add-module-exports"]
}.eslintrc
{
"ecmaFeatures": { "globalReturn": true, "jsx": true, "modules": true },
"env": { "browser": true, "es6": true, "node": true },
"globals": {
"document": false, "escape": false, "navigator": false, "unescape": false,
"window": false, "describe": true, "before": true, "it": true,
"expect": true, "sinon": true
},
"parser": "babel-eslint",
"plugins": [],
"rules": { /* rules omitted for brevity */ }
}Repository and dependencies
Starter project repository: https://github.com/krasimir/webpack-library-starter
Dev dependencies (excerpt):
{
"babel": "6.3.13",
"babel-core": "6.1.18",
"babel-eslint": "4.1.3",
"babel-loader": "6.1.0",
"babel-plugin-add-module-exports": "0.1.2",
"babel-preset-es2015": "6.3.13",
"chai": "3.4.1",
"eslint": "1.7.2",
"eslint-loader": "1.1.0",
"mocha": "2.3.4",
"webpack": "1.12.9"
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
