The perfect AMD setup
I've been using AMD modules (RequireJS) for various projects for a while. It has some advantages over 'CommonJS' (ie. Browserify) solutions, but has additional complexity around package installation.
One thing I disliked about AMD was having to maintain a large
paths config to resolve third-party package sub-dependencies. Here's an example of one of these:
This config means that anywhere in my app code or third-party code I will be able to call
require('gaia-component'), and it'll actually require
js/bower_components/gaia-component, under the hood.
gaia-component is a dependency of the
gaia-header module, this path config must be in place, otherwise
gaia-header will error.
Bower/AMD package installation process is involved and error prone:
$ bower install cool-package
- Search through source code of
cool-packageand identify any sub-dependency
- Amend the
require.configpaths to map any sub dependency paths to their correct locations.
- Run app, on failure return to step 2.
By contrast the NPM installation process is dreamy:
$ npm install cool-package
The good news is there's a better solution for Bower and AMD users which enables:
$ bower install cool-package
Seamlessly resolving any nested
require('some-sub-dependency'). Read on, all will be revealed...
A better solution
I was discussing this problem with James Burke and he kindly wrote a small command-line tool called 'adapt-pkg-main' to help. The documentation in the repo is thorough, but I'll try to outline briefly how it works.
Step One: Package main adapters
Adapt-pkg-main will look for packages in a given packages directory (eg.
bower_components/). For each package in the directory it will look for a package description file (eg.
package.json have the convention of a
"main" attribute which declares the package's entry point (eg.
Adapt-pkg-main will use this
"main" value to create a new 'adapter' module for each package like so:
cool-package.js adapter is AMD by default, but this 'adapterText' can be configured:
The second part of the trick is to configure RequireJS to assume all unprefixed paths to look in
After this one-time configuration, paths will be resolved as follows:
You'll notice that the unprefixed paths now resolve to the adapter files that
adapt-pkg-main created, so the full resolution (via the adapter module) will look like:
Once you have this require configuration, it makes sense to use relative style paths whenever you're requiring a file that doesn't live in
bower_components/, otherwise it'll have to prefixed with
root/. Just write your
require() paths exactly as you would in Node.
It makes sense to plug the adapter creation step into a Bower
postinstall hook so we can forget all about it. Create a
.bowerrc file at the root of you project (if you don't already have one) with the following:
Drop a comment if you have any questions or improvements :)
bower install sweet-dreams