Using browserify/webpack? Why not try jspm and SystemJS

Using browserify/webpack? Why not try jspm and SystemJS

Okay, so I get this vague idea that browserify and webpack are extremely popular bundling tools. They are all anyone ever talks about when it comes to that topic. Let me get this out straight away: I don't think these are bad tools, and they can possibly do some things better than what I'm about to tout, but I want the tool I love to use to get its fair share of the spotlight. So here we go.

Jspm and SystemJS takes a completely different stance to the other two bundlers I mentioned. In fact, by default it doesn't actually bundle anything. During development, all third party and local files are requested dynamically, as they are imported. It takes the stance of building code for the future web, with optional extras to deal with the fact that it indeed isn't the future just yet. A lot of that cruft can just be removed when browser support is more up to date. When HTTP/2 becomes a thing, we shouldn't even need to bundle anything at all.

Jspm

Jspm is a CLI tool similar to bower (but way more useful) - it allows you to download node packages for use in the browser. Now those coming from other bundling tools are probably pretty used to this. Jspm actually makes the distinction between your development tooling (node_modules) and something more akin to the way bower works, with a jspm_packages folder. I personally like this as I can't ever accidentally try and use node only code in my front end code.

It gets different here because you can also install from other registries than npm. Want to install bluebird from github? Just type the following:

jspm install bluebird=github:petkaantonov/bluebird

Or from npm:

jspm install npm:bluebird

There's even a registry hosted by jspm for the more popular packages, allowing you to install more simply:

jspm install bluebird

The majority of the time I will use the npm repository, but it's very often I've found there are packages out there that just aren't on npm.

Another great feature of jspm is the way dependencies are mapped.

System.config({
  "map": {
    "babel": "npm:babel-core@5.8.12",
    "babel-runtime": "npm:babel-runtime@5.8.12",
    "bluebird": "npm:bluebird@2.9.34",
    "core-js": "npm:core-js@0.9.18",
    "lodash": "npm:lodash@3.10.0",
    "react": "npm:react@0.13.3",
    "react-router": "npm:react-router@1.0.0-beta3",
    "github:jspm/nodelibs-buffer@0.1.0": {
      "buffer": "npm:buffer@3.3.1"
    },
    "github:jspm/nodelibs-events@0.1.1": {
      "events": "npm:events@1.0.2"
    },
    "github:jspm/nodelibs-path@0.1.0": {
      "path-browserify": "npm:path-browserify@0.0.0"
    },
    "github:jspm/nodelibs-process@0.1.1": {
      "process": "npm:process@0.10.1"
    },
    "github:jspm/nodelibs-stream@0.1.0": {
      "stream-browserify": "npm:stream-browserify@1.0.0"
    },

    ...

Example output

When you install an application, it automatically creates or updates a config file, which stores the dependency tree for your entire app. This is great for a number of reasons:

  • It automatically resolves non major versions to the latest minor version, as it can quickly tell that you have two versions of the same thing
  • It gives you the option to override, using jspm resolve --only npm:pkg@ver to convert your entire application to that version of a package. Great when your 3rd party dependency is relying on something you're not happy with
  • Gives full control to how your dependencies are named, where paths map to, shims, etc.

There's more to it, of course, but I only want to give a brief overview. Checking out the wiki will give a better idea of how it all works together.

SystemJS

This is where the magic really happens. SystemJS allows for dynamically loading from AMD, CommonJS, ES6/7 and even global based code, applying any compiling steps needed automatically. This made moving from requirejs to jspm a cinch: it already knew how to load it, and I could start writing new stuff in es6 that depended on the old stuff.

SystemJS uses the es6-module-loader which allows automatic transpiling for es6 using babel or traceur. It automatically handles polyfills for you too by detecting what is used in your code.

Add in a powerful plugin system and you have everything you need.


None of this is without its flaws.

These tools are much younger than browserify and possibly webpack, so there can be issues that make you want to tear your hair out. Saying that, fixes come out exceptionally quickly, and the creator and main contributor Guy Bedford is extremely dedicated (just look at that contribution chart) and this post.

I've noticed it can get very slow during development - and this is another problem with us being in the present and not the future. Bundling your third party dependencies does lessen the burden by a considerable margin though. This particular line from that last link is my saviour for now:

jspm bundle app/**/* - [app/**/*] dependency-bundle

It traces your entire app and its third party dependencies, and then removes your app from the bundle... leaving you with just your third party dependencies. This could just as easily be:

jspm bundle react + bluebird + react-router dependency-bundle.js

It is surprisingly powerful. If you load this bundle at the top of your file, any dynamic imports will find that they've already been loaded and continue on their way.


Until the web catches up, these 'fixes' are what we have to do for any sort of progress. But at least with jspm and SystemJS, when that time comes, we just remove them and our apps gets even faster.