An unexpected journey with webpack and RequireJS

So the other day I ran into what seemed like a bug in some software I was using during a test. Narrowing things down in the codebase, it looked as though the error may have been related to a library in use by the application I was testing.

I needed an easy way to isolate the code that seemed to be causing the error from the rest of the application, which took me down a rabbit hole to learn a little bit more about a few different technologies.

The Situation

So I had a JavaScript project that I wanted to build and include in a little test webpage. A few things in the repo stood out:

  • package.json - So it's using node.js in some measure.
    • This also had a number of entries under the scripts section, including some that made use of gulp (another build tool)
  • gulpfile.js - Another strong indicator that gulp was in use. Didn't need to dig too deeply into this side of things, though it did indicate that webpack was in use.
  • webpack.config.js / webpack.prod.config.js - The webpack configuration files.

So now I had a fair idea of what we had to work with, time to try and build it.

Building with npm and gulp

This part was actually extremely straightforward, so I won't spend too long here. After cloning the repo, all I had to do was:

  • npm install
  • npm build (which is just a script that runs gulp build)

After a bunch of downloads and a few warnings, I had 3 new files sitting in my ./build folder. So easy!

  • idtoken-verifier.js
  • idtoken-verifier.min.js
  • idtoken-verifier.min.js.map

For the purposes of my standalone test website, I should only need the main file.

So I built it, but how do I use it?

Now that I had the library built, I just had to include the script in a little test webpage, call the functionality I needed, and I'd be done. But looking at the start of the built script.. it didn't look as straightforward as I expected.

(function webpackUniversalModuleDefinition(root, factory) {
    if(typeof exports === 'object' && typeof module === 'object')
        module.exports = factory();
    else if(typeof define === 'function' && define.amd)
        define("idtoken-verifier", [], factory);
    else if(typeof exports === 'object')
        exports["idtoken-verifier"] = factory();
    else
        root["idtoken-verifier"] = factory();
})(this, function() {

Looks like webpack plays a part here.. and something called a 'universal module definition'.

Webpack

I started off by doing a little reading about webpack config, and in particular this umd thing:

So it seems that webpack allows you to export 'wrapped' modules in a number of formats, and umd is a combination of amd, commonjs2 and/or a property in the root.

Sure enough, looking at the output section of the webpack config I came across some references to umd:

output: {
  path: path.join(__dirname, '../build'),
  filename: '[name].min.js',
  library: 'idtoken-verifier',
  libraryTarget: 'umd',
  umdNamedDefine: true
},

At this point I decided to learn how to use an amd module, and after some quick googling I was lead to RequireJS.

RequireJS

RequireJS is a JavaScript module loader, and supports AMD (or 'Asynchronous Module Definition') modules. Since I just wanted to get this code working, I went back to Google to find a quick example of how to use RequireJS, finding myself on the following page:

So it sounds like RequireJS handles a lot of the module injection/ordering that I would have otherwise had to think about myself. I decided to make life easy for myself and use a CDN hosted version, so all I had to do was stick the following code in the head section of my test page:

<script data-main="main" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.js"></script>

This loaded RequireJS and told it to inject the main.js file, which looks like:

require(['idtoken-verifier'], function(IdTokenVerifier){
    var verifier = new IdTokenVerifier({});
    // Do interesting things here..
});

Sticking to the 'easy and obvious' pattern that I'd seen so far, this would inject the idtoken-verifier.js file, and then make the library accessible inside this require block. Nice!

Bringing it all together with Plunker

I wanted to keep all of the files together in an easily accessible format so others could test and play with it. After a little looking around I ended up using Plunker since it let me have multiple files (unlike JSFiddle):

Success?

I found that trying to test the Plunker hosted version complicated things a bit, so I ended up downloading the zipped files and hosting them locally with Python while I tested things:

python -m SimpleHTTPServer

Running it through the tool.. everything worked as expected, no bugs. sadface I could have spent more time digging into the specifics and trying to unearth what actually lead to the bug in the first place, but given I had other things I needed to test I decided to leave that as a possible future endeavour, if I run into it again.

Conclusion

So an unexpected rabbit hole lead me to understand a little more about Webpack, RequireJS and how to use packaged JavaScript applications. Not a bad little lesson :)

Had a similar experience or got any interesting tips to share about any of these technologies? Would love to hear from you in the comments!