Use different configuration options for different modules - requirejs

In my require.js configuration, I'm using urlArgs: "bust=" + (new Date()).getTime(), to ensure that my module scripts are not being cached at all during development (this has the effect of adding a unique query parameter for each request, so it looks to a cache like a different resource).
However, there are several third party libraries I'm using that I'm not changing at all and would like for them to be cached to speed up loading and my overall development cycle. Is there a way to only apply the caching-busting config to certain modules, for instance based on the path?

The urlArgs option is used by the nameToUrl method of requirejs' context. This means that within a context all modules will share the option. Different contexts can have different options, but mixing contexts will be more hassle than it's worth: it won't be enough to define two sets of configuration options, but different modules would have to be required differently and modules from different context can't be listed in a single dependencies list.
Nevertheless, here is an example of how this can be accomplished (fiddle):
// default context
require.config({
urlArgs: "boost=" + (new Date()).getTime(),
paths: {
jquery: "//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min",
}
});
// cached (non-boosted) context
var reqCached = require.config({
context: "cached",
paths: {
jquery: "//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min",
}
});
// define a module "inline", normally it'll go into <base>/main.js
define("main", ["jquery"], function($) {
$("body").append("<div>boosted jquery</div>");
reqCached(["jquery"], function($) {
$("body").append("<div>cached jquery</div>");
});
});
// bootstrap the "application" - load and execute the main module
requirejs(["main"], function(main) {
});
In developer tools you can see that this contrived example loads two versions of jquery - one cache-boosted (the dependency of the "main" module) and one plain (manually required with the reqCached context). Alas, it is impossible - or I'm unaware of a way - to mix and match the two contexts so they provide different sets of modules transparently.

Related

Intern: DRY Runners for multiple project in one workspace

While realizing that my project setup might be the root cause of my problem, here's what I'd like some help with (moving from another framework to Intern):
I've different (>20) projects in one dev workspace, so I've (>20) different directories with test code. I'd like a project to be testable on itself, but also would like to execute all suites together.
I am specifying to use RequireJS as an AMD loader and where my tests can be found per project (one for nodeJS, one for browsers) through a config file. PLUS I have one overall config file specifying all the files.
You can image there is quite the duplication, what would be a good approach to DRY this up?
Note: it would also be welcome to help to describe my problem better (more generic or less TLDR)!
Since Intern configuration files are just AMD modules you can use the normal dependency loading mechanism to load and combine configuration data from multiple files:
// in your “do everything” master configuration
define([
'projectA/tests/intern',
'projectB/tests/intern',
'projectC/tests/intern',
// …
], function () {
var configs = Array.prototype.slice.call(arguments, 0);
function getCombined(key) {
return Array.prototype.concat.apply([], configs.map(function (config) {
return config[key];
}));
}
return {
suites: getCombined('suites'),
functionalSuites: getCombined('functionalSuites'),
// …
};
});

Re-declaring libraries as dependencies within every single module

I'm working on the project that uses require.js and I was faced with a simple question about declaring libraries as dependencies within every single modules. I saw many examples with require.js and in every of them libraries like Backbone, jquery, underscore etc was declared in every module. And it would be fine if your application consists of only couple of modules. But if your application has 40-50 modules it becomes a bit tedious to define jquery every single time.
On the other hand we can load all libraries into the require() this way:
// Just an example
require.config({
paths: {
jquery: '../bower_components/jquery/jquery',
underscore: '../bower_components/underscore/underscore',
backbone: '../bower_components/backbone/backbone'
}
});
require(['jquery', 'underscore', 'backbone'], function () {
require([
'app',
'bootstrap'
], function (App) {
var app = new App();
document.body.appendChild(app.el);
});
});
And then we can use Backbone, '_' and '$' into the App without declaring them as dependencies.
So I'm trying to understand why we should define them every time. Is it just a strict adherence to the Module pattern or something else?
Thanks.
Having your libraries passed in as arguments to your require factory function is a form of dependency injection. Now, depending on exactly how AMD-friendly your library dependencies are, you may even be able to use several different versions of the library at once in an application. Writing your modules in this way with explicit library dependencies can allow you future flexibility of mixing modules which require jQuery 1.8 and jQuery 2.0, for instance. A different jQuery version can be passed to each module.
When you don’t define all your dependencies explicitly every time, then you’re relying on the global variable (window.$ or window._) created as a side effect of loading the library. In that case, all the modules have to depend on the same version of the library.

