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.

CSS Magic
CSS Magic
CSS Magic
How to Build a JavaScript Library with Webpack and ES6

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
+-- test

The 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 dev

invokes 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"
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

babelwebpackjavascript librarynpm scriptses6umd
CSS Magic
Written by

CSS Magic

Learn and create, pioneering the AI era.

0 followers
Reader feedback

How this landed with the community

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.