Blogi 25.5.2015

Why We Should Stop Using Bower – And How to Do It

Gofore

Kiva kun löysit tämän artikkelin! Se sisältää varmasti hyvää tietoa, mutta pidäthän mielessä, että se on kirjoitettu 9 vuotta sitten.

If you are using some of the currently common frontend toolchains, chances are you are using Bower. In short, Bower is a package manager for JavaScript libraries and other frontend dependencies. It’s something that usually sneaks in with other build tools. It’s lightweight and does its job, so you’ll probably just set it up and forget it. Turns out you don’t need Bower and, in fact, it may actually be causing some of your problems. Let’s take a closer look.

What is wrong with Bower

As a Bower user, you probably are more than familiar with this sight:
bower-unable-to-find-suitable
You may be accustomed to giving little hints to Bower so it can figure out which package versions are compliant. When you skim through the listed requirements, you cannot escape the feeling that something feels inherently wrong when your package manager needs your help for doing its core job.
The reason, as it turns out, is that Bower doesn’t support nested dependencies. If the packages you use contain subdependencies, Bower resolves them into a flat dependency list, which must satisfy all subdependency requirements. If Bower cannot find a version of the dependency that meets all the conditions, then you get a conflict and must resolve it by hand. Bower can persist this choice into a resolutions list and it’s often not a big deal when you are only dealing with one or two of these resolutions. But the more dependencies you add, the more difficult it becomes to find versions that satisfy all of your dependencies.
If your packages are large and few in number, flat dependency management may even be a plus. After all, it means that Bower is checking you don’t mix non-compliant modules. But that’s also the problem: in an attempt to avoid conflicts, flat dependency management encourages you to use fewer dependencies that do more things at once. A good example of this is jQuery and its plugins. jQuery contains many commonly required features like AJAX and data manipulation methods. Libraries that require these features, often register themself as jQuery plugins. This allows these plugins to share some common functionality. But this approach doesn’t scale very well: If you need to share code between several plugins, you will find yourself reinventing dependency management on top of jQuery.
Bower is also redundant. Given the recent popular trends in adopting node and npm in frontend build tooling, you probably already have a package management system. Practices in asset modularization have also evolved, leaving Bower with a lot of boilerplate you can get rid of. This includes not only your own code, but the repositories you reference. Bower blindly pulls out all of their files, including internal tooling and build scripts you don’t actually need or want. While later on ignore support has been added, it is an opt-in configuration not all modules obey.
The problems don’t end here. Bower is also unreliable. It is actually not a real package repository, but rather just a way to handle metadata. So when Github gets DDOSed, your build breaks. In Bower’s defense, things have been improving on this front. Bower does local caching and is also beginning to support private registries. But things on this front are still far from perfect. Bower’s package cache is global by default so CI configuration is easy to get wrong, which may lead to parallel builds breaking randomly. Also, in the absense of SaaS-provided private registries, hosting a private Bower yourself is still an endeavour not to be undertaken lightly.
Just don’t get me wrong: back in 2012 Bower was a good tool and probably the only good one to manage frontend assets. But things have moved forward. And so should we.

Step 1 – Move dependencies from bower.json to package.json

You are probably already using npm as part of your frontend build tooling. Most of your frontend packages are probably already available on npm too, so there’s little reason to pull them from Bower instead of npm.
Let’s consider the following bower.json as an example:

{
  "name": "my-bower-project",
  "version": "0.1.0",
  "dependencies": {
    "angularjs": "~1.3.15",
    "jquery": "~2.1.3",
    "lodash": "~3.6.0"
  }
}

The equivalent file in npm is package.json. If you don’t have it already, generate it with:

npm init

Now, proceed to move your current bower dependencies from bower.json to your package.json.
For example, when can add jQuery as npm dependency by invoking:

npm install --save jquery

Npm loads jquery into your local node_modules folder, and stores relevant metadata to package.json.
Repeat this for all of the dependencies that are available on npm and you end up with something like:

{
  ...
  "dependencies": {
    "angular": "^1.3.15",
    "jquery": "^2.1.3",
    "lodash": "^3.6.0"
  }
}

You can now load the dependencies straight from node_modules folder just like you loaded your Bower components from bower_components. Just with this small change, you already gain benefits. Unlike Bower, npm provides you with a real package repository. Also, since you don’t need to load packages from third party repositories, your builds are less likely to break.

Step 2 – Use require instead of wiredep

Dependencies in Bower are commonly wired together with wiredep. Wiredep is a tool that determines the right order of loading for your Bower packages and injects them into your source file inside placeholder comments.
Npm doesn’t work like that. With npm, you just write your application as a collection of CommonJS modules. Any module can import its dependencies and export a public API for consumption by its clients.
Say you got app.js that depends on several modules: jQuery, Lodash and Angular.js. With require you simply add these as dependencies:

