Authors: Marthinus Engelbrecht, Shingirai Rumuma
When I first started to write this blog we were using Bower and I was convinced that it was the right tool for the job. We needed a package manager for a browser based application and that is what Bower is. But I was getting some push back from 2 colleagues of mine for whom I have a lot of respect. This lead me on a journey of enriching my own understanding of package managers. After a lot of researching, I roped in my colleague Shingirai Rumuma to help me put together this blog.
We decided to compare the following package managers to help you choose the right tool for you:
- npm: commonly know as node package manager (Random Fact: It doesn’t actually stand for that.)
If you wish to just see our conclusion you can scroll down to the conclusion section. Or if you are interested in a more in-depth discussion please read on.
[alert type=”info” class=”” id=””]All of the examples in this article requires NodeJS[/alert]
We have hashed out some criteria that we believe are important for choosing the right tool, as follows:
- Type of dependency tree.
- Module System Support.
- Registry and repository structure.
- Private Registry Support.
Any constructive criticism is welcome, we are all here to learn. See the comments section at the bottom of the article.
Before we start, we just want to define what is meant when we use the following terms:
- Package, and
- Modules System
Dependency tree type
Bower works on the concept of a flat dependency tree, yes it’s a tree and it’s flat. You can only have one specific version of a package in your project (i.e. angular 1.3.0). Bower does this by forcing you to decide when there are conflicts.
However, you can install a dependency under an alias like below:
Bower doesn’t enforce a module system, which means some packages might use global variables. Because of this aliasing might cause one version of a package to overwrite the global variables declared by another version, leading to unexpected behaviour. The ability to install a package under an alias is only useful if you have a multi page app that uses different versions of a package or if you are using different packages that make use of a module system (i.e AMD, CommonJS or ECMAScript modules).
npm uses a nested dependency tree. Each individual module can make use of it’s own version of a dependency, but one module can’t have two versions of the same dependency. If you want to see an example of this you can clone this git repository, and run the index.js file using node.js. Nested dependency trees might lead to multiple versions of the same dependency being downloaded. You can reduce duplicate dependencies where possible by running the dedupe command:
$ npm dedupe
JSPM uses a flat versioned dependency tree, which allows you to install multiple versions of the same repository without aliasing. JSPM is tightly coupled to SystemJS module loader and unlike Bower it has an opinion on global state, preventing global variables unless you explicitly tell it to use global variables. This makes it useful for instance when you are migrating from one version of a framework to another version, that contains breaking api changes, but you want to migrate systematically over time.
Conceptually the tree of a JSPM application could look something like this:
It is really just a list of dependencies, where a dependency consists of a name and version.
You can install multiple versions of the same package using syntax similar to bower:
Module System Support
Modules systems significantly influence client/server side support due two main reasons:
- Asynchronous module loading in browsers.
- The need for some magic to make CommonJS module format work.
Bower could in theory work with any module system, it doesn’t have any specific module loader associated with it and it doesn’t care in any real sense what module system you use. However, you can list what module system your package supports in your bower.json as metadata that might be useful to others in the future.
Bower is mostly meant for browser-based applications and was built to be used there. For the most part, bower packages make use of either global variables or AMD modules.
JSPM supports any module system, but more specifically allows you to use CommonJS, AMD, and ECMAScript module systems together. It contains a repository of package-overrides that might include shims, this allows interoperability between the previously mentioned modules systems. The shims are used by SystemJS module loader, which then in-turn provides a compatibility layer to load any module format. This allows you to use different module systems together in the same application. You can also create your own package-overrides and submit a pull request to have it added to the repo. This is paving the road to the end of the disparity between CommonJS and AMD.
Some packages might need some manual configuration, you can find more information on it here.
npm also supports any module system, in fact, you can use npm just like bower, by including the paths to your node modules in your script tag and loading them in a browser. The problem is that most modules won’t work because npm encourages CommonJS by offering built-in support for it.
The advantage of supporting CommonJS is that you don’t have to worry about the relative/absolute path to each module, you can just reference it by name. This is possible due to each package specifying the path to its entry point and the name of the package in the main and name fields of the package.json file respectively. You can read more about it here.
There are 2 common ways of making the magic needed to get CommonJS to play nice with the browser happen:
- Using browserify. Ben Clinkinbeard explains really well how browserify works in his article.
I prefer using the latter because it allows you to use AMD modules alongside CommonJS and ECMAScript modules.
Registry and Repository Structure
Bower is really simplistic when it comes to this. It’s just an array of key value pairs, where the key is the name of the package and the value is the git clone url for the repository. Package version numbers are based on tagged branches in git. This simplicity is what I like most about bower. To register a package is a simple shell command:
After running this command all new tags will be available as new versions. You are also authenticated based on your git credentials. This negates the need of having separate credentials just for Bower.
npm makes use of a CouchDB database for its registry. Metadata about npm package’s are stored as a CouchDB document. The actual package is stored on a file system. Packages in npm are, simply put, tarballs. The metadata in CouchDB points to the location of the tarball.
To publish a package to npm you are required to register a user. New tags in your GitHub repository are not automatically available for install through npm as a new version of your package, you first need to publish them. However, the npm version command does automatically tag your GitHub repository.
There has been blogs written questioning the need for npm to have it’s own repository instead of just using GitHub. CouchDB and tarballs does seem to add a lot complexity to the mix, which Bower and JSPM have been able to avoid. With that said, GitHub started at round about the same time as npm.
JSPM allows interoperability with already existing registries by delegating the responsibility of repository operations: lookups, downloads and build operations to registry adaptors. Github and npm adaptors are provided by default. There is an adaptor available for bower here. (Note: ensure that you install jspm local to your project. We had issues where it couldn’t find the adaptor). You can also define your own adaptors, instructions on how to do it can be found here.
You can install anything from a registry for which you have an adaptor installed by using the following syntax:
$jspm install npm:firstname.lastname@example.org
JSPM’s own internal registry structure is pretty simple. It has a registry.json file, you can see it here. This file contains a list of key value pairs that represents each jspm package. The key is the name of the package and the value is a string that takes the format :. As mentioned previously, it also contains a repository of package-overrides to ensure interoperability with all module systems and other registries.
When you install a bower package which use the @ symbol for adding the version number not bower’s # symbol syntax.
JSPM doesn’t have it’s own repository, it leverages Github for this, in a similar way as bower. So if you want to create a new JSPM package you can create a Github repo, there is more information on configuring a JSPM package here.
Private Registry Support
npm’s private repo is a bit more complicated due to it using CouchDB.
There are 4 private solutions available:
- Sonatype Nexus supports npm. (It doesn’t allow you to use the npm search command)
- Artifactory also has a solution here
- npme (if you want to play with it you can find a docker container here)
- The instructions for the open source solution can be found here. (Note: This is more complex to set up. There is also a docker container for it here, which might make life a little easier.)
Bowers private registry installation is really simple:
- Install private bower:
- Run it:
- Add a .bowerrc file with the following in your component or app that uses the registry:
- It will then automatically go fetch non-private packages from the web outside.
- To publish a package you just register it as you would normally do:
JSPM’s private registry is free:
- To create your own JSPM registry, clone the JSPM registry.
$ jspm registry config jspm
Enter the registry repo path [email@example.com:jspm/registry]: path/to/jspm-registry/.git
- To add packages to your registry, push to your GitHub repository.
- To create a new version, tag your GitHub repository. More detailed instructions available here
Other important information
npm actually does two things, It’s a package manager and a build tool, which allows you to define scripts that can be executed using the npm run command. The code below would execute the jscs and jshint tasks on the source directory:
“quality”: “jscs source && jshint source”
So is Bower dead? Not yet, but I don’t see much use for it anymore. JSPM essentially replaces it, and provides access to a broader ecosystem of modules.
npm is by far the most solid package manager and it has stood the test of time, and has now released version 3 that is maximally flat. npm@3 automatically de-duplicates your node_modules on install. This results in a flatter dependency tree, helping it gel a little better with Windows. npm is actively seeking to become more client-side friendly. To demonstrate it’s popularity, npm is used install Bower and JSPM. npm’s usage is also encouraged by the fact that it’s included in NodeJS by default.
In information technology it’s difficult to predict what might happen, but I believe JSPM is the best option for client-side. You can use it for server-side applications but there are some issues. JSPM’s flat-versioned dependencies structure, ECMAScript module support, GitHub repository, and a simple key value pair as a registry is just beautiful. JSPM is cutting edge, and if you don’t mind bleeding a little to be ahead of the game I would definitely recommend it. In fact, I’m using it.