A couple of years ago, my former colleague Alex Sexton wrote about the techniques that we use at Bazaarvoice to deploy client-side JavaScript applications and then load those applications in a browser. Alex went into great detail, and it’s a good, if long, read. The core idea, though, is pretty simple: an application is bootstrapped by a “scout” file that lives at a URL that never changes, and that has a very short TTL. Its job is to load other static resources with long TTLs that live at versioned URLs — that is, URLs that change with each new deployment of the application. This strategy balances two concerns: the bulk of application resources become highly cacheable, while still being easy to update.
In order for a scout file to perform its duty, it needs to load JavaScript, load CSS, and host the config that says which JS and CSS to load. Depending on the application, other functionality might be useful: the ability to detect old IE; the ability to detect DOM ready; the ability to queue calls to the application’s methods, so they can be invoked for real when the core application resources arrive.
At Bazaarvoice, we’ve been building a lot of new client-side applications lately — internal and external — and we’ve realized two things: one, it’s very silly for each application to reinvent this particular wheel; two, there’s nothing especially top secret about this wheel that would prevent us from sharing it with others.
To that end, I’m happy to release scoutfile
as an NPM module that you can use in your projects to generate a scout file. It’s a project that Lon Ingram and I worked on, and it provides both a Grunt task and a Node interface for creating a scout file for your application. With scoutfile
, your JavaScript application can specify the common functionality required in your scout file — for example, the ability to load JS, load CSS, and detect old IE. Then, you provide any code that is unique to your application that should be included in your scout file. The scoutfile
module uses Webpack under the hood, which means you can use loaders like json!
and css!
for common tasks.
The most basic usage is to npm install scoutfile
, then create a scout file in your application. In your scout file, you specify the functionality you need from scoutfile
:
var App = require('scoutfile/lib/browser/application');
var loader = require('scoutfile/lib/browser/loader');
var config = require('json!./config.json');
var MyApp = App('MyApp');
MyApp.config = config;
loader.loadScript(config.appJS);
loader.loadStyleSheet(config.appCSS);
Next, you can generate your scout file using a simple Node script:
var scout = require('scoutfile');
scout.generate({
appModules: [
{
name: 'MyApp',
path: './app/scout.js'
}
],
// Specify `pretty` to get un-uglified output.
pretty: true
}).then(function (scout) {
console.log(scout);
});
The README contains a lot more details, including how to use flags to differentiate production vs. development builds; how to configure the Grunt task; how to configure the “namespace” that is occupied on window
(a necessary evil if you want to queue calls before your main application renders); and more.
There are also several open issues to improve or add functionality. You can check out the developer README if you’re interested in contributing.