-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a new nifty client-side module autoloader
- Loading branch information
1 parent
d926f90
commit 4264b4e
Showing
2 changed files
with
135 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
}; | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
|
||
}()); | ||
|