|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 /** |
|
8 * Require.jsm is a small module loader that loads JavaScript modules as |
|
9 * defined by AMD/RequireJS and CommonJS, or specifically as used by: |
|
10 * GCLI, Orion, Firebug, CCDump, NetPanel/HTTPMonitor and others. |
|
11 * |
|
12 * To date, no attempt has been made to ensure that Require.jsm closely follows |
|
13 * either the AMD or CommonJS specs. It is hoped that a more formal JavaScript |
|
14 * module standard will arrive before this is necessary. In the mean time it |
|
15 * serves the projects it loads. |
|
16 */ |
|
17 |
|
18 this.EXPORTED_SYMBOLS = [ "define", "require" ]; |
|
19 |
|
20 const console = (function() { |
|
21 const tempScope = {}; |
|
22 Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope); |
|
23 return tempScope.console; |
|
24 })(); |
|
25 |
|
26 /** |
|
27 * Define a module along with a payload. |
|
28 * @param moduleName Name for the payload |
|
29 * @param deps Ignored. For compatibility with CommonJS AMD Spec |
|
30 * @param payload Function with (require, exports, module) params |
|
31 */ |
|
32 this.define = function define(moduleName, deps, payload) { |
|
33 if (typeof moduleName != "string") { |
|
34 throw new Error("Error: Module name is not a string"); |
|
35 } |
|
36 |
|
37 if (arguments.length == 2) { |
|
38 payload = deps; |
|
39 } |
|
40 else { |
|
41 payload.deps = deps; |
|
42 } |
|
43 |
|
44 if (define.debugDependencies) { |
|
45 console.log("define: " + moduleName + " -> " + payload.toString() |
|
46 .slice(0, 40).replace(/\n/, '\\n').replace(/\r/, '\\r') + "..."); |
|
47 } |
|
48 |
|
49 if (moduleName in define.modules) { |
|
50 throw new Error("Error: Redefining module: " + moduleName); |
|
51 } |
|
52 |
|
53 // Mark the payload so we know we need to call it to get the real module |
|
54 payload.__uncompiled = true; |
|
55 define.modules[moduleName] = payload; |
|
56 } |
|
57 |
|
58 /** |
|
59 * The global store of un-instantiated modules |
|
60 */ |
|
61 define.modules = {}; |
|
62 |
|
63 /** |
|
64 * Should we console.log on module definition/instantiation/requirement? |
|
65 */ |
|
66 define.debugDependencies = false; |
|
67 |
|
68 |
|
69 /** |
|
70 * We invoke require() in the context of a Domain so we can have multiple |
|
71 * sets of modules running separate from each other. |
|
72 * This contrasts with JSMs which are singletons, Domains allows us to |
|
73 * optionally load a CommonJS module twice with separate data each time. |
|
74 * Perhaps you want 2 command lines with a different set of commands in each, |
|
75 * for example. |
|
76 */ |
|
77 function Domain() { |
|
78 this.modules = {}; |
|
79 |
|
80 if (define.debugDependencies) { |
|
81 this.depth = ""; |
|
82 } |
|
83 } |
|
84 |
|
85 /** |
|
86 * Lookup module names and resolve them by calling the definition function if |
|
87 * needed. |
|
88 * There are 2 ways to call this, either with an array of dependencies and a |
|
89 * callback to call when the dependencies are found (which can happen |
|
90 * asynchronously in an in-page context) or with a single string an no |
|
91 * callback where the dependency is resolved synchronously and returned. |
|
92 * The API is designed to be compatible with the CommonJS AMD spec and |
|
93 * RequireJS. |
|
94 * @param deps A name, or array of names for the payload |
|
95 * @param callback Function to call when the dependencies are resolved |
|
96 * @return The module required or undefined for array/callback method |
|
97 */ |
|
98 Domain.prototype.require = function(config, deps, callback) { |
|
99 if (arguments.length <= 2) { |
|
100 callback = deps; |
|
101 deps = config; |
|
102 config = undefined; |
|
103 } |
|
104 |
|
105 if (Array.isArray(deps)) { |
|
106 var params = deps.map(function(dep) { |
|
107 return this.lookup(dep); |
|
108 }, this); |
|
109 if (callback) { |
|
110 callback.apply(null, params); |
|
111 } |
|
112 return undefined; |
|
113 } |
|
114 else { |
|
115 return this.lookup(deps); |
|
116 } |
|
117 }; |
|
118 |
|
119 /** |
|
120 * Lookup module names and resolve them by calling the definition function if |
|
121 * needed. |
|
122 * @param moduleName A name for the payload to lookup |
|
123 * @return The module specified by aModuleName or null if not found |
|
124 */ |
|
125 Domain.prototype.lookup = function(moduleName) { |
|
126 if (moduleName in this.modules) { |
|
127 var module = this.modules[moduleName]; |
|
128 if (define.debugDependencies) { |
|
129 console.log(this.depth + " Using module: " + moduleName); |
|
130 } |
|
131 return module; |
|
132 } |
|
133 |
|
134 if (!(moduleName in define.modules)) { |
|
135 throw new Error("Missing module: " + moduleName); |
|
136 } |
|
137 |
|
138 var module = define.modules[moduleName]; |
|
139 |
|
140 if (define.debugDependencies) { |
|
141 console.log(this.depth + " Compiling module: " + moduleName); |
|
142 } |
|
143 |
|
144 if (module.__uncompiled) { |
|
145 if (define.debugDependencies) { |
|
146 this.depth += "."; |
|
147 } |
|
148 |
|
149 var exports = {}; |
|
150 try { |
|
151 var params = module.deps.map(function(dep) { |
|
152 if (dep === "require") { |
|
153 return this.require.bind(this); |
|
154 } |
|
155 if (dep === "exports") { |
|
156 return exports; |
|
157 } |
|
158 if (dep === "module") { |
|
159 return { id: moduleName, uri: "" }; |
|
160 } |
|
161 return this.lookup(dep); |
|
162 }.bind(this)); |
|
163 |
|
164 var reply = module.apply(null, params); |
|
165 module = (reply !== undefined) ? reply : exports; |
|
166 } |
|
167 catch (ex) { |
|
168 dump("Error using module '" + moduleName + "' - " + ex + "\n"); |
|
169 throw ex; |
|
170 } |
|
171 |
|
172 if (define.debugDependencies) { |
|
173 this.depth = this.depth.slice(0, -1); |
|
174 } |
|
175 } |
|
176 |
|
177 // cache the resulting module object for next time |
|
178 this.modules[moduleName] = module; |
|
179 |
|
180 return module; |
|
181 }; |
|
182 |
|
183 /** |
|
184 * Expose the Domain constructor and a global domain (on the define function |
|
185 * to avoid exporting more than we need. This is a common pattern with |
|
186 * require systems) |
|
187 */ |
|
188 define.Domain = Domain; |
|
189 define.globalDomain = new Domain(); |
|
190 |
|
191 /** |
|
192 * Expose a default require function which is the require of the global |
|
193 * sandbox to make it easy to use. |
|
194 */ |
|
195 this.require = define.globalDomain.require.bind(define.globalDomain); |