Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add a new nifty client-side module autoloader

  • Loading branch information...
commit 4264b4e2b37f2f76bd0cdd908aa2c9a6654a8644 1 parent d926f90
Tim Caswell authored
Showing with 135 additions and 0 deletions.
  1. +100 −0 autoloader.js
  2. +35 −0 autoloader/bootstrap.js
100 autoloader.js
View
@@ -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 autoloader/bootstrap.js
View
@@ -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;
+
+}());
+
Please sign in to comment.
Something went wrong with that request. Please try again.