Skip to content

Commit

Permalink
Add a new nifty client-side module autoloader
Browse files Browse the repository at this point in the history
  • Loading branch information
creationix committed Jul 22, 2011
1 parent d926f90 commit 4264b4e
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 0 deletions.
100 changes: 100 additions & 0 deletions autoloader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// TODO: Put in some sort of lock so that the same dep won't get loaded
// multiple times in parallel.
// TODO: Use etag or last-modified and/or keep stuff in memory for improved
// performance.
// The user will need to npm install ugilify-js on their own to use that option.

var Fs = require('fs'),
Path = require('path'),
QueryString = require('querystring');

var findAll = new RegExp("require\\(['\"][^\"']+['\"]\\)", "g");
var findName = new RegExp("require\\(['\"]([^\"']+)['\"]\\)");

module.exports = function setup(mount, folder, uglify) {

if (uglify) {
var jsp = require("uglify-js").parser;
var pro = require("uglify-js").uglify;
}

return function handle(req, res, next) {
if (!req.uri) { req.uri = Url.parse(req.url); }
if (req.uri.pathname !== mount) return next();
var names = req.uri.query.split(",");

var has = {};
var scripts = [];
var header;

function loadScript(name, callback) {
if (!name) throw new Error("must provide name");
if (has.hasOwnProperty(name)) return process.nextTick(callback);
var path = Path.join(folder, name + ".js");
Fs.readFile(path, 'utf8', function (err, js) {
if (err) return callback(err);
var matches = js.match(findAll);

if (matches) {
matches = Array.prototype.slice.call(matches).map(function (dep) {
return dep.match(findName)[1];
});

function getDep(err) {
if (err) return next(err);
var dep = matches.shift();
if (!dep) return doneDeps();
loadScript(dep, getDep);
}
getDep();

} else {
doneDeps();
}
function doneDeps() {
if (!has[name]) { // Make sure only one version is in the generated JS
has[name] = true;
scripts.push([name, js]);
}
callback();
}

});
}

function getName(err) {
if (err) return next(err);
var name = names.shift();
if (!name) return done();
loadScript(name, getName);
}
Fs.readFile(__dirname + "/autoloader/bootstrap.js", 'utf8', function (err, js) {
if (err) return next(err);
header = js;
getName();
});

function done(err) {
var js = header;
scripts.forEach(function (tuple) {
var name = tuple[0];
var content = tuple[1];
js += "\ndefine('" + name + "', function (module, exports) {\n\n" + content + "\n})\n";
});

if (uglify) {
var ast = jsp.parse(js); // parse code and get the initial AST
ast = pro.ast_mangle(ast); // get a new AST with mangled names
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
js = pro.gen_code(ast); // compressed code here
}

res.writeHead(200, {
"Content-Type": "application/javascript",
"Content-Length": Buffer.byteLength(js)
});
res.end(js);
}
};
};

35 changes: 35 additions & 0 deletions autoloader/bootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// This is a super simple module system for use in the browser. You first
// define your module using the "define" function and then later require it
// using "require" This does not support circular dependencies or require search
// paths. It's a simple module repository for writing clean code.

(function () {

// Store our repository in private variables in this closure.
var defs = {},
modules = {};

// When the user defines a module's setup function, store it here.
function define(name, fn) {
defs[name] = fn;
}

// The first time a module is used, it's description is executed and cached.
function require(name) {
if (modules.hasOwnProperty(name)) return modules[name];
if (defs.hasOwnProperty(name)) {
var exports = modules[name] = {};
var module = {exports:exports};
var fn = defs[name];
fn(module, exports);
return modules[name] = module.exports;
}
throw new Error("Module not found: " + name);
}

// Expose our public API on the global object.
this.define = define;
this.require = require;

}());

0 comments on commit 4264b4e

Please sign in to comment.