forked from benjamn/install
-
Notifications
You must be signed in to change notification settings - Fork 0
/
install.js
189 lines (172 loc) · 8.45 KB
/
install.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
(function(global, undefined) {
// Defining the `install` function more than once leads to mayhem, so
// return immedately if a property called `install` is already defined on
// the global object.
if (global.install)
return;
// The `installed` object maps absolute module identifiers to module
// definitions available for requirement.
var installed = {};
// I make frequent use of `hasOwn.call` to test for the presence of object
// properties without traversing the prototype chain.
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 `install` function globally.
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 `require` function takes an absolute module identifier and returns
// the `exports` object defined by that module. An error is thrown if no
// module with the given identifier is installed.
function require(moduleId) {
if (hasOwn.call(installed, moduleId)) {
var module = installed[moduleId];
if (!hasOwn.call(module, "exports")) {
// Each module receives a version of `require` that knows how
// to `absolutize` relative module identifiers with respect to
// `moduleId`.
module.call(global, function(id) {
return require(absolutize(id, moduleId));
}, module.exports = {}, module);
}
// Note that `module.exports` may be redefined during evaluation
// of the module.
return module.exports;
}
// Since modules are evaluated only after all their requirements have
// been installed, this error generally means that `require` was
// called with an identifier that was not seen (or was not understood)
// by the dependency scanner.
throw new Error('module "' + moduleId + '" not installed');
}
// Given two module identifiers `id` and `baseId`, the `absolutize`
// function returns the absolute form of `id`, as if `id` were required
// from a module with the identifier `baseId`. For more information about
// relative identifiers, refer to the
// [spec](http://wiki.commonjs.org/wiki/Modules/1.1#Module_Identifiers).
var pathNormExp = /\/(\.?|[^\/]+\/\.\.)\//;
function absolutize(id, baseId) {
if (id.charAt(0) === ".") {
// Note: if `baseId` is omitted, then `"/undefined/../" + id` will
// be the starting point for normalization, which works just fine!
id = "/" + baseId + "/../" + id;
while (id != (baseId = id.replace(pathNormExp, "/")))
id = baseId;
id = id.replace(/^\//, "");
}
return id;
}
// The `flushQueue` function attempts to evaluate the oldest module in the
// queue, provided all of its dependencies have been installed. This
// provision is important because it ensures that the module can call
// `require` without fear of missing dependencies.
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 `flushQueue` before invoking
// `module.call`. The `setTimeout` function allows the stack to
// unwind before flushing resumes, so that the browser has a chance
// to report exceptions and/or handle other events.
global.setTimeout(resume, 0);
module.call(global, require);
flushing = undefined;
}
}
// If `install` is called during the evaluation of a queued module,
// `flushQueue` could be invoked recursively. To prevent double evaluation,
// `flushQueue` sets `flushing` to a truthy value before it evaluates a
// module and refuses to evaluate any modules if `flushing` is truthy
// already.
var flushing;
// Since `resume` is only ever invoked from `setTimeout`, there is no risk
// that `flushQueue` is already executing, so it is safe to clear the
// `flushing` flag unconditionally.
function resume() {
flushing = undefined;
flushQueue();
}
// To be recognized as dependencies, calls to `require` must use string
// literal identifiers.
var requireExp = /\brequire\(['"]([^'"]+)['"]\)/g;
// A module is `ready` to be evaluated if
//
// 1. it has an `.exports` property (indicating that it has already begun to be evaluated) or
// 1. all of its direct dependencies are installed and `ready` to be evaluated.
//
// 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 `.toString` and
// `.call` methods. If you have a really long module that you
// don't want to waste time scanning, just override its
// `.toString` function to return something equivalent (with
// regard to dependencies) but shorter.
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 `.seen` before its dependencies
// are traversed, so that if we encounter the same module again we
// can immediately return `true`.
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 `module.deps`, so that we
// can avoid considering it again if `ready` is called
// multiple times.
if (hasOwn.call(installed, id) && ready(installed[id])) {
delete deps[id];
// If any dependency is missing or not `ready`, then the
// current module is not yet `ready`. The `break` is not
// strictly necessary here, but immediately terminating
// the loop postpones work that can be done later.
} else {
result = false;
break;
}
}
}
// Ordinarily I would be more paranoid about always resetting
// `module.seen` to `false`, but if you thoroughly examine the code
// above, you'll find that the only real threat of exceptions comes
// from evaluating `code = module + ""` in a recursive call to
// `ready`. So if you decide to override the `.toString` method of a
// module for performance reasons, get it right.
module.seen = false;
}
return result;
}
// The most reliable way to get the global object:
// [http://stackoverflow.com/a/3277192/128454](http://stackoverflow.com/a/3277192/128454)
}(Function("return this")()));