RequireJS Path With JS Extension Not Resolving

I am using a library that (very selfishly, IMHO) assumes that the baseUrl will point to the company's CDN:
baseUrl: "[http protocol slash slash]cdn.wijmo.com/amd-js/"
At first I thought that I would just copy the contents of the above Url to my own folder (such as /scripts/wijmo/amd-js), but that doesn't work because the good folks at Wijmo hardcoded path references in their AMD define statements, such as this:
define(["./wijmo.widget"], function () { ... });
What the above means (if I understand things properly) is that if you have any other non-Wijmo AMD modules then you must either:
(a) place them under the amd-js path, perhaps in a sub-folder named "myScripts"
(b) use hard-coded RequireJS path references to your own AMDs, like this:
paths: {
"myAMD_1": "http://www.example.com/scripts/myScripts/myAMD_1",
"myAMD_2": "/scripts/myScripts/myAMD_2.js"
}
(a) works, but it means that the baseUrl cannot point to the Wijmo CDN because I don't have access to the Wijmo CDN site so I must move the files published under amd-js to my own server.
(b) sort of work, and here is my problem: If I use syntax myAMD_1 then all is well. But that doesn't let me test on my local development machine, which uses localhost. (I don't want to get into detecting which server I'm running on and customize the paths value... I want the path to remain the same before and after I publish to my http server.)
Now, according to the RequireJS documentation:
There may be times when you do want to reference a script directly and not conform to the "baseUrl + paths" rules for finding it. If a module ID has one of the following characteristics, the ID will not be passed through the "baseUrl + paths" configuration, and just be treated like a regular URL that is relative to the document:
* Ends in ".js".
* Starts with a "/".
* Contains an URL protocol, like "http:" or "https:".
When I try to end (terminate) my path reference with .js (as shown in AMD_2 above), RequireJS doesn't find my AMD and because it ends up looking for myAMD_2.js.js (notice the two .js suffixes). So it looks like RequireJS is no longer honoring what the docs say it employs as a path-resolution algorithm. With the .js suffix not working properly, I can't easily fix references to my own AMDs because I don't know for sure what server name or physical path structure they'll be published to--I really want to use relative paths.
Finally, I don't want to change Wijmo's AMD modules not only because they're are dozens of them, but also because I would need to re-apply my customizations each time they issue a Wijmo update.
So if my baseUrl has to point to a hard-coded Wijmo path then how can I use my own AMDs without placing them in a subfolder under the Wijmo path and without making any fixed-path or Url assumptions about where my own AMDs are published?
I can suggest a couple of approaches to consider here--both have some drawbacks, but can work.
The first approach is to provide a path for each and every Wijmo module that needs to be loaded. This will work, but you have touched on the obvious drawbacks of this approach in the description of the question: Wijmo has many modules that will need to be referenced, and maintaining the module list across updates in the future may be problematic.
If you can live with those drawbacks, here is what the RequireJS config would look like:
require.config({
paths: {
'wijmo.wijgrid': 'http://cdn.wijmo.com/amd-js/wijmo.wijgrid',
'wijmo.widget': 'http://cdn.wijmo.com/amd-js/wijmo.widget',
'wijmo.wijutil': 'http://cdn.wijmo.com/amd-js/wijmo.wijutil',
// ... List all relevant Wijmo modules here
}
});
require(['wijmo.wijgrid'], function() { /* ... */ });
The second approach is to initially configure the RequireJS baseUrl to load the Wijmo modules. Then once Wijmo modules have been loaded, re-configure RequireJS to be able to load your local app modules. The drawback of this approach is that all the Wijmo modules will need to be loaded up front, so you lose the ability to require Wijmo modules as needed within your own modules. This drawback will need to be balanced against the nastiness of listing out explicit paths for all the Wijmo modules as done in the first approach.
For example:
require.config({
baseUrl: 'http://cdn.wijmo.com/amd-js',
paths: {
// ... List minimal modules such as Jquery and Globalize as per Wijmo documentation
}
});
require(['wijmo.wijgrid'], function() {
require.config({
baseUrl: '.'
});
require(['main'], function() {
/* ... */
});
});

RequireJS - loading multiple config files

