1. 4
    Add a browser build to an npm module
    6m 16s

Add a browser build to an npm module

Kent C. Dodds
InstructorKent C. Dodds

Share this video with your friends

Send Tweet
Published 6 years ago
Updated 3 years ago

Currently, our library is being distributed as a CommonJS module, but we should support the browser as well. In this lesson, we're going to use webpack to create a UMD (Universal Module Definition) build of our module so users can consume it in a browser.

[00:00] So far, we've only considered node as a consuming platform for our module. There could be people who want to use our library in the browser. Right now, with the way that we distribute our file, we're depending on Require, which is CommonJS, which the browser does not support.

[00:16] We're going to add support for using our module in the browser. We're going to use something that's called UMD, or Universal Module Definition. To do this, we're going to use a JavaScript bundler called webpack. We'll install that as a dev dependency.

[00:29] Next, we'll create a new file called webpack.config.babel.js. The .babel allows us to write our webpack configuration in ES6.

[00:39] I'm going to go ahead and pre-fill this. We are importing join from path. We have this include being dirname and source. We're using that for includes on our loaders to run these loaders only on things inside of our source directory.

[00:57] Then, we have our entry as the index file. Our output will be in the dist directory. Our library target will be UMD, Universal Module Definition. It works in CommonJS, the browser, and AMD. Then, the context over browser, the library will be called starwars-names. You can get that off of the window object.

[01:20] Then, we have the source map dev tool so that we include source maps in our build. Now, because we're including the babel-loader and json-loader, we'll need to install those.

[01:32] Now, let's create our npm script for building these. We already have a build script. I'm going to rename this to build-main because we're going to have three build scripts. The next build script is going to be build-umd. Then, we'll have a third build-umd.min.

[01:51] For build-umd, it will simply be webpack --output-filename index.umd.js. It will be very similar for our min. The only difference will be we'll add a min here for the filename. We'll add the -p, which means run this in production mode. That will tell webpack to minify our code and optimize it for delivery to a browser.

[02:15] Now, if we run these scripts, we can run npm run build-umd. Let's check out our dist. We now have this UMD file. This is the Universal Module Definition function here. It has all of our code in there. We even have our json directory in here as a module export.

[02:36] Everything will work fine even in a browser. You'll notice also in this bundle that it includes the code for unique-random-array. Actually, unique-random-array has dependencies in that code as included as well.

[02:47] This is probably fine for a module like this one, but if you're building a plug-in for React or Angular, then you'll probably not want to bundle the entire library in your small module. You'll want to adjust your webpack configuration a little bit so that webpack excludes those things from the bundle, and instead requires those based off of the environment that it's in, whether it be global, or CommonJS, or AMD.

[03:14] That's something to definitely be aware of if you're building something that should sit alongside of another bigger library like React, or Angular, or Ember.

[03:25] Let's also go ahead and run our npm run build-umd.min. This will generate the minified version of this file optimized for delivery to a browser.

[03:38] Now, we have three scripts that we need to make sure we run before we do any releasing of our library. We're going to utilize a package called npm-run-all, which will help clean up our scripts a little bit.

[03:49] Install this as a dependency. With that installed, we'll add a build script that will utilize npm-run-all. It will run these in parallel because they are not dependent on each other at all. We'll run build:*. That will include build-main, build-umd, and build-umd.min.

[04:13] Now, we can run npm run build. Remember, we'll run our pre-build to remove the dist directory. Then, it will run this build script to run all of these build scripts in parallel.

[04:24] Now, we'd look at the dist directory. We have our index. We also have our starwars-names still. This can be consumed in a CommonJS scenario easily.

[04:34] Then, we have our UMD. This can be consumed in a browser, or in a CommonJS scenario, or in AMD scenario. Then, we have our umd-min, which is the same thing, just minified. That is what we need to do to support the browser.

[04:51] To review, there are a couple of things that we did. First, we installed webpack. Then, we created this webpack configuration. Then, we added json-loader and babel-loader as our dependencies. We also created a couple of scripts here.

[05:07] We changed our current build to build-main. Then, we added build-umd and umd.min. Then, we changed our build script to an npm-run-all script, which runs all of our builds in parallel.

[05:19] A final word about distribution. We're not actually committing these generated files to Git. People can't go to our GitHub repo and download these UMD files. Instead, we can point people to npmcdn.com where they can get these files.

[05:35] We can see an example of that here. If you go to npmcdn.com, you'll see the example of React. Here, they have the react.min.js. This is the entire React library.

[05:47] For our library, we could simply go to starwars-names. This is going to pre-load us with the default, which currently can't be consumed by the browser. When we actually publish this, people would be able to go to dist/index.umd.js. This will actually have the file that we're publishing to the registry. That's how people will consume our package if they're using the UMD build.

Mauricio Robayo
Mauricio Robayo
~ 4 years ago

For webpack v4:

import { join } from "path";

export default {
  entry: "./src/index.js",
  output: {
    path: join(__dirname, "dist"),
    libraryTarget: "umd",
    library: "starWarsNames"
  devtool: "source-map",
  module: {
    rules: [{ test: /\.js$/, use: "babel-loader" }]

And the npm scripts for webpack:

    "build:umd": "webpack --output-filename index.umd.js --mode development",
    "build:umd.min": "webpack --output-filename index.umd.min.js --mode production",
~ 4 years ago

Thank you for the updated script!

Babs Craig
Babs Craig
~ 3 years ago

You might also need to npm i -D @babel/core and make sure to install npm i -D babel-core@7 if you get this error:

Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: Cannot find module '@babel/core'

You want both the babel-loader and babel-core to be the same version - in this case version 7. Also, take out: { test: /\.json$/, use: "json-loader" } or it will cause an error as per: