@lwrjs/everywhere 0.9.0-alpha.9 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/build/__generated_site_amd_modules__/1/application/amd/l/en-US/ai/amd-bootstrap/configuration/ci/-/-/s/9c07e4a986679d89bf69e66fe60ce615/config.js +10 -0
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/@lwrjs/app-service/amd-bootstrap/module/amd/v/0_9_0/s/c40e6caf07454ce13961de8ca76ab7f0/@lwrjs_app-service_amd-bootstrap_module_amd.js +18 -0
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwc/v/2_38_1/s/8cf6b94d9c0b398c70c97fdca2759caa/lwc.js +8934 -0
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwr/everywhereAmd/v/0_9_0/s/c538071acf5bde58e816967a14c465c3/lwr_everywhereAmd.js +66 -0
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwr/init/v/0_9_0/s/206e6e0af6f019fdfd86d87d56331bc2/lwr_init.js +97 -0
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwr/loader/v/{0_9_0-alpha_9/s/07df3ed331f5f6b3c21336c94dac7c0b → 0_9_0/s/37491c0be6765e2cd302cf146e9d624a}/lwr_loader.js +362 -137
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwr/metrics/v/0_9_0/s/3426e42c3e22ce07ab2adc62c4c523a0/lwr_metrics.js +66 -0
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwr/profiler/v/0_9_0/s/e25f3b0b8b5c096acebf847d0a83ea62/lwr_profiler.js +68 -0
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwr/vault/v/0_9_0/s/c92abd8c1fec2d7eff62e4b097abbe14/lwr_vault.js +21 -0
- package/build/__generated_site_amd_modules__/1/resource/amd/lwr-error-shim.js/v/{0_9_0-alpha_9 → 0_9_0/lwr-error-shim.js} +1 -1
- package/build/__generated_site_amd_modules__/1/resource/amd/lwr-loader-shim.bundle.js/v/0_9_0/lwr-loader-shim.bundle.js +1547 -0
- package/build/assets/amd/lwr-everywhere-debug.js +39 -27
- package/build/assets/amd/lwr-everywhere-min.js +1 -1
- package/build/assets/amd/lwr-everywhere.js +39 -27
- package/build/assets/core/lwr-everywhere-debug.js +36 -24
- package/build/assets/core/lwr-everywhere-min.js +1 -1
- package/build/assets/core/lwr-everywhere.js +36 -24
- package/build/assets/esm/lwr-everywhere-debug.js +1 -1
- package/build/assets/esm/lwr-everywhere-min.js +1 -1
- package/build/assets/esm/lwr-everywhere.js +1 -1
- package/build/modules/@salesforce/lds-default-luvio/lds-default-luvio.js +11 -7
- package/build/modules/lwr/everywhere/everywhere.js +6 -7
- package/build/modules/lwr/everywhereAmd/everywhereAmd.js +9 -11
- package/build/modules/lwr/everywhereEsm/everywhereEsm.js +1 -2
- package/build/modules/lwr/host/host.js +23 -27
- package/build/modules/lwr/setupLDS/network.js +9 -17
- package/build/modules/lwr/setupLDS/setupLDS.js +6 -4
- package/build/modules/lwr/setupNodeLDS/network.js +9 -17
- package/build/modules/lwr/setupNodeLDS/setupNodeLDS.js +3 -1
- package/build/modules/lwr/vault/vault.js +3 -1
- package/package.json +11 -8
- package/build/__generated_site_amd_modules__/1/application/amd/l/en-US/ai/amd-bootstrap/configuration/ci/-/-/s/102a7c627b0f61cba691a7fa94d6a856/config.js +0 -10
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/@lwrjs/app-service/amd-bootstrap/module/amd/v/0_9_0-alpha_9/s/c40e6caf07454ce13961de8ca76ab7f0/@lwrjs_app-service_amd-bootstrap_module_amd.js +0 -14
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwc/v/2_33_0/s/90a5f16131da45cef1653112862ca9dc/lwc.js +0 -7090
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwr/everywhereAmd/v/0_9_0-alpha_9/s/c538071acf5bde58e816967a14c465c3/lwr_everywhereAmd.js +0 -48
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwr/init/v/0_9_0-alpha_9/s/6258bcb20cc2d5d6a9f3c5a6f9d0acaf/lwr_init.js +0 -58
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwr/metrics/v/0_9_0-alpha_9/s/dfbc827104d50ff7d16b59c9d0e06aca/lwr_metrics.js +0 -56
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwr/profiler/v/0_9_0-alpha_9/s/5a10236d8c52bc9f6dee22875cd7016d/lwr_profiler.js +0 -51
- package/build/__generated_site_amd_modules__/1/module/amd/1/l/en-US/mi/lwr/vault/v/0_9_0-alpha_9/s/c92abd8c1fec2d7eff62e4b097abbe14/lwr_vault.js +0 -13
- package/build/__generated_site_amd_modules__/1/resource/amd/lwr-loader-shim.js/v/0_9_0-alpha_9 +0 -311
- package/build/__generated_site_amd_modules__/en-US/index.html +0 -26
- package/build/__generated_site_amd_modules__/index.html +0 -26
|
@@ -0,0 +1,1547 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2021, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
6
|
+
*/
|
|
7
|
+
/* LWR Module Loader Shim v0.9.0 */
|
|
8
|
+
(function () {
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
// Bootstrap / shim
|
|
12
|
+
const BOOTSTRAP_PREFIX = 'lwr.bootstrap.';
|
|
13
|
+
const BOOTSTRAP_ERROR = `${BOOTSTRAP_PREFIX}error`;
|
|
14
|
+
|
|
15
|
+
var Phase;
|
|
16
|
+
(function (Phase) {
|
|
17
|
+
Phase[Phase["Start"] = 0] = "Start";
|
|
18
|
+
Phase[Phase["End"] = 1] = "End";
|
|
19
|
+
})(Phase || (Phase = {}));
|
|
20
|
+
// Attach a custom dispatcher
|
|
21
|
+
let customDispatcher;
|
|
22
|
+
function attachDispatcher(dispatcher) {
|
|
23
|
+
customDispatcher = dispatcher;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check if the Performance API is available
|
|
27
|
+
// e.g. JSDom (used in Jest) doesn't implement these
|
|
28
|
+
const perf = globalThis.performance;
|
|
29
|
+
const isPerfSupported = typeof perf !== 'undefined' && typeof perf.mark === 'function' && typeof perf.clearMarks === 'function' && typeof perf.measure === 'function' && typeof perf.clearMeasures === 'function';
|
|
30
|
+
|
|
31
|
+
// For marking request metrics
|
|
32
|
+
// Fallback to the Performance API if there is no custom dispatcher
|
|
33
|
+
function logOperationStart({
|
|
34
|
+
id,
|
|
35
|
+
specifier
|
|
36
|
+
}) {
|
|
37
|
+
if (customDispatcher) {
|
|
38
|
+
customDispatcher({
|
|
39
|
+
id,
|
|
40
|
+
phase: Phase.Start,
|
|
41
|
+
specifier
|
|
42
|
+
});
|
|
43
|
+
} else if (isPerfSupported) {
|
|
44
|
+
perf.mark(id + (specifier ? `.${specifier}` : ''));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// For measuring duration metrics
|
|
49
|
+
// Fallback to the Performance API if there is no custom dispatcher
|
|
50
|
+
/* istanbul ignore next */
|
|
51
|
+
function logOperationEnd({
|
|
52
|
+
id,
|
|
53
|
+
specifier
|
|
54
|
+
}) {
|
|
55
|
+
if (customDispatcher) {
|
|
56
|
+
customDispatcher({
|
|
57
|
+
id,
|
|
58
|
+
phase: Phase.End,
|
|
59
|
+
specifier
|
|
60
|
+
});
|
|
61
|
+
} else if (isPerfSupported) {
|
|
62
|
+
const suffix = specifier ? `.${specifier}` : '';
|
|
63
|
+
const markName = id + suffix;
|
|
64
|
+
const measureName = `${id}.duration${suffix}`;
|
|
65
|
+
perf.measure(measureName, markName);
|
|
66
|
+
|
|
67
|
+
// Clear the created mark and measure to avoid filling the performance entry buffer
|
|
68
|
+
// Even if they get deleted, existing PerformanceObservers preserve copies of the entries
|
|
69
|
+
perf.clearMarks(markName);
|
|
70
|
+
perf.clearMeasures(measureName);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function createLoader(name, definition, config, externalModules) {
|
|
75
|
+
if (!definition || typeof definition[2] !== 'function') {
|
|
76
|
+
throw new Error(`Expected loader with specifier "${name}" to be a module`);
|
|
77
|
+
}
|
|
78
|
+
// Create a Loader instance
|
|
79
|
+
const exports = {};
|
|
80
|
+
definition[2].call(null, exports);
|
|
81
|
+
const { Loader } = exports;
|
|
82
|
+
const loader = new Loader(config);
|
|
83
|
+
if (externalModules && externalModules.length) {
|
|
84
|
+
loader.registerExternalModules(externalModules);
|
|
85
|
+
}
|
|
86
|
+
// Define the loader module with public API: { define, load, services }
|
|
87
|
+
const exporter = (exports) => {
|
|
88
|
+
Object.assign(exports, {
|
|
89
|
+
define: loader.define.bind(loader),
|
|
90
|
+
load: loader.load.bind(loader),
|
|
91
|
+
services: loader.services,
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
loader.define(name, ['exports'], exporter);
|
|
95
|
+
return loader;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const REQUIRED_MODULES_TIMEOUT = 300 * 1000;
|
|
99
|
+
|
|
100
|
+
// Check for errors with autoBoot and customInit
|
|
101
|
+
function validatePreInit(autoBoot, customInit) {
|
|
102
|
+
// If autoBoot === false, there must be a customInit hook
|
|
103
|
+
if (!autoBoot && !customInit) {
|
|
104
|
+
throw new Error('The customInit hook is required when autoBoot is false');
|
|
105
|
+
}
|
|
106
|
+
// If autoBoot === true, there must NOT be a customInit hook
|
|
107
|
+
if (autoBoot && customInit) {
|
|
108
|
+
throw new Error('The customInit hook must not be defined when autoBoot is true');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Process the customInit hook
|
|
112
|
+
function customInit(config, initializeApp, define, onBootstrapError) {
|
|
113
|
+
// Validate config
|
|
114
|
+
const { autoBoot, customInit } = config;
|
|
115
|
+
validatePreInit(autoBoot, customInit);
|
|
116
|
+
// Set up arguments and call the customInit hook, if available
|
|
117
|
+
if (customInit) {
|
|
118
|
+
const lwr = {
|
|
119
|
+
initializeApp,
|
|
120
|
+
define,
|
|
121
|
+
onBootstrapError,
|
|
122
|
+
attachDispatcher,
|
|
123
|
+
};
|
|
124
|
+
customInit(lwr, config);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* global document */
|
|
129
|
+
/* eslint-disable lwr/no-unguarded-apis */
|
|
130
|
+
const hasSetTimeout = typeof setTimeout === 'function';
|
|
131
|
+
const hasConsole = typeof console !== 'undefined';
|
|
132
|
+
/* eslint-enable lwr/no-unguarded-apis */
|
|
133
|
+
class LoaderShim {
|
|
134
|
+
constructor(global) {
|
|
135
|
+
this.defineCache = {};
|
|
136
|
+
this.orderedDefs = [];
|
|
137
|
+
// Start watchdog timer
|
|
138
|
+
if (hasSetTimeout) {
|
|
139
|
+
this.watchdogTimerId = this.startWatchdogTimer();
|
|
140
|
+
}
|
|
141
|
+
// Parse configuration
|
|
142
|
+
this.global = global;
|
|
143
|
+
this.config = global.LWR;
|
|
144
|
+
this.loaderSpecifier = 'lwr/loader/v/0_9_0';
|
|
145
|
+
// Set up error handler
|
|
146
|
+
this.errorHandler = this.config.onError;
|
|
147
|
+
// Set up the temporary LWR.define function and customInit hook
|
|
148
|
+
const tempDefine = this.tempDefine.bind(this);
|
|
149
|
+
global.LWR.define = tempDefine;
|
|
150
|
+
this.bootReady = this.config.autoBoot;
|
|
151
|
+
try {
|
|
152
|
+
this.createProfilerModule(this.config);
|
|
153
|
+
customInit(Object.freeze(this.config), this.postCustomInit.bind(this), tempDefine, (e) => {
|
|
154
|
+
// customInit handlers can overwrite
|
|
155
|
+
// the error handler with onBootstrapError
|
|
156
|
+
this.errorHandler = e;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
this.enterErrorState(e);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Return true if the app can be initialized
|
|
164
|
+
canInit() {
|
|
165
|
+
// Initialize the app if:
|
|
166
|
+
// - bootReady: autoBoot is on OR customInit has finished
|
|
167
|
+
// - all required modules are defined
|
|
168
|
+
const allDefined = this.config.requiredModules.every((m) => this.orderedDefs.includes(m));
|
|
169
|
+
return this.bootReady && allDefined;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Create a temporary LWR.define() function which captures all
|
|
173
|
+
* calls that occur BEFORE the full loader module is available
|
|
174
|
+
*
|
|
175
|
+
* Each call to LWR.define() is stored in 2 ways:
|
|
176
|
+
* - in a map as [moduleName, arguments] pairs
|
|
177
|
+
* - each moduleName is pushed onto an array, to preserve
|
|
178
|
+
* the order in which the modules were defined
|
|
179
|
+
*/
|
|
180
|
+
tempDefine(...args) {
|
|
181
|
+
// Cache the incoming module
|
|
182
|
+
const moduleName = args[0];
|
|
183
|
+
this.defineCache[moduleName] = args;
|
|
184
|
+
this.orderedDefs.push(moduleName);
|
|
185
|
+
if (this.canInit()) {
|
|
186
|
+
if (hasSetTimeout) {
|
|
187
|
+
// requiredModules are defined, clear watchdog timer
|
|
188
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
189
|
+
clearTimeout(this.watchdogTimerId);
|
|
190
|
+
}
|
|
191
|
+
this.initApp();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Called by the customInit hook via lwr.initializeApp()
|
|
195
|
+
postCustomInit() {
|
|
196
|
+
this.bootReady = true;
|
|
197
|
+
if (this.canInit()) {
|
|
198
|
+
this.initApp();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Create the loader and initialize the application
|
|
202
|
+
initApp() {
|
|
203
|
+
try {
|
|
204
|
+
const loaderConfig = {
|
|
205
|
+
endpoints: this.config.endpoints,
|
|
206
|
+
baseUrl: this.config.baseUrl,
|
|
207
|
+
profiler: { logOperationStart, logOperationEnd },
|
|
208
|
+
// TODO: can be removed following https://github.com/salesforce-experience-platform-emu/lwr/issues/1087
|
|
209
|
+
appMetadata: {
|
|
210
|
+
appId: this.config.appId,
|
|
211
|
+
bootstrapModule: this.config.bootstrapModule,
|
|
212
|
+
rootComponent: this.config.rootComponent,
|
|
213
|
+
rootComponents: this.config.rootComponents,
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
const loader = createLoader(this.loaderSpecifier, this.defineCache[this.loaderSpecifier], loaderConfig, this.config.preloadModules);
|
|
217
|
+
this.mountApp(loader);
|
|
218
|
+
}
|
|
219
|
+
catch (e) {
|
|
220
|
+
this.enterErrorState(e);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
waitForDOMContentLoaded() {
|
|
224
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
225
|
+
if (typeof document === undefined) {
|
|
226
|
+
return Promise.resolve();
|
|
227
|
+
}
|
|
228
|
+
// Resolve if document is already "ready" https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState
|
|
229
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
230
|
+
if (document.readyState === 'interactive' || document.readyState === 'complete') {
|
|
231
|
+
return Promise.resolve();
|
|
232
|
+
}
|
|
233
|
+
return new Promise((resolve) => {
|
|
234
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
235
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
236
|
+
resolve();
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
// Create a module out of the profiler
|
|
241
|
+
// Note: The profiler is also available as a module through lwc module resolution (see package.json)
|
|
242
|
+
createProfilerModule(globalLWR) {
|
|
243
|
+
const exporter = (exports) => {
|
|
244
|
+
Object.assign(exports, { logOperationStart, logOperationEnd });
|
|
245
|
+
};
|
|
246
|
+
globalLWR.define('lwr/profiler/v/0_9_0', ['exports'], exporter);
|
|
247
|
+
}
|
|
248
|
+
// Set up the application globals, import map, root custom element...
|
|
249
|
+
mountApp(loader) {
|
|
250
|
+
const { bootstrapModule, rootComponent, rootComponents, ssrProps, endpoints, imports, index } = this.config;
|
|
251
|
+
// Set global LWR.define to loader.define
|
|
252
|
+
this.global.LWR = Object.freeze({
|
|
253
|
+
define: loader.define.bind(loader),
|
|
254
|
+
rootComponent,
|
|
255
|
+
rootComponents,
|
|
256
|
+
ssrProps,
|
|
257
|
+
endpoints,
|
|
258
|
+
imports: imports || {},
|
|
259
|
+
index: index || {},
|
|
260
|
+
});
|
|
261
|
+
// Redefine all modules in the temporary cache
|
|
262
|
+
this.orderedDefs.forEach((specifier) => {
|
|
263
|
+
if (specifier !== this.loaderSpecifier) {
|
|
264
|
+
loader.define(...this.defineCache[specifier]);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
// by default, app initialization is gated on waiting for document to be parsed (via DOMContentLoaded)
|
|
268
|
+
const { disableInitDefer } = this.config;
|
|
269
|
+
// Load the import mappings and application bootstrap module
|
|
270
|
+
loader
|
|
271
|
+
.registerImportMappings({ imports, index }, [bootstrapModule, rootComponent])
|
|
272
|
+
.then(() => {
|
|
273
|
+
if (!disableInitDefer) {
|
|
274
|
+
return this.waitForDOMContentLoaded();
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
.then(() => loader.load(bootstrapModule))
|
|
278
|
+
.catch((reason) => {
|
|
279
|
+
this.enterErrorState(new Error(`Application ${rootComponent || bootstrapModule} could not be loaded: ${reason}`));
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
// Trigger bootstrap error state, and call error handler if registered
|
|
283
|
+
enterErrorState(error) {
|
|
284
|
+
logOperationStart({ id: BOOTSTRAP_ERROR });
|
|
285
|
+
if (this.errorHandler) {
|
|
286
|
+
this.errorHandler(error);
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
if (hasConsole) {
|
|
290
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
291
|
+
console.error(`An error occurred during LWR bootstrap. ${error.message}`, error.stack);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// eslint-disable-next-line no-undef, lwr/no-unguarded-apis
|
|
296
|
+
startWatchdogTimer() {
|
|
297
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
298
|
+
return setTimeout(() => {
|
|
299
|
+
this.enterErrorState(new Error('Failed to load required modules - timed out'));
|
|
300
|
+
}, REQUIRED_MODULES_TIMEOUT);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// The loader module is ALWAYS required
|
|
305
|
+
const GLOBAL = globalThis;
|
|
306
|
+
GLOBAL.LWR.requiredModules = GLOBAL.LWR.requiredModules || [];
|
|
307
|
+
if (GLOBAL.LWR.requiredModules.indexOf('lwr/loader/v/0_9_0') < 0) {
|
|
308
|
+
GLOBAL.LWR.requiredModules.push('lwr/loader/v/0_9_0');
|
|
309
|
+
}
|
|
310
|
+
new LoaderShim(GLOBAL);
|
|
311
|
+
|
|
312
|
+
}());
|
|
313
|
+
|
|
314
|
+
LWR.define('lwr/loader/v/0_9_0', ['exports'], function (exports) { 'use strict';
|
|
315
|
+
|
|
316
|
+
const templateRegex = /\{([0-9]+)\}/g;
|
|
317
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
318
|
+
function templateString(template, args) {
|
|
319
|
+
return template.replace(templateRegex, (_, index) => {
|
|
320
|
+
return args[index];
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
325
|
+
function generateErrorMessage(errorInfo, args) {
|
|
326
|
+
const message = Array.isArray(args) ? templateString(errorInfo.message, args) : errorInfo.message;
|
|
327
|
+
return `LWR${errorInfo.code}: ${message}`;
|
|
328
|
+
}
|
|
329
|
+
class LoaderError extends Error {
|
|
330
|
+
constructor(errorInfo, errorArgs) {
|
|
331
|
+
super();
|
|
332
|
+
this.message = generateErrorMessage(errorInfo, errorArgs);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function invariant(condition, errorInfo) {
|
|
336
|
+
if (!condition) {
|
|
337
|
+
throw new LoaderError(errorInfo);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const MISSING_NAME = Object.freeze({
|
|
341
|
+
code: 3000,
|
|
342
|
+
message: 'A module name is required.',
|
|
343
|
+
level: 0,
|
|
344
|
+
});
|
|
345
|
+
const FAIL_INSTANTIATE = Object.freeze({
|
|
346
|
+
code: 3004,
|
|
347
|
+
message: 'Failed to instantiate module: {0}',
|
|
348
|
+
level: 0,
|
|
349
|
+
});
|
|
350
|
+
const NO_AMD_REQUIRE = Object.freeze({
|
|
351
|
+
code: 3005,
|
|
352
|
+
message: 'AMD require not supported.',
|
|
353
|
+
level: 0,
|
|
354
|
+
});
|
|
355
|
+
const FAILED_DEP = Object.freeze({
|
|
356
|
+
code: 3006,
|
|
357
|
+
level: 0,
|
|
358
|
+
message: 'Failed to load dependency: {0}',
|
|
359
|
+
});
|
|
360
|
+
const INVALID_DEPS = Object.freeze({
|
|
361
|
+
code: 3007,
|
|
362
|
+
message: 'Unexpected value received for dependencies argument; expected an array.',
|
|
363
|
+
level: 0,
|
|
364
|
+
});
|
|
365
|
+
const FAIL_LOAD = Object.freeze({
|
|
366
|
+
code: 3008,
|
|
367
|
+
level: 0,
|
|
368
|
+
message: 'Error loading {0}',
|
|
369
|
+
});
|
|
370
|
+
const UNRESOLVED = Object.freeze({
|
|
371
|
+
code: 3009,
|
|
372
|
+
level: 0,
|
|
373
|
+
message: 'Unable to resolve bare specifier: {0}',
|
|
374
|
+
});
|
|
375
|
+
const NO_BASE_URL = Object.freeze({
|
|
376
|
+
code: 3010,
|
|
377
|
+
level: 0,
|
|
378
|
+
message: 'baseUrl not set',
|
|
379
|
+
});
|
|
380
|
+
Object.freeze({
|
|
381
|
+
code: 3011,
|
|
382
|
+
level: 0,
|
|
383
|
+
message: 'Cannot set a loader service multiple times',
|
|
384
|
+
});
|
|
385
|
+
const INVALID_HOOK = Object.freeze({
|
|
386
|
+
code: 3012,
|
|
387
|
+
level: 0,
|
|
388
|
+
message: 'Invalid hook received',
|
|
389
|
+
});
|
|
390
|
+
const INVALID_LOADER_SERVICE_RESPONSE = Object.freeze({
|
|
391
|
+
code: 3013,
|
|
392
|
+
level: 0,
|
|
393
|
+
message: 'Invalid response received from hook',
|
|
394
|
+
});
|
|
395
|
+
const MODULE_LOAD_TIMEOUT = Object.freeze({
|
|
396
|
+
code: 3014,
|
|
397
|
+
level: 0,
|
|
398
|
+
message: 'Error loading {0} - timed out',
|
|
399
|
+
});
|
|
400
|
+
const HTTP_FAIL_LOAD = Object.freeze({
|
|
401
|
+
code: 3015,
|
|
402
|
+
level: 0,
|
|
403
|
+
message: 'Error loading {0}, status code {1}',
|
|
404
|
+
});
|
|
405
|
+
const STALE_HOOK_ERROR = Object.freeze({
|
|
406
|
+
code: 3016,
|
|
407
|
+
level: 0,
|
|
408
|
+
message: 'An error occurred handling module conflict',
|
|
409
|
+
});
|
|
410
|
+
const MODULE_ALREADY_LOADED = Object.freeze({
|
|
411
|
+
code: 3017,
|
|
412
|
+
level: 0,
|
|
413
|
+
message: 'Marking module(s) as externally loaded, but they are already loaded:',
|
|
414
|
+
});
|
|
415
|
+
const FAIL_HOOK_LOAD = Object.freeze({
|
|
416
|
+
code: 3018,
|
|
417
|
+
level: 0,
|
|
418
|
+
message: 'Error loading "{0}" from hook',
|
|
419
|
+
});
|
|
420
|
+
const NO_MAPPING_URL = Object.freeze({
|
|
421
|
+
code: 3019,
|
|
422
|
+
level: 0,
|
|
423
|
+
message: 'Mapping endpoint not set',
|
|
424
|
+
});
|
|
425
|
+
const BAD_IMPORT_METADATA = Object.freeze({
|
|
426
|
+
code: 3020,
|
|
427
|
+
level: 0,
|
|
428
|
+
message: 'Invalid import metadata: {0} {1}',
|
|
429
|
+
});
|
|
430
|
+
/* importMap errors */
|
|
431
|
+
Object.freeze({
|
|
432
|
+
code: 3011,
|
|
433
|
+
level: 0,
|
|
434
|
+
message: 'import map is not valid',
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
/* eslint-disable lwr/no-unguarded-apis */
|
|
438
|
+
const hasDocument = typeof document !== 'undefined';
|
|
439
|
+
const hasSetTimeout = typeof setTimeout === 'function';
|
|
440
|
+
const hasConsole = typeof console !== 'undefined';
|
|
441
|
+
/* eslint-enable lwr/no-unguarded-apis */
|
|
442
|
+
|
|
443
|
+
function getBaseUrl() {
|
|
444
|
+
let baseUrl = undefined;
|
|
445
|
+
if (hasDocument) {
|
|
446
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
447
|
+
const baseEl = document.querySelector('base[href]');
|
|
448
|
+
baseUrl = baseEl && baseEl.href;
|
|
449
|
+
}
|
|
450
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
451
|
+
if (!baseUrl && typeof location !== 'undefined') {
|
|
452
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
453
|
+
baseUrl = location.href.split('#')[0].split('?')[0];
|
|
454
|
+
const lastSepIndex = baseUrl.lastIndexOf('/');
|
|
455
|
+
if (lastSepIndex !== -1) {
|
|
456
|
+
baseUrl = baseUrl.slice(0, lastSepIndex + 1);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return baseUrl;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Check if a string is a URL based on Common Internet Scheme Syntax
|
|
463
|
+
* https://www.ietf.org/rfc/rfc1738.txt
|
|
464
|
+
*
|
|
465
|
+
* URL Format:
|
|
466
|
+
* <scheme>:<scheme-specific-part>
|
|
467
|
+
* Common Internet Scheme Syntax:
|
|
468
|
+
* The scheme specific part starts with a double slash('//')
|
|
469
|
+
*
|
|
470
|
+
* A valid URL has a colon that is followed by a double slash.
|
|
471
|
+
*
|
|
472
|
+
* @param url - the url that is being checked
|
|
473
|
+
* @returns boolean
|
|
474
|
+
*
|
|
475
|
+
* @example Valid URLs
|
|
476
|
+
* 'https://salesforce.com'
|
|
477
|
+
* 'http://localhost:3000'
|
|
478
|
+
*
|
|
479
|
+
* @example Invalid URLs
|
|
480
|
+
* 'salesforce.com'
|
|
481
|
+
* 'localhost:3000'
|
|
482
|
+
* '@salesforce/label/type:namespace:name'
|
|
483
|
+
*/
|
|
484
|
+
function isUrl(url) {
|
|
485
|
+
return url.indexOf('://') !== -1;
|
|
486
|
+
}
|
|
487
|
+
// Borrowed and adapted from https://github.com/systemjs/systemjs/blob/master/src/common.js
|
|
488
|
+
// Resolves the first path segment relative to the second/parent URL
|
|
489
|
+
// eg: resolveIfNotPlainOrUrl('../test', 'http://www.site.com/one/two') => 'http://www.site.com/test'
|
|
490
|
+
// eg: resolveIfNotPlainOrUrl('./x/y/z', 'https://my.com/segment')).toBe('https://my.com/x/y/z')
|
|
491
|
+
function resolveIfNotPlainOrUrl(relUrl, parentUrl) {
|
|
492
|
+
const backslashRegEx = /\\/g;
|
|
493
|
+
if (relUrl.indexOf('\\') !== -1)
|
|
494
|
+
relUrl = relUrl.replace(backslashRegEx, '/');
|
|
495
|
+
// protocol-relative
|
|
496
|
+
if (relUrl[0] === '/' && relUrl[1] === '/') {
|
|
497
|
+
return parentUrl.slice(0, parentUrl.indexOf(':') + 1) + relUrl;
|
|
498
|
+
}
|
|
499
|
+
// relative-url
|
|
500
|
+
else if ((relUrl[0] === '.' &&
|
|
501
|
+
(relUrl[1] === '/' ||
|
|
502
|
+
(relUrl[1] === '.' && (relUrl[2] === '/' || (relUrl.length === 2 && (relUrl += '/')))) ||
|
|
503
|
+
(relUrl.length === 1 && (relUrl += '/')))) ||
|
|
504
|
+
relUrl[0] === '/') {
|
|
505
|
+
const parentProtocol = parentUrl.slice(0, parentUrl.indexOf(':') + 1);
|
|
506
|
+
let pathname;
|
|
507
|
+
if (parentUrl[parentProtocol.length + 1] === '/') {
|
|
508
|
+
// resolving to a :// so we need to read out the auth and host
|
|
509
|
+
if (parentProtocol !== 'file:') {
|
|
510
|
+
pathname = parentUrl.slice(parentProtocol.length + 2);
|
|
511
|
+
pathname = pathname.slice(pathname.indexOf('/') + 1);
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
pathname = parentUrl.slice(8);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
// resolving to :/ so pathname is the /... part
|
|
519
|
+
pathname = parentUrl.slice(parentProtocol.length + (parentUrl[parentProtocol.length] === '/' ? 1 : 0));
|
|
520
|
+
}
|
|
521
|
+
if (relUrl[0] === '/')
|
|
522
|
+
return parentUrl.slice(0, parentUrl.length - pathname.length - 1) + relUrl;
|
|
523
|
+
// join together and split for removal of .. and . segments
|
|
524
|
+
// looping the string instead of anything fancy for perf reasons
|
|
525
|
+
// '../../../../../z' resolved to 'x/y' is just 'z'
|
|
526
|
+
const segmented = pathname.slice(0, pathname.lastIndexOf('/') + 1) + relUrl;
|
|
527
|
+
const output = [];
|
|
528
|
+
let segmentIndex = -1;
|
|
529
|
+
for (let i = 0; i < segmented.length; i++) {
|
|
530
|
+
// busy reading a segment - only terminate on '/'
|
|
531
|
+
if (segmentIndex !== -1) {
|
|
532
|
+
if (segmented[i] === '/') {
|
|
533
|
+
output.push(segmented.slice(segmentIndex, i + 1));
|
|
534
|
+
segmentIndex = -1;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
// new segment - check if it is relative
|
|
538
|
+
else if (segmented[i] === '.') {
|
|
539
|
+
// ../ segment
|
|
540
|
+
if (segmented[i + 1] === '.' && (segmented[i + 2] === '/' || i + 2 === segmented.length)) {
|
|
541
|
+
output.pop();
|
|
542
|
+
i += 2;
|
|
543
|
+
}
|
|
544
|
+
// ./ segment
|
|
545
|
+
else if (segmented[i + 1] === '/' || i + 1 === segmented.length) {
|
|
546
|
+
i += 1;
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
// the start of a new segment as below
|
|
550
|
+
segmentIndex = i;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
// it is the start of a new segment
|
|
554
|
+
else {
|
|
555
|
+
segmentIndex = i;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
// finish reading out the last segment
|
|
559
|
+
if (segmentIndex !== -1)
|
|
560
|
+
output.push(segmented.slice(segmentIndex));
|
|
561
|
+
return parentUrl.slice(0, parentUrl.length - pathname.length) + output.join('');
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
function resolveUrl(relUrl, parentUrl) {
|
|
565
|
+
const resolvedUrl = resolveIfNotPlainOrUrl(relUrl, parentUrl) ||
|
|
566
|
+
(isUrl(relUrl) ? relUrl : resolveIfNotPlainOrUrl('./' + relUrl, parentUrl));
|
|
567
|
+
return resolvedUrl;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function createScript(url) {
|
|
571
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
572
|
+
const script = document.createElement('script');
|
|
573
|
+
script.async = true;
|
|
574
|
+
script.crossOrigin = 'anonymous';
|
|
575
|
+
script.src = url;
|
|
576
|
+
return script;
|
|
577
|
+
}
|
|
578
|
+
let lastWindowError$1, lastWindowErrorUrl;
|
|
579
|
+
function loadModuleDef(url) {
|
|
580
|
+
return new Promise(function (resolve, reject) {
|
|
581
|
+
if (hasDocument) {
|
|
582
|
+
/* eslint-disable lwr/no-unguarded-apis, no-undef */
|
|
583
|
+
const script = createScript(url);
|
|
584
|
+
script.addEventListener('error', () => {
|
|
585
|
+
reject(new LoaderError(FAIL_LOAD, [url]));
|
|
586
|
+
});
|
|
587
|
+
script.addEventListener('load', () => {
|
|
588
|
+
document.head.removeChild(script);
|
|
589
|
+
if (lastWindowErrorUrl === url) {
|
|
590
|
+
reject(lastWindowError$1);
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
resolve();
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
document.head.appendChild(script);
|
|
597
|
+
/* eslint-enable lwr/no-unguarded-apis, no-undef */
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
if (hasDocument) {
|
|
602
|
+
// When a script is executed, runtime errors are on the global/window scope which are NOT caught by the script's onerror handler.
|
|
603
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
604
|
+
window.addEventListener('error', (evt) => {
|
|
605
|
+
lastWindowErrorUrl = evt.filename;
|
|
606
|
+
lastWindowError$1 = evt.error;
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Bootstrap / shim
|
|
611
|
+
|
|
612
|
+
// Loader: modules
|
|
613
|
+
const LOADER_PREFIX = 'lwr.loader.';
|
|
614
|
+
const MODULE_DEFINE = `${LOADER_PREFIX}module.define`;
|
|
615
|
+
const MODULE_FETCH = `${LOADER_PREFIX}module.fetch`;
|
|
616
|
+
const MODULE_ERROR = `${LOADER_PREFIX}module.error`;
|
|
617
|
+
|
|
618
|
+
// Loader: mappings
|
|
619
|
+
const MAPPINGS_FETCH = `${LOADER_PREFIX}mappings.fetch`;
|
|
620
|
+
const MAPPINGS_ERROR = `${LOADER_PREFIX}mappings.error`;
|
|
621
|
+
|
|
622
|
+
/* spec based import map resolver */
|
|
623
|
+
class ImportMetadataResolver {
|
|
624
|
+
constructor(config, invalidationCallback) {
|
|
625
|
+
// Default to empty mappings
|
|
626
|
+
this.importURICache = new Map();
|
|
627
|
+
this.pendingURICache = new Map();
|
|
628
|
+
this.loadMappingHooks = [];
|
|
629
|
+
this.config = config;
|
|
630
|
+
this.invalidationCallback = invalidationCallback;
|
|
631
|
+
}
|
|
632
|
+
addLoadMappingHook(hook) {
|
|
633
|
+
this.loadMappingHooks.push(hook);
|
|
634
|
+
}
|
|
635
|
+
getMappingEndpoint() {
|
|
636
|
+
return this.config.endpoints && this.config.endpoints.uris
|
|
637
|
+
? this.config.endpoints.uris.mapping
|
|
638
|
+
: undefined;
|
|
639
|
+
}
|
|
640
|
+
getModifiersAsUrlParams() {
|
|
641
|
+
const modifiers = this.config.endpoints ? this.config.endpoints.modifiers : undefined;
|
|
642
|
+
if (!modifiers) {
|
|
643
|
+
// No modifiers return an empty string to append to the URL
|
|
644
|
+
return '';
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
const qs = Object.keys(modifiers)
|
|
648
|
+
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(modifiers[key])}`)
|
|
649
|
+
.join('&');
|
|
650
|
+
return `?${qs}`;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
buildMappingUrl(specifier) {
|
|
654
|
+
const mappingEndpoint = this.getMappingEndpoint();
|
|
655
|
+
const specifiers = encodeURIComponent(specifier);
|
|
656
|
+
const modifiers = this.getModifiersAsUrlParams();
|
|
657
|
+
return `${mappingEndpoint}${specifiers}${modifiers}`;
|
|
658
|
+
}
|
|
659
|
+
getBaseUrl() {
|
|
660
|
+
return this.config.baseUrl;
|
|
661
|
+
}
|
|
662
|
+
registerImportMappings(newImportMetadata, rootSpecifiers) {
|
|
663
|
+
if (!rootSpecifiers || rootSpecifiers.length === 0) {
|
|
664
|
+
const imports = newImportMetadata ? JSON.stringify(newImportMetadata) : 'undefined';
|
|
665
|
+
throw new LoaderError(BAD_IMPORT_METADATA, [imports, rootSpecifiers ? '[]' : 'undefined']);
|
|
666
|
+
}
|
|
667
|
+
if (!newImportMetadata) {
|
|
668
|
+
throw new LoaderError(BAD_IMPORT_METADATA, ['undefined', JSON.stringify(rootSpecifiers)]);
|
|
669
|
+
}
|
|
670
|
+
if (!newImportMetadata.imports || Object.keys(newImportMetadata.imports).length === 0) {
|
|
671
|
+
throw new LoaderError(BAD_IMPORT_METADATA, [
|
|
672
|
+
JSON.stringify(newImportMetadata),
|
|
673
|
+
JSON.stringify(rootSpecifiers),
|
|
674
|
+
]);
|
|
675
|
+
}
|
|
676
|
+
const index = newImportMetadata.index || {};
|
|
677
|
+
for (const [uri, specifiers] of Object.entries(newImportMetadata.imports)) {
|
|
678
|
+
specifiers.forEach((specifier) => {
|
|
679
|
+
const indexValue = index[specifier];
|
|
680
|
+
const existing = this.importURICache.get(specifier);
|
|
681
|
+
if (!existing) {
|
|
682
|
+
this.saveImportURIRecord(specifier, uri, indexValue, rootSpecifiers.includes(specifier));
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
const identity = indexValue || uri;
|
|
686
|
+
const existingIdentity = existing.identity || existing.uri;
|
|
687
|
+
if (existingIdentity !== identity) {
|
|
688
|
+
this.invalidationCallback({
|
|
689
|
+
name: specifier,
|
|
690
|
+
oldUrl: existingIdentity,
|
|
691
|
+
newUrl: identity,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
// Get URL from the local cache or return undefiend
|
|
699
|
+
getURI(specifier) {
|
|
700
|
+
return this.importURICache.has(specifier)
|
|
701
|
+
? resolveUrl(this.importURICache.get(specifier).uri, this.getBaseUrl())
|
|
702
|
+
: undefined;
|
|
703
|
+
}
|
|
704
|
+
resolveLocal(specifier) {
|
|
705
|
+
const uri = this.getURI(specifier);
|
|
706
|
+
if (uri) {
|
|
707
|
+
return uri;
|
|
708
|
+
}
|
|
709
|
+
else if (isUrl(specifier) || specifier.startsWith('/')) {
|
|
710
|
+
return specifier;
|
|
711
|
+
}
|
|
712
|
+
return undefined;
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Resolves a the URI for a specified module. It will return the value in this order:
|
|
716
|
+
*
|
|
717
|
+
* 1) Mapping from local URI cache
|
|
718
|
+
* 2) The URI if a specifier is already an absolute URI
|
|
719
|
+
* 3) Mapping fetched from a registered loader hook
|
|
720
|
+
* 4)
|
|
721
|
+
* @param specifier
|
|
722
|
+
* @returns module URI
|
|
723
|
+
*/
|
|
724
|
+
async resolve(specifier) {
|
|
725
|
+
let uri = this.getURI(specifier);
|
|
726
|
+
if (uri) {
|
|
727
|
+
return uri;
|
|
728
|
+
}
|
|
729
|
+
else if (isUrl(specifier) || specifier.startsWith('/')) {
|
|
730
|
+
return specifier;
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
const pending = this.pendingURICache.get(specifier);
|
|
734
|
+
if (pending) {
|
|
735
|
+
return pending;
|
|
736
|
+
}
|
|
737
|
+
this.config.profiler.logOperationStart({ id: MAPPINGS_FETCH, specifier });
|
|
738
|
+
const fetchMappingService = this.hasMappingHooks()
|
|
739
|
+
? this.evaluateMappingHooks
|
|
740
|
+
: this.fetchNewMappings;
|
|
741
|
+
const promise = fetchMappingService
|
|
742
|
+
.bind(this)(specifier)
|
|
743
|
+
.then((importMetadata) => {
|
|
744
|
+
if (!importMetadata || !importMetadata.imports) {
|
|
745
|
+
throw new LoaderError(UNRESOLVED, [specifier]);
|
|
746
|
+
}
|
|
747
|
+
this.registerImportMappings(importMetadata, [specifier]);
|
|
748
|
+
uri = this.getURI(specifier);
|
|
749
|
+
if (!uri) {
|
|
750
|
+
throw new LoaderError(UNRESOLVED, [specifier]);
|
|
751
|
+
}
|
|
752
|
+
this.config.profiler.logOperationEnd({ id: MAPPINGS_FETCH, specifier });
|
|
753
|
+
return uri;
|
|
754
|
+
})
|
|
755
|
+
.finally(() => {
|
|
756
|
+
this.pendingURICache.delete(specifier);
|
|
757
|
+
});
|
|
758
|
+
this.pendingURICache.set(specifier, promise);
|
|
759
|
+
return promise;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
hasMappingHooks() {
|
|
763
|
+
return this.loadMappingHooks.length > 0;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Evaluates mapping hooks. Returns first match. If all hooks return null call the mapping service.
|
|
767
|
+
* @param specifier Request module identifier
|
|
768
|
+
* @returns Import Metadata from the module root
|
|
769
|
+
*/
|
|
770
|
+
async evaluateMappingHooks(specifier) {
|
|
771
|
+
// Check with any registered loadMappingHooks
|
|
772
|
+
const loadMappingHooks = this.loadMappingHooks;
|
|
773
|
+
if (loadMappingHooks.length) {
|
|
774
|
+
const knownModules = Array.from(this.importURICache.keys());
|
|
775
|
+
for (let i = 0; i < loadMappingHooks.length; i++) {
|
|
776
|
+
const loadMappingHook = loadMappingHooks[i];
|
|
777
|
+
// eslint-disable-next-line no-await-in-loop
|
|
778
|
+
const response = await loadMappingHook(specifier, { knownModules });
|
|
779
|
+
// undefined (but not null) is considered an un expected response so we will stop processing hooks here and throw an error
|
|
780
|
+
if (response || response === undefined) {
|
|
781
|
+
return response;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
// If we still do not have a match call the mapping service
|
|
786
|
+
return this.fetchNewMappings(specifier);
|
|
787
|
+
}
|
|
788
|
+
async fetchNewMappings(specifier) {
|
|
789
|
+
if (typeof globalThis.fetch !== 'function') {
|
|
790
|
+
throw new LoaderError(UNRESOLVED, [specifier]);
|
|
791
|
+
}
|
|
792
|
+
// TODO For module invalidation with bundles it is recommended we have to send back all loaded root specified
|
|
793
|
+
// to ensure we detect all conflicts.
|
|
794
|
+
const uri = resolveUrl(this.buildMappingUrl(specifier), this.getBaseUrl());
|
|
795
|
+
return globalThis.fetch(uri).then((res) => {
|
|
796
|
+
if (!res.ok) {
|
|
797
|
+
this.config.profiler.logOperationStart({ id: MAPPINGS_ERROR, specifier });
|
|
798
|
+
throw new LoaderError(UNRESOLVED, [specifier]);
|
|
799
|
+
}
|
|
800
|
+
return res
|
|
801
|
+
.json()
|
|
802
|
+
.then((ret) => {
|
|
803
|
+
return ret;
|
|
804
|
+
})
|
|
805
|
+
.catch((err) => {
|
|
806
|
+
throw new LoaderError(UNRESOLVED, [specifier]);
|
|
807
|
+
});
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
saveImportURIRecord(specifier, uri, identity, isRoot) {
|
|
811
|
+
if (!identity || uri === identity) {
|
|
812
|
+
this.importURICache.set(specifier, {
|
|
813
|
+
uri,
|
|
814
|
+
isRoot: isRoot,
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
this.importURICache.set(specifier, {
|
|
819
|
+
uri,
|
|
820
|
+
identity,
|
|
821
|
+
isRoot: isRoot,
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function reportError(error) {
|
|
828
|
+
// TODO eventually this should be configurable instrumentation to send this somewhere
|
|
829
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
830
|
+
if (hasConsole)
|
|
831
|
+
console.error(error);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function evaluateHandleStaleModuleHooks(handleStaleModuleHooks, hookArgs) {
|
|
835
|
+
const { name, oldUrl, newUrl } = hookArgs;
|
|
836
|
+
// keep evaluating hooks if return value is null
|
|
837
|
+
for (let i = 0; i < handleStaleModuleHooks.length; i++) {
|
|
838
|
+
const hook = handleStaleModuleHooks[i];
|
|
839
|
+
try {
|
|
840
|
+
const hookResult = hook({ name, oldUrl, newUrl });
|
|
841
|
+
if (hookResult !== null) {
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
catch (e) {
|
|
846
|
+
reportError(new LoaderError(STALE_HOOK_ERROR));
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const MODULE_LOAD_TIMEOUT_TIMER = 300000;
|
|
852
|
+
|
|
853
|
+
/* global console,process */
|
|
854
|
+
let lastWindowError;
|
|
855
|
+
if (hasDocument) {
|
|
856
|
+
globalThis.addEventListener('error', (evt) => {
|
|
857
|
+
lastWindowError = evt.error;
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
861
|
+
if (!hasSetTimeout && hasConsole) {
|
|
862
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
863
|
+
console.warn('setTimeout API is not available, watchdog timer on load hook will not be set');
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
function isCustomResponse(response) {
|
|
867
|
+
return (Object.prototype.hasOwnProperty.call(response, 'data') &&
|
|
868
|
+
!Object.prototype.hasOwnProperty.call(response, 'blob'));
|
|
869
|
+
}
|
|
870
|
+
function isFetchResponse(response) {
|
|
871
|
+
// if it quacks like a duck...
|
|
872
|
+
return typeof response.blob === 'function';
|
|
873
|
+
}
|
|
874
|
+
function isResponseAPromise(response) {
|
|
875
|
+
return !!(response && response.then);
|
|
876
|
+
}
|
|
877
|
+
async function evaluateLoadHookResponse(response, id) {
|
|
878
|
+
return Promise.resolve().then(async () => {
|
|
879
|
+
if (!response.status) {
|
|
880
|
+
throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
|
|
881
|
+
}
|
|
882
|
+
if (response.status !== 200) {
|
|
883
|
+
throw new LoaderError(HTTP_FAIL_LOAD, [id, `${response.status}`]);
|
|
884
|
+
}
|
|
885
|
+
const isResponse = isFetchResponse(response);
|
|
886
|
+
let code;
|
|
887
|
+
if (isCustomResponse(response)) {
|
|
888
|
+
code = response.data;
|
|
889
|
+
}
|
|
890
|
+
else if (isResponse) {
|
|
891
|
+
// handle fetch response
|
|
892
|
+
code = await response.text();
|
|
893
|
+
}
|
|
894
|
+
else {
|
|
895
|
+
throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
|
|
896
|
+
}
|
|
897
|
+
if (!code) {
|
|
898
|
+
throw new LoaderError(FAIL_LOAD, [id]);
|
|
899
|
+
}
|
|
900
|
+
code = `${code}\n//# sourceURL=${id}`; // append sourceURL for debugging
|
|
901
|
+
try {
|
|
902
|
+
// TODO eval source maps for debugging
|
|
903
|
+
eval(code);
|
|
904
|
+
}
|
|
905
|
+
catch (e) {
|
|
906
|
+
throw new LoaderError(FAIL_LOAD, [id]);
|
|
907
|
+
}
|
|
908
|
+
if (lastWindowError) {
|
|
909
|
+
throw new LoaderError(FAIL_LOAD, [id]);
|
|
910
|
+
}
|
|
911
|
+
return true;
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
async function evaluateLoadHook(id, hookPromise) {
|
|
915
|
+
if (!hasSetTimeout) {
|
|
916
|
+
return hookPromise;
|
|
917
|
+
}
|
|
918
|
+
return new Promise((resolve, reject) => {
|
|
919
|
+
// wrap the hook in a watchdog timer
|
|
920
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
921
|
+
const timer = setTimeout(() => {
|
|
922
|
+
reject(new LoaderError(MODULE_LOAD_TIMEOUT, [id]));
|
|
923
|
+
}, MODULE_LOAD_TIMEOUT_TIMER);
|
|
924
|
+
hookPromise
|
|
925
|
+
.then((response) => {
|
|
926
|
+
resolve(response);
|
|
927
|
+
})
|
|
928
|
+
.catch(() => {
|
|
929
|
+
reject(new LoaderError(FAIL_HOOK_LOAD, [id]));
|
|
930
|
+
})
|
|
931
|
+
.finally(() => {
|
|
932
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
933
|
+
clearTimeout(timer);
|
|
934
|
+
});
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/* global console,process */
|
|
939
|
+
class ModuleRegistry {
|
|
940
|
+
constructor(config) {
|
|
941
|
+
// A registry for named AMD defines containing the *metadata* of AMD module
|
|
942
|
+
this.namedDefineRegistry = new Map();
|
|
943
|
+
// The evaluated module registry where the module identifier (name or URL?) is the key
|
|
944
|
+
this.moduleRegistry = new Map();
|
|
945
|
+
// Aliases of modules in the registry
|
|
946
|
+
this.aliases = new Map();
|
|
947
|
+
this.profiler = config.profiler;
|
|
948
|
+
this.resolver = new ImportMetadataResolver(config, this.importMetadataInvalidationCallback.bind(this));
|
|
949
|
+
}
|
|
950
|
+
async load(id, importer) {
|
|
951
|
+
const resolvedId = await this.resolve(id, importer);
|
|
952
|
+
const moduleRecord = this.getModuleRecord(resolvedId, id);
|
|
953
|
+
if (moduleRecord.evaluated) {
|
|
954
|
+
return moduleRecord.module;
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
if (!moduleRecord.evaluationPromise) {
|
|
958
|
+
moduleRecord.evaluationPromise = this.topLevelEvaluation(moduleRecord);
|
|
959
|
+
}
|
|
960
|
+
return moduleRecord.evaluationPromise;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
async resolve(id, importer) {
|
|
964
|
+
const parentUrl = this.resolver.getBaseUrl(); // only support baseUrl for now
|
|
965
|
+
let resolved;
|
|
966
|
+
let aliasedId = id;
|
|
967
|
+
const resolveHooks = this.resolveHook;
|
|
968
|
+
if (resolveHooks) {
|
|
969
|
+
for (let i = 0; i < resolveHooks.length; i++) {
|
|
970
|
+
const resolveHook = resolveHooks[i];
|
|
971
|
+
const response = resolveHook(aliasedId, { parentUrl });
|
|
972
|
+
let result;
|
|
973
|
+
if (response || response === null) {
|
|
974
|
+
// eslint-disable-next-line no-await-in-loop
|
|
975
|
+
result = isResponseAPromise(response) ? await response : response;
|
|
976
|
+
}
|
|
977
|
+
// if result is not null, attempt resolution
|
|
978
|
+
if (result !== null) {
|
|
979
|
+
if (typeof result === 'string') {
|
|
980
|
+
if (resolveIfNotPlainOrUrl(result, parentUrl)) {
|
|
981
|
+
// string response can't be a URL
|
|
982
|
+
throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
|
|
983
|
+
}
|
|
984
|
+
aliasedId = result; // the next hook will receive the new id
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
resolved =
|
|
988
|
+
result && result.url && (resolveIfNotPlainOrUrl(result.url, parentUrl) || result.url);
|
|
989
|
+
if (!resolved) {
|
|
990
|
+
throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
|
|
991
|
+
}
|
|
992
|
+
// Don't process any more hooks if we have resolved
|
|
993
|
+
break;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
if (aliasedId !== id) {
|
|
997
|
+
// resolved module id is the aliased module if it has already been defined
|
|
998
|
+
if (!resolved && this.namedDefineRegistry.has(aliasedId)) {
|
|
999
|
+
return aliasedId;
|
|
1000
|
+
}
|
|
1001
|
+
else {
|
|
1002
|
+
id = aliasedId;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
if (!resolved) {
|
|
1007
|
+
const resolvedOrPlain = resolveIfNotPlainOrUrl(id, parentUrl) || id;
|
|
1008
|
+
// if module registry already has named module the resolved id is the plain id
|
|
1009
|
+
if (this.moduleRegistry.has(resolvedOrPlain)) {
|
|
1010
|
+
return resolvedOrPlain;
|
|
1011
|
+
}
|
|
1012
|
+
const resolvedUrl = this.resolver.resolveLocal(resolvedOrPlain);
|
|
1013
|
+
if (resolvedUrl) {
|
|
1014
|
+
// return the plain id if it is already defined && the resolvedUrl is NOT already in the module registry
|
|
1015
|
+
if (this.namedDefineRegistry.has(resolvedOrPlain) &&
|
|
1016
|
+
this.namedDefineRegistry.get(resolvedOrPlain).defined) {
|
|
1017
|
+
const record = this.moduleRegistry.get(resolvedUrl);
|
|
1018
|
+
if (!record || !this.aliases.has(resolvedOrPlain)) {
|
|
1019
|
+
return resolvedOrPlain;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return resolvedUrl;
|
|
1023
|
+
}
|
|
1024
|
+
if (this.namedDefineRegistry.has(resolvedOrPlain)) {
|
|
1025
|
+
return resolvedOrPlain;
|
|
1026
|
+
}
|
|
1027
|
+
try {
|
|
1028
|
+
resolved = await this.resolver.resolve(resolvedOrPlain);
|
|
1029
|
+
}
|
|
1030
|
+
catch (e) {
|
|
1031
|
+
// defer to error handling below for unresolved
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
if (!resolved || !isUrl(resolved)) {
|
|
1035
|
+
if (this.namedDefineRegistry.has(id)) {
|
|
1036
|
+
return id;
|
|
1037
|
+
}
|
|
1038
|
+
throw new LoaderError(UNRESOLVED, [id]);
|
|
1039
|
+
}
|
|
1040
|
+
if (importer && isUrl(resolved)) {
|
|
1041
|
+
resolved += `?importer=${encodeURIComponent(importer)}`;
|
|
1042
|
+
}
|
|
1043
|
+
return resolved;
|
|
1044
|
+
}
|
|
1045
|
+
has(id) {
|
|
1046
|
+
return this.moduleRegistry.has(id);
|
|
1047
|
+
}
|
|
1048
|
+
define(name, dependencies, exporter) {
|
|
1049
|
+
const mod = this.namedDefineRegistry.get(name);
|
|
1050
|
+
// Don't allow redefining a module.
|
|
1051
|
+
if (mod && mod.defined) {
|
|
1052
|
+
if (process.env.NODE_ENV !== 'production' && hasConsole) {
|
|
1053
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
1054
|
+
console.warn(`Module redefine attempted: ${name}`);
|
|
1055
|
+
}
|
|
1056
|
+
this.lastDefine = mod;
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
const moduleDef = {
|
|
1060
|
+
name,
|
|
1061
|
+
dependencies,
|
|
1062
|
+
exporter,
|
|
1063
|
+
defined: true,
|
|
1064
|
+
};
|
|
1065
|
+
if (mod && mod.external) {
|
|
1066
|
+
// if module is "external", resolve the external promise to notify any dependees
|
|
1067
|
+
mod.external.resolveExternal(moduleDef);
|
|
1068
|
+
}
|
|
1069
|
+
this.profiler.logOperationStart({ id: MODULE_DEFINE, specifier: name });
|
|
1070
|
+
this.namedDefineRegistry.set(name, moduleDef);
|
|
1071
|
+
this.lastDefine = moduleDef;
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Marks modules as "externally" loaded/provided, so that the loader does not attempt to fetch them.
|
|
1075
|
+
*
|
|
1076
|
+
* @param modules - list of module identifiers
|
|
1077
|
+
*/
|
|
1078
|
+
registerExternalModules(modules) {
|
|
1079
|
+
modules.map((id) => {
|
|
1080
|
+
if (!this.namedDefineRegistry.has(id)) {
|
|
1081
|
+
let resolveExternal;
|
|
1082
|
+
let timer;
|
|
1083
|
+
const moduleDefPromise = new Promise((resolve, reject) => {
|
|
1084
|
+
resolveExternal = resolve;
|
|
1085
|
+
// watch the external for timeout
|
|
1086
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
1087
|
+
timer = setTimeout(() => {
|
|
1088
|
+
reject(new LoaderError(MODULE_LOAD_TIMEOUT, [id]));
|
|
1089
|
+
}, MODULE_LOAD_TIMEOUT_TIMER);
|
|
1090
|
+
}).finally(() => {
|
|
1091
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
1092
|
+
clearTimeout(timer);
|
|
1093
|
+
});
|
|
1094
|
+
const moduleDef = {
|
|
1095
|
+
name: id,
|
|
1096
|
+
defined: false,
|
|
1097
|
+
external: {
|
|
1098
|
+
resolveExternal,
|
|
1099
|
+
moduleDefPromise,
|
|
1100
|
+
},
|
|
1101
|
+
};
|
|
1102
|
+
this.namedDefineRegistry.set(id, moduleDef);
|
|
1103
|
+
}
|
|
1104
|
+
else if (process.env.NODE_ENV !== 'production' && hasConsole) {
|
|
1105
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
1106
|
+
console.warn(MODULE_ALREADY_LOADED.message, id);
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
getImportMetadataResolver() {
|
|
1111
|
+
return this.resolver;
|
|
1112
|
+
}
|
|
1113
|
+
// Returns an existing module record by the resolvedId or aliased id
|
|
1114
|
+
getExistingModuleRecord(resolvedId, aliasId) {
|
|
1115
|
+
const moduleRecord = this.moduleRegistry.get(resolvedId);
|
|
1116
|
+
if (moduleRecord) {
|
|
1117
|
+
this.storeModuleAlias(aliasId, resolvedId);
|
|
1118
|
+
return moduleRecord;
|
|
1119
|
+
}
|
|
1120
|
+
// Check if this is a known alias
|
|
1121
|
+
if (resolvedId !== aliasId) {
|
|
1122
|
+
const alias = this.aliases.get(aliasId);
|
|
1123
|
+
if (alias) {
|
|
1124
|
+
const aliasedModule = this.moduleRegistry.get(alias);
|
|
1125
|
+
if (aliasedModule) {
|
|
1126
|
+
return aliasedModule;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
return moduleRecord;
|
|
1131
|
+
}
|
|
1132
|
+
getModuleRecord(resolvedId, id) {
|
|
1133
|
+
// Look for an existing record
|
|
1134
|
+
const existingRecord = this.getExistingModuleRecord(resolvedId, id);
|
|
1135
|
+
if (existingRecord) {
|
|
1136
|
+
// return existing
|
|
1137
|
+
return existingRecord;
|
|
1138
|
+
}
|
|
1139
|
+
// Create a new Module Record
|
|
1140
|
+
const instantiation = this.getModuleDef(resolvedId, id);
|
|
1141
|
+
const dependencyRecords = instantiation.then((moduleDef) => {
|
|
1142
|
+
const dependencies = moduleDef.dependencies;
|
|
1143
|
+
// get dep and filter out exports
|
|
1144
|
+
const filtered = dependencies
|
|
1145
|
+
.map((dep) => {
|
|
1146
|
+
if (dep === 'exports') {
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
invariant(dep !== 'require', NO_AMD_REQUIRE);
|
|
1150
|
+
return this.getModuleDependencyRecord.call(this, dep);
|
|
1151
|
+
})
|
|
1152
|
+
.filter((depRecord) => depRecord !== undefined);
|
|
1153
|
+
return Promise.all(filtered);
|
|
1154
|
+
});
|
|
1155
|
+
const newModuleRecord = {
|
|
1156
|
+
id: resolvedId,
|
|
1157
|
+
module: Object.create(null),
|
|
1158
|
+
dependencyRecords,
|
|
1159
|
+
instantiation,
|
|
1160
|
+
evaluated: false,
|
|
1161
|
+
evaluationPromise: null,
|
|
1162
|
+
};
|
|
1163
|
+
this.moduleRegistry.set(resolvedId, newModuleRecord);
|
|
1164
|
+
this.storeModuleAlias(id, resolvedId);
|
|
1165
|
+
return newModuleRecord;
|
|
1166
|
+
}
|
|
1167
|
+
storeModuleAlias(aliasId, resolvedId) {
|
|
1168
|
+
if (aliasId !== resolvedId) {
|
|
1169
|
+
if (!this.aliases.has(aliasId)) {
|
|
1170
|
+
this.aliases.set(aliasId, resolvedId);
|
|
1171
|
+
}
|
|
1172
|
+
else if (process.env.NODE_ENV !== 'production' && hasConsole) {
|
|
1173
|
+
// Warn the user if they were not aliasing to the resolvedId
|
|
1174
|
+
const currentResolvedId = this.aliases.get(aliasId);
|
|
1175
|
+
if (currentResolvedId !== resolvedId) {
|
|
1176
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
1177
|
+
console.warn(`Alias update attempt: ${aliasId}=>${currentResolvedId}, ${resolvedId}`);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
async getModuleDependencyRecord(dependency) {
|
|
1183
|
+
const resolvedDepId = await this.resolve(dependency);
|
|
1184
|
+
return this.getModuleRecord(resolvedDepId, dependency);
|
|
1185
|
+
}
|
|
1186
|
+
// execute the "top-level code" (the code outside of functions) of a module
|
|
1187
|
+
async topLevelEvaluation(moduleRecord) {
|
|
1188
|
+
await this.instantiateAll(moduleRecord, {});
|
|
1189
|
+
return this.evaluateModule(moduleRecord, {});
|
|
1190
|
+
}
|
|
1191
|
+
// Returns a promise when a module and all of it's dependencies have finished instantiation
|
|
1192
|
+
async instantiateAll(moduleRecord, instantiatedMap) {
|
|
1193
|
+
if (!instantiatedMap[moduleRecord.id]) {
|
|
1194
|
+
instantiatedMap[moduleRecord.id] = true;
|
|
1195
|
+
const dependencyModuleRecords = await moduleRecord.dependencyRecords;
|
|
1196
|
+
if (dependencyModuleRecords) {
|
|
1197
|
+
for (let i = 0; i < dependencyModuleRecords.length; i++) {
|
|
1198
|
+
const depRecord = dependencyModuleRecords[i];
|
|
1199
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1200
|
+
await this.instantiateAll(depRecord, instantiatedMap);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
async evaluateModule(moduleRecord, evaluationMap) {
|
|
1206
|
+
const dependencyModuleRecords = await moduleRecord.dependencyRecords;
|
|
1207
|
+
if (dependencyModuleRecords.length > 0) {
|
|
1208
|
+
evaluationMap[moduleRecord.id] = true;
|
|
1209
|
+
// evaluate dependencies first
|
|
1210
|
+
await this.evaluateModuleDependencies(dependencyModuleRecords, evaluationMap);
|
|
1211
|
+
}
|
|
1212
|
+
const { exporter, dependencies } = await moduleRecord.instantiation;
|
|
1213
|
+
// The exports object automatically gets filled in by the exporter evaluation
|
|
1214
|
+
const exports = {};
|
|
1215
|
+
const depsMapped = await Promise.all(dependencies.map(async (dep) => {
|
|
1216
|
+
if (dep === 'exports') {
|
|
1217
|
+
return exports;
|
|
1218
|
+
}
|
|
1219
|
+
const resolvedDepId = await this.resolve(dep);
|
|
1220
|
+
const moduleRecord = this.moduleRegistry.get(resolvedDepId);
|
|
1221
|
+
if (!moduleRecord) {
|
|
1222
|
+
throw new LoaderError(FAILED_DEP, [resolvedDepId]);
|
|
1223
|
+
}
|
|
1224
|
+
const module = moduleRecord.module;
|
|
1225
|
+
/**
|
|
1226
|
+
* Circular dependencies are handled properly when named exports are used,
|
|
1227
|
+
* however, for default exports there is a bug: https://github.com/rollup/rollup/issues/3384
|
|
1228
|
+
*
|
|
1229
|
+
* The workaround below applies for circular dependencies (!moduleRecord.evaluated)
|
|
1230
|
+
*/
|
|
1231
|
+
if (!moduleRecord.evaluated) {
|
|
1232
|
+
return this.getCircularDependencyWrapper(module);
|
|
1233
|
+
}
|
|
1234
|
+
if (module) {
|
|
1235
|
+
return module.__defaultInterop ? module.default : module;
|
|
1236
|
+
}
|
|
1237
|
+
throw new LoaderError(FAILED_DEP, [resolvedDepId]);
|
|
1238
|
+
}));
|
|
1239
|
+
// W-10029836 - In the case where we could be instantiating multiple graphs at the same time lets make sure the module have not already been evaluated
|
|
1240
|
+
if (moduleRecord.evaluated) {
|
|
1241
|
+
return moduleRecord.module;
|
|
1242
|
+
}
|
|
1243
|
+
// evaluates the module function
|
|
1244
|
+
let moduleDefault = exporter(...depsMapped);
|
|
1245
|
+
// value is returned from exporter, then we are not using named exports
|
|
1246
|
+
if (moduleDefault !== undefined) {
|
|
1247
|
+
moduleDefault = { default: moduleDefault };
|
|
1248
|
+
// __defaultInterop is ONLY used to support backwards compatibility
|
|
1249
|
+
// of importing default exports the "wrong" way (when not using named exports).
|
|
1250
|
+
// See https://github.com/salesforce-experience-platform-emu/lwr/pull/816
|
|
1251
|
+
Object.defineProperty(moduleDefault, '__defaultInterop', { value: true });
|
|
1252
|
+
}
|
|
1253
|
+
// if no return value, then we are using the exports object
|
|
1254
|
+
else {
|
|
1255
|
+
// handle only default export with Rollup forced named exports
|
|
1256
|
+
if (this.isNamedExportDefaultOnly(exports)) {
|
|
1257
|
+
Object.defineProperty(exports, '__useDefault', { value: true });
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
const moduleExports = moduleDefault || exports;
|
|
1261
|
+
// update the module record
|
|
1262
|
+
// copy over enumerable public methods to module
|
|
1263
|
+
for (const key in moduleExports) {
|
|
1264
|
+
Object.defineProperty(moduleRecord.module, key, {
|
|
1265
|
+
enumerable: true,
|
|
1266
|
+
set(value) {
|
|
1267
|
+
moduleExports[key] = value;
|
|
1268
|
+
},
|
|
1269
|
+
get() {
|
|
1270
|
+
return moduleExports[key];
|
|
1271
|
+
},
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
// copy non-enumerable to module
|
|
1275
|
+
if (moduleExports.__useDefault) {
|
|
1276
|
+
Object.defineProperty(moduleRecord.module, '__useDefault', { value: true });
|
|
1277
|
+
}
|
|
1278
|
+
if (moduleExports.__defaultInterop) {
|
|
1279
|
+
Object.defineProperty(moduleRecord.module, '__defaultInterop', { value: true });
|
|
1280
|
+
}
|
|
1281
|
+
if (moduleExports.__esModule) {
|
|
1282
|
+
Object.defineProperty(moduleRecord.module, '__esModule', { value: true });
|
|
1283
|
+
}
|
|
1284
|
+
moduleRecord.evaluated = true;
|
|
1285
|
+
Object.freeze(moduleRecord.module);
|
|
1286
|
+
return moduleRecord.module;
|
|
1287
|
+
}
|
|
1288
|
+
// Determines if named exports module has only default export
|
|
1289
|
+
isNamedExportDefaultOnly(exports) {
|
|
1290
|
+
return (exports !== undefined &&
|
|
1291
|
+
Object.getOwnPropertyNames(exports).length === 2 &&
|
|
1292
|
+
Object.prototype.hasOwnProperty.call(exports, 'default') &&
|
|
1293
|
+
Object.prototype.hasOwnProperty.call(exports, '__esModule'));
|
|
1294
|
+
}
|
|
1295
|
+
// Wrap the dependency in a function that can be called and detected by __circular__ property.
|
|
1296
|
+
// The LWC engine checks for __circular__ to detect circular dependencies.
|
|
1297
|
+
getCircularDependencyWrapper(module) {
|
|
1298
|
+
const tmp = () => {
|
|
1299
|
+
return module.__useDefault || module.__defaultInterop ? module.default : module;
|
|
1300
|
+
};
|
|
1301
|
+
tmp.__circular__ = true;
|
|
1302
|
+
return tmp;
|
|
1303
|
+
}
|
|
1304
|
+
async evaluateModuleDependencies(dependencyModuleRecords, evaluationMap) {
|
|
1305
|
+
for (let i = 0; i < dependencyModuleRecords.length; i++) {
|
|
1306
|
+
const depRecord = dependencyModuleRecords[i];
|
|
1307
|
+
if (!depRecord.evaluated && !evaluationMap[depRecord.id]) {
|
|
1308
|
+
evaluationMap[depRecord.id] = true;
|
|
1309
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1310
|
+
await this.evaluateModule(depRecord, evaluationMap);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
async getModuleDef(resolvedId, originalId) {
|
|
1315
|
+
// reset lastDefine
|
|
1316
|
+
this.lastDefine = undefined;
|
|
1317
|
+
// the module name can be the resolved ID or the original ID if neither are URL's.
|
|
1318
|
+
const moduleName = !isUrl(resolvedId)
|
|
1319
|
+
? resolvedId
|
|
1320
|
+
: originalId !== resolvedId
|
|
1321
|
+
? originalId
|
|
1322
|
+
: undefined;
|
|
1323
|
+
let moduleDef = moduleName && this.namedDefineRegistry.get(moduleName);
|
|
1324
|
+
if (moduleDef && moduleDef.external) {
|
|
1325
|
+
return moduleDef.external.moduleDefPromise;
|
|
1326
|
+
}
|
|
1327
|
+
if (moduleDef && moduleDef.defined) {
|
|
1328
|
+
return moduleDef;
|
|
1329
|
+
}
|
|
1330
|
+
const parentUrl = this.resolver.getBaseUrl(); // only support baseUrl for now
|
|
1331
|
+
const specifier = moduleName || originalId;
|
|
1332
|
+
this.profiler.logOperationStart({ id: MODULE_FETCH, specifier });
|
|
1333
|
+
return Promise.resolve()
|
|
1334
|
+
.then(async () => {
|
|
1335
|
+
const loadHooks = this.loadHook;
|
|
1336
|
+
if (loadHooks) {
|
|
1337
|
+
for (let i = 0; i < loadHooks.length; i++) {
|
|
1338
|
+
const loadHook = loadHooks[i];
|
|
1339
|
+
const response = loadHook(resolvedId, parentUrl);
|
|
1340
|
+
const result = (isResponseAPromise(response)
|
|
1341
|
+
? // eslint-disable-next-line no-await-in-loop
|
|
1342
|
+
await evaluateLoadHook(resolvedId, response)
|
|
1343
|
+
: response);
|
|
1344
|
+
if (result === undefined) {
|
|
1345
|
+
throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
|
|
1346
|
+
}
|
|
1347
|
+
if (result && result !== null) {
|
|
1348
|
+
return evaluateLoadHookResponse(result, resolvedId);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
return false;
|
|
1353
|
+
})
|
|
1354
|
+
.then((result) => {
|
|
1355
|
+
if (result !== true && hasDocument) {
|
|
1356
|
+
return loadModuleDef(resolvedId);
|
|
1357
|
+
}
|
|
1358
|
+
})
|
|
1359
|
+
.then(() => {
|
|
1360
|
+
// Attempt to retrieve the module definition by name first
|
|
1361
|
+
moduleDef = moduleName && this.namedDefineRegistry.get(moduleName);
|
|
1362
|
+
// Fallback to the last loader.define call
|
|
1363
|
+
if (!moduleDef) {
|
|
1364
|
+
moduleDef = this.lastDefine;
|
|
1365
|
+
}
|
|
1366
|
+
// This should not happen
|
|
1367
|
+
if (!moduleDef) {
|
|
1368
|
+
throw new LoaderError(FAIL_INSTANTIATE, [resolvedId]);
|
|
1369
|
+
}
|
|
1370
|
+
this.profiler.logOperationEnd({ id: MODULE_FETCH, specifier });
|
|
1371
|
+
return moduleDef;
|
|
1372
|
+
})
|
|
1373
|
+
.catch((e) => {
|
|
1374
|
+
this.profiler.logOperationStart({ id: MODULE_ERROR, specifier });
|
|
1375
|
+
throw e;
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
addLoaderPlugin(hooks) {
|
|
1379
|
+
if (typeof hooks !== 'object') {
|
|
1380
|
+
throw new LoaderError(INVALID_HOOK);
|
|
1381
|
+
}
|
|
1382
|
+
const { loadModule: loadHook, resolveModule: resolveHook, loadMapping } = hooks;
|
|
1383
|
+
if (resolveHook) {
|
|
1384
|
+
if (this.resolveHook) {
|
|
1385
|
+
this.resolveHook.push(resolveHook);
|
|
1386
|
+
}
|
|
1387
|
+
else {
|
|
1388
|
+
this.resolveHook = [resolveHook];
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
if (loadHook) {
|
|
1392
|
+
if (this.loadHook) {
|
|
1393
|
+
this.loadHook.push(loadHook);
|
|
1394
|
+
}
|
|
1395
|
+
else {
|
|
1396
|
+
this.loadHook = [loadHook];
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
if (loadMapping) {
|
|
1400
|
+
this.resolver.addLoadMappingHook(loadMapping);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
importMetadataInvalidationCallback({ name, oldUrl, newUrl }) {
|
|
1404
|
+
const handleStaleModuleHooks = this.handleStaleModuleHook;
|
|
1405
|
+
if (handleStaleModuleHooks) {
|
|
1406
|
+
evaluateHandleStaleModuleHooks(handleStaleModuleHooks, {
|
|
1407
|
+
name,
|
|
1408
|
+
oldUrl,
|
|
1409
|
+
newUrl,
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
else {
|
|
1413
|
+
if (process.env.NODE_ENV !== 'production' && hasConsole) {
|
|
1414
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
1415
|
+
console.warn(`stale module detected ${name}, current URL:${oldUrl}, new URL:${newUrl}`);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
registerHandleStaleModuleHook(handleStaleModule) {
|
|
1420
|
+
if (this.handleStaleModuleHook) {
|
|
1421
|
+
this.handleStaleModuleHook.push(handleStaleModule);
|
|
1422
|
+
}
|
|
1423
|
+
else {
|
|
1424
|
+
this.handleStaleModuleHook = [handleStaleModule];
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
/**
|
|
1430
|
+
* The LWR loader is inspired and borrows from the algorithms and native browser principles of https://github.com/systemjs/systemjs
|
|
1431
|
+
*/
|
|
1432
|
+
class Loader {
|
|
1433
|
+
constructor(config) {
|
|
1434
|
+
let baseUrl = config.baseUrl;
|
|
1435
|
+
const mappingEndpoint = config.endpoints ? config.endpoints.uris.mapping : undefined;
|
|
1436
|
+
let profiler = config.profiler;
|
|
1437
|
+
if (!mappingEndpoint) {
|
|
1438
|
+
throw new LoaderError(NO_MAPPING_URL);
|
|
1439
|
+
}
|
|
1440
|
+
// add a trailing slash, if it does not exist
|
|
1441
|
+
config.endpoints.uris.mapping = mappingEndpoint.replace(/\/?$/, '/');
|
|
1442
|
+
if (baseUrl) {
|
|
1443
|
+
// add a trailing slash, if it does not exist
|
|
1444
|
+
baseUrl = baseUrl.replace(/\/?$/, '/');
|
|
1445
|
+
}
|
|
1446
|
+
if (!baseUrl) {
|
|
1447
|
+
baseUrl = getBaseUrl();
|
|
1448
|
+
}
|
|
1449
|
+
if (!baseUrl) {
|
|
1450
|
+
throw new LoaderError(NO_BASE_URL);
|
|
1451
|
+
}
|
|
1452
|
+
if (!profiler) {
|
|
1453
|
+
// default noop profiler
|
|
1454
|
+
profiler = {
|
|
1455
|
+
logOperationStart: () => {
|
|
1456
|
+
/* noop */
|
|
1457
|
+
},
|
|
1458
|
+
logOperationEnd: () => {
|
|
1459
|
+
/* noop */
|
|
1460
|
+
},
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
this.registry = new ModuleRegistry(Object.freeze({ endpoints: config.endpoints, baseUrl, profiler }));
|
|
1464
|
+
// TODO: W-10539691 - temp workaround for LWR-Java -- remove once appId is implemented there
|
|
1465
|
+
if (config.appMetadata && !config.appMetadata.appId) {
|
|
1466
|
+
// Parse the appId from the bootstrapModule
|
|
1467
|
+
// LWR-Java bootstrap module format: @lwr-bootstrap/my/app/v/0_0_1 -- my/app is the appId
|
|
1468
|
+
const match = config.appMetadata.bootstrapModule.match(/@lwr-bootstrap\/(.+)\/v\/.+/);
|
|
1469
|
+
const appId = match && match[1];
|
|
1470
|
+
config.appMetadata.appId = appId;
|
|
1471
|
+
}
|
|
1472
|
+
// TODO: https://github.com/salesforce-experience-platform-emu/lwr/issues/1087
|
|
1473
|
+
this.services = Object.freeze({
|
|
1474
|
+
addLoaderPlugin: this.registry.addLoaderPlugin.bind(this.registry),
|
|
1475
|
+
handleStaleModule: this.registry.registerHandleStaleModuleHook.bind(this.registry),
|
|
1476
|
+
appMetadata: config.appMetadata,
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Defines/registers a single named AMD module definition.
|
|
1481
|
+
*
|
|
1482
|
+
* @param {string} name The module name
|
|
1483
|
+
* @param {string[]} dependencies A list of module dependencies (module imports)
|
|
1484
|
+
* @param {Function} execute The function containing the module code. AKA exporter as it also returns the modules exports when executed
|
|
1485
|
+
* @return {void}
|
|
1486
|
+
*/
|
|
1487
|
+
define(name, dependencies, execute) {
|
|
1488
|
+
invariant(typeof name === 'string', MISSING_NAME);
|
|
1489
|
+
let ctor = execute;
|
|
1490
|
+
let deps = dependencies;
|
|
1491
|
+
// Convert no dependencies form `define('name', function(){}, {});` to: `define('name', [], function(){}, {})`
|
|
1492
|
+
if (typeof deps === 'function') {
|
|
1493
|
+
ctor = dependencies;
|
|
1494
|
+
deps = [];
|
|
1495
|
+
}
|
|
1496
|
+
invariant(Array.isArray(deps), INVALID_DEPS);
|
|
1497
|
+
this.registry.define(name, deps, ctor);
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Retrieves/loads a module, returning it from the registry if it exists and fetching it if it doesn't.
|
|
1501
|
+
*
|
|
1502
|
+
* @param {string} id - A module identifier or URL
|
|
1503
|
+
* @param {string} importer - The versioned specifier of the module importer
|
|
1504
|
+
* Used when the ID is not versioned (eg: variable dynamic imports)
|
|
1505
|
+
* @return {Promise<Module>}
|
|
1506
|
+
*/
|
|
1507
|
+
async load(id, importer) {
|
|
1508
|
+
return this.registry.load(id, importer);
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Checks if a Module exists in the registry. Note, returns false even if the ModuleDefinition exists but the Module has not been instantiated yet (executed).
|
|
1512
|
+
*
|
|
1513
|
+
* @param {string} id - A module identifier or URL
|
|
1514
|
+
* @return {boolean}
|
|
1515
|
+
*/
|
|
1516
|
+
has(id) {
|
|
1517
|
+
return this.registry.has(id);
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1520
|
+
* Resolves the module identifier or URL. Returns the module identifier if the moduleDefinition exists, or the full resolved URL if a URL is given.
|
|
1521
|
+
*
|
|
1522
|
+
* @param {string} id - A module identifier or URL
|
|
1523
|
+
* @param {string} importer - The versioned specifier of the module importer
|
|
1524
|
+
* Used when the ID is not versioned (eg: variable dynamic imports)
|
|
1525
|
+
* @return {string}
|
|
1526
|
+
*/
|
|
1527
|
+
async resolve(id, importer) {
|
|
1528
|
+
return this.registry.resolve(id, importer);
|
|
1529
|
+
}
|
|
1530
|
+
async registerImportMappings(mappings, rootSpecifiers) {
|
|
1531
|
+
this.registry.getImportMetadataResolver().registerImportMappings(mappings, rootSpecifiers);
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Marks modules as "externally" loaded/provided (e.g. preloaded), so that the loader does not attempt to load them.
|
|
1535
|
+
*
|
|
1536
|
+
* @param modules - list of module identifiers
|
|
1537
|
+
*/
|
|
1538
|
+
registerExternalModules(modules) {
|
|
1539
|
+
this.registry.registerExternalModules(modules);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
exports.Loader = Loader;
|
|
1544
|
+
|
|
1545
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
1546
|
+
|
|
1547
|
+
});
|