Pancake/RequireJS Build Step
We're using RequireJS for AMD module support. Require also has tooling for optimization/building, with some caveats. If built into a single file, the tiny Almond AMD Shim can replace RequireJS.
The Build Tool
r.js, the script that handles building can be run in Node (preferred) or Rhino. Installation:
npm install requirejs
Or, install globally to use as an executable:
npm install -g requirejs
Typically, you create a file called app.build.js with the misc configuration options to be passed to r.js. Here's a sample config with all the options. Assuming require.js is installed globally, run:
r.js -o app.build.js
You can also pass args to r.js directly.
Library Compatibility with Build Tool
We get the following error when trying to build with Zepto:
node.js:134 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: Error: Error evaluating module "undefined" at location "/...": ReferenceError: window is not defined fileName:/... lineNumber: undefined http://requirejs.org/docs/errors.html#defineerror
Zepto is not currently AMD-compatible. The aforementioned link contains a pull request for Zepto that adds AMD compat for client-side. The patch is very small: basically just a define returning the global Zepto object. In a vanilla project, using Zepto with the added define shim compiles without a hitch.
Backbone and Underscore do not support AMD. Previously, jashenkas said future versions probably will. More recently, he has moved away from that position. James Burke maintains forks of Backbone and Underscore that are AMD compatible. We are currently using them.
jQuery 1.7 is AMD compatible. The RequireJS jQuery sample project has a file called require-jquery.js, which combines RequireJS with a vanilla copy of jQuery 1.7. You can also separate require from jquery in the sample project; it works fine.
In an isolated test, loading Backbone, _ and jQuery, naming the jquery module $ causes a "window undefined" error. I think this is because RequireJS does some special stuff with modules named "jquery", requiring us to use that module name. Loading Zepto as $ causes no problems, though.
In an isolated test, compiling the following files into a module works:
'$', 'underscore', 'backbone', 'models/livecollection', 'models/sitemodel', 'views/listview', 'views/widgetview', 'views/singleview', 'lib/translatekeys', 'lib/decorateboot'
Adding config! to the the mix throws the error seen above. It looks like the compile error stems from the config plugin, not from jQuery or Zepto. Reducing config! to the simplest possible load method implementation still causes an error. It looks like we need to add a write method?
Creating (what I think is) the simplest possible plugin results in the same error:
define(['$'], function($) { var load = function (name, req, load, config) { load(); }; var write = function (pluginName, moduleName, write) { write(); }; return { load: load, write: write }; });
However, loading the text plugin is not an issue. Removing the $ dependency fixes the issue. We can even remove the write declaration:
define(function() { var load = function (name, req, load, config) { load(); }; return { load: load }; });
So it's the reference to window in the plugin context that causes issues. I think we can use the second AMD formulation to get around this by checking if we're in a build.
Side-note: I think plugin may require a file after the ! to operate on. Side-note: mapping custom paths for plugins doesn't seem to work so well.
AMD compatibility resources
The Use Require plugin is supposed to aid the issue of referencing globals... but I think it might be orthogonal to our issue. to investigate.
https://github.com/amdjs provides AMD-compatible forks of popular libraries.
https://github.com/volojs/volo is a tool by James Burke (of RequireJS) that includes an automated amdify command.
An example build plugin: https://github.com/jrburke/requirejs/blob/master/tests/plugins/indexBuilder.js