@lwrjs/loader 0.6.0-alpha.11 → 0.6.0-alpha.15

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.
@@ -4,243 +4,308 @@
4
4
  * SPDX-License-Identifier: MIT
5
5
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
6
6
  */
7
- /* LWR Legacy Module Loader Shim v0.6.0-alpha.11 */
7
+ /* LWR Legacy Module Loader Shim v0.6.0-alpha.15 */
8
8
  (function () {
9
- 'use strict';
9
+ 'use strict';
10
10
 
11
- function createLoader(name, definition, baseUrl, externalModules) {
12
- if (!definition || typeof definition[2] !== 'function') {
13
- throw new Error(`Expected loader with specifier "${name}" to be a module`);
14
- }
15
- // Create a Loader instance
16
- const exports = {};
17
- definition[2].call(null, exports);
18
- const { Loader } = exports;
19
- const loader = new Loader(baseUrl);
20
- // register externally loaded modules
21
- if (externalModules && externalModules.length) {
22
- loader.registerExternalModules(externalModules);
23
- }
24
- // Define the loader module with public API: { define, load, services }
25
- const exporter = (exports) => {
26
- Object.assign(exports, {
27
- define: loader.define.bind(loader),
28
- load: loader.load.bind(loader),
29
- services: loader.services,
30
- });
31
- return;
32
- };
33
- loader.define(name, ['exports'], exporter, definition[3]);
34
- return loader;
35
- }
11
+ // Bootstrap / shim
12
+ const BOOTSTRAP_PREFIX = 'lwr.bootstrap.';
13
+ const BOOTSTRAP_ERROR = `${BOOTSTRAP_PREFIX}error`;
36
14
 
37
- const REQUIRED_MODULES_TIMEOUT = 300 * 1000;
15
+ var Phase;
38
16
 
39
- var Phase;
40
- (function (Phase) {
41
- Phase[Phase["Start"] = 0] = "Start";
42
- Phase[Phase["End"] = 1] = "End";
43
- })(Phase || (Phase = {}));
44
- function attachDispatcher(dispatcher) {
45
- }
17
+ (function (Phase) {
18
+ Phase[Phase["Start"] = 0] = "Start";
19
+ Phase[Phase["End"] = 1] = "End";
20
+ })(Phase || (Phase = {}));
46
21
 
47
- // Check for errors with autoBoot and customInit
48
- function validatePreInit(autoBoot, customInit) {
49
- // If autoBoot === false, there must be a customInit hook
50
- if (!autoBoot && !customInit) {
51
- throw new Error('The customInit hook is required when autoBoot is false');
52
- }
53
- // If autoBoot === true, there must NOT be a customInit hook
54
- if (autoBoot && customInit) {
55
- throw new Error('The customInit hook must not be defined when autoBoot is true');
56
- }
57
- }
58
- // Process the customInit hook
59
- function customInit(config, initializeApp, define, onBootstrapError) {
60
- // Validate config
61
- const { autoBoot, customInit } = config;
62
- validatePreInit(autoBoot, customInit);
63
- // Set up arguments and call the customInit hook, if available
64
- if (customInit) {
65
- const lwr = {
66
- initializeApp,
67
- define,
68
- onBootstrapError,
69
- attachDispatcher,
70
- };
71
- customInit(lwr, config);
72
- }
73
- }
22
+ // Attach a custom dispatcher
23
+ let customDispatcher;
24
+ function attachDispatcher(dispatcher) {
25
+ customDispatcher = dispatcher;
26
+ } // Check if the Performance API is available
27
+ // e.g. JSDom (used in Jest) doesn't implement these
74
28
 
75
- /* global document */
76
- /* eslint-disable lwr/no-unguarded-apis */
77
- const hasSetTimeout = typeof setTimeout === 'function';
78
- const hasConsole = typeof console !== 'undefined';
79
- /* eslint-enable lwr/no-unguarded-apis */
80
- class LoaderShim {
81
- constructor(global) {
82
- this.defineCache = {};
83
- this.orderedDefs = [];
84
- // Parse configuration
85
- this.global = global;
86
- this.config = global.LWR;
87
- this.loaderModule = 'lwr/loaderLegacy/v/0_6_0-alpha_11';
88
- // Set up the temporary LWR.define function and customInit hook
89
- const tempDefine = this.tempDefine.bind(this);
90
- global.LWR.define = tempDefine;
91
- this.bootReady = this.config.autoBoot;
92
- // Start watchdog timer
93
- if (hasSetTimeout) {
94
- this.watchdogTimerId = this.startWatchdogTimer();
95
- }
96
- try {
97
- customInit(Object.freeze(this.config), this.postCustomInit.bind(this), tempDefine, (e) => {
98
- this.errorHandler = e;
99
- });
100
- }
101
- catch (e) {
102
- this.enterErrorState(e);
103
- }
104
- }
105
- // Return true if the app can be initialized
106
- canInit() {
107
- // Initialize the app if:
108
- // - bootReady: autoBoot is on OR customInit has finished
109
- // - all required modules are defined
110
- const allDefined = this.config.requiredModules.every((m) => this.orderedDefs.includes(m));
111
- return this.bootReady && allDefined;
112
- }
113
- /**
114
- * Create a temporary LWR.define() function which captures all
115
- * calls that occur BEFORE the full loader module is available
116
- *
117
- * Each call to LWR.define() is stored in 2 ways:
118
- * - in a map as [moduleName, arguments] pairs
119
- * - each moduleName is pushed onto an array, to preserve
120
- * the order in which the modules were defined
121
- */
122
- tempDefine(...args) {
123
- // Cache the incoming module
124
- const moduleName = args[0];
125
- this.defineCache[moduleName] = args;
126
- this.orderedDefs.push(moduleName);
127
- if (this.canInit()) {
128
- if (hasSetTimeout) {
129
- // requiredModules are defined, clear watchdog timer
130
- // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
131
- clearTimeout(this.watchdogTimerId);
132
- }
133
- this.initApp();
134
- }
135
- }
136
- // Called by the customInit hook via lwr.initializeApp()
137
- postCustomInit() {
138
- this.bootReady = true;
139
- if (this.canInit()) {
140
- this.initApp();
141
- }
142
- }
143
- // Create the loader and initialize the application
144
- initApp() {
145
- try {
146
- const loader = createLoader(this.loaderModule, this.defineCache[this.loaderModule], this.config.baseUrl, this.config.preloadModules);
147
- this.mountApp(loader);
148
- }
149
- catch (e) {
150
- this.enterErrorState(e);
151
- }
152
- }
153
- waitForDOMContentLoaded() {
154
- // eslint-disable-next-line lwr/no-unguarded-apis
155
- if (typeof document === undefined) {
156
- return Promise.resolve();
157
- }
158
- // Resolve if document is already "ready" https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState
159
- // eslint-disable-next-line lwr/no-unguarded-apis
160
- if (document.readyState === 'interactive' || document.readyState === 'complete') {
161
- return Promise.resolve();
162
- }
163
- return new Promise((resolve) => {
164
- // eslint-disable-next-line lwr/no-unguarded-apis
165
- document.addEventListener('DOMContentLoaded', () => {
166
- resolve();
167
- });
168
- });
169
- }
170
- // Set up the application globals, import map, root custom element...
171
- mountApp(loader) {
172
- const { bootstrapModule, rootComponent, importMappings, rootComponents, endpoints } = this.config;
173
- // Set global LWR.define to loader.define
174
- this.global.LWR = Object.freeze({
175
- define: loader.define.bind(loader),
176
- rootComponent,
177
- rootComponents,
178
- importMappings,
179
- endpoints,
180
- });
181
- // Redefine all modules in the temporary cache
182
- this.orderedDefs.forEach((specifier) => {
183
- if (specifier !== this.loaderModule) {
184
- loader.define(...this.defineCache[specifier]);
185
- }
186
- });
187
- // Define a dummy profiler since the "lwr/profiler" module is used in "lwr/init" (which is global to all server modes)
188
- const profilerExporter = (exports) => {
189
- Object.assign(exports, {
190
- logOperationStart: () => {
191
- /* noop */
192
- },
193
- });
194
- return;
195
- };
196
- loader.define('lwr/profiler/v/0_6_0-alpha_11', ['exports'], profilerExporter, {});
197
- // by default, app initialization is gated on waiting for document to be parsed (via DOMContentLoaded)
198
- const { disableInitDefer } = this.config;
199
- // Load the import mappings and application bootstrap module
200
- loader
201
- .registerImportMappings(importMappings)
202
- .then(() => {
203
- if (!disableInitDefer) {
204
- return this.waitForDOMContentLoaded();
205
- }
206
- })
207
- .then(() => loader.load(bootstrapModule))
208
- .catch((reason) => {
209
- this.enterErrorState(new Error(`Application ${rootComponent} could not be loaded: ${reason}`));
210
- });
211
- }
212
- // Trigger bootstrap error state, and call error handler if registered
213
- enterErrorState(error) {
214
- if (this.errorHandler) {
215
- this.errorHandler(error);
216
- }
217
- else {
218
- if (hasConsole) {
219
- // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
220
- console.error(`An error occurred during LWR bootstrap. ${error.message}`, error.stack);
221
- }
222
- }
223
- }
224
- // eslint-disable-next-line no-undef, lwr/no-unguarded-apis
225
- startWatchdogTimer() {
226
- // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
227
- return setTimeout(() => {
228
- this.enterErrorState(new Error('Failed to load required modules - timed out'));
229
- }, REQUIRED_MODULES_TIMEOUT);
230
- }
29
+ const perf = globalThis.performance;
30
+ const isPerfSupported = typeof perf !== 'undefined' && typeof perf.mark === 'function' && typeof perf.clearMarks === 'function' && typeof perf.measure === 'function' && typeof perf.clearMeasures === 'function'; // For marking request metrics
31
+ // Fallback to the Performance API if there is no custom dispatcher
32
+
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}` : ''));
231
45
  }
46
+ } // For measuring duration metrics
47
+ // Fallback to the Performance API if there is no custom dispatcher
48
+
49
+ /* istanbul ignore next */
232
50
 
233
- // The loader module is ALWAYS required
234
- const GLOBAL = globalThis;
235
- GLOBAL.LWR.requiredModules = GLOBAL.LWR.requiredModules || [];
236
- if (GLOBAL.LWR.requiredModules.indexOf('lwr/loaderLegacy/v/0_6_0-alpha_11') < 0) {
237
- GLOBAL.LWR.requiredModules.push('lwr/loaderLegacy/v/0_6_0-alpha_11');
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); // Clear the created mark and measure to avoid filling the performance entry buffer
66
+ // Even if they get deleted, existing PerformanceObservers preserve copies of the entries
67
+
68
+ perf.clearMarks(markName);
69
+ perf.clearMeasures(measureName);
238
70
  }
239
- new LoaderShim(GLOBAL);
71
+ }
72
+
73
+ function createLoader(name, definition, config, externalModules) {
74
+ if (!definition || typeof definition[2] !== 'function') {
75
+ throw new Error(`Expected loader with specifier "${name}" to be a module`);
76
+ }
77
+ // Create a Loader instance
78
+ const exports = {};
79
+ definition[2].call(null, exports);
80
+ const { Loader } = exports;
81
+ const loader = new Loader(config);
82
+ // register externally loaded modules
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
+ return;
94
+ };
95
+ loader.define(name, ['exports'], exporter, definition[3]);
96
+ return loader;
97
+ }
98
+
99
+ const REQUIRED_MODULES_TIMEOUT = 300 * 1000;
100
+
101
+ // Check for errors with autoBoot and customInit
102
+ function validatePreInit(autoBoot, customInit) {
103
+ // If autoBoot === false, there must be a customInit hook
104
+ if (!autoBoot && !customInit) {
105
+ throw new Error('The customInit hook is required when autoBoot is false');
106
+ }
107
+ // If autoBoot === true, there must NOT be a customInit hook
108
+ if (autoBoot && customInit) {
109
+ throw new Error('The customInit hook must not be defined when autoBoot is true');
110
+ }
111
+ }
112
+ // Process the customInit hook
113
+ function customInit(config, initializeApp, define, onBootstrapError) {
114
+ // Validate config
115
+ const { autoBoot, customInit } = config;
116
+ validatePreInit(autoBoot, customInit);
117
+ // Set up arguments and call the customInit hook, if available
118
+ if (customInit) {
119
+ const lwr = {
120
+ initializeApp,
121
+ define,
122
+ onBootstrapError,
123
+ attachDispatcher,
124
+ };
125
+ customInit(lwr, config);
126
+ }
127
+ }
128
+
129
+ /* global document */
130
+ /* eslint-disable lwr/no-unguarded-apis */
131
+ const hasSetTimeout = typeof setTimeout === 'function';
132
+ const hasConsole = typeof console !== 'undefined';
133
+ /* eslint-enable lwr/no-unguarded-apis */
134
+ class LoaderShim {
135
+ constructor(global) {
136
+ this.defineCache = {};
137
+ this.orderedDefs = [];
138
+ // Parse configuration
139
+ this.global = global;
140
+ this.config = global.LWR;
141
+ this.loaderModule = 'lwr/loaderLegacy/v/0_6_0-alpha_15';
142
+ // Set up the temporary LWR.define function and customInit hook
143
+ const tempDefine = this.tempDefine.bind(this);
144
+ global.LWR.define = tempDefine;
145
+ this.bootReady = this.config.autoBoot;
146
+ // Start watchdog timer
147
+ if (hasSetTimeout) {
148
+ this.watchdogTimerId = this.startWatchdogTimer();
149
+ }
150
+ try {
151
+ customInit(Object.freeze(this.config), this.postCustomInit.bind(this), tempDefine, (e) => {
152
+ this.errorHandler = e;
153
+ });
154
+ }
155
+ catch (e) {
156
+ this.enterErrorState(e);
157
+ }
158
+ }
159
+ // Return true if the app can be initialized
160
+ canInit() {
161
+ // Initialize the app if:
162
+ // - bootReady: autoBoot is on OR customInit has finished
163
+ // - all required modules are defined
164
+ const allDefined = this.config.requiredModules.every((m) => this.orderedDefs.includes(m));
165
+ return this.bootReady && allDefined;
166
+ }
167
+ /**
168
+ * Create a temporary LWR.define() function which captures all
169
+ * calls that occur BEFORE the full loader module is available
170
+ *
171
+ * Each call to LWR.define() is stored in 2 ways:
172
+ * - in a map as [moduleName, arguments] pairs
173
+ * - each moduleName is pushed onto an array, to preserve
174
+ * the order in which the modules were defined
175
+ */
176
+ tempDefine(...args) {
177
+ // Cache the incoming module
178
+ const moduleName = args[0];
179
+ this.defineCache[moduleName] = args;
180
+ this.orderedDefs.push(moduleName);
181
+ if (this.canInit()) {
182
+ if (hasSetTimeout) {
183
+ // requiredModules are defined, clear watchdog timer
184
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
185
+ clearTimeout(this.watchdogTimerId);
186
+ }
187
+ this.initApp();
188
+ }
189
+ }
190
+ // Called by the customInit hook via lwr.initializeApp()
191
+ postCustomInit() {
192
+ this.bootReady = true;
193
+ if (this.canInit()) {
194
+ this.initApp();
195
+ }
196
+ }
197
+ // Create the loader and initialize the application
198
+ initApp() {
199
+ try {
200
+ const loaderConfig = {
201
+ baseUrl: this.config.baseUrl,
202
+ profiler: { logOperationStart, logOperationEnd },
203
+ // TODO: can be removed following https://github.com/salesforce/lwr/issues/1087
204
+ appMetadata: {
205
+ appId: this.config.appId,
206
+ bootstrapModule: this.config.bootstrapModule,
207
+ rootComponent: this.config.rootComponent,
208
+ rootComponents: this.config.rootComponents,
209
+ },
210
+ };
211
+ const loader = createLoader(this.loaderModule, this.defineCache[this.loaderModule], loaderConfig, this.config.preloadModules);
212
+ this.createProfilerModule(loader);
213
+ this.mountApp(loader);
214
+ }
215
+ catch (e) {
216
+ this.enterErrorState(e);
217
+ }
218
+ }
219
+ waitForDOMContentLoaded() {
220
+ // eslint-disable-next-line lwr/no-unguarded-apis
221
+ if (typeof document === undefined) {
222
+ return Promise.resolve();
223
+ }
224
+ // Resolve if document is already "ready" https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState
225
+ // eslint-disable-next-line lwr/no-unguarded-apis
226
+ if (document.readyState === 'interactive' || document.readyState === 'complete') {
227
+ return Promise.resolve();
228
+ }
229
+ return new Promise((resolve) => {
230
+ // eslint-disable-next-line lwr/no-unguarded-apis
231
+ document.addEventListener('DOMContentLoaded', () => {
232
+ resolve();
233
+ });
234
+ });
235
+ }
236
+ // Create a module out of the profiler
237
+ // Note: The profiler is also available as a module through lwc module resolution (see package.json)
238
+ createProfilerModule(loader) {
239
+ const exporter = (exports) => {
240
+ Object.assign(exports, { logOperationStart, logOperationEnd });
241
+ };
242
+ loader.define('lwr/profiler/v/0_6_0-alpha_15', ['exports'], exporter, {});
243
+ }
244
+ // Set up the application globals, import map, root custom element...
245
+ mountApp(loader) {
246
+ const { bootstrapModule, rootComponent, importMappings, rootComponents, endpoints } = this.config;
247
+ // Set global LWR.define to loader.define
248
+ this.global.LWR = Object.freeze({
249
+ define: loader.define.bind(loader),
250
+ rootComponent,
251
+ rootComponents,
252
+ importMappings,
253
+ endpoints,
254
+ });
255
+ // Redefine all modules in the temporary cache
256
+ this.orderedDefs.forEach((specifier) => {
257
+ if (specifier !== this.loaderModule) {
258
+ loader.define(...this.defineCache[specifier]);
259
+ }
260
+ });
261
+ // by default, app initialization is gated on waiting for document to be parsed (via DOMContentLoaded)
262
+ const { disableInitDefer } = this.config;
263
+ // Load the import mappings and application bootstrap module
264
+ loader
265
+ .registerImportMappings(importMappings)
266
+ .then(() => {
267
+ if (!disableInitDefer) {
268
+ return this.waitForDOMContentLoaded();
269
+ }
270
+ })
271
+ .then(() => loader.load(bootstrapModule))
272
+ .catch((reason) => {
273
+ this.enterErrorState(new Error(`Application ${rootComponent} could not be loaded: ${reason}`));
274
+ });
275
+ }
276
+ // Trigger bootstrap error state, and call error handler if registered
277
+ enterErrorState(error) {
278
+ logOperationStart({ id: BOOTSTRAP_ERROR });
279
+ if (this.errorHandler) {
280
+ this.errorHandler(error);
281
+ }
282
+ else {
283
+ if (hasConsole) {
284
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
285
+ console.error(`An error occurred during LWR bootstrap. ${error.message}`, error.stack);
286
+ }
287
+ }
288
+ }
289
+ // eslint-disable-next-line no-undef, lwr/no-unguarded-apis
290
+ startWatchdogTimer() {
291
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
292
+ return setTimeout(() => {
293
+ this.enterErrorState(new Error('Failed to load required modules - timed out'));
294
+ }, REQUIRED_MODULES_TIMEOUT);
295
+ }
296
+ }
297
+
298
+ // The loader module is ALWAYS required
299
+ const GLOBAL = globalThis;
300
+ GLOBAL.LWR.requiredModules = GLOBAL.LWR.requiredModules || [];
301
+ if (GLOBAL.LWR.requiredModules.indexOf('lwr/loaderLegacy/v/0_6_0-alpha_15') < 0) {
302
+ GLOBAL.LWR.requiredModules.push('lwr/loaderLegacy/v/0_6_0-alpha_15');
303
+ }
304
+ new LoaderShim(GLOBAL);
240
305
 
241
306
  }());
242
307
 
243
- LWR.define('lwr/loaderLegacy/v/0_6_0-alpha_11', ['exports'], function (exports) { 'use strict';
308
+ LWR.define('lwr/loaderLegacy/v/0_6_0-alpha_15', ['exports'], function (exports) { 'use strict';
244
309
 
245
310
  const templateRegex = /\{([0-9]+)\}/g;
246
311
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -638,14 +703,22 @@ LWR.define('lwr/loaderLegacy/v/0_6_0-alpha_11', ['exports'], function (exports)
638
703
  }
639
704
  }
640
705
 
706
+ // Bootstrap / shim
707
+
708
+ const LOADER_PREFIX = 'lwr.loader.';
709
+ const MODULE_DEFINE = `${LOADER_PREFIX}module.define`;
710
+ const MODULE_FETCH = `${LOADER_PREFIX}module.fetch`;
711
+ const MODULE_ERROR = `${LOADER_PREFIX}module.error`; // Loader: mappings
712
+
641
713
  /* global console,process */
642
714
  class ModuleRegistry {
643
- constructor(baseUrl) {
715
+ constructor(config) {
644
716
  // A registry for named AMD defines containing the *metadata* of AMD module
645
717
  this.namedDefineRegistry = new Map();
646
718
  // The evaluted module registry where the module identifier (name or URL?) is the key
647
719
  this.moduleRegistry = new Map();
648
- this.baseUrl = baseUrl;
720
+ this.baseUrl = config.baseUrl;
721
+ this.profiler = config.profiler;
649
722
  }
650
723
  async load(id, importer) {
651
724
  const resolvedId = await this.resolve(id, importer);
@@ -760,6 +833,7 @@ LWR.define('lwr/loaderLegacy/v/0_6_0-alpha_11', ['exports'], function (exports)
760
833
  // if module is "external", resolve the external promise to notify any dependees
761
834
  mod.external.resolveExternal(moduleDef);
762
835
  }
836
+ this.profiler.logOperationStart({ id: MODULE_DEFINE, specifier: name });
763
837
  this.namedDefineRegistry.set(name, moduleDef);
764
838
  this.lastDefine = moduleDef;
765
839
  // Check signatures of dependencies against those in the namedDefineRegistry
@@ -1030,6 +1104,8 @@ LWR.define('lwr/loaderLegacy/v/0_6_0-alpha_11', ['exports'], function (exports)
1030
1104
  return moduleDef;
1031
1105
  }
1032
1106
  const parentUrl = this.baseUrl; // only support baseUrl for now
1107
+ const specifier = moduleName || originalId;
1108
+ this.profiler.logOperationStart({ id: MODULE_FETCH, specifier });
1033
1109
  return Promise.resolve()
1034
1110
  .then(async () => {
1035
1111
  const loadHooks = this.loadHook;
@@ -1067,9 +1143,11 @@ LWR.define('lwr/loaderLegacy/v/0_6_0-alpha_11', ['exports'], function (exports)
1067
1143
  if (!moduleDef) {
1068
1144
  throw new LoaderError(FAIL_INSTANTIATE, [resolvedId]);
1069
1145
  }
1146
+ this.profiler.logOperationEnd({ id: MODULE_FETCH, specifier });
1070
1147
  return moduleDef;
1071
1148
  })
1072
1149
  .catch((e) => {
1150
+ this.profiler.logOperationStart({ id: MODULE_ERROR, specifier });
1073
1151
  throw e;
1074
1152
  });
1075
1153
  }
@@ -1297,7 +1375,10 @@ LWR.define('lwr/loaderLegacy/v/0_6_0-alpha_11', ['exports'], function (exports)
1297
1375
  * The LWR loader is inspired and borrows from the algorithms and native browser principles of https://github.com/systemjs/systemjs
1298
1376
  */
1299
1377
  class Loader {
1300
- constructor(baseUrl) {
1378
+ constructor(config) {
1379
+ config = config || {};
1380
+ let baseUrl = config.baseUrl;
1381
+ let profiler = config.profiler;
1301
1382
  if (baseUrl) {
1302
1383
  // add a trailing slash, if it does not exist
1303
1384
  baseUrl = baseUrl.replace(/\/?$/, '/');
@@ -1309,10 +1390,23 @@ LWR.define('lwr/loaderLegacy/v/0_6_0-alpha_11', ['exports'], function (exports)
1309
1390
  throw new LoaderError(NO_BASE_URL);
1310
1391
  }
1311
1392
  this.baseUrl = baseUrl;
1312
- this.registry = new ModuleRegistry(baseUrl);
1393
+ if (!profiler) {
1394
+ // default noop profiler
1395
+ profiler = {
1396
+ logOperationStart: () => {
1397
+ /* noop */
1398
+ },
1399
+ logOperationEnd: () => {
1400
+ /* noop */
1401
+ },
1402
+ };
1403
+ }
1404
+ this.registry = new ModuleRegistry({ baseUrl, profiler });
1405
+ // TODO: https://github.com/salesforce/lwr/issues/1087
1313
1406
  this.services = Object.freeze({
1314
1407
  addLoaderPlugin: this.registry.addLoaderPlugin.bind(this.registry),
1315
1408
  handleStaleModule: this.registry.registerHandleStaleModuleHook.bind(this.registry),
1409
+ appMetadata: config.appMetadata,
1316
1410
  });
1317
1411
  }
1318
1412
  /**