RequireJS: Is there a way to achieve multiple base URLs?
The problem
I had a similar problem while trying to set up a testing environment. I had a file structure like this:
myApp/
src/
js/
app.js
data.js
lib/underscore.js
test/
karma.conf.js
test-main.js
matchers.js
spec/
data.js
Here's where it gets tricky: my app scripts (app.js
and data.js
) assume a RequireJS configuration that resolves data
to src/js/data.js
, lib/underscore
to src/js/lib/underscore.js
etc, so I need that configuration in my test environment as well:
test/test-main.js
-----------------
require.config({
// Karma serves files under /base, which is the basePath from your config file
baseUrl: '/base/src/js',
// ...
});
Now I can write my tests:
test/spec/data.js
-----------------
define(['data', '../../test/matchers'], function(dataModule) {
describe('The data module', function() {
it('should satisfy my custom matcher', function() {
expect(dataModule).toSatisfyMyCustomMatcher();
});
});
});
With some custom matchers:
test/matchers.js
----------------
define([], function() {
beforeEach(function() {
this.addMatchers({
toSatisfyMyCustomMatcher: function() {
return this.actual.isGood;
},
});
});
});
However, that '../../test/matchers'
part is horrendously ugly. The test specifications shouldn't be bothered with knowing file paths to other modules - that's RequireJS's job. Instead we want to use symbolic names.
The solution
The RequireJS paths config can also map directories.
The path that is used for a module name should not include an extension, since the path mapping could be for a directory.
So, the solution is a simple path config:
test/test-main.js
-----------------
require.config({
baseUrl: '/base/src/js',
paths: {
test: '../../test',
},
// ...
});
Now I can refer to the test
directory as if it were a child of the baseUrl
:
test/spec/data.js
-----------------
define(['data', 'test/matchers'], function(dataModule) {
// ...
});
Which in my case effectively comes out pretty much the same as if I could have multiple baseUrl
s.
Answered by James Burke on RequireJS Github Issue's page: Issue #447: Multiple Base URLs · jrburke/requirejs.
Turns out to be quite simple if data-main is the only entry point to your scripts(comments for more info), I solved my particular problem with the following:
My app's index.html
:
<script src="http://framework.jpillora.com/js/lib/require.js"
data-main="http://framework.jpillora.com/js/framework" > </script>
has the requirejs entry point set to framework.js
:
var framework = ... //set using script elements src attribute
require.config({
baseUrl: 'js/',
//Framework paths
paths: {
'framework': framework,
'lib' : framework + 'js/lib',
'ext' : framework + 'js/ext',
'util' : framework + 'js/util'
},
//Shortcuts
map: {
'*': {
...
}
},
//Non-modularised libraries with deps
shim: {
...
}
});
require(['main']);
So instead of normally doing index.html->main.js
, we're adding an extra step index.html->framework.js->main.js
, which gives the app code knowledge of paths to the framework code.
For example, in the app http://prettyprint.jpillora.com/, once require has loaded framework.js
, it will setup paths to lib/...
which to http://framework.jpillora.com/ and set the baseUrl
as ./js/
so once main
is required, it will have the base url set to it's own domain and lib
pointing to another domain.
Which results in require(['lib/foo', 'view/bar']);
resolving to:
http://framework.jpillora.com/js/lib/foo.js
and
http://prettyprint.jpillora.com/js/view/bar.js
As displayed here, the app is only a main.js
everything else comes from the framework
:
So finally, whenever I load an app's main.js
via with the above framework.js
, I then have access to all of my commonly used libraries and utility classes. See app source.
Also note, with the r.js
optimiser and a nice local file structure, one can also optimise the app into a single js file pulling only what's required from framework
.