JSPM is a package manager on top of the SystemJS loader. Advertised as frictionless, it gives you all the comfort of modern JS development with ES6 modules, Babel transpilation, packaging, and proper dependency management.
Install & init
To get started, the easiest way is to use NPM:
npm install -g jspm
(you might need to add sudo in some cases)
The next step is to initialize JSPM. Theoretically, you could do it by hand, but it's far easier to use the built-in next-next-finish-style wizard.
This creates (or updates) a package.json with a jspm section. This lists the jspm dependencies, separated from the other ones.
Also creates a config.js, which is the main configuration file. The first part reflects what you answered at the wizard, and there is a map property which maps the dependencies' names to paths.
For a simple hello world example, create a main.js with the content:
Then add an index.html:
The important parts are the three script tags. The system.js is the SystemJS loader, which is the core element. The config.js is the configuration we've just generated; it specifies how to resolve the dependencies and also some other configurations.
Then the System.import loads the script, which in turn prints the Hello world! to the console.
To run it, simply host it on a web server and open in a browser. There is no compilation step, every change is reflected in the page after a reload. The easiest way is to use
npm install -g http-server, followed by
http-server, which hosts the current directory at port 8080.
All we've added so far is boilerplate. Let's do something useful with JSPM and add an external dependency!
To add a dependency, the easiest way is to use the JSPM CLI, which takes care of not only adding it to the package.json, but also updates the config.js accordingly.
To install lodash, simply issue:
jspm install lodash
Then it's ready to use in your scripts. Update the main.js to use the newly introduced dependency:
import _ from "lodash";
console.log(_.map([1, 2], (i) => i + 1));
As you can see, you can use ES6 imports out-of-the-box. The modules themselves can use ES6, AMD, CommonJS, and global. As most of the modules use UMD, which is a combination of formats for compatibility, JSPM should work with just about anything.
Jspm install uses the JSPM registry by default. You can use GitHub or NPM if you'd like by adding the github: or the npm: prefix to the package name.
To uninstall something, again, use the JSPM CLI:
jspm uninstall lodash
This removes the package from the package.json as well as from the config.js.
Add CSS & SCSS
Since CSS is just a static file that does not need any preprocessing, you can simply put it next to your app and reference it from the html. This works, but this approach is not modular. After a while, you'll end up with a bunch of link tags, and the link between the component and its styling is lost.
Fortunately, plugins can be added that control how resources are processed. One such plugin is plugin-css , which, as you can probably guess, loads css files.
Add a style.css, with some easily-recognisable content:
background-color: red !important;
Then install the plugin with JSPM CLI:
jspm install css
And finally, add the reference to the main.js:
Note the bang after the filename.
After reloading the page, the background is now red, which indicates that CSS imports are working.
While CSS is good for some quick styling, any moderately-sized project would benefit from a CSS preprocessor. Some people prefer LESS over SCSS, but I tend to use the latter.
To add SCSS support, first rename the style.css to style.scss. Then add some SCSS-only stuff, just to verify it's indeed preprocessed:
background-color: $color !important;
The next step is to install the Sass plugin:
jspm install scss=sass
And finally, update the reference in the main.js to point to the renamed file:
After a page reload, the background is now blue.
One other thing that you might notice is that it takes a few seconds to load the page. This is because everything runs inside the browser, and it needs to download like 7 MBs of libraries just to do the SCSS compilation. This sluggishness is gone in production, as packaging runs these compilations ahead of time.
Bootstrap integration is a particularly good challenge for build tools, as it contains static files (fonts) as well as js and css, and it's also trivial to check visually.
The first step is to install the dependencies:
jspm install jquery bootstrap css
Then update the main.js to include both the js and the css from Bootstrap:
import $ from "jquery";
import bootstrap from "bootstrap";
Then add some visuals to the page, both some Bootstrap classes, and an icon to test-drive the fonts too:
<span class="glyphicon glyphicon-search"></span>
After a refresh, the magnifier icon is shown with the bootstrap margin.
Package to production
Up until this point, the user experience is questionable. To say the least, loading SystemJS adds an extra roundtrip, let alone various plugins might load a lot of libraries.
JSPM supports packaging the app, which not only compiles everything ahead of time, but also packages the app into a single script. This renders the bundle on par with other tools in regard to performance.
To bundle the main.js into bundle.js, use:
jspm bundle-sfx main bundle.js
After that, instead of all the other script tags, just include the bundle:
The app works as before, but is considerably faster.
To further save a few bytes, JSPM can minify too. To enable it, use the
jspm bundle-sfx main bundle.js --minify
Notice that it will also mangle the variables. As some libraries rely on names to work (most notably Angular 1.2 for dependency injection), it may break your app. To disable it, use
--no-mangle. This increases the bundle size by a few bytes, but it's hardly noticeable after gzip.
Problems with the packaging
At first sight, it seems your app is contained entirely in the bundle.js, and that's the only file to deploy to production. But unfortunately, this is not true. If you look at the bootstrap example, you'll notice that the fonts are still served from the jspm_packages folder. In effect, the jspm_packages folder needs to be served along with your app, with all the dependencies, making the package unnecessarily hefty.
Another problem is that URL rewriting is done by the individual plugins (for example, the icons in bootstrap are located by the css plugin). This means that the configuration is likely to be different depending on which plugins you use. As a challenge, try to work your way with the Sass plugin to integrate Bootstrap.
Also, you might have a hard time referencing static files directly from js. For example, there is an images plugin, but that does not do any packaging.
The first thing I've noticed is that it's very easy to get started with JSPM. With just a few commands and a few lines of code, you have a project up and running with proper dependency management, Babel transpilation, and ES6 modules.
I also liked that the development runs entirely inside the browser, without any server-side tools. Just modify and reload, only a basic http server is needed.
The config.js gives control over how plugins and the loader work. There are many subtle options that can be set up here.
And finally, there are plugins for the basic things. If you are just getting started with JSPM, it's likely that you'll find a plugin for your needs.
What I immediately noticed is that the package.json and the config.js must be in sync at all times, otherwise the dependencies are likely to break. The CLI tools, like jspm install and jspm uninstall take care of updating everything, but keep in mind that you have to use them.
As the CLI tools manage parts of the package.json and the config.js, it makes them fragile. You edit some parts of them by hand, but you manage other parts via tools. This also makes merging more difficult, in case of a conflict.
Handling static resources seems unsolved. There is no central way of doing this (except to reference them by hand to the jspm_packages), and it's controlled by the plugins. This makes the whole process inconsistent; for some use cases, it works like a charm, but for others, it requires googling around and diving into various configurations.
The biggest drawback I've seen is that you need to serve the jspm_packages folder along with the "self-executing" bundle, which defeats the notion of self-executability.
On most cases, you can alleviate this by using a different build tool along with JSPM, like Grunt or Gulp, but those need different configurations.
Finally, after I added the Sass plugin, the developer experience deteriorated. It took more than 4 seconds to load all the files, which is simply too long. As far as I know, there is a dev server which supports live reloading, that might solve this with some config.
JSPM is a well-engineered technology and I really like the notion that it runs everything in the browser, a unique feature of its kind. But given the alternatives and its clumsy way of handling some basic web-dev tasks, I'm unlikely to choose it for my next projects. As I've seen from various places, some people are using it successfully; I'm sure that after you've figured out the workarounds for your particular project, it does a good job. But unless you have a good reason, consider choosing something else.