jashkenas/backbone — pull request #3421 by dgbeck (2014-12-22 06:24:53+00:00)
Title: Make jquery in commonjs a standard dependency

## Context

Looks like this all started back in #2862, which added a UMD wrapper in which jquery was omitted as a dependency when a commonjs environment was detected, based on the reasoning that commonjs is for the server side and jquery is not needed on the server side. In this first iteration of the UMD, `Backbone.$` was left undefined in the commonjs environment. However, commonjs was becoming increasingly common on the client side thanks to tools like browserify, Web Pack, component, cartero, et. al. #2862 was then merged so that backbone could be used with jquery in commonjs environments, which added a `require( 'jquery' )` but wrapped it in a try / catch. However, this "light dependency" approach "broke" browserify, as discussed in #2997, since browserify would fail if jquery was not present. As a result of this issue the [require + try / catch was removed](https://github.com/jashkenas/backbone/commit/f1479e62eb74a2dd85fdf2e20d9c907c5757313e) altogether, and it was made a requirement to explicitly set `Backbone.$` in all commonjs environments.

```
var Backbone = require('backbone');
Backbone.$ = require('jquery');
```

Unfortunately, we've seen this solution also has its share of problems:

* the requirement of setting `Backbone.$ = require('jquery')` is not intuitive and most people miss it, which is a constant source of friction (see #3038 and #3240).
* that pattern can not be used in Firefox plugins (see #3156).
* there are issues when a third party module requires jquery to be set on `Backbone.$` (see #3291). Under the current paradigm, eventhough the module needs jquery to be available at `Backbone.$`, potentially even at load time, the responsibly for putting it there falls on the application developer, not the module author. The module author has no way of guaranteeing what the application developer is going to put there when, and the application developer is burdened by what is really an internal concern of the module.

## Proposed solution

The proposed solution in this PR is to go with the straight forward solution of treating jquery as a standard dependency (as it is in AMD environments). What follows is a break down of how this solution would play out with the various use cases.

### On the server side (node)

#### For people using jquery
Everything just works.

#### For people who do not need a DOM library
jquery will install and will load when the node application starts up, as soon as backbone is `require`d, eventhough it is not needed. However both the disk space it consumes and the time it takes to load are too small to have any consequential impact.

#### For people who want to use a DOM library other than jquery
Same as above case, except that `Backbone.$` will need to be set to the alternate DOM library during startup. (Assuming node_modules has been [deduped](https://docs.npmjs.com/cli/dedupe), the backbone module is shared accross the entire application and thus setting `Backbone.$` at application startup is sufficient to ensure that the alternate DOM library is always used by backbone.)

### On the client side

#### For people using jquery
This is the most common case, I think. Using this approach, everything just works for this case as expected. (Note that `npm dedupe` still needs to be run to make sure that only one copy of jquery will be floating around.)

#### For people who want to use a DOM library other than jquery
This is the group of people who would be inconvenienced by this approach. Luckily the inconvenience is pretty minimal. They need to either

a) Explicitly set `Backbone.$` to the DOM library of their choice whenever a page is loaded, and then configure the build tool to ignore the jquery dependency (so that jquery is not dead weight in their js bundle). With browserify, the `exclude` option can be used to have `require('jquery')` return `undefined`:

```
browserify main.js --exclude jquery > bundle.js
```

With Web Pack,

```
{
plugins: [
new webpack.IgnorePlugin(/^jquery$/)
]
}
```

b) configure their build tool to "alias" jquery for another library. With browserify, this can be done with aliasify,

```
npm install --save aliasify
```

Add an "aliasify" section to the application's `package.json`:

```
{
"aliasify": {
aliases: {
"jquery": "zepto"
}
}
}
```

Using WebPack, the [resolve.alias](http://webpack.github.io/docs/configuration.html#resolve-alias) configuration option can be used:

```
{
context: __dirname + "/app",
entry: "./entry",
output: {
path: __dirname + "/dist",
filename: "bundle.js"
}
resolve : {
alias : {
"jquery": "zepto"
}
}
}
```

#### For people who want to use a DOM library with backbone and jquery in other places

Just set `Backbone.$` explicitly to the library of their choosing.

## Comparison with try / catch

Another solution worth considering is to go back to #2862 which wraps the `require( 'jquery' )` in a try / catch. This solution would also address most of the problems with the current approach. However,

1. I don't see a significant upside over the standard dependency approach.
2. #2997 would again be a problem, which we know for sure, since we have been there before.
2. The lack of clarity as to what is actually supposed to be at `Backbone.$` presents a problem for module and meta framework authors. I agree with @akre54's [comment](https://github.com/jashkenas/backbone/issues/2997#issuecomment-67651342):

> Marionette should be using the same jQuery as Backbone and the same jQuery as your app. It becomes an absolute mess when your view's $el property has a different $.fn than the rest of your app

But where is the global jquery object? External modules and meta frameworks can not assume `Backbone.$` is jquery and interact with it as such if there is no firm stance taken. What is `Backbone.$` supposed to be in the baseline case? Is it jquery? Is it zepto? What the heck is it?

If backbone has a standard dependency on jquery then it is clear - the baseline case is that `Backbone.$` === the global jquery object.