install.js | |
---|---|
(function(global, undefined) { | |
Defining the | if (global.install)
return; |
The | var installed = {}; |
I make frequent use of | var hasOwn = installed.hasOwnProperty; |
Anonymous modules are pushed onto a queue so that (when ready) they can be executed in order of installation. | var qhead = {};
var qtail = qhead; |
Define the | global.install = function(id, module) { |
To install a named module, pass an absolute module identifier string followed by a module definition. Note that named modules are not evaluated until they are required for the first time. | if (typeof id === "string" && module) {
if (!hasOwn.call(installed, id)) {
installed[module.id = id] = module;
flushQueue();
} |
To install an anonymous module, pass a module definition without an identifier. Anonymous modules are executed in order of installation, as soon as their requirements have been installed. | } else if (id && typeof id.call === "function") {
qtail = qtail.next = { module: id };
if (qhead.next === qtail)
flushQueue();
}
}; |
The | function require(moduleId) {
if (hasOwn.call(installed, moduleId)) {
var module = installed[moduleId];
if (!hasOwn.call(module, "exports")) { |
Each module receives a version of | module.call(global, function(id) {
return require(absolutize(id, moduleId));
}, module.exports = {}, module);
} |
Note that | return module.exports;
} |
Since modules are evaluated only after all their requirements have
been installed, this error generally means that | throw new Error('module "' + moduleId + '" not installed');
} |
Given two module identifiers | var pathNormExp = /\/(\.?|[^\/]+\/\.\.)\//;
function absolutize(id, baseId) {
if (id.charAt(0) === ".") { |
Note: if | id = "/" + baseId + "/../" + id;
while (id != (baseId = id.replace(pathNormExp, "/")))
id = baseId;
id = id.replace(/^\//, "");
}
return id;
} |
The | function flushQueue() {
var next = qhead.next, module;
if (next && !flushing && ready(module = next.module)) {
flushing = qhead = next; |
Module evaluation might throw an exception, so we need to
schedule the next call to | global.setTimeout(resume, 0);
module.call(global, require);
flushing = undefined;
}
} |
If | var flushing; |
Since | function resume() {
flushing = undefined;
flushQueue();
} |
To be recognized as dependencies, calls to | var requireExp = /\brequire\(['"]([^'"]+)['"]\)/g; |
A module is
Note that the above definition is recursive. | function ready(module) {
var deps, code, match, id, result = true;
if (!module.seen &&
!hasOwn.call(module, "exports"))
{ |
Here's a little secret: module definitions don't have to be
functions, as long as they have a suitable | deps = module.deps;
if (!deps) {
code = module + "";
deps = module.deps = {};
requireExp.lastIndex = 0;
while ((match = requireExp.exec(code)))
deps[absolutize(match[1], module.id)] = true;
} |
There may be cycles in the dependency graph, so we must be
careful that the recursion always terminates. Each module we
check is temporarily marked as | module.seen = true;
for (id in deps) {
if (hasOwn.call(deps, id)) { |
Once a dependency is determined to be satisfied, we
remove its identifier from | if (hasOwn.call(installed, id) && ready(installed[id])) {
delete deps[id]; |
If any dependency is missing or not | } else {
result = false;
break;
}
}
} |
Ordinarily I would be more paranoid about always resetting
| module.seen = false;
}
return result;
} |
The most reliable way to get the global object: http://stackoverflow.com/a/3277192/128454 | }(Function("return this")()));
|