I'm trying to modularize my existing project by breaking out functionality into separate applications that share a lot of common code. It's a Backbone/Marionette app, and everything is working fine in development mode, but I'm having trouble getting optimization to work. I currently have two pages, with 2 main files and 2 application files. The main
files both contain requirejs.config blocks which are almost identical, except the second one uses the map
config option to map the app
module to loginApp
. The reason for this is that most of the other modules depend on the app
module for some application-wide functionality, including messaging and some global state variables.
main.js:
requirejs.config({
shim: { ... },
paths: { ... }
});
define(['vendor'], function() {
// This loads app.js
require(['app'], function(Application) {
Application.start();
});
});
main-login.js:
requirejs.config({
shim: { ... },
paths: { ... },
map: {
"*": { "app": "loginApp" }
}
});
define(['vendor'], function() {
// This loads loginApp.js because of the mapping above
require(['app'], function(Application) {
Application.start();
});
});
This works great until I optimize. I'm getting an error about a missing file, but having worked with requirejs long enough, I know that really has nothing to do with the problem. :)
From the docs:
Note: when doing builds with map config, the map config needs to be fed to the optimizer, and the build output must still contain a requirejs config call that sets up the map config. The optimizer does not do ID renaming during the build, because some dependency references in a project could depend on runtime variable state. So the optimizer does not invalidate the need for a map config after the build.
My build.js file looks like this:
({
baseUrl: "js",
dir: "build",
mainConfigFile: "js/main.js",
removeCombined: true,
findNestedDependencies: true,
skipDirOptimize: true,
inlineText: true,
useStrict: true,
wrap: true,
keepBuildDir: false,
optimize: "uglify2",
modules: [
{
name: "vendor"
},
{
name: "main",
exclude: ["vendor"]
},
{
name: "main-login",
exclude: ["vendor"],
override: {
mainConfigFile: "js/main-login.js",
map: {
"*": {
"app": "loginApp"
}
}
}
}
]
});
I'd like to avoid having 2 separate build files, if possible, and I'm working on breaking out the requirejs.config
block into a single, shared file and having the 2 main
files load that and then load the app
files (this is similar to how the multipage example works) but I need that map config to work in the optimizer in order for this to work. Any ideas what I'm missing here?
Update
I've split out the config into its own file, config.js
, which gets included by the main-*
files. In the main-login.js
file, I include the map config above the define
and everything works in development mode.
require.config({
map: {
"*": {
"app": "loginApp"
}
}
});
define(['module', 'config'], function(module, config) {
...
The build.js
file is the same as above, except with the second mainConfigFile
removed. Optimization still fails, though. What I think is happening is, since this is a Marionette app, it's common practice to pass the Application object as a dependency to other parts of the app, including views, controllers and models. When I optimize, I run into two different problems. If I leave removeCombined
as true
, the optimizer will build in the dependencies from the first app, then remove those files, so when it sees them in the second app, it will fail because it can't find the source files anymore. Setting this to false
seems reasonable, but the problem is this then gives me the following error:
Error: RangeError: Maximum call stack size exceeded
I can't find any consistent information on this particular error. It might have something to do with the hbs
plugin (similar to text
but for pre-compiling Handlebars templates) but I'm not positive that's the case. Since there's no stack trace, I'm not sure where to start looking. My gut feeling is it's a circular dependency somewhere, though. So, my updated question is, how should a multi-page Marionette app be decoupled so as to make sharing code (not just 3rd party code, but custom code such as data models and views) possible? Do I need to remove any dependencies on the core Application object? (That would require an awful lot of refactoring.) Since it works just fine in development mode, is there some trick to r.js
's config I'm overlooking? I've tried adding app
to the exclude
lists as well as stubModules
but nothing seems to work. I'm thinking of just creating 2 build files and being done with it, but I'd really like to know how to solve this the "right" way.