I am trying to load several RequireJS configs. In my html I am loading my main config via
<script src="../lib/require.js" data-main="../app/requireConfig"></script>
and once the document is ready I want to load all my plugin configs. So I created a new define file which holds a function that calls require.config:
define(['sharedServices/logger'], function (logger) {
function configVideo() {
logger.info('Adding video modules');
require.config({
path: {
Capabilities: 'videoProvider/Capabilities',
VideoProviderEnums: 'videoProvider/VideoProviderEnums',
VideoProviderCommon: 'videoProvider/VideoProviderCommon',
VideoProviderInstance: 'videoProvider/VideoProviderInstance',
DummyVideoInstance: 'videoProvider/DummyProvider/DummyVideoInstance'
}
});
}
return {
configVideo: configVideo
};
})
However, I get the following error:
Uncaught Error: Mismatched anonymous define() module: function (logger) {
The error you're getting isn't directly related to the stated problem (loading multiple configurations), but is caused by the way your code loading is organized. As the manual says:
To avoid the error:
Be sure to load all scripts that call define() via the RequireJS API. Do not manually code script tags in HTML to load scripts that have define() calls in them.
If you manually code an HTML script tag, be sure it only includes named modules, and that an anonymous module that will have the same name as one of the modules in that file is not loaded.
So the problem now is that when loading the module manually (as you state "when the document is ready", could you clarify how the quoted source is actually loaded?) requirejs doesn't know where the module came from, so it can't assign it a name. If the module were loaded via requirejs api (e.g. if it appeared in a dependencies list of a define call) and it were requirejs itself that determined its script path, it would name the module after the file.
In general it is advisable to have just a single script tag loading all the requirejs-managed javascript. This makes the development setup more closely match the eventual optimized situation (where all the scripts are concatenated together). It is still possible to make require.config calls inside individual modules if necessary and make some code execute only after document is ready. As an example, many our apps do something like the following in their main.js (the module loaded by the requirejs script tag):
// sort of bootstrap config
require.config({
packages: [{
name: "our-framework",
location: "../../our-framework/src/"
}],
// here some app-specific requirejs options
waitSeconds: 5
});
// load the framework, the "our-framework/rjs-config" contains
// framework specific requirejs config (another require.config call)
require(["our-framework/rjs-config"], function() {
// in this context both require configs are loaded
require(["application"], function(application) {
application.init().run();
});
});

JamJS - Still pulls for dependencies separately when compiled - Underscore not loaded for context?

A am relatively new to JamJS, and struggle to make it work properly.
I've got a very simple project based on Backbone and RequireJS, where I use JamJS to manage dependencies, namely: Backbone, _, Less, $, modernizr, underscore, bootstrap.
I tend to follow the method used by Backbone Boilerplate.
This is the code I use to get the Jam-compiled requireJS config file, together with my application-specific require config:
in html:
< script data-main="/assets/js/app/config" src="/assets/js/jam/compiled.min.js"> < /script>
'Compiled.min.js' is obviously the 600kb minified file generated by Jam.
The '/app/config' file is my Require.js configuration file where I'm planning to include all my project-specific code, not managed by the dependency manager.
Here's app/config.js:
require.config({
baseUrl:'/assets/js/',
deps: ['less','modernizer','bootstrap-jam','app/main'],
paths: {
'homeView': 'app/views/homeView'
// Use the underscore build of Lo-Dash to minimize incompatibilities.
,'lodash': '../jam/lodash/dist/lodash.underscore.min'
},
map: {
},
shim: {
}
});
(the files in deps are the ones I need on every page + my main.js - kind of a router.
The problem is that, in my homeView (which is initialized by main.js), I do this sort of thing:
define(['backbone'], function (Backbone) {
return Backbone.View.extend({
el:$('#homepageWrapper'),
initialize: function () {
this.$('#subTitle').text('This text is generated by homeView - the default Backbone View.');
}
})
});
As you can see I want Backbone to be available in this view. I assume that it's available through the compiled Jam code, however when I look in the Network panel in the Web Inspector, I see that this dependency is pulled in separately- this happens to any resource I try to use, not just Backbone.
I suspect that it might be something to do with the error I get as well, which I haven't been able to figure out. When using the compiled jam config, I get:
Uncaught Error: Module name "underscore" has not been loaded yet for
context: _. Use require([])
I'd really appreciate help with this
Thanks.

Resources