var $ = require('jquery'),
    _ = require('lodash'),
    angular = require('angular');

Note that require doesn’t work in browsers out-of-the-box. Now, instead of wiredep, you now need another additional tool for resolving and bundling together these modules.
One of the popular options for bundling is Browserify.
First, start by installing Browserify globally as a command-line tool:

npm install -g browserify

We can now pass our app script to browserify:

browserify app.js -o bundle.js

And out comes a full, browser-friendly bundle which can be included with a single script tag:

<script src="bundle.js"></script>

Stay productive by installing and using watchify to efficiently recompile your bundle on each change:

watchify app.js -o bundle.js

Step 3 – Addressing any further concerns

Often that’s all you need to do. Just remove Bower from your dependencies and start building on your new stack.
However, you may still have some lingering concerns that we’ll attempt to address next.

But but but… I use private repositories!

No worries! You can generally still use them with npm:

{
  ...
  "dependencies": {
     "lib1": "git+https://jsalonen@github.com/jsalonen/lib1.git",
     "lib2": "git+https://[TOKEN]:x-oauth-basic@bitbucket.org/jsalonen/lib2.git"
  }
}

When it comes to popular repository hosting services, Github is the easiest to use as it has support for basic authentication. Bitbucket doesn’t support that, but you can still pull repositories off it by supplementing your dependency with an OAuth access token. Similar methods apply to other Git repository hosting services.
Note that npm also supports private registries. When you find yourself getting accustomed to npm, you may find yourself splitting your code into a number of smaller, private modules. With a private npm registry like npm Enterprise or Sonatype Nexus, you can publish and depend upon these modules with minimal hassle. Additionally, you can use private registries to mirror public npm modules to speed up module downloads and make your infrastructure more resilient to problems in the public part of the registry.

But but but… I can’t get rid of all Bower dependencies!

If you want to start using browserify but don’t want to port all your packages over to npm, you can still pull some of the packages from Bower with debowerify.
Let’s say you want to add a dependency to typeahead.js. Here’s how to install it with Bower:

bower install --save typeahead.js

The npm way would be to load it using require:

var typeahead = require('typeahead.js');

To enable this way of loading, install and add debowerify as a dependency:

npm install --save-dev debowerify

Then just use it as a browserify transformation:

browserify -t debowerify app.js -o bundle.js

Note that you still need to specify any such dependencies in bower.json and install them with Bower.
Debowerify only works if the ”main” entry is defined in the component’s bower.json.

But but but… I need modules outside Bower and npm!

Vendor scripts relying on browser DOM can be adapted to Browserify with browserify-shim.
If you need to use scripts relying on Asynchronous module definition (AMD), check out deAMDify.

But but but… my frontend and backend dependencies get mixed!

If you want to keep your server-side and browser-side applications decoupled, you can just create two separate npm modules and manage dependencies separately.
Sometimes decoupling server and client is not desirable. This could be the case if you are writing an isomorphic application (e.g. a single-page app that uses server-side prerendering).
If this is the case, specify dependencies in package.json as usual. Proceed to use the browser field in package.json to override file resolution for browser-specific versions of files.
Say that you want to use lodash on the client and server, but want to use a modern build on the server and compat build in the browser.
Begin by adding both lodash versions as npm dependencies:

npm install --save lodash
npm install --save lodash-compat

Proceed to add a browser field entry for lodash to hint that you want the compat build whenever lodash is required in the browser:

{
  ...
  "browser": {
    "lodash": "lodash-compat"
  }
}

Different versions of lodash are now used on the server and in the browser, allowing you to adapt your code to both environments!

Final words

I have to be honest with you: Bower isn’t really completely evil and I actually think it really works for a variety of tasks. Bower is also being actively developed, and some of the issues I pointed out in the beginning will probably be addressed in the future.
The bigger thing is that the JavaScript ecosystem has changed. Tools like npm and browserify allow us to modularize our applications without coupling our modules to the DOM and relying on global state. With the upcoming ES6 module standard even more is yet to come. So why don’t you jump on the module bandwagon today and even more great things will soon come your way!

Further Reading

npm and front-end packaging (The npm Blog)

Browserify VS Webpack – JS Drama (Namal Goel)

The jQuery Module Fallacy (Ben Drucker)

Why MongoDB Didn’t Conquer the World: Part 1 (Juhana Huotarinen)

 
We are always on the lookout for skilled developers to join our award-winning team. We have exciting projects running throughout 2018 in Finland, Germany, Spain and the UK – get in touch to find out more

Jyväskylä

Takaisin ylös