]>
Commit | Line | Data |
---|---|---|
399fa2ee SR |
1 | import RegisterLoader from 'es-module-loader/core/register-loader.js'; |
2 | import { InternalModuleNamespace as ModuleNamespace } from 'es-module-loader/core/loader-polyfill.js'; | |
3 | ||
4 | import { baseURI, global, isBrowser } from 'es-module-loader/core/common.js'; | |
5 | import { resolveIfNotPlain } from 'es-module-loader/core/resolve.js'; | |
6 | ||
7 | var loader; | |
8 | ||
9 | // <script type="module"> support | |
10 | var anonSources = {}; | |
11 | if (typeof document != 'undefined' && document.getElementsByTagName) { | |
493ad1a2 | 12 | var handleError = function(err) { |
36653517 PO |
13 | // dispatch an error event so that we can display in errors in browsers |
14 | // that don't yet support unhandledrejection | |
999b5da7 PO |
15 | if (window.onunhandledrejection === undefined) { |
16 | try { | |
17 | var evt = new Event('error'); | |
18 | } catch (_eventError) { | |
19 | var evt = document.createEvent('Event'); | |
20 | evt.initEvent('error', true, true); | |
21 | } | |
22 | evt.message = err.message; | |
5d00fd9b PO |
23 | if (err.fileName) { |
24 | evt.filename = err.fileName; | |
25 | evt.lineno = err.lineNumber; | |
26 | evt.colno = err.columnNumber; | |
27 | } else if (err.sourceURL) { | |
28 | evt.filename = err.sourceURL; | |
29 | evt.lineno = err.line; | |
30 | evt.colno = err.column; | |
31 | } | |
999b5da7 PO |
32 | evt.error = err; |
33 | window.dispatchEvent(evt); | |
36653517 | 34 | } |
36653517 PO |
35 | |
36 | // throw so it still shows up in the console | |
37 | throw err; | |
38 | } | |
39 | ||
493ad1a2 | 40 | var ready = function() { |
399fa2ee SR |
41 | document.removeEventListener('DOMContentLoaded', ready, false ); |
42 | ||
43 | var anonCnt = 0; | |
44 | ||
45 | var scripts = document.getElementsByTagName('script'); | |
46 | for (var i = 0; i < scripts.length; i++) { | |
47 | var script = scripts[i]; | |
48 | if (script.type == 'module' && !script.loaded) { | |
49 | script.loaded = true; | |
50 | if (script.src) { | |
36653517 | 51 | loader.import(script.src).catch(handleError); |
399fa2ee SR |
52 | } |
53 | // anonymous modules supported via a custom naming scheme and registry | |
54 | else { | |
50c31776 | 55 | var uri = './<anon' + ++anonCnt + '>.js'; |
399fa2ee SR |
56 | if (script.id !== ""){ |
57 | uri = "./" + script.id; | |
58 | } | |
59 | ||
60 | var anonName = resolveIfNotPlain(uri, baseURI); | |
61 | anonSources[anonName] = script.innerHTML; | |
36653517 | 62 | loader.import(anonName).catch(handleError); |
399fa2ee SR |
63 | } |
64 | } | |
65 | } | |
66 | } | |
67 | ||
68 | // simple DOM ready | |
69 | if (document.readyState === 'complete') | |
70 | setTimeout(ready); | |
71 | else | |
72 | document.addEventListener('DOMContentLoaded', ready, false); | |
73 | } | |
74 | ||
75 | function BrowserESModuleLoader(baseKey) { | |
76 | if (baseKey) | |
77 | this.baseKey = resolveIfNotPlain(baseKey, baseURI) || resolveIfNotPlain('./' + baseKey, baseURI); | |
78 | ||
79 | RegisterLoader.call(this); | |
80 | ||
81 | var loader = this; | |
82 | ||
83 | // ensure System.register is available | |
84 | global.System = global.System || {}; | |
85 | if (typeof global.System.register == 'function') | |
86 | var prevRegister = global.System.register; | |
87 | global.System.register = function() { | |
88 | loader.register.apply(loader, arguments); | |
89 | if (prevRegister) | |
90 | prevRegister.apply(this, arguments); | |
91 | }; | |
92 | } | |
93 | BrowserESModuleLoader.prototype = Object.create(RegisterLoader.prototype); | |
94 | ||
95 | // normalize is never given a relative name like "./x", that part is already handled | |
96 | BrowserESModuleLoader.prototype[RegisterLoader.resolve] = function(key, parent) { | |
97 | var resolved = RegisterLoader.prototype[RegisterLoader.resolve].call(this, key, parent || this.baseKey) || key; | |
98 | if (!resolved) | |
99 | throw new RangeError('ES module loader does not resolve plain module names, resolving "' + key + '" to ' + parent); | |
100 | ||
101 | return resolved; | |
102 | }; | |
103 | ||
104 | function xhrFetch(url, resolve, reject) { | |
105 | var xhr = new XMLHttpRequest(); | |
493ad1a2 | 106 | var load = function(source) { |
399fa2ee SR |
107 | resolve(xhr.responseText); |
108 | } | |
493ad1a2 | 109 | var error = function() { |
399fa2ee SR |
110 | reject(new Error('XHR error' + (xhr.status ? ' (' + xhr.status + (xhr.statusText ? ' ' + xhr.statusText : '') + ')' : '') + ' loading ' + url)); |
111 | } | |
112 | ||
113 | xhr.onreadystatechange = function () { | |
114 | if (xhr.readyState === 4) { | |
115 | // in Chrome on file:/// URLs, status is 0 | |
116 | if (xhr.status == 0) { | |
117 | if (xhr.responseText) { | |
118 | load(); | |
119 | } | |
120 | else { | |
121 | // when responseText is empty, wait for load or error event | |
122 | // to inform if it is a 404 or empty file | |
123 | xhr.addEventListener('error', error); | |
124 | xhr.addEventListener('load', load); | |
125 | } | |
126 | } | |
127 | else if (xhr.status === 200) { | |
128 | load(); | |
129 | } | |
130 | else { | |
131 | error(); | |
132 | } | |
133 | } | |
134 | }; | |
135 | xhr.open("GET", url, true); | |
136 | xhr.send(null); | |
137 | } | |
138 | ||
139 | var WorkerPool = function (script, size) { | |
edb78799 PO |
140 | var current = document.currentScript; |
141 | // IE doesn't support currentScript | |
142 | if (!current) { | |
d1316334 | 143 | // Find an entry with out basename |
edb78799 | 144 | var scripts = document.getElementsByTagName('script'); |
d1316334 PO |
145 | for (var i = 0; i < scripts.length; i++) { |
146 | if (scripts[i].src.indexOf("browser-es-module-loader.js") !== -1) { | |
147 | current = scripts[i]; | |
148 | break; | |
149 | } | |
150 | } | |
151 | if (!current) | |
152 | throw Error("Could not find own <script> element"); | |
edb78799 PO |
153 | } |
154 | script = current.src.substr(0, current.src.lastIndexOf("/")) + "/" + script; | |
399fa2ee SR |
155 | this._workers = new Array(size); |
156 | this._ind = 0; | |
157 | this._size = size; | |
158 | this._jobs = 0; | |
159 | this.onmessage = undefined; | |
160 | this._stopTimeout = undefined; | |
858ea4a7 PO |
161 | for (var i = 0; i < size; i++) { |
162 | var wrkr = new Worker(script); | |
399fa2ee SR |
163 | wrkr._count = 0; |
164 | wrkr._ind = i; | |
165 | wrkr.onmessage = this._onmessage.bind(this, wrkr); | |
36efb978 | 166 | wrkr.onerror = this._onerror.bind(this); |
399fa2ee SR |
167 | this._workers[i] = wrkr; |
168 | } | |
169 | ||
170 | this._checkJobs(); | |
171 | }; | |
172 | WorkerPool.prototype = { | |
173 | postMessage: function (msg) { | |
174 | if (this._stopTimeout !== undefined) { | |
175 | clearTimeout(this._stopTimeout); | |
176 | this._stopTimeout = undefined; | |
177 | } | |
858ea4a7 | 178 | var wrkr = this._workers[this._ind % this._size]; |
399fa2ee SR |
179 | wrkr._count++; |
180 | this._jobs++; | |
181 | wrkr.postMessage(msg); | |
182 | this._ind++; | |
183 | }, | |
184 | ||
185 | _onmessage: function (wrkr, evt) { | |
186 | wrkr._count--; | |
187 | this._jobs--; | |
188 | this.onmessage(evt, wrkr); | |
189 | this._checkJobs(); | |
190 | }, | |
191 | ||
36efb978 PO |
192 | _onerror: function(err) { |
193 | try { | |
194 | var evt = new Event('error'); | |
195 | } catch (_eventError) { | |
196 | var evt = document.createEvent('Event'); | |
197 | evt.initEvent('error', true, true); | |
198 | } | |
199 | evt.message = err.message; | |
200 | evt.filename = err.filename; | |
201 | evt.lineno = err.lineno; | |
202 | evt.colno = err.colno; | |
203 | evt.error = err.error; | |
204 | window.dispatchEvent(evt); | |
205 | }, | |
206 | ||
399fa2ee SR |
207 | _checkJobs: function () { |
208 | if (this._jobs === 0 && this._stopTimeout === undefined) { | |
209 | // wait for 2s of inactivity before stopping (that should be enough for local loading) | |
210 | this._stopTimeout = setTimeout(this._stop.bind(this), 2000); | |
211 | } | |
212 | }, | |
213 | ||
214 | _stop: function () { | |
152c3995 | 215 | this._workers.forEach(function(wrkr) { |
399fa2ee | 216 | wrkr.terminate(); |
152c3995 | 217 | }); |
399fa2ee SR |
218 | } |
219 | }; | |
220 | ||
221 | var promiseMap = new Map(); | |
aadcf47d | 222 | var babelWorker = new WorkerPool('babel-worker.js', 3); |
399fa2ee SR |
223 | babelWorker.onmessage = function (evt) { |
224 | var promFuncs = promiseMap.get(evt.data.key); | |
225 | promFuncs.resolve(evt.data); | |
226 | promiseMap.delete(evt.data.key); | |
227 | }; | |
228 | ||
229 | // instantiate just needs to run System.register | |
230 | // so we fetch the source, convert into the Babel System module format, then evaluate it | |
231 | BrowserESModuleLoader.prototype[RegisterLoader.instantiate] = function(key, processAnonRegister) { | |
232 | var loader = this; | |
233 | ||
234 | // load as ES with Babel converting into System.register | |
235 | return new Promise(function(resolve, reject) { | |
236 | // anonymous module | |
237 | if (anonSources[key]) { | |
238 | resolve(anonSources[key]) | |
239 | anonSources[key] = undefined; | |
240 | } | |
241 | // otherwise we fetch | |
242 | else { | |
243 | xhrFetch(key, resolve, reject); | |
244 | } | |
245 | }) | |
246 | .then(function(source) { | |
247 | // check our cache first | |
1433360a PO |
248 | var cacheEntry = localStorage.getItem(key); |
249 | if (cacheEntry) { | |
250 | cacheEntry = JSON.parse(cacheEntry); | |
399fa2ee | 251 | // TODO: store a hash instead |
1433360a PO |
252 | if (cacheEntry.source === source) { |
253 | return Promise.resolve({key: key, code: cacheEntry.code, source: cacheEntry.source}); | |
399fa2ee SR |
254 | } |
255 | } | |
256 | return new Promise(function (resolve, reject) { | |
257 | promiseMap.set(key, {resolve: resolve, reject: reject}); | |
258 | babelWorker.postMessage({key: key, source: source}); | |
259 | }); | |
260 | }).then(function (data) { | |
261 | // evaluate without require, exports and module variables | |
262 | // we leave module in for now to allow module.require access | |
55d5353f | 263 | try { |
39193b28 PO |
264 | var cacheEntry = JSON.stringify({source: data.source, code: data.code}); |
265 | localStorage.setItem(key, cacheEntry); | |
55d5353f PO |
266 | } catch (e) { |
267 | if (window.console) { | |
268 | window.console.warn('Unable to cache transpiled version of ' + key + ': ' + e); | |
777df7c2 | 269 | } |
d6c17390 | 270 | } |
399fa2ee SR |
271 | (0, eval)(data.code + '\n//# sourceURL=' + data.key + '!transpiled'); |
272 | processAnonRegister(); | |
273 | }); | |
274 | }; | |
275 | ||
276 | // create a default loader instance in the browser | |
277 | if (isBrowser) | |
278 | loader = new BrowserESModuleLoader(); | |
279 | ||
280 | export default BrowserESModuleLoader; |