@lwrjs/everywhere 0.8.0-alpha.10
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/LICENSE +10 -0
- package/README.md +282 -0
- package/build/assets/amd/lwr-everywhere-debug.js +1344 -0
- package/build/assets/amd/lwr-everywhere-min.js +1 -0
- package/build/assets/amd/lwr-everywhere.js +1344 -0
- package/build/assets/core/lwr-everywhere-debug.js +1344 -0
- package/build/assets/core/lwr-everywhere-min.js +1 -0
- package/build/assets/core/lwr-everywhere.js +1344 -0
- package/build/assets/esm/lwr-everywhere-debug.js +58 -0
- package/build/assets/esm/lwr-everywhere-min.js +1 -0
- package/build/assets/esm/lwr-everywhere.js +58 -0
- package/build/es/amd-client.d.ts +6 -0
- package/build/es/amd-client.js +39 -0
- package/build/es/esm-client.d.ts +3 -0
- package/build/es/esm-client.js +13 -0
- package/build/es/generate.d.ts +7 -0
- package/build/es/generate.js +127 -0
- package/build/es/index.d.ts +13 -0
- package/build/es/index.js +36 -0
- package/build/es/utils.d.ts +22 -0
- package/build/es/utils.js +40 -0
- package/build/modules/lwr/everywhere/everywhere.js +37 -0
- package/build/modules/lwr/everywhereAmd/everywhereAmd.js +61 -0
- package/build/modules/lwr/everywhereEsm/everywhereEsm.js +16 -0
- package/build/modules/lwr/host/host.html +5 -0
- package/build/modules/lwr/host/host.js +127 -0
- package/build/modules/lwr/vault/vault.js +10 -0
- package/package.cjs +9 -0
- package/package.json +67 -0
|
@@ -0,0 +1,1344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022, 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
|
+
// Construct the Client Bootstrap Config object
|
|
8
|
+
function getConfiguration(options) {
|
|
9
|
+
const { format, server, apiVersion, apiPrefix, locale, bundle, debug } = options;
|
|
10
|
+
const thisUrl = new URL(import.meta.url);
|
|
11
|
+
// URLs
|
|
12
|
+
const ORIGIN = server || thisUrl.origin || '';
|
|
13
|
+
const PREFIX = `${apiPrefix}/${apiVersion}`;
|
|
14
|
+
const POSTFIX = `0${locale ? `/l/${locale}` : ''}`; // hardcode compat='0': LWR-S doesn't accept '1' and LWR-JS ignores it
|
|
15
|
+
const BUNDLE_ID = '/bi/3_A,B,O,S,EG,JLMT,C,D,F,H,I,K,N,P,Q,R'; // core strategies
|
|
16
|
+
const ENDPOINT = bundle
|
|
17
|
+
? `${ORIGIN}${PREFIX}/bundle/${format}/${POSTFIX}${BUNDLE_ID}-0/module/mi/`
|
|
18
|
+
: `${ORIGIN}${PREFIX}/module/${format}/${POSTFIX}/mi/`;
|
|
19
|
+
// Component specifiers and URIs
|
|
20
|
+
const BOOT_MODULE = `lwr/everywhere${format === 'esm' ? 'Esm' : 'Amd'}/v/0_0_1`;
|
|
21
|
+
const BOOT_URI = `${ENDPOINT}${encodeURIComponent(BOOT_MODULE)}/latest${debug ? '?debug' : ''}`;
|
|
22
|
+
// Client Bootstrap Config
|
|
23
|
+
const config = {
|
|
24
|
+
appId: 'lwre',
|
|
25
|
+
autoBoot: true,
|
|
26
|
+
bootstrapModule: BOOT_MODULE,
|
|
27
|
+
baseUrl: ORIGIN,
|
|
28
|
+
imports: { [BOOT_URI]: [BOOT_MODULE] },
|
|
29
|
+
index: { [BOOT_MODULE]: BOOT_URI },
|
|
30
|
+
endpoints: {
|
|
31
|
+
uris: {
|
|
32
|
+
module: ENDPOINT,
|
|
33
|
+
mapping: `${ORIGIN}${PREFIX}/mapping/${format}/${POSTFIX}${bundle ? BUNDLE_ID : ''}/mp/`,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
// Add the debug modifier to the endpoints
|
|
38
|
+
if (debug && config.endpoints) {
|
|
39
|
+
config.endpoints.modifiers = { debug: 'true' };
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
options: { NODE_ENV: debug ? 'development' : 'production' },
|
|
43
|
+
config, // globalThis.LWR
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Copyright (c) 2021, salesforce.com, inc.
|
|
49
|
+
* All rights reserved.
|
|
50
|
+
* SPDX-License-Identifier: MIT
|
|
51
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
52
|
+
*/
|
|
53
|
+
/* LWR Module Loader v0.8.0-alpha.10 */
|
|
54
|
+
const templateRegex = /\{([0-9]+)\}/g;
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
+
function templateString(template, args) {
|
|
57
|
+
return template.replace(templateRegex, (_, index) => {
|
|
58
|
+
return args[index];
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
|
+
function generateErrorMessage(errorInfo, args) {
|
|
64
|
+
const message = Array.isArray(args) ? templateString(errorInfo.message, args) : errorInfo.message;
|
|
65
|
+
return `LWR${errorInfo.code}: ${message}`;
|
|
66
|
+
}
|
|
67
|
+
class LoaderError extends Error {
|
|
68
|
+
constructor(errorInfo, errorArgs) {
|
|
69
|
+
super();
|
|
70
|
+
this.message = generateErrorMessage(errorInfo, errorArgs);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function invariant(condition, errorInfo) {
|
|
74
|
+
if (!condition) {
|
|
75
|
+
throw new LoaderError(errorInfo);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const MISSING_NAME = Object.freeze({
|
|
79
|
+
code: 3000,
|
|
80
|
+
message: 'A module name is required.',
|
|
81
|
+
level: 0,
|
|
82
|
+
});
|
|
83
|
+
const FAIL_INSTANTIATE = Object.freeze({
|
|
84
|
+
code: 3004,
|
|
85
|
+
message: 'Failed to instantiate module: {0}',
|
|
86
|
+
level: 0,
|
|
87
|
+
});
|
|
88
|
+
const NO_AMD_REQUIRE = Object.freeze({
|
|
89
|
+
code: 3005,
|
|
90
|
+
message: 'AMD require not supported.',
|
|
91
|
+
level: 0,
|
|
92
|
+
});
|
|
93
|
+
const FAILED_DEP = Object.freeze({
|
|
94
|
+
code: 3006,
|
|
95
|
+
level: 0,
|
|
96
|
+
message: 'Failed to load dependency: {0}',
|
|
97
|
+
});
|
|
98
|
+
const INVALID_DEPS = Object.freeze({
|
|
99
|
+
code: 3007,
|
|
100
|
+
message: 'Unexpected value received for dependencies argument; expected an array.',
|
|
101
|
+
level: 0,
|
|
102
|
+
});
|
|
103
|
+
const FAIL_LOAD = Object.freeze({
|
|
104
|
+
code: 3008,
|
|
105
|
+
level: 0,
|
|
106
|
+
message: 'Error loading {0}',
|
|
107
|
+
});
|
|
108
|
+
const UNRESOLVED = Object.freeze({
|
|
109
|
+
code: 3009,
|
|
110
|
+
level: 0,
|
|
111
|
+
message: 'Unable to resolve bare specifier: {0}',
|
|
112
|
+
});
|
|
113
|
+
const NO_BASE_URL = Object.freeze({
|
|
114
|
+
code: 3010,
|
|
115
|
+
level: 0,
|
|
116
|
+
message: 'baseUrl not set',
|
|
117
|
+
});
|
|
118
|
+
Object.freeze({
|
|
119
|
+
code: 3011,
|
|
120
|
+
level: 0,
|
|
121
|
+
message: 'Cannot set a loader service multiple times',
|
|
122
|
+
});
|
|
123
|
+
const INVALID_HOOK = Object.freeze({
|
|
124
|
+
code: 3012,
|
|
125
|
+
level: 0,
|
|
126
|
+
message: 'Invalid hook received',
|
|
127
|
+
});
|
|
128
|
+
const INVALID_LOADER_SERVICE_RESPONSE = Object.freeze({
|
|
129
|
+
code: 3013,
|
|
130
|
+
level: 0,
|
|
131
|
+
message: 'Invalid response received from hook',
|
|
132
|
+
});
|
|
133
|
+
const MODULE_LOAD_TIMEOUT = Object.freeze({
|
|
134
|
+
code: 3014,
|
|
135
|
+
level: 0,
|
|
136
|
+
message: 'Error loading {0} - timed out',
|
|
137
|
+
});
|
|
138
|
+
const HTTP_FAIL_LOAD = Object.freeze({
|
|
139
|
+
code: 3015,
|
|
140
|
+
level: 0,
|
|
141
|
+
message: 'Error loading {0}, status code {1}',
|
|
142
|
+
});
|
|
143
|
+
const STALE_HOOK_ERROR = Object.freeze({
|
|
144
|
+
code: 3016,
|
|
145
|
+
level: 0,
|
|
146
|
+
message: 'An error occurred handling module conflict',
|
|
147
|
+
});
|
|
148
|
+
const MODULE_ALREADY_LOADED = Object.freeze({
|
|
149
|
+
code: 3017,
|
|
150
|
+
level: 0,
|
|
151
|
+
message: 'Marking module(s) as externally loaded, but they are already loaded: {0}',
|
|
152
|
+
});
|
|
153
|
+
const FAIL_HOOK_LOAD = Object.freeze({
|
|
154
|
+
code: 3018,
|
|
155
|
+
level: 0,
|
|
156
|
+
message: 'Error loading "{0}" from hook',
|
|
157
|
+
});
|
|
158
|
+
const NO_MAPPING_URL = Object.freeze({
|
|
159
|
+
code: 3019,
|
|
160
|
+
level: 0,
|
|
161
|
+
message: 'Mapping endpoint not set',
|
|
162
|
+
});
|
|
163
|
+
const BAD_IMPORT_METADATA = Object.freeze({
|
|
164
|
+
code: 3020,
|
|
165
|
+
level: 0,
|
|
166
|
+
message: 'Invalid import metadata: {0} {1}',
|
|
167
|
+
});
|
|
168
|
+
/* importMap errors */
|
|
169
|
+
Object.freeze({
|
|
170
|
+
code: 3011,
|
|
171
|
+
level: 0,
|
|
172
|
+
message: 'import map is not valid',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
/* eslint-disable lwr/no-unguarded-apis */
|
|
176
|
+
const hasDocument = typeof document !== 'undefined';
|
|
177
|
+
const hasSetTimeout = typeof setTimeout === 'function';
|
|
178
|
+
const hasConsole = typeof console !== 'undefined';
|
|
179
|
+
/* eslint-enable lwr/no-unguarded-apis */
|
|
180
|
+
|
|
181
|
+
function getBaseUrl() {
|
|
182
|
+
let baseUrl = undefined;
|
|
183
|
+
if (hasDocument) {
|
|
184
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
185
|
+
const baseEl = document.querySelector('base[href]');
|
|
186
|
+
baseUrl = baseEl && baseEl.href;
|
|
187
|
+
}
|
|
188
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
189
|
+
if (!baseUrl && typeof location !== 'undefined') {
|
|
190
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
191
|
+
baseUrl = location.href.split('#')[0].split('?')[0];
|
|
192
|
+
const lastSepIndex = baseUrl.lastIndexOf('/');
|
|
193
|
+
if (lastSepIndex !== -1) {
|
|
194
|
+
baseUrl = baseUrl.slice(0, lastSepIndex + 1);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return baseUrl;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Check if a string is a URL based on Common Internet Scheme Syntax
|
|
201
|
+
* https://www.ietf.org/rfc/rfc1738.txt
|
|
202
|
+
*
|
|
203
|
+
* URL Format:
|
|
204
|
+
* <scheme>:<scheme-specific-part>
|
|
205
|
+
* Common Internet Scheme Syntax:
|
|
206
|
+
* The scheme specific part starts with a double slash('//')
|
|
207
|
+
*
|
|
208
|
+
* A valid URL has a colon that is followed by a double slash.
|
|
209
|
+
*
|
|
210
|
+
* @param url - the url that is being checked
|
|
211
|
+
* @returns boolean
|
|
212
|
+
*
|
|
213
|
+
* @example Valid URLs
|
|
214
|
+
* 'https://salesforce.com'
|
|
215
|
+
* 'http://localhost:3000'
|
|
216
|
+
*
|
|
217
|
+
* @example Invalid URLs
|
|
218
|
+
* 'salesforce.com'
|
|
219
|
+
* 'localhost:3000'
|
|
220
|
+
* '@salesforce/label/type:namespace:name'
|
|
221
|
+
*/
|
|
222
|
+
function isUrl(url) {
|
|
223
|
+
return url.indexOf('://') !== -1;
|
|
224
|
+
}
|
|
225
|
+
// Borrowed and adapted from https://github.com/systemjs/systemjs/blob/master/src/common.js
|
|
226
|
+
// Resolves the first path segment relative to the second/parent URL
|
|
227
|
+
// eg: resolveIfNotPlainOrUrl('../test', 'http://www.site.com/one/two') => 'http://www.site.com/test'
|
|
228
|
+
// eg: resolveIfNotPlainOrUrl('./x/y/z', 'https://my.com/segment')).toBe('https://my.com/x/y/z')
|
|
229
|
+
function resolveIfNotPlainOrUrl(relUrl, parentUrl) {
|
|
230
|
+
const backslashRegEx = /\\/g;
|
|
231
|
+
if (relUrl.indexOf('\\') !== -1)
|
|
232
|
+
relUrl = relUrl.replace(backslashRegEx, '/');
|
|
233
|
+
// protocol-relative
|
|
234
|
+
if (relUrl[0] === '/' && relUrl[1] === '/') {
|
|
235
|
+
return parentUrl.slice(0, parentUrl.indexOf(':') + 1) + relUrl;
|
|
236
|
+
}
|
|
237
|
+
// relative-url
|
|
238
|
+
else if ((relUrl[0] === '.' &&
|
|
239
|
+
(relUrl[1] === '/' ||
|
|
240
|
+
(relUrl[1] === '.' && (relUrl[2] === '/' || (relUrl.length === 2 && (relUrl += '/')))) ||
|
|
241
|
+
(relUrl.length === 1 && (relUrl += '/')))) ||
|
|
242
|
+
relUrl[0] === '/') {
|
|
243
|
+
const parentProtocol = parentUrl.slice(0, parentUrl.indexOf(':') + 1);
|
|
244
|
+
let pathname;
|
|
245
|
+
if (parentUrl[parentProtocol.length + 1] === '/') {
|
|
246
|
+
// resolving to a :// so we need to read out the auth and host
|
|
247
|
+
if (parentProtocol !== 'file:') {
|
|
248
|
+
pathname = parentUrl.slice(parentProtocol.length + 2);
|
|
249
|
+
pathname = pathname.slice(pathname.indexOf('/') + 1);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
pathname = parentUrl.slice(8);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
// resolving to :/ so pathname is the /... part
|
|
257
|
+
pathname = parentUrl.slice(parentProtocol.length + (parentUrl[parentProtocol.length] === '/' ? 1 : 0));
|
|
258
|
+
}
|
|
259
|
+
if (relUrl[0] === '/')
|
|
260
|
+
return parentUrl.slice(0, parentUrl.length - pathname.length - 1) + relUrl;
|
|
261
|
+
// join together and split for removal of .. and . segments
|
|
262
|
+
// looping the string instead of anything fancy for perf reasons
|
|
263
|
+
// '../../../../../z' resolved to 'x/y' is just 'z'
|
|
264
|
+
const segmented = pathname.slice(0, pathname.lastIndexOf('/') + 1) + relUrl;
|
|
265
|
+
const output = [];
|
|
266
|
+
let segmentIndex = -1;
|
|
267
|
+
for (let i = 0; i < segmented.length; i++) {
|
|
268
|
+
// busy reading a segment - only terminate on '/'
|
|
269
|
+
if (segmentIndex !== -1) {
|
|
270
|
+
if (segmented[i] === '/') {
|
|
271
|
+
output.push(segmented.slice(segmentIndex, i + 1));
|
|
272
|
+
segmentIndex = -1;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// new segment - check if it is relative
|
|
276
|
+
else if (segmented[i] === '.') {
|
|
277
|
+
// ../ segment
|
|
278
|
+
if (segmented[i + 1] === '.' && (segmented[i + 2] === '/' || i + 2 === segmented.length)) {
|
|
279
|
+
output.pop();
|
|
280
|
+
i += 2;
|
|
281
|
+
}
|
|
282
|
+
// ./ segment
|
|
283
|
+
else if (segmented[i + 1] === '/' || i + 1 === segmented.length) {
|
|
284
|
+
i += 1;
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
// the start of a new segment as below
|
|
288
|
+
segmentIndex = i;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// it is the start of a new segment
|
|
292
|
+
else {
|
|
293
|
+
segmentIndex = i;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// finish reading out the last segment
|
|
297
|
+
if (segmentIndex !== -1)
|
|
298
|
+
output.push(segmented.slice(segmentIndex));
|
|
299
|
+
return parentUrl.slice(0, parentUrl.length - pathname.length) + output.join('');
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function resolveUrl(relUrl, parentUrl) {
|
|
303
|
+
const resolvedUrl = resolveIfNotPlainOrUrl(relUrl, parentUrl) ||
|
|
304
|
+
(isUrl(relUrl) ? relUrl : resolveIfNotPlainOrUrl('./' + relUrl, parentUrl));
|
|
305
|
+
return resolvedUrl;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function createScript(url) {
|
|
309
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
310
|
+
const script = document.createElement('script');
|
|
311
|
+
script.async = true;
|
|
312
|
+
script.crossOrigin = 'anonymous';
|
|
313
|
+
script.src = url;
|
|
314
|
+
return script;
|
|
315
|
+
}
|
|
316
|
+
let lastWindowError$1, lastWindowErrorUrl;
|
|
317
|
+
function loadModuleDef(url) {
|
|
318
|
+
return new Promise(function (resolve, reject) {
|
|
319
|
+
if (hasDocument) {
|
|
320
|
+
/* eslint-disable lwr/no-unguarded-apis, no-undef */
|
|
321
|
+
const script = createScript(url);
|
|
322
|
+
script.addEventListener('error', () => {
|
|
323
|
+
reject(new LoaderError(FAIL_LOAD, [url]));
|
|
324
|
+
});
|
|
325
|
+
script.addEventListener('load', () => {
|
|
326
|
+
document.head.removeChild(script);
|
|
327
|
+
if (lastWindowErrorUrl === url) {
|
|
328
|
+
reject(lastWindowError$1);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
resolve();
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
document.head.appendChild(script);
|
|
335
|
+
/* eslint-enable lwr/no-unguarded-apis, no-undef */
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
if (hasDocument) {
|
|
340
|
+
// When a script is executed, runtime errors are on the global/window scope which are NOT caught by the script's onerror handler.
|
|
341
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
342
|
+
window.addEventListener('error', (evt) => {
|
|
343
|
+
lastWindowErrorUrl = evt.filename;
|
|
344
|
+
lastWindowError$1 = evt.error;
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Bootstrap / shim
|
|
349
|
+
|
|
350
|
+
const LOADER_PREFIX = 'lwr.loader.';
|
|
351
|
+
const MODULE_DEFINE = `${LOADER_PREFIX}module.define`;
|
|
352
|
+
const MODULE_FETCH = `${LOADER_PREFIX}module.fetch`;
|
|
353
|
+
const MODULE_ERROR = `${LOADER_PREFIX}module.error`;
|
|
354
|
+
|
|
355
|
+
const MAPPINGS_FETCH = `${LOADER_PREFIX}mappings.fetch`;
|
|
356
|
+
const MAPPINGS_ERROR = `${LOADER_PREFIX}mappings.error`;
|
|
357
|
+
|
|
358
|
+
/* spec based import map resolver */
|
|
359
|
+
class ImportMetadataResolver {
|
|
360
|
+
constructor(config, invalidationCallback) {
|
|
361
|
+
// Default to empty mappings
|
|
362
|
+
this.importURICache = new Map();
|
|
363
|
+
this.pendingURICache = new Map();
|
|
364
|
+
this.loadMappingHooks = [];
|
|
365
|
+
this.config = config;
|
|
366
|
+
this.invalidationCallback = invalidationCallback;
|
|
367
|
+
}
|
|
368
|
+
addLoadMappingHook(hook) {
|
|
369
|
+
this.loadMappingHooks.push(hook);
|
|
370
|
+
}
|
|
371
|
+
getMappingEndpoint() {
|
|
372
|
+
return this.config.endpoints && this.config.endpoints.uris
|
|
373
|
+
? this.config.endpoints.uris.mapping
|
|
374
|
+
: undefined;
|
|
375
|
+
}
|
|
376
|
+
getModifiersAsUrlParams() {
|
|
377
|
+
const modifiers = this.config.endpoints ? this.config.endpoints.modifiers : undefined;
|
|
378
|
+
if (!modifiers) {
|
|
379
|
+
// No modifiers return an empty string to append to the URL
|
|
380
|
+
return '';
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
const qs = Object.keys(modifiers)
|
|
384
|
+
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(modifiers[key])}`)
|
|
385
|
+
.join('&');
|
|
386
|
+
return `?${qs}`;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
buildMappingUrl(specifier) {
|
|
390
|
+
const mappingEndpoint = this.getMappingEndpoint();
|
|
391
|
+
const specifiers = encodeURIComponent(specifier);
|
|
392
|
+
const modifiers = this.getModifiersAsUrlParams();
|
|
393
|
+
return `${mappingEndpoint}${specifiers}${modifiers}`;
|
|
394
|
+
}
|
|
395
|
+
getBaseUrl() {
|
|
396
|
+
return this.config.baseUrl;
|
|
397
|
+
}
|
|
398
|
+
registerImportMappings(newImportMetadata, rootSpecifiers) {
|
|
399
|
+
if (!rootSpecifiers || rootSpecifiers.length === 0) {
|
|
400
|
+
const imports = newImportMetadata ? JSON.stringify(newImportMetadata) : 'undefined';
|
|
401
|
+
throw new LoaderError(BAD_IMPORT_METADATA, [imports, rootSpecifiers ? '[]' : 'undefined']);
|
|
402
|
+
}
|
|
403
|
+
if (!newImportMetadata) {
|
|
404
|
+
throw new LoaderError(BAD_IMPORT_METADATA, ['undefined', JSON.stringify(rootSpecifiers)]);
|
|
405
|
+
}
|
|
406
|
+
if (!newImportMetadata.imports || Object.keys(newImportMetadata.imports).length === 0) {
|
|
407
|
+
throw new LoaderError(BAD_IMPORT_METADATA, [
|
|
408
|
+
JSON.stringify(newImportMetadata),
|
|
409
|
+
JSON.stringify(rootSpecifiers),
|
|
410
|
+
]);
|
|
411
|
+
}
|
|
412
|
+
const index = newImportMetadata.index || {};
|
|
413
|
+
for (const [uri, specifiers] of Object.entries(newImportMetadata.imports)) {
|
|
414
|
+
specifiers.forEach((specifier) => {
|
|
415
|
+
const indexValue = index[specifier];
|
|
416
|
+
const existing = this.importURICache.get(specifier);
|
|
417
|
+
if (!existing) {
|
|
418
|
+
this.saveImportURIRecord(specifier, uri, indexValue, rootSpecifiers.includes(specifier));
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
const identity = indexValue || uri;
|
|
422
|
+
const existingIdentity = existing.identity || existing.uri;
|
|
423
|
+
if (existingIdentity !== identity) {
|
|
424
|
+
this.invalidationCallback({
|
|
425
|
+
name: specifier,
|
|
426
|
+
oldUrl: existingIdentity,
|
|
427
|
+
newUrl: identity,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// Get URL from the local cache or return undefiend
|
|
435
|
+
getURI(specifier) {
|
|
436
|
+
return this.importURICache.has(specifier)
|
|
437
|
+
? resolveUrl(this.importURICache.get(specifier).uri, this.getBaseUrl())
|
|
438
|
+
: undefined;
|
|
439
|
+
}
|
|
440
|
+
resolveLocal(specifier) {
|
|
441
|
+
const uri = this.getURI(specifier);
|
|
442
|
+
if (uri) {
|
|
443
|
+
return uri;
|
|
444
|
+
}
|
|
445
|
+
else if (isUrl(specifier) || specifier.startsWith('/')) {
|
|
446
|
+
return specifier;
|
|
447
|
+
}
|
|
448
|
+
return undefined;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Resolves a the URI for a specified module. It will return the value in this order:
|
|
452
|
+
*
|
|
453
|
+
* 1) Mapping from local URI cache
|
|
454
|
+
* 2) The URI if a specifier is already an absolute URI
|
|
455
|
+
* 3) Mapping fetched from a registered loader hook
|
|
456
|
+
* 4)
|
|
457
|
+
* @param specifier
|
|
458
|
+
* @returns module URI
|
|
459
|
+
*/
|
|
460
|
+
async resolve(specifier) {
|
|
461
|
+
let uri = this.getURI(specifier);
|
|
462
|
+
if (uri) {
|
|
463
|
+
return uri;
|
|
464
|
+
}
|
|
465
|
+
else if (isUrl(specifier) || specifier.startsWith('/')) {
|
|
466
|
+
return specifier;
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
const pending = this.pendingURICache.get(specifier);
|
|
470
|
+
if (pending) {
|
|
471
|
+
return pending;
|
|
472
|
+
}
|
|
473
|
+
this.config.profiler.logOperationStart({ id: MAPPINGS_FETCH, specifier });
|
|
474
|
+
const fetchMappingService = this.hasMappingHooks()
|
|
475
|
+
? this.evaluateMappingHooks
|
|
476
|
+
: this.fetchNewMappings;
|
|
477
|
+
const promise = fetchMappingService
|
|
478
|
+
.bind(this)(specifier)
|
|
479
|
+
.then((importMetadata) => {
|
|
480
|
+
if (!importMetadata || !importMetadata.imports) {
|
|
481
|
+
throw new LoaderError(UNRESOLVED, [specifier]);
|
|
482
|
+
}
|
|
483
|
+
this.registerImportMappings(importMetadata, [specifier]);
|
|
484
|
+
uri = this.getURI(specifier);
|
|
485
|
+
if (!uri) {
|
|
486
|
+
throw new LoaderError(UNRESOLVED, [specifier]);
|
|
487
|
+
}
|
|
488
|
+
this.config.profiler.logOperationEnd({ id: MAPPINGS_FETCH, specifier });
|
|
489
|
+
return uri;
|
|
490
|
+
})
|
|
491
|
+
.finally(() => {
|
|
492
|
+
this.pendingURICache.delete(specifier);
|
|
493
|
+
});
|
|
494
|
+
this.pendingURICache.set(specifier, promise);
|
|
495
|
+
return promise;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
hasMappingHooks() {
|
|
499
|
+
return this.loadMappingHooks.length > 0;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Evaluates mapping hooks. Returns first match. If all hooks return null call the mapping service.
|
|
503
|
+
* @param specifier Request module identifier
|
|
504
|
+
* @returns Import Metadata from the module root
|
|
505
|
+
*/
|
|
506
|
+
async evaluateMappingHooks(specifier) {
|
|
507
|
+
// Check with any registered loadMappingHooks
|
|
508
|
+
const loadMappingHooks = this.loadMappingHooks;
|
|
509
|
+
if (loadMappingHooks.length) {
|
|
510
|
+
const knownModules = Array.from(this.importURICache.keys());
|
|
511
|
+
for (let i = 0; i < loadMappingHooks.length; i++) {
|
|
512
|
+
const loadMappingHook = loadMappingHooks[i];
|
|
513
|
+
// eslint-disable-next-line no-await-in-loop
|
|
514
|
+
const response = await loadMappingHook(specifier, { knownModules });
|
|
515
|
+
// undefined (but not null) is considered an un expected response so we will stop processing hooks here and throw an error
|
|
516
|
+
if (response || response === undefined) {
|
|
517
|
+
return response;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// If we still do not have a match call the mapping service
|
|
522
|
+
return this.fetchNewMappings(specifier);
|
|
523
|
+
}
|
|
524
|
+
async fetchNewMappings(specifier) {
|
|
525
|
+
if (typeof globalThis.fetch !== 'function') {
|
|
526
|
+
throw new LoaderError(UNRESOLVED, [specifier]);
|
|
527
|
+
}
|
|
528
|
+
// TODO For module invalidation with bundles it is recommended we have to send back all loaded root specified
|
|
529
|
+
// to ensure we detect all conflicts.
|
|
530
|
+
const uri = resolveUrl(this.buildMappingUrl(specifier), this.getBaseUrl());
|
|
531
|
+
return globalThis.fetch(uri).then((res) => {
|
|
532
|
+
if (!res.ok) {
|
|
533
|
+
this.config.profiler.logOperationStart({ id: MAPPINGS_ERROR, specifier });
|
|
534
|
+
throw new LoaderError(UNRESOLVED, [specifier]);
|
|
535
|
+
}
|
|
536
|
+
return res
|
|
537
|
+
.json()
|
|
538
|
+
.then((ret) => {
|
|
539
|
+
return ret;
|
|
540
|
+
})
|
|
541
|
+
.catch((err) => {
|
|
542
|
+
throw new LoaderError(UNRESOLVED, [specifier]);
|
|
543
|
+
});
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
saveImportURIRecord(specifier, uri, identity, isRoot) {
|
|
547
|
+
if (!identity || uri === identity) {
|
|
548
|
+
this.importURICache.set(specifier, {
|
|
549
|
+
uri,
|
|
550
|
+
isRoot: isRoot,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
this.importURICache.set(specifier, {
|
|
555
|
+
uri,
|
|
556
|
+
identity,
|
|
557
|
+
isRoot: isRoot,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function reportError(error) {
|
|
564
|
+
// TODO eventually this should be configurable instrumentation to send this somewhere
|
|
565
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
566
|
+
if (hasConsole)
|
|
567
|
+
console.error(error);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function evaluateHandleStaleModuleHooks(handleStaleModuleHooks, hookArgs) {
|
|
571
|
+
const { name, oldUrl, newUrl } = hookArgs;
|
|
572
|
+
// keep evaluating hooks if return value is null
|
|
573
|
+
for (let i = 0; i < handleStaleModuleHooks.length; i++) {
|
|
574
|
+
const hook = handleStaleModuleHooks[i];
|
|
575
|
+
try {
|
|
576
|
+
const hookResult = hook({ name, oldUrl, newUrl });
|
|
577
|
+
if (hookResult !== null) {
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
catch (e) {
|
|
582
|
+
reportError(new LoaderError(STALE_HOOK_ERROR));
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const MODULE_LOAD_TIMEOUT_TIMER = 300000;
|
|
588
|
+
|
|
589
|
+
let lastWindowError;
|
|
590
|
+
if (hasDocument) {
|
|
591
|
+
globalThis.addEventListener('error', (evt) => {
|
|
592
|
+
lastWindowError = evt.error;
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
if (!hasSetTimeout && hasConsole) {
|
|
596
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
597
|
+
console.warn('setTimeout API is not available, watchdog timer on load hook will not be set');
|
|
598
|
+
}
|
|
599
|
+
function isCustomResponse(response) {
|
|
600
|
+
return (Object.prototype.hasOwnProperty.call(response, 'data') &&
|
|
601
|
+
!Object.prototype.hasOwnProperty.call(response, 'blob'));
|
|
602
|
+
}
|
|
603
|
+
function isFetchResponse(response) {
|
|
604
|
+
// if it quacks like a duck...
|
|
605
|
+
return typeof response.blob === 'function';
|
|
606
|
+
}
|
|
607
|
+
function isResponseAPromise(response) {
|
|
608
|
+
return !!(response && response.then);
|
|
609
|
+
}
|
|
610
|
+
async function evaluateLoadHookResponse(response, id) {
|
|
611
|
+
return Promise.resolve().then(async () => {
|
|
612
|
+
if (!response.status) {
|
|
613
|
+
throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
|
|
614
|
+
}
|
|
615
|
+
if (response.status !== 200) {
|
|
616
|
+
throw new LoaderError(HTTP_FAIL_LOAD, [id, `${response.status}`]);
|
|
617
|
+
}
|
|
618
|
+
const isResponse = isFetchResponse(response);
|
|
619
|
+
let code;
|
|
620
|
+
if (isCustomResponse(response)) {
|
|
621
|
+
code = response.data;
|
|
622
|
+
}
|
|
623
|
+
else if (isResponse) {
|
|
624
|
+
// handle fetch response
|
|
625
|
+
code = await response.text();
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
|
|
629
|
+
}
|
|
630
|
+
if (!code) {
|
|
631
|
+
throw new LoaderError(FAIL_LOAD, [id]);
|
|
632
|
+
}
|
|
633
|
+
code = `${code}\n//# sourceURL=${id}`; // append sourceURL for debugging
|
|
634
|
+
try {
|
|
635
|
+
// TODO eval source maps for debugging
|
|
636
|
+
eval(code);
|
|
637
|
+
}
|
|
638
|
+
catch (e) {
|
|
639
|
+
throw new LoaderError(FAIL_LOAD, [id]);
|
|
640
|
+
}
|
|
641
|
+
if (lastWindowError) {
|
|
642
|
+
throw new LoaderError(FAIL_LOAD, [id]);
|
|
643
|
+
}
|
|
644
|
+
return true;
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
async function evaluateLoadHook(id, hookPromise) {
|
|
648
|
+
if (!hasSetTimeout) {
|
|
649
|
+
return hookPromise;
|
|
650
|
+
}
|
|
651
|
+
return new Promise((resolve, reject) => {
|
|
652
|
+
// wrap the hook in a watchdog timer
|
|
653
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
654
|
+
const timer = setTimeout(() => {
|
|
655
|
+
reject(new LoaderError(MODULE_LOAD_TIMEOUT, [id]));
|
|
656
|
+
}, MODULE_LOAD_TIMEOUT_TIMER);
|
|
657
|
+
hookPromise
|
|
658
|
+
.then((response) => {
|
|
659
|
+
resolve(response);
|
|
660
|
+
})
|
|
661
|
+
.catch(() => {
|
|
662
|
+
reject(new LoaderError(FAIL_HOOK_LOAD, [id]));
|
|
663
|
+
})
|
|
664
|
+
.finally(() => {
|
|
665
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
666
|
+
clearTimeout(timer);
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/* global console,process */
|
|
672
|
+
class ModuleRegistry {
|
|
673
|
+
constructor(config) {
|
|
674
|
+
// A registry for named AMD defines containing the *metadata* of AMD module
|
|
675
|
+
this.namedDefineRegistry = new Map();
|
|
676
|
+
// The evaluted module registry where the module identifier (name or URL?) is the key
|
|
677
|
+
this.moduleRegistry = new Map();
|
|
678
|
+
this.profiler = config.profiler;
|
|
679
|
+
this.resolver = new ImportMetadataResolver(config, this.importMetadataInvalidationCallback.bind(this));
|
|
680
|
+
}
|
|
681
|
+
async load(id, importer) {
|
|
682
|
+
const resolvedId = await this.resolve(id, importer);
|
|
683
|
+
const moduleRecord = this.getModuleRecord(resolvedId, id);
|
|
684
|
+
if (moduleRecord.evaluated) {
|
|
685
|
+
return moduleRecord.module;
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
if (!moduleRecord.evaluationPromise) {
|
|
689
|
+
moduleRecord.evaluationPromise = this.topLevelEvaluation(moduleRecord);
|
|
690
|
+
}
|
|
691
|
+
return moduleRecord.evaluationPromise;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
async resolve(id, importer) {
|
|
695
|
+
const parentUrl = this.resolver.getBaseUrl(); // only support baseUrl for now
|
|
696
|
+
let resolved;
|
|
697
|
+
let aliasedId = id;
|
|
698
|
+
const resolveHooks = this.resolveHook;
|
|
699
|
+
if (resolveHooks) {
|
|
700
|
+
for (let i = 0; i < resolveHooks.length; i++) {
|
|
701
|
+
const resolveHook = resolveHooks[i];
|
|
702
|
+
const response = resolveHook(aliasedId, { parentUrl });
|
|
703
|
+
let result;
|
|
704
|
+
if (response || response === null) {
|
|
705
|
+
// eslint-disable-next-line no-await-in-loop
|
|
706
|
+
result = isResponseAPromise(response) ? await response : response;
|
|
707
|
+
}
|
|
708
|
+
// if result is not null, attempt resolution
|
|
709
|
+
if (result !== null) {
|
|
710
|
+
if (typeof result === 'string') {
|
|
711
|
+
if (resolveIfNotPlainOrUrl(result, parentUrl)) {
|
|
712
|
+
// string response can't be a URL
|
|
713
|
+
throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
|
|
714
|
+
}
|
|
715
|
+
aliasedId = result; // the next hook will receive the new id
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
resolved =
|
|
719
|
+
result && result.url && (resolveIfNotPlainOrUrl(result.url, parentUrl) || result.url);
|
|
720
|
+
if (!resolved) {
|
|
721
|
+
throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
|
|
722
|
+
}
|
|
723
|
+
// Don't process any more hooks if we have resolved
|
|
724
|
+
break;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (aliasedId !== id) {
|
|
728
|
+
// resolved module id is the aliased module if it has already been defined
|
|
729
|
+
if (!resolved && this.namedDefineRegistry.has(aliasedId)) {
|
|
730
|
+
return aliasedId;
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
id = aliasedId;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (!resolved) {
|
|
738
|
+
const resolvedOrPlain = resolveIfNotPlainOrUrl(id, parentUrl) || id;
|
|
739
|
+
// if module registry already has named module the resolved id is the plain id
|
|
740
|
+
if (this.moduleRegistry.has(resolvedOrPlain)) {
|
|
741
|
+
return resolvedOrPlain;
|
|
742
|
+
}
|
|
743
|
+
const resolvedUrl = this.resolver.resolveLocal(resolvedOrPlain);
|
|
744
|
+
if (resolvedUrl) {
|
|
745
|
+
// return the plain id if it is already defined && the resolvedUrl is NOT already in the module registry
|
|
746
|
+
if (this.namedDefineRegistry.has(resolvedOrPlain) &&
|
|
747
|
+
this.namedDefineRegistry.get(resolvedOrPlain).defined) {
|
|
748
|
+
const record = this.moduleRegistry.get(resolvedUrl);
|
|
749
|
+
if (!record || !record.aliases.has(resolvedOrPlain)) {
|
|
750
|
+
return resolvedOrPlain;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return resolvedUrl;
|
|
754
|
+
}
|
|
755
|
+
if (this.namedDefineRegistry.has(resolvedOrPlain)) {
|
|
756
|
+
return resolvedOrPlain;
|
|
757
|
+
}
|
|
758
|
+
try {
|
|
759
|
+
resolved = await this.resolver.resolve(resolvedOrPlain);
|
|
760
|
+
}
|
|
761
|
+
catch (e) {
|
|
762
|
+
// defer to error handling below for unresolved
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
if (!resolved || !isUrl(resolved)) {
|
|
766
|
+
if (this.namedDefineRegistry.has(id)) {
|
|
767
|
+
return id;
|
|
768
|
+
}
|
|
769
|
+
throw new LoaderError(UNRESOLVED, [id]);
|
|
770
|
+
}
|
|
771
|
+
if (importer && isUrl(resolved)) {
|
|
772
|
+
resolved += `?importer=${encodeURIComponent(importer)}`;
|
|
773
|
+
}
|
|
774
|
+
return resolved;
|
|
775
|
+
}
|
|
776
|
+
has(id) {
|
|
777
|
+
return this.moduleRegistry.has(id);
|
|
778
|
+
}
|
|
779
|
+
define(name, dependencies, exporter) {
|
|
780
|
+
const mod = this.namedDefineRegistry.get(name);
|
|
781
|
+
// Don't allow redefining a module.
|
|
782
|
+
if (mod && mod.defined) {
|
|
783
|
+
if (process.env.NODE_ENV !== 'production' && hasConsole) {
|
|
784
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
785
|
+
console.warn(`Module redefine attempted: ${name}`);
|
|
786
|
+
}
|
|
787
|
+
this.lastDefine = mod;
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
const moduleDef = {
|
|
791
|
+
name,
|
|
792
|
+
dependencies,
|
|
793
|
+
exporter,
|
|
794
|
+
defined: true,
|
|
795
|
+
};
|
|
796
|
+
if (mod && mod.external) {
|
|
797
|
+
// if module is "external", resolve the external promise to notify any dependees
|
|
798
|
+
mod.external.resolveExternal(moduleDef);
|
|
799
|
+
}
|
|
800
|
+
this.profiler.logOperationStart({ id: MODULE_DEFINE, specifier: name });
|
|
801
|
+
this.namedDefineRegistry.set(name, moduleDef);
|
|
802
|
+
this.lastDefine = moduleDef;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Marks modules as "externally" loaded/provided, so that the loader does not attempt to fetch them.
|
|
806
|
+
*
|
|
807
|
+
* @param modules - list of module identifiers
|
|
808
|
+
*/
|
|
809
|
+
registerExternalModules(modules) {
|
|
810
|
+
const alreadyRegistered = [];
|
|
811
|
+
modules.map((id) => {
|
|
812
|
+
if (this.namedDefineRegistry.has(id)) {
|
|
813
|
+
alreadyRegistered.push(id);
|
|
814
|
+
}
|
|
815
|
+
else {
|
|
816
|
+
let resolveExternal;
|
|
817
|
+
let timer;
|
|
818
|
+
const moduleDefPromise = new Promise((resolve, reject) => {
|
|
819
|
+
resolveExternal = resolve;
|
|
820
|
+
// watch the external for timeout
|
|
821
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
822
|
+
timer = setTimeout(() => {
|
|
823
|
+
reject(new LoaderError(MODULE_LOAD_TIMEOUT, [id]));
|
|
824
|
+
}, MODULE_LOAD_TIMEOUT_TIMER);
|
|
825
|
+
}).finally(() => {
|
|
826
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
827
|
+
clearTimeout(timer);
|
|
828
|
+
});
|
|
829
|
+
const moduleDef = {
|
|
830
|
+
name: id,
|
|
831
|
+
defined: false,
|
|
832
|
+
external: {
|
|
833
|
+
resolveExternal,
|
|
834
|
+
moduleDefPromise,
|
|
835
|
+
},
|
|
836
|
+
};
|
|
837
|
+
this.namedDefineRegistry.set(id, moduleDef);
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
// throw error for modules that were already registered
|
|
841
|
+
if (alreadyRegistered.length) {
|
|
842
|
+
throw new LoaderError(MODULE_ALREADY_LOADED, [alreadyRegistered.join(', ')]);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
getImportMetadataResolver() {
|
|
846
|
+
return this.resolver;
|
|
847
|
+
}
|
|
848
|
+
getModuleRecord(resolvedId, id) {
|
|
849
|
+
let moduleRecord = this.moduleRegistry.get(resolvedId);
|
|
850
|
+
if (moduleRecord) {
|
|
851
|
+
// Make sure the original id is in the alias set
|
|
852
|
+
if (!moduleRecord.aliases.has(id)) {
|
|
853
|
+
moduleRecord.aliases.add(id);
|
|
854
|
+
}
|
|
855
|
+
return moduleRecord;
|
|
856
|
+
}
|
|
857
|
+
// loop thru registry to see if there is a known alias id
|
|
858
|
+
const keys = this.moduleRegistry.keys();
|
|
859
|
+
for (const key of keys) {
|
|
860
|
+
const entry = this.moduleRegistry.get(key);
|
|
861
|
+
if (entry === null || entry === void 0 ? void 0 : entry.aliases.has(id)) {
|
|
862
|
+
return entry;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
const instantiation = this.getModuleDef(resolvedId, id);
|
|
866
|
+
const dependencyRecords = instantiation.then((moduleDef) => {
|
|
867
|
+
const dependencies = moduleDef.dependencies;
|
|
868
|
+
// get dep and filter out exports
|
|
869
|
+
const filtered = dependencies
|
|
870
|
+
.map((dep) => {
|
|
871
|
+
if (dep === 'exports') {
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
invariant(dep !== 'require', NO_AMD_REQUIRE);
|
|
875
|
+
return this.getModuleDependencyRecord.call(this, dep);
|
|
876
|
+
})
|
|
877
|
+
.filter((depRecord) => depRecord !== undefined);
|
|
878
|
+
return Promise.all(filtered);
|
|
879
|
+
});
|
|
880
|
+
moduleRecord = {
|
|
881
|
+
id: resolvedId,
|
|
882
|
+
aliases: new Set([id]),
|
|
883
|
+
module: Object.create(null),
|
|
884
|
+
dependencyRecords,
|
|
885
|
+
instantiation,
|
|
886
|
+
evaluated: false,
|
|
887
|
+
evaluationPromise: null,
|
|
888
|
+
};
|
|
889
|
+
this.moduleRegistry.set(resolvedId, moduleRecord);
|
|
890
|
+
return moduleRecord;
|
|
891
|
+
}
|
|
892
|
+
async getModuleDependencyRecord(dependency) {
|
|
893
|
+
const resolvedDepId = await this.resolve(dependency);
|
|
894
|
+
return this.getModuleRecord(resolvedDepId, dependency);
|
|
895
|
+
}
|
|
896
|
+
// execute the "top-level code" (the code outside of functions) of a module
|
|
897
|
+
async topLevelEvaluation(moduleRecord) {
|
|
898
|
+
await this.instantiateAll(moduleRecord, {});
|
|
899
|
+
return this.evaluateModule(moduleRecord, {});
|
|
900
|
+
}
|
|
901
|
+
// Returns a promise when a module and all of it's dependencies have finished instantiation
|
|
902
|
+
async instantiateAll(moduleRecord, instantiatedMap) {
|
|
903
|
+
if (!instantiatedMap[moduleRecord.id]) {
|
|
904
|
+
instantiatedMap[moduleRecord.id] = true;
|
|
905
|
+
const dependencyModuleRecords = await moduleRecord.dependencyRecords;
|
|
906
|
+
if (dependencyModuleRecords) {
|
|
907
|
+
for (let i = 0; i < dependencyModuleRecords.length; i++) {
|
|
908
|
+
const depRecord = dependencyModuleRecords[i];
|
|
909
|
+
// eslint-disable-next-line no-await-in-loop
|
|
910
|
+
await this.instantiateAll(depRecord, instantiatedMap);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
async evaluateModule(moduleRecord, evaluationMap) {
|
|
916
|
+
const dependencyModuleRecords = await moduleRecord.dependencyRecords;
|
|
917
|
+
if (dependencyModuleRecords.length > 0) {
|
|
918
|
+
evaluationMap[moduleRecord.id] = true;
|
|
919
|
+
// evaluate dependencies first
|
|
920
|
+
await this.evaluateModuleDependencies(dependencyModuleRecords, evaluationMap);
|
|
921
|
+
}
|
|
922
|
+
const { exporter, dependencies } = await moduleRecord.instantiation;
|
|
923
|
+
// The exports object automatically gets filled in by the exporter evaluation
|
|
924
|
+
const exports = {};
|
|
925
|
+
const depsMapped = await Promise.all(dependencies.map(async (dep) => {
|
|
926
|
+
if (dep === 'exports') {
|
|
927
|
+
return exports;
|
|
928
|
+
}
|
|
929
|
+
const resolvedDepId = await this.resolve(dep);
|
|
930
|
+
const moduleRecord = this.moduleRegistry.get(resolvedDepId);
|
|
931
|
+
if (!moduleRecord) {
|
|
932
|
+
throw new LoaderError(FAILED_DEP, [resolvedDepId]);
|
|
933
|
+
}
|
|
934
|
+
const module = moduleRecord.module;
|
|
935
|
+
/**
|
|
936
|
+
* Circular dependencies are handled properly when named exports are used,
|
|
937
|
+
* however, for default exports there is a bug: https://github.com/rollup/rollup/issues/3384
|
|
938
|
+
*
|
|
939
|
+
* The workaround below applies for circular dependencies (!moduleRecord.evaluated)
|
|
940
|
+
*/
|
|
941
|
+
if (!moduleRecord.evaluated) {
|
|
942
|
+
return this.getCircularDependencyWrapper(module);
|
|
943
|
+
}
|
|
944
|
+
if (module) {
|
|
945
|
+
return module.__defaultInterop ? module.default : module;
|
|
946
|
+
}
|
|
947
|
+
throw new LoaderError(FAILED_DEP, [resolvedDepId]);
|
|
948
|
+
}));
|
|
949
|
+
// 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
|
|
950
|
+
if (moduleRecord.evaluated) {
|
|
951
|
+
return moduleRecord.module;
|
|
952
|
+
}
|
|
953
|
+
// evaluates the module function
|
|
954
|
+
let moduleDefault = exporter(...depsMapped);
|
|
955
|
+
// value is returned from exporter, then we are not using named exports
|
|
956
|
+
if (moduleDefault !== undefined) {
|
|
957
|
+
moduleDefault = { default: moduleDefault };
|
|
958
|
+
// __defaultInterop is ONLY used to support backwards compatibility
|
|
959
|
+
// of importing default exports the "wrong" way (when not using named exports).
|
|
960
|
+
// See https://github.com/salesforce/lwr/pull/816
|
|
961
|
+
Object.defineProperty(moduleDefault, '__defaultInterop', { value: true });
|
|
962
|
+
}
|
|
963
|
+
// if no return value, then we are using the exports object
|
|
964
|
+
else {
|
|
965
|
+
// handle only default export with Rollup forced named exports
|
|
966
|
+
if (this.isNamedExportDefaultOnly(exports)) {
|
|
967
|
+
Object.defineProperty(exports, '__useDefault', { value: true });
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
const moduleExports = moduleDefault || exports;
|
|
971
|
+
// update the module record
|
|
972
|
+
// copy over enumerable public methods to module
|
|
973
|
+
for (const key in moduleExports) {
|
|
974
|
+
Object.defineProperty(moduleRecord.module, key, {
|
|
975
|
+
enumerable: true,
|
|
976
|
+
set(value) {
|
|
977
|
+
moduleExports[key] = value;
|
|
978
|
+
},
|
|
979
|
+
get() {
|
|
980
|
+
return moduleExports[key];
|
|
981
|
+
},
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
// copy non-enumerable to module
|
|
985
|
+
if (moduleExports.__useDefault) {
|
|
986
|
+
Object.defineProperty(moduleRecord.module, '__useDefault', { value: true });
|
|
987
|
+
}
|
|
988
|
+
if (moduleExports.__defaultInterop) {
|
|
989
|
+
Object.defineProperty(moduleRecord.module, '__defaultInterop', { value: true });
|
|
990
|
+
}
|
|
991
|
+
if (moduleExports.__esModule) {
|
|
992
|
+
Object.defineProperty(moduleRecord.module, '__esModule', { value: true });
|
|
993
|
+
}
|
|
994
|
+
moduleRecord.evaluated = true;
|
|
995
|
+
Object.freeze(moduleRecord.module);
|
|
996
|
+
return moduleRecord.module;
|
|
997
|
+
}
|
|
998
|
+
// Determines if named exports module has only default export
|
|
999
|
+
isNamedExportDefaultOnly(exports) {
|
|
1000
|
+
return (exports !== undefined &&
|
|
1001
|
+
Object.getOwnPropertyNames(exports).length === 2 &&
|
|
1002
|
+
Object.prototype.hasOwnProperty.call(exports, 'default') &&
|
|
1003
|
+
Object.prototype.hasOwnProperty.call(exports, '__esModule'));
|
|
1004
|
+
}
|
|
1005
|
+
// Wrap the dependency in a function that can be called and detected by __circular__ property.
|
|
1006
|
+
// The LWC engine checks for __circular__ to detect circular dependencies.
|
|
1007
|
+
getCircularDependencyWrapper(module) {
|
|
1008
|
+
const tmp = () => {
|
|
1009
|
+
return module.__useDefault || module.__defaultInterop ? module.default : module;
|
|
1010
|
+
};
|
|
1011
|
+
tmp.__circular__ = true;
|
|
1012
|
+
return tmp;
|
|
1013
|
+
}
|
|
1014
|
+
async evaluateModuleDependencies(dependencyModuleRecords, evaluationMap) {
|
|
1015
|
+
for (let i = 0; i < dependencyModuleRecords.length; i++) {
|
|
1016
|
+
const depRecord = dependencyModuleRecords[i];
|
|
1017
|
+
if (!depRecord.evaluated && !evaluationMap[depRecord.id]) {
|
|
1018
|
+
evaluationMap[depRecord.id] = true;
|
|
1019
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1020
|
+
await this.evaluateModule(depRecord, evaluationMap);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
async getModuleDef(resolvedId, originalId) {
|
|
1025
|
+
// reset lastDefine
|
|
1026
|
+
this.lastDefine = undefined;
|
|
1027
|
+
// the module name can be the resolved ID or the original ID if neither are URL's.
|
|
1028
|
+
const moduleName = !isUrl(resolvedId)
|
|
1029
|
+
? resolvedId
|
|
1030
|
+
: originalId !== resolvedId
|
|
1031
|
+
? originalId
|
|
1032
|
+
: undefined;
|
|
1033
|
+
let moduleDef = moduleName && this.namedDefineRegistry.get(moduleName);
|
|
1034
|
+
if (moduleDef && moduleDef.external) {
|
|
1035
|
+
return moduleDef.external.moduleDefPromise;
|
|
1036
|
+
}
|
|
1037
|
+
if (moduleDef && moduleDef.defined) {
|
|
1038
|
+
return moduleDef;
|
|
1039
|
+
}
|
|
1040
|
+
const parentUrl = this.resolver.getBaseUrl(); // only support baseUrl for now
|
|
1041
|
+
const specifier = moduleName || originalId;
|
|
1042
|
+
this.profiler.logOperationStart({ id: MODULE_FETCH, specifier });
|
|
1043
|
+
return Promise.resolve()
|
|
1044
|
+
.then(async () => {
|
|
1045
|
+
const loadHooks = this.loadHook;
|
|
1046
|
+
if (loadHooks) {
|
|
1047
|
+
for (let i = 0; i < loadHooks.length; i++) {
|
|
1048
|
+
const loadHook = loadHooks[i];
|
|
1049
|
+
const response = loadHook(resolvedId, parentUrl);
|
|
1050
|
+
const result = (isResponseAPromise(response)
|
|
1051
|
+
? // eslint-disable-next-line no-await-in-loop
|
|
1052
|
+
await evaluateLoadHook(resolvedId, response)
|
|
1053
|
+
: response);
|
|
1054
|
+
if (result === undefined) {
|
|
1055
|
+
throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
|
|
1056
|
+
}
|
|
1057
|
+
if (result && result !== null) {
|
|
1058
|
+
return evaluateLoadHookResponse(result, resolvedId);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
return false;
|
|
1063
|
+
})
|
|
1064
|
+
.then((result) => {
|
|
1065
|
+
if (result !== true && hasDocument) {
|
|
1066
|
+
return loadModuleDef(resolvedId);
|
|
1067
|
+
}
|
|
1068
|
+
})
|
|
1069
|
+
.then(() => {
|
|
1070
|
+
// Attempt to retrieve the module definition by name first
|
|
1071
|
+
moduleDef = moduleName && this.namedDefineRegistry.get(moduleName);
|
|
1072
|
+
// Fallback to the last loader.define call
|
|
1073
|
+
if (!moduleDef) {
|
|
1074
|
+
moduleDef = this.lastDefine;
|
|
1075
|
+
}
|
|
1076
|
+
// This should not happen
|
|
1077
|
+
if (!moduleDef) {
|
|
1078
|
+
throw new LoaderError(FAIL_INSTANTIATE, [resolvedId]);
|
|
1079
|
+
}
|
|
1080
|
+
this.profiler.logOperationEnd({ id: MODULE_FETCH, specifier });
|
|
1081
|
+
return moduleDef;
|
|
1082
|
+
})
|
|
1083
|
+
.catch((e) => {
|
|
1084
|
+
this.profiler.logOperationStart({ id: MODULE_ERROR, specifier });
|
|
1085
|
+
throw e;
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
addLoaderPlugin(hooks) {
|
|
1089
|
+
if (typeof hooks !== 'object') {
|
|
1090
|
+
throw new LoaderError(INVALID_HOOK);
|
|
1091
|
+
}
|
|
1092
|
+
const { loadModule: loadHook, resolveModule: resolveHook, loadMapping } = hooks;
|
|
1093
|
+
if (resolveHook) {
|
|
1094
|
+
if (this.resolveHook) {
|
|
1095
|
+
this.resolveHook.push(resolveHook);
|
|
1096
|
+
}
|
|
1097
|
+
else {
|
|
1098
|
+
this.resolveHook = [resolveHook];
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
if (loadHook) {
|
|
1102
|
+
if (this.loadHook) {
|
|
1103
|
+
this.loadHook.push(loadHook);
|
|
1104
|
+
}
|
|
1105
|
+
else {
|
|
1106
|
+
this.loadHook = [loadHook];
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
if (loadMapping) {
|
|
1110
|
+
this.resolver.addLoadMappingHook(loadMapping);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
importMetadataInvalidationCallback({ name, oldUrl, newUrl }) {
|
|
1114
|
+
const handleStaleModuleHooks = this.handleStaleModuleHook;
|
|
1115
|
+
if (handleStaleModuleHooks) {
|
|
1116
|
+
evaluateHandleStaleModuleHooks(handleStaleModuleHooks, {
|
|
1117
|
+
name,
|
|
1118
|
+
oldUrl,
|
|
1119
|
+
newUrl,
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
else {
|
|
1123
|
+
if (hasConsole) {
|
|
1124
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
1125
|
+
console.warn(`stale module detected ${name}, current URL:${oldUrl}, new URL:${newUrl}`);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
registerHandleStaleModuleHook(handleStaleModule) {
|
|
1130
|
+
if (this.handleStaleModuleHook) {
|
|
1131
|
+
this.handleStaleModuleHook.push(handleStaleModule);
|
|
1132
|
+
}
|
|
1133
|
+
else {
|
|
1134
|
+
this.handleStaleModuleHook = [handleStaleModule];
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* The LWR loader is inspired and borrows from the algorithms and native browser principles of https://github.com/systemjs/systemjs
|
|
1141
|
+
*/
|
|
1142
|
+
class Loader {
|
|
1143
|
+
constructor(config) {
|
|
1144
|
+
let baseUrl = config.baseUrl;
|
|
1145
|
+
const mappingEndpoint = config.endpoints ? config.endpoints.uris.mapping : undefined;
|
|
1146
|
+
let profiler = config.profiler;
|
|
1147
|
+
if (!mappingEndpoint) {
|
|
1148
|
+
throw new LoaderError(NO_MAPPING_URL);
|
|
1149
|
+
}
|
|
1150
|
+
// add a trailing slash, if it does not exist
|
|
1151
|
+
config.endpoints.uris.mapping = mappingEndpoint.replace(/\/?$/, '/');
|
|
1152
|
+
if (baseUrl) {
|
|
1153
|
+
// add a trailing slash, if it does not exist
|
|
1154
|
+
baseUrl = baseUrl.replace(/\/?$/, '/');
|
|
1155
|
+
}
|
|
1156
|
+
if (!baseUrl) {
|
|
1157
|
+
baseUrl = getBaseUrl();
|
|
1158
|
+
}
|
|
1159
|
+
if (!baseUrl) {
|
|
1160
|
+
throw new LoaderError(NO_BASE_URL);
|
|
1161
|
+
}
|
|
1162
|
+
if (!profiler) {
|
|
1163
|
+
// default noop profiler
|
|
1164
|
+
profiler = {
|
|
1165
|
+
logOperationStart: () => {
|
|
1166
|
+
/* noop */
|
|
1167
|
+
},
|
|
1168
|
+
logOperationEnd: () => {
|
|
1169
|
+
/* noop */
|
|
1170
|
+
},
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
this.registry = new ModuleRegistry(Object.freeze({ endpoints: config.endpoints, baseUrl, profiler }));
|
|
1174
|
+
// TODO: W-10539691 - temp workaround for LWR-Java -- remove once appId is implemented there
|
|
1175
|
+
if (config.appMetadata && !config.appMetadata.appId) {
|
|
1176
|
+
// Parse the appId from the bootstrapModule
|
|
1177
|
+
// LWR-Java bootstrap module format: @lwr-bootstrap/my/app/v/0_0_1 -- my/app is the appId
|
|
1178
|
+
const match = config.appMetadata.bootstrapModule.match(/@lwr-bootstrap\/(.+)\/v\/.+/);
|
|
1179
|
+
const appId = match && match[1];
|
|
1180
|
+
config.appMetadata.appId = appId;
|
|
1181
|
+
}
|
|
1182
|
+
// TODO: https://github.com/salesforce/lwr/issues/1087
|
|
1183
|
+
this.services = Object.freeze({
|
|
1184
|
+
addLoaderPlugin: this.registry.addLoaderPlugin.bind(this.registry),
|
|
1185
|
+
handleStaleModule: this.registry.registerHandleStaleModuleHook.bind(this.registry),
|
|
1186
|
+
appMetadata: config.appMetadata,
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Defines/registers a single named AMD module definition.
|
|
1191
|
+
*
|
|
1192
|
+
* @param {string} name The module name
|
|
1193
|
+
* @param {string[]} dependencies A list of module dependencies (module imports)
|
|
1194
|
+
* @param {Function} execute The function containing the module code. AKA exporter as it also returns the modules exports when executed
|
|
1195
|
+
* @return {void}
|
|
1196
|
+
*/
|
|
1197
|
+
define(name, dependencies, execute) {
|
|
1198
|
+
invariant(typeof name === 'string', MISSING_NAME);
|
|
1199
|
+
let ctor = execute;
|
|
1200
|
+
let deps = dependencies;
|
|
1201
|
+
// Convert no dependencies form `define('name', function(){}, {});` to: `define('name', [], function(){}, {})`
|
|
1202
|
+
if (typeof deps === 'function') {
|
|
1203
|
+
ctor = dependencies;
|
|
1204
|
+
deps = [];
|
|
1205
|
+
}
|
|
1206
|
+
invariant(Array.isArray(deps), INVALID_DEPS);
|
|
1207
|
+
this.registry.define(name, deps, ctor);
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Retrieves/loads a module, returning it from the registry if it exists and fetching it if it doesn't.
|
|
1211
|
+
*
|
|
1212
|
+
* @param {string} id - A module identifier or URL
|
|
1213
|
+
* @param {string} importer - The versioned specifier of the module importer
|
|
1214
|
+
* Used when the ID is not versioned (eg: variable dynamic imports)
|
|
1215
|
+
* @return {Promise<Module>}
|
|
1216
|
+
*/
|
|
1217
|
+
async load(id, importer) {
|
|
1218
|
+
return this.registry.load(id, importer);
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* 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).
|
|
1222
|
+
*
|
|
1223
|
+
* @param {string} id - A module identifier or URL
|
|
1224
|
+
* @return {boolean}
|
|
1225
|
+
*/
|
|
1226
|
+
has(id) {
|
|
1227
|
+
return this.registry.has(id);
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Resolves the module identifier or URL. Returns the module identifier if the moduleDefinition exists, or the full resolved URL if a URL is given.
|
|
1231
|
+
*
|
|
1232
|
+
* @param {string} id - A module identifier or URL
|
|
1233
|
+
* @param {string} importer - The versioned specifier of the module importer
|
|
1234
|
+
* Used when the ID is not versioned (eg: variable dynamic imports)
|
|
1235
|
+
* @return {string}
|
|
1236
|
+
*/
|
|
1237
|
+
async resolve(id, importer) {
|
|
1238
|
+
return this.registry.resolve(id, importer);
|
|
1239
|
+
}
|
|
1240
|
+
async registerImportMappings(mappings, rootSpecifiers) {
|
|
1241
|
+
this.registry.getImportMetadataResolver().registerImportMappings(mappings, rootSpecifiers);
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Marks modules as "externally" loaded/provided (e.g. preloaded), so that the loader does not attempt to load them.
|
|
1245
|
+
*
|
|
1246
|
+
* @param modules - list of module identifiers
|
|
1247
|
+
*/
|
|
1248
|
+
registerExternalModules(modules) {
|
|
1249
|
+
this.registry.registerExternalModules(modules);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/** LWR Everywhere module for AMD **/
|
|
1254
|
+
// Process the LWR Client Bootstrap Config
|
|
1255
|
+
const configOptions = { "format": "amd", "apiVersion": "v57.0", "apiPrefix": "/lwr", "bundle": true, "debug": false };
|
|
1256
|
+
const configuration = getConfiguration(configOptions);
|
|
1257
|
+
const { bootstrapModule, endpoints, baseUrl, imports, index } = configuration.config;
|
|
1258
|
+
globalThis.process = { env: { NODE_ENV: configuration.options.NODE_ENV } }; // Required by LWC
|
|
1259
|
+
// Initialize the AMD loader (its code is bundled into the LWR Everywhere module)
|
|
1260
|
+
const loader = new Loader({ endpoints, baseUrl });
|
|
1261
|
+
loader.define('lwr/loader/v/0_0_1', ['exports'], (exports) => {
|
|
1262
|
+
Object.assign(exports, {
|
|
1263
|
+
define: loader.define.bind(loader),
|
|
1264
|
+
load: loader.load.bind(loader),
|
|
1265
|
+
});
|
|
1266
|
+
});
|
|
1267
|
+
await loader.registerImportMappings({ imports, index }, [bootstrapModule]);
|
|
1268
|
+
globalThis.LWR = Object.freeze({ define: loader.define.bind(loader) });
|
|
1269
|
+
// Define preload modules (ie: ABS, vault, etc) BEFORE loading them
|
|
1270
|
+
LWR.define("lwr/everywhereAmd/v/0_0_1", ["exports", "lwr/vault/v/0_0_1"], function (exports, _0_8_0Alpha_10) {
|
|
1271
|
+
async function authFetch(url, accept = "javascript") {
|
|
1272
|
+
const authInfo = _0_8_0Alpha_10.getAuthInfo();
|
|
1273
|
+
if (authInfo) {
|
|
1274
|
+
return fetch(url, {
|
|
1275
|
+
headers: {
|
|
1276
|
+
Accept: `application/${accept}`,
|
|
1277
|
+
Authorization: `Bearer ${authInfo.access_token}`
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
return null;
|
|
1282
|
+
}
|
|
1283
|
+
function everywhereHooks(serviceAPI, mappingEndpoint, coreVersion) {
|
|
1284
|
+
serviceAPI.addLoaderPlugin({
|
|
1285
|
+
loadModule: async (url) => {
|
|
1286
|
+
return authFetch(url);
|
|
1287
|
+
},
|
|
1288
|
+
loadMapping: async (specifier) => {
|
|
1289
|
+
const res = await authFetch(`${mappingEndpoint}${encodeURIComponent(specifier)}`, "json");
|
|
1290
|
+
if (res && res.ok) {
|
|
1291
|
+
return res.json();
|
|
1292
|
+
}
|
|
1293
|
+
return null;
|
|
1294
|
+
},
|
|
1295
|
+
resolveModule: (specifier) => {
|
|
1296
|
+
if (specifier && !/\/v\/[^/]+?$/.test(specifier)) {
|
|
1297
|
+
return `${specifier}/v/${coreVersion}`;
|
|
1298
|
+
}
|
|
1299
|
+
return null;
|
|
1300
|
+
}
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
function initAmd(services, mappingEndpoint, coreVersion) {
|
|
1304
|
+
if (mappingEndpoint.match(/\/v5\d\.0\//)) {
|
|
1305
|
+
everywhereHooks(services, mappingEndpoint, coreVersion);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
function authenticate(authInfo) {
|
|
1309
|
+
if (authInfo) {
|
|
1310
|
+
_0_8_0Alpha_10.setAuthInfo(authInfo);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
exports.authenticate = authenticate;
|
|
1314
|
+
exports.initAmd = initAmd;
|
|
1315
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1316
|
+
});
|
|
1317
|
+
LWR.define("lwr/vault/v/0_0_1", ["exports"], function (exports) {
|
|
1318
|
+
let authInfo;
|
|
1319
|
+
function getAuthInfo() {
|
|
1320
|
+
return authInfo;
|
|
1321
|
+
}
|
|
1322
|
+
function setAuthInfo(ai) {
|
|
1323
|
+
authInfo = ai;
|
|
1324
|
+
}
|
|
1325
|
+
exports.getAuthInfo = getAuthInfo;
|
|
1326
|
+
exports.setAuthInfo = setAuthInfo;
|
|
1327
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1328
|
+
});
|
|
1329
|
+
// Set up the AMD loader hooks for OAuth
|
|
1330
|
+
const { initAmd, authenticate } = await loader.load(bootstrapModule);
|
|
1331
|
+
initAmd(loader.services, endpoints?.uris.mapping, '0_0_1');
|
|
1332
|
+
// Wrap the createComponent API to allow proper ordering of module definitions:
|
|
1333
|
+
// 1. LWRE module bundle (this): lwr/loader, lwr/everywhereAmd, lwr/vault
|
|
1334
|
+
// 2. lwr/everywhere (deps: lwc, synthetic shadow, lwr/host, etc)
|
|
1335
|
+
// 3. Salesforce hosted UI content (i.e. embedded components and deps)
|
|
1336
|
+
async function createComponent(specifier, nodeId, properties = {}, config) {
|
|
1337
|
+
// Do not load the "lwr/everywhere" API module ahead of time
|
|
1338
|
+
// This import MUST be done AFTER the loader hooks are in place via authenticate() so
|
|
1339
|
+
// modules [statically] imported from core have the appropriate Authorization headers
|
|
1340
|
+
const { createComponent: cc } = await loader.load(bootstrapModule.replace('Amd', ''));
|
|
1341
|
+
return cc(specifier, nodeId, properties, config);
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
export { authenticate, createComponent };
|