UNPKG

52 kBJavaScriptView Raw
1/* single-spa@5.0.0 - SystemJS - dev */
2System.register([], function (exports) {
3 'use strict';
4 return {
5 execute: function () {
6
7 exports({
8 addErrorHandler: addErrorHandler,
9 checkActivityFunctions: checkActivityFunctions,
10 ensureJQuerySupport: ensureJQuerySupport,
11 getAppNames: getAppNames,
12 getAppStatus: getAppStatus,
13 getMountedApps: getMountedApps,
14 mountRootParcel: mountRootParcel,
15 navigateToUrl: navigateToUrl,
16 registerApplication: registerApplication,
17 removeErrorHandler: removeErrorHandler,
18 setBootstrapMaxTime: setBootstrapMaxTime,
19 setMountMaxTime: setMountMaxTime,
20 setUnloadMaxTime: setUnloadMaxTime,
21 setUnmountMaxTime: setUnmountMaxTime,
22 start: start,
23 triggerAppChange: triggerAppChange,
24 unloadApplication: unloadApplication
25 });
26
27 var singleSpa = /*#__PURE__*/Object.freeze({
28 __proto__: null,
29 get start () { return start; },
30 get ensureJQuerySupport () { return ensureJQuerySupport; },
31 get setBootstrapMaxTime () { return setBootstrapMaxTime; },
32 get setMountMaxTime () { return setMountMaxTime; },
33 get setUnmountMaxTime () { return setUnmountMaxTime; },
34 get setUnloadMaxTime () { return setUnloadMaxTime; },
35 get registerApplication () { return registerApplication; },
36 get getMountedApps () { return getMountedApps; },
37 get getAppStatus () { return getAppStatus; },
38 get unloadApplication () { return unloadApplication; },
39 get checkActivityFunctions () { return checkActivityFunctions; },
40 get getAppNames () { return getAppNames; },
41 get navigateToUrl () { return navigateToUrl; },
42 get triggerAppChange () { return triggerAppChange; },
43 get addErrorHandler () { return addErrorHandler; },
44 get removeErrorHandler () { return removeErrorHandler; },
45 get mountRootParcel () { return mountRootParcel; },
46 get NOT_LOADED () { return NOT_LOADED; },
47 get LOADING_SOURCE_CODE () { return LOADING_SOURCE_CODE; },
48 get NOT_BOOTSTRAPPED () { return NOT_BOOTSTRAPPED; },
49 get BOOTSTRAPPING () { return BOOTSTRAPPING; },
50 get NOT_MOUNTED () { return NOT_MOUNTED; },
51 get MOUNTING () { return MOUNTING; },
52 get UPDATING () { return UPDATING; },
53 get LOAD_ERROR () { return LOAD_ERROR; },
54 get MOUNTED () { return MOUNTED; },
55 get UNMOUNTING () { return UNMOUNTING; },
56 get SKIP_BECAUSE_BROKEN () { return SKIP_BECAUSE_BROKEN; }
57 });
58
59 var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
60
61 var NativeCustomEvent = commonjsGlobal.CustomEvent;
62
63 function useNative () {
64 try {
65 var p = new NativeCustomEvent('cat', { detail: { foo: 'bar' } });
66 return 'cat' === p.type && 'bar' === p.detail.foo;
67 } catch (e) {
68 }
69 return false;
70 }
71
72 /**
73 * Cross-browser `CustomEvent` constructor.
74 *
75 * https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent.CustomEvent
76 *
77 * @public
78 */
79
80 var customEvent = useNative() ? NativeCustomEvent :
81
82 // IE >= 9
83 'undefined' !== typeof document && 'function' === typeof document.createEvent ? function CustomEvent (type, params) {
84 var e = document.createEvent('CustomEvent');
85 if (params) {
86 e.initCustomEvent(type, params.bubbles, params.cancelable, params.detail);
87 } else {
88 e.initCustomEvent(type, false, false, void 0);
89 }
90 return e;
91 } :
92
93 // IE <= 8
94 function CustomEvent (type, params) {
95 var e = document.createEventObject();
96 e.type = type;
97 if (params) {
98 e.bubbles = Boolean(params.bubbles);
99 e.cancelable = Boolean(params.cancelable);
100 e.detail = params.detail;
101 } else {
102 e.bubbles = false;
103 e.cancelable = false;
104 e.detail = void 0;
105 }
106 return e;
107 };
108
109 function _typeof(obj) {
110 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
111 _typeof = function (obj) {
112 return typeof obj;
113 };
114 } else {
115 _typeof = function (obj) {
116 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
117 };
118 }
119
120 return _typeof(obj);
121 }
122
123 var errorHandlers = [];
124 function handleAppError(err, app) {
125 var transformedErr = transformErr(err, app);
126
127 if (errorHandlers.length) {
128 errorHandlers.forEach(function (handler) {
129 return handler(transformedErr);
130 });
131 } else {
132 setTimeout(function () {
133 throw transformedErr;
134 });
135 }
136 }
137 function addErrorHandler(handler) {
138 if (typeof handler !== "function") {
139 throw Error(formatErrorMessage(28, "a single-spa error handler must be a function"));
140 }
141
142 errorHandlers.push(handler);
143 }
144 function removeErrorHandler(handler) {
145 if (typeof handler !== "function") {
146 throw Error(formatErrorMessage(29, "a single-spa error handler must be a function"));
147 }
148
149 var removedSomething = false;
150 errorHandlers = errorHandlers.filter(function (h) {
151 var isHandler = h === handler;
152 removedSomething = removedSomething || isHandler;
153 return !isHandler;
154 });
155 return removedSomething;
156 }
157 function formatErrorMessage(code, msg) {
158 for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
159 args[_key - 2] = arguments[_key];
160 }
161
162 return "single-spa minified message #".concat(code, ": ").concat(msg ? msg + " " : "", "See https://single-spa.js.org/error/?code=").concat(code).concat(args.length ? "&arg=".concat(args.join("&arg=")) : "");
163 }
164 function transformErr(ogErr, appOrParcel) {
165 var errPrefix = "".concat(objectType(appOrParcel), " '").concat(toName(appOrParcel), "' died in status ").concat(appOrParcel.status, ": ");
166 var result;
167
168 if (ogErr instanceof Error) {
169 try {
170 ogErr.message = errPrefix + ogErr.message;
171 } catch (err) {
172 /* Some errors have read-only message properties, in which case there is nothing
173 * that we can do.
174 */
175 }
176
177 result = ogErr;
178 } else {
179 console.warn(formatErrorMessage(30, "While ".concat(appOrParcel.status, ", '").concat(toName(appOrParcel), "' rejected its lifecycle function promise with a non-Error. This will cause stack traces to not be accurate."), appOrParcel.status, toName(appOrParcel)));
180
181 try {
182 result = Error(errPrefix + JSON.stringify(ogErr));
183 } catch (err) {
184 // If it's not an Error and you can't stringify it, then what else can you even do to it?
185 result = ogErr;
186 }
187 }
188
189 result.appOrParcelName = toName(appOrParcel);
190 return result;
191 }
192
193 var NOT_LOADED = exports('NOT_LOADED', "NOT_LOADED");
194 var LOADING_SOURCE_CODE = exports('LOADING_SOURCE_CODE', "LOADING_SOURCE_CODE");
195 var NOT_BOOTSTRAPPED = exports('NOT_BOOTSTRAPPED', "NOT_BOOTSTRAPPED");
196 var BOOTSTRAPPING = exports('BOOTSTRAPPING', "BOOTSTRAPPING");
197 var NOT_MOUNTED = exports('NOT_MOUNTED', "NOT_MOUNTED");
198 var MOUNTING = exports('MOUNTING', "MOUNTING");
199 var MOUNTED = exports('MOUNTED', "MOUNTED");
200 var UPDATING = exports('UPDATING', "UPDATING");
201 var UNMOUNTING = exports('UNMOUNTING', "UNMOUNTING");
202 var UNLOADING = "UNLOADING";
203 var LOAD_ERROR = exports('LOAD_ERROR', "LOAD_ERROR");
204 var SKIP_BECAUSE_BROKEN = exports('SKIP_BECAUSE_BROKEN', "SKIP_BECAUSE_BROKEN");
205 function isActive(app) {
206 return app.status === MOUNTED;
207 }
208 function isntActive(app) {
209 return !isActive(app);
210 }
211 function isLoaded(app) {
212 return app.status !== NOT_LOADED && app.status !== LOADING_SOURCE_CODE && app.status !== LOAD_ERROR;
213 }
214 function isntLoaded(app) {
215 return !isLoaded(app);
216 }
217 function shouldBeActive(app) {
218 try {
219 return app.activeWhen(window.location);
220 } catch (err) {
221 handleAppError(err, app);
222 app.status = SKIP_BECAUSE_BROKEN;
223 }
224 }
225 function shouldntBeActive(app) {
226 try {
227 return !app.activeWhen(window.location);
228 } catch (err) {
229 handleAppError(err, app);
230 app.status = SKIP_BECAUSE_BROKEN;
231 }
232 }
233 function notSkipped(item) {
234 return item !== SKIP_BECAUSE_BROKEN && (!item || item.status !== SKIP_BECAUSE_BROKEN);
235 }
236 function withoutLoadErrors(app) {
237 return app.status === LOAD_ERROR ? new Date().getTime() - app.loadErrorTime >= 200 : true;
238 }
239 function toName(app) {
240 return app.name;
241 }
242 function isParcel(appOrParcel) {
243 return Boolean(appOrParcel.unmountThisParcel);
244 }
245 function objectType(appOrParcel) {
246 return isParcel(appOrParcel) ? "parcel" : "application";
247 }
248
249 // Object.assign() is not available in IE11. And the babel compiled output for object spread
250 // syntax checks a bunch of Symbol stuff and is almost a kb. So this function is the smaller replacement.
251 function assign() {
252 for (var i = arguments.length - 1; i > 0; i--) {
253 for (var key in arguments[i]) {
254 if (key === "__proto__") {
255 continue;
256 }
257
258 arguments[i - 1][key] = arguments[i][key];
259 }
260 }
261
262 return arguments[0];
263 }
264
265 /* the array.prototype.find polyfill on npmjs.com is ~20kb (not worth it)
266 * and lodash is ~200kb (not worth it)
267 */
268 function find(arr, func) {
269 for (var i = 0; i < arr.length; i++) {
270 if (func(arr[i])) {
271 return arr[i];
272 }
273 }
274
275 return null;
276 }
277
278 function validLifecycleFn(fn) {
279 return fn && (typeof fn === "function" || isArrayOfFns(fn));
280
281 function isArrayOfFns(arr) {
282 return Array.isArray(arr) && !find(arr, function (item) {
283 return typeof item !== "function";
284 });
285 }
286 }
287 function flattenFnArray(appOrParcel, lifecycle) {
288 var fns = appOrParcel[lifecycle] || [];
289 fns = Array.isArray(fns) ? fns : [fns];
290
291 if (fns.length === 0) {
292 fns = [function () {
293 return Promise.resolve();
294 }];
295 }
296
297 var type = objectType(appOrParcel);
298 var name = toName(appOrParcel);
299 return function (props) {
300 return fns.reduce(function (resultPromise, fn, index) {
301 return resultPromise.then(function () {
302 var thisPromise = fn(props);
303 return smellsLikeAPromise(thisPromise) ? thisPromise : Promise.reject(formatErrorMessage(15, "Within ".concat(type, " ").concat(name, ", the lifecycle function ").concat(lifecycle, " at array index ").concat(index, " did not return a promise"), type, name, lifecycle, index));
304 });
305 }, Promise.resolve());
306 };
307 }
308 function smellsLikeAPromise(promise) {
309 return promise && typeof promise.then === "function" && typeof promise.catch === "function";
310 }
311
312 function toBootstrapPromise(appOrParcel, hardFail) {
313 return Promise.resolve().then(function () {
314 if (appOrParcel.status !== NOT_BOOTSTRAPPED) {
315 return appOrParcel;
316 }
317
318 appOrParcel.status = BOOTSTRAPPING;
319 return reasonableTime(appOrParcel, "bootstrap").then(function () {
320 appOrParcel.status = NOT_MOUNTED;
321 return appOrParcel;
322 }).catch(function (err) {
323 appOrParcel.status = SKIP_BECAUSE_BROKEN;
324
325 if (hardFail) {
326 throw transformErr(err, appOrParcel);
327 } else {
328 handleAppError(err, appOrParcel);
329 return appOrParcel;
330 }
331 });
332 });
333 }
334
335 function toUnmountPromise(appOrParcel, hardFail) {
336 return Promise.resolve().then(function () {
337 if (appOrParcel.status !== MOUNTED) {
338 return appOrParcel;
339 }
340
341 appOrParcel.status = UNMOUNTING;
342 var unmountChildrenParcels = Object.keys(appOrParcel.parcels).map(function (parcelId) {
343 return appOrParcel.parcels[parcelId].unmountThisParcel();
344 });
345 return Promise.all(unmountChildrenParcels).then(unmountAppOrParcel, function (parcelError) {
346 // There is a parcel unmount error
347 return unmountAppOrParcel().then(function () {
348 // Unmounting the app/parcel succeeded, but unmounting its children parcels did not
349 var parentError = Error(parcelError.message);
350
351 if (hardFail) {
352 var transformedErr = transformErr(parentError, appOrParcel);
353 appOrParcel.status = SKIP_BECAUSE_BROKEN;
354 throw transformedErr;
355 } else {
356 handleAppError(parentError, appOrParcel);
357 appOrParcel.status = SKIP_BECAUSE_BROKEN;
358 }
359 });
360 }).then(function () {
361 return appOrParcel;
362 });
363
364 function unmountAppOrParcel() {
365 // We always try to unmount the appOrParcel, even if the children parcels failed to unmount.
366 return reasonableTime(appOrParcel, "unmount").then(function () {
367 // The appOrParcel needs to stay in a broken status if its children parcels fail to unmount
368 {
369 appOrParcel.status = NOT_MOUNTED;
370 }
371 }).catch(function (err) {
372 if (hardFail) {
373 var transformedErr = transformErr(err, appOrParcel);
374 appOrParcel.status = SKIP_BECAUSE_BROKEN;
375 throw transformedErr;
376 } else {
377 handleAppError(err, appOrParcel);
378 appOrParcel.status = SKIP_BECAUSE_BROKEN;
379 }
380 });
381 }
382 });
383 }
384
385 var beforeFirstMountFired = false;
386 var firstMountFired = false;
387 function toMountPromise(appOrParcel, hardFail) {
388 return Promise.resolve().then(function () {
389 if (appOrParcel.status !== NOT_MOUNTED) {
390 return appOrParcel;
391 }
392
393 if (!beforeFirstMountFired) {
394 window.dispatchEvent(new customEvent("single-spa:before-first-mount"));
395 beforeFirstMountFired = true;
396 }
397
398 return reasonableTime(appOrParcel, "mount").then(function () {
399 appOrParcel.status = MOUNTED;
400
401 if (!firstMountFired) {
402 window.dispatchEvent(new customEvent("single-spa:first-mount"));
403 firstMountFired = true;
404 }
405
406 return appOrParcel;
407 }).catch(function (err) {
408 // If we fail to mount the appOrParcel, we should attempt to unmount it before putting in SKIP_BECAUSE_BROKEN
409 // We temporarily put the appOrParcel into MOUNTED status so that toUnmountPromise actually attempts to unmount it
410 // instead of just doing a no-op.
411 appOrParcel.status = MOUNTED;
412 return toUnmountPromise(appOrParcel).then(setSkipBecauseBroken, setSkipBecauseBroken);
413
414 function setSkipBecauseBroken() {
415 if (!hardFail) {
416 handleAppError(err, appOrParcel);
417 appOrParcel.status = SKIP_BECAUSE_BROKEN;
418 return appOrParcel;
419 } else {
420 var transformedErr = transformErr(err, appOrParcel);
421 appOrParcel.status = SKIP_BECAUSE_BROKEN;
422 throw transformedErr;
423 }
424 }
425 });
426 });
427 }
428
429 function toUpdatePromise(parcel) {
430 return Promise.resolve().then(function () {
431 if (parcel.status !== MOUNTED) {
432 throw Error(formatErrorMessage(32, "Cannot update parcel '".concat(toName(parcel), "' because it is not mounted"), toName(parcel)));
433 }
434
435 parcel.status = UPDATING;
436 return reasonableTime(parcel, "update").then(function () {
437 parcel.status = MOUNTED;
438 return parcel;
439 }).catch(function (err) {
440 var transformedErr = transformErr(err, parcel);
441 parcel.status = SKIP_BECAUSE_BROKEN;
442 throw transformedErr;
443 });
444 });
445 }
446
447 var parcelCount = 0;
448 var rootParcels = {
449 parcels: {}
450 }; // This is a public api, exported to users of single-spa
451
452 function mountRootParcel() {
453 return mountParcel.apply(rootParcels, arguments);
454 }
455 function mountParcel(config, customProps) {
456 var owningAppOrParcel = this; // Validate inputs
457
458 if (!config || _typeof(config) !== "object" && typeof config !== "function") {
459 throw Error(formatErrorMessage(2, "Cannot mount parcel without a config object or config loading function"));
460 }
461
462 if (config.name && typeof config.name !== "string") {
463 throw Error(formatErrorMessage(3, "Parcel name must be a string, if provided. Was given ".concat(_typeof(config.name)), _typeof(config.name)));
464 }
465
466 if (_typeof(customProps) !== "object") {
467 throw Error(formatErrorMessage(4, "Parcel ".concat(name, " has invalid customProps -- must be an object but was given ").concat(_typeof(customProps)), name, _typeof(customProps)));
468 }
469
470 if (!customProps.domElement) {
471 throw Error(formatErrorMessage(5, "Parcel ".concat(name, " cannot be mounted without a domElement provided as a prop"), name));
472 }
473
474 var id = parcelCount++;
475 var passedConfigLoadingFunction = typeof config === "function";
476 var configLoadingFunction = passedConfigLoadingFunction ? config : function () {
477 return Promise.resolve(config);
478 }; // Internal representation
479
480 var parcel = {
481 id: id,
482 parcels: {},
483 status: passedConfigLoadingFunction ? LOADING_SOURCE_CODE : NOT_BOOTSTRAPPED,
484 customProps: customProps,
485 parentName: toName(owningAppOrParcel),
486 unmountThisParcel: function unmountThisParcel() {
487 if (parcel.status !== MOUNTED) {
488 throw Error(formatErrorMessage(6, "Cannot unmount parcel '".concat(name, "' -- it is in a ").concat(parcel.status, " status"), name, parcel.status));
489 }
490
491 return toUnmountPromise(parcel, true).then(function (value) {
492 if (parcel.parentName) {
493 delete owningAppOrParcel.parcels[parcel.id];
494 }
495
496 return value;
497 }).then(function (value) {
498 resolveUnmount(value);
499 return value;
500 }).catch(function (err) {
501 parcel.status = SKIP_BECAUSE_BROKEN;
502 rejectUnmount(err);
503 throw err;
504 });
505 }
506 }; // We return an external representation
507
508 var externalRepresentation; // Add to owning app or parcel
509
510 owningAppOrParcel.parcels[id] = parcel;
511 var loadPromise = configLoadingFunction();
512
513 if (!loadPromise || typeof loadPromise.then !== "function") {
514 throw Error(formatErrorMessage(7, "When mounting a parcel, the config loading function must return a promise that resolves with the parcel config"));
515 }
516
517 loadPromise = loadPromise.then(function (config) {
518 if (!config) {
519 throw Error(formatErrorMessage(8, "When mounting a parcel, the config loading function returned a promise that did not resolve with a parcel config"));
520 }
521
522 var name = config.name || "parcel-".concat(id);
523
524 if (!validLifecycleFn(config.bootstrap)) {
525 throw Error(formatErrorMessage(9, "Parcel ".concat(name, " must have a valid bootstrap function"), name));
526 }
527
528 if (!validLifecycleFn(config.mount)) {
529 throw Error(formatErrorMessage(10, "Parcel ".concat(name, " must have a valid mount function"), name));
530 }
531
532 if (!validLifecycleFn(config.unmount)) {
533 throw Error(formatErrorMessage(11, "Parcel ".concat(name, " must have a valid unmount function"), name));
534 }
535
536 if (config.update && !validLifecycleFn(config.update)) {
537 throw Error(formatErrorMessage(12, "Parcel ".concat(name, " provided an invalid update function"), name));
538 }
539
540 var bootstrap = flattenFnArray(config, "bootstrap");
541 var mount = flattenFnArray(config, "mount");
542 var unmount = flattenFnArray(config, "unmount");
543 parcel.status = NOT_BOOTSTRAPPED;
544 parcel.name = name;
545 parcel.bootstrap = bootstrap;
546 parcel.mount = mount;
547 parcel.unmount = unmount;
548 parcel.timeouts = ensureValidAppTimeouts(config.timeouts);
549
550 if (config.update) {
551 parcel.update = flattenFnArray(config, "update");
552
553 externalRepresentation.update = function (customProps) {
554 parcel.customProps = customProps;
555 return promiseWithoutReturnValue(toUpdatePromise(parcel));
556 };
557 }
558 }); // Start bootstrapping and mounting
559 // The .then() causes the work to be put on the event loop instead of happening immediately
560
561 var bootstrapPromise = loadPromise.then(function () {
562 return toBootstrapPromise(parcel, true);
563 });
564 var mountPromise = bootstrapPromise.then(function () {
565 return toMountPromise(parcel, true);
566 });
567 var resolveUnmount, rejectUnmount;
568 var unmountPromise = new Promise(function (resolve, reject) {
569 resolveUnmount = resolve;
570 rejectUnmount = reject;
571 });
572 externalRepresentation = {
573 mount: function mount() {
574 return promiseWithoutReturnValue(Promise.resolve().then(function () {
575 if (parcel.status !== NOT_MOUNTED) {
576 throw Error(formatErrorMessage(13, "Cannot mount parcel '".concat(name, "' -- it is in a ").concat(parcel.status, " status"), name, parcel.status));
577 } // Add to owning app or parcel
578
579
580 owningAppOrParcel.parcels[id] = parcel;
581 return toMountPromise(parcel);
582 }));
583 },
584 unmount: function unmount() {
585 return promiseWithoutReturnValue(parcel.unmountThisParcel());
586 },
587 getStatus: function getStatus() {
588 return parcel.status;
589 },
590 loadPromise: promiseWithoutReturnValue(loadPromise),
591 bootstrapPromise: promiseWithoutReturnValue(bootstrapPromise),
592 mountPromise: promiseWithoutReturnValue(mountPromise),
593 unmountPromise: promiseWithoutReturnValue(unmountPromise)
594 };
595 return externalRepresentation;
596 }
597
598 function promiseWithoutReturnValue(promise) {
599 return promise.then(function () {
600 return null;
601 });
602 }
603
604 function getProps(appOrParcel) {
605 var result = assign({}, appOrParcel.customProps, {
606 name: toName(appOrParcel),
607 mountParcel: mountParcel.bind(appOrParcel),
608 singleSpa: singleSpa
609 });
610
611 if (isParcel(appOrParcel)) {
612 result.unmountSelf = appOrParcel.unmountThisParcel;
613 }
614
615 return result;
616 }
617
618 var defaultWarningMillis = 1000;
619 var globalTimeoutConfig = {
620 bootstrap: {
621 millis: 4000,
622 dieOnTimeout: false,
623 warningMillis: defaultWarningMillis
624 },
625 mount: {
626 millis: 3000,
627 dieOnTimeout: false,
628 warningMillis: defaultWarningMillis
629 },
630 unmount: {
631 millis: 3000,
632 dieOnTimeout: false,
633 warningMillis: defaultWarningMillis
634 },
635 unload: {
636 millis: 3000,
637 dieOnTimeout: false,
638 warningMillis: defaultWarningMillis
639 },
640 update: {
641 millis: 3000,
642 dieOnTimeout: false,
643 warningMillis: defaultWarningMillis
644 }
645 };
646 function setBootstrapMaxTime(time, dieOnTimeout, warningMillis) {
647 if (typeof time !== "number" || time <= 0) {
648 throw Error(formatErrorMessage(16, "bootstrap max time must be a positive integer number of milliseconds"));
649 }
650
651 globalTimeoutConfig.bootstrap = {
652 millis: time,
653 dieOnTimeout: dieOnTimeout,
654 warningMillis: warningMillis || defaultWarningMillis
655 };
656 }
657 function setMountMaxTime(time, dieOnTimeout, warningMillis) {
658 if (typeof time !== "number" || time <= 0) {
659 throw Error(formatErrorMessage(17, "mount max time must be a positive integer number of milliseconds"));
660 }
661
662 globalTimeoutConfig.mount = {
663 millis: time,
664 dieOnTimeout: dieOnTimeout,
665 warningMillis: warningMillis || defaultWarningMillis
666 };
667 }
668 function setUnmountMaxTime(time, dieOnTimeout, warningMillis) {
669 if (typeof time !== "number" || time <= 0) {
670 throw Error(formatErrorMessage(18, "unmount max time must be a positive integer number of milliseconds"));
671 }
672
673 globalTimeoutConfig.unmount = {
674 millis: time,
675 dieOnTimeout: dieOnTimeout,
676 warningMillis: warningMillis || defaultWarningMillis
677 };
678 }
679 function setUnloadMaxTime(time, dieOnTimeout, warningMillis) {
680 if (typeof time !== "number" || time <= 0) {
681 throw Error(formatErrorMessage(19, "unload max time must be a positive integer number of milliseconds"));
682 }
683
684 globalTimeoutConfig.unload = {
685 millis: time,
686 dieOnTimeout: dieOnTimeout,
687 warningMillis: warningMillis || defaultWarningMillis
688 };
689 }
690 function reasonableTime(appOrParcel, lifecycle) {
691 var timeoutConfig = appOrParcel.timeouts[lifecycle];
692 var warningPeriod = timeoutConfig.warningMillis;
693 var type = objectType(appOrParcel);
694 return new Promise(function (resolve, reject) {
695 var finished = false;
696 var errored = false;
697 appOrParcel[lifecycle](getProps(appOrParcel)).then(function (val) {
698 finished = true;
699 resolve(val);
700 }).catch(function (val) {
701 finished = true;
702 reject(val);
703 });
704 setTimeout(function () {
705 return maybeTimingOut(1);
706 }, warningPeriod);
707 setTimeout(function () {
708 return maybeTimingOut(true);
709 }, timeoutConfig.millis);
710 var errMsg = formatErrorMessage(31, "Lifecycle function ".concat(lifecycle, " for ").concat(type, " ").concat(toName(appOrParcel), " lifecycle did not resolve or reject for ").concat(timeoutConfig.millis, " ms."), lifecycle, type, toName(appOrParcel), timeoutConfig.millis);
711
712 function maybeTimingOut(shouldError) {
713 if (!finished) {
714 if (shouldError === true) {
715 errored = true;
716
717 if (timeoutConfig.dieOnTimeout) {
718 reject(Error(errMsg));
719 } else {
720 console.error(errMsg); //don't resolve or reject, we're waiting this one out
721 }
722 } else if (!errored) {
723 var numWarnings = shouldError;
724 var numMillis = numWarnings * warningPeriod;
725 console.warn(errMsg);
726
727 if (numMillis + warningPeriod < timeoutConfig.millis) {
728 setTimeout(function () {
729 return maybeTimingOut(numWarnings + 1);
730 }, warningPeriod);
731 }
732 }
733 }
734 }
735 });
736 }
737 function ensureValidAppTimeouts(timeouts) {
738 var result = {};
739
740 for (var key in globalTimeoutConfig) {
741 result[key] = assign({}, globalTimeoutConfig[key], timeouts && timeouts[key] || {});
742 }
743
744 return result;
745 }
746
747 function toLoadPromise(app) {
748 return Promise.resolve().then(function () {
749 if (app.status !== NOT_LOADED && app.status !== LOAD_ERROR) {
750 return app;
751 }
752
753 app.status = LOADING_SOURCE_CODE;
754 var appOpts, isUserErr;
755 return Promise.resolve().then(function () {
756 var loadPromise = app.loadImpl(getProps(app));
757
758 if (!smellsLikeAPromise(loadPromise)) {
759 // The name of the app will be prepended to this error message inside of the handleAppError function
760 isUserErr = true;
761 throw Error(formatErrorMessage(33, "single-spa loading function did not return a promise. Check the second argument to registerApplication('".concat(toName(app), "', loadingFunction, activityFunction)"), toName(app)));
762 }
763
764 return loadPromise.then(function (val) {
765 app.loadErrorTime = null;
766 appOpts = val;
767 var validationErrMessage, validationErrCode;
768
769 if (_typeof(appOpts) !== "object") {
770 validationErrCode = 34;
771
772 {
773 validationErrMessage = "does not export anything";
774 }
775 }
776
777 if (!validLifecycleFn(appOpts.bootstrap)) {
778 validationErrCode = 35;
779
780 {
781 validationErrMessage = "does not export a bootstrap function or array of functions";
782 }
783 }
784
785 if (!validLifecycleFn(appOpts.mount)) {
786 validationErrCode = 36;
787
788 {
789 validationErrMessage = "does not export a bootstrap function or array of functions";
790 }
791 }
792
793 if (!validLifecycleFn(appOpts.unmount)) {
794 validationErrCode = 37;
795
796 {
797 validationErrMessage = "does not export a bootstrap function or array of functions";
798 }
799 }
800
801 var type = objectType(appOpts);
802
803 if (validationErrCode) {
804 var appOptsStr;
805
806 try {
807 appOptsStr = JSON.stringify(appOpts);
808 } catch (_unused) {}
809
810 console.error(formatErrorMessage(validationErrCode, "The loading function for single-spa ".concat(type, " '").concat(toName(app), "' resolved with the following, which does not have bootstrap, mount, and unmount functions"), type, toName(app), appOptsStr), appOpts);
811 handleAppError(validationErrMessage, app);
812 app.status = SKIP_BECAUSE_BROKEN;
813 return app;
814 }
815
816 if (appOpts.devtools && appOpts.devtools.overlays) {
817 app.devtools.overlays = assign({}, app.devtools.overlays, appOpts.devtools.overlays);
818 }
819
820 app.status = NOT_BOOTSTRAPPED;
821 app.bootstrap = flattenFnArray(appOpts, "bootstrap");
822 app.mount = flattenFnArray(appOpts, "mount");
823 app.unmount = flattenFnArray(appOpts, "unmount");
824 app.unload = flattenFnArray(appOpts, "unload");
825 app.timeouts = ensureValidAppTimeouts(appOpts.timeouts);
826 return app;
827 });
828 }).catch(function (err) {
829 handleAppError(err, app);
830
831 if (isUserErr) {
832 app.status = SKIP_BECAUSE_BROKEN;
833 } else {
834 app.status = LOAD_ERROR;
835 app.loadErrorTime = new Date().getTime();
836 }
837
838 return app;
839 });
840 });
841 }
842
843 /* We capture navigation event listeners so that we can make sure
844 * that application navigation listeners are not called until
845 * single-spa has ensured that the correct applications are
846 * unmounted and mounted.
847 */
848
849 var capturedEventListeners = {
850 hashchange: [],
851 popstate: []
852 };
853 var routingEventsListeningTo = ["hashchange", "popstate"];
854 function navigateToUrl(obj) {
855 var url;
856
857 if (typeof obj === "string") {
858 url = obj;
859 } else if (this && this.href) {
860 url = this.href;
861 } else if (obj && obj.currentTarget && obj.currentTarget.href && obj.preventDefault) {
862 url = obj.currentTarget.href;
863 obj.preventDefault();
864 } else {
865 throw Error(formatErrorMessage(14, "singleSpaNavigate/navigateToUrl must be either called with a string url, with an <a> tag as its context, or with an event whose currentTarget is an <a> tag"));
866 }
867
868 var current = parseUri(window.location.href);
869 var destination = parseUri(url);
870
871 if (url.indexOf("#") === 0) {
872 window.location.hash = destination.hash;
873 } else if (current.host !== destination.host && destination.host) {
874 {
875 window.location.href = url;
876 }
877 } else if (destination.pathname === current.pathname && destination.search === current.pathname) {
878 window.location.hash = destination.hash;
879 } else {
880 // different path, host, or query params
881 window.history.pushState(null, null, url);
882 }
883 }
884 function callCapturedEventListeners(eventArguments) {
885 var _this = this;
886
887 if (eventArguments) {
888 var eventType = eventArguments[0].type;
889
890 if (routingEventsListeningTo.indexOf(eventType) >= 0) {
891 capturedEventListeners[eventType].forEach(function (listener) {
892 try {
893 // The error thrown by application event listener should not break single-spa down.
894 // Just like https://github.com/single-spa/single-spa/blob/85f5042dff960e40936f3a5069d56fc9477fac04/src/navigation/reroute.js#L140-L146 did
895 listener.apply(_this, eventArguments);
896 } catch (e) {
897 setTimeout(function () {
898 throw e;
899 });
900 }
901 });
902 }
903 }
904 }
905
906 function urlReroute() {
907 reroute([], arguments);
908 } // We will trigger an app change for any routing events.
909
910
911 window.addEventListener("hashchange", urlReroute);
912 window.addEventListener("popstate", urlReroute); // Monkeypatch addEventListener so that we can ensure correct timing
913
914 var originalAddEventListener = window.addEventListener;
915 var originalRemoveEventListener = window.removeEventListener;
916
917 window.addEventListener = function (eventName, fn) {
918 if (typeof fn === "function") {
919 if (routingEventsListeningTo.indexOf(eventName) >= 0 && !find(capturedEventListeners[eventName], function (listener) {
920 return listener === fn;
921 })) {
922 capturedEventListeners[eventName].push(fn);
923 return;
924 }
925 }
926
927 return originalAddEventListener.apply(this, arguments);
928 };
929
930 window.removeEventListener = function (eventName, listenerFn) {
931 if (typeof listenerFn === "function") {
932 if (routingEventsListeningTo.indexOf(eventName) >= 0) {
933 capturedEventListeners[eventName] = capturedEventListeners[eventName].filter(function (fn) {
934 return fn !== listenerFn;
935 });
936 return;
937 }
938 }
939
940 return originalRemoveEventListener.apply(this, arguments);
941 };
942
943 var originalPushState = window.history.pushState;
944
945 window.history.pushState = function (state) {
946 var result = originalPushState.apply(this, arguments);
947 urlReroute(createPopStateEvent(state));
948 return result;
949 };
950
951 var originalReplaceState = window.history.replaceState;
952
953 window.history.replaceState = function (state) {
954 var result = originalReplaceState.apply(this, arguments);
955 urlReroute(createPopStateEvent(state));
956 return result;
957 };
958
959 function createPopStateEvent(state) {
960 // https://github.com/single-spa/single-spa/issues/224 and https://github.com/single-spa/single-spa-angular/issues/49
961 // We need a popstate event even though the browser doesn't do one by default when you call replaceState, so that
962 // all the applications can reroute.
963 try {
964 return new PopStateEvent("popstate", {
965 state: state
966 });
967 } catch (err) {
968 // IE 11 compatibility https://github.com/single-spa/single-spa/issues/299
969 // https://docs.microsoft.com/en-us/openspecs/ie_standards/ms-html5e/bd560f47-b349-4d2c-baa8-f1560fb489dd
970 var evt = document.createEvent("PopStateEvent");
971 evt.initPopStateEvent("popstate", false, false, state);
972 return evt;
973 }
974 }
975 /* For convenience in `onclick` attributes, we expose a global function for navigating to
976 * whatever an <a> tag's href is.
977 */
978
979
980 window.singleSpaNavigate = navigateToUrl;
981
982 function parseUri(str) {
983 var anchor = document.createElement("a");
984 anchor.href = str;
985 return anchor;
986 }
987
988 var hasInitialized = false;
989 function ensureJQuerySupport() {
990 var jQuery = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window.jQuery;
991
992 if (!jQuery) {
993 if (window.$ && window.$.fn && window.$.fn.jquery) {
994 jQuery = window.$;
995 }
996 }
997
998 if (jQuery && !hasInitialized) {
999 var originalJQueryOn = jQuery.fn.on;
1000 var originalJQueryOff = jQuery.fn.off;
1001
1002 jQuery.fn.on = function (eventString, fn) {
1003 return captureRoutingEvents.call(this, originalJQueryOn, window.addEventListener, eventString, fn, arguments);
1004 };
1005
1006 jQuery.fn.off = function (eventString, fn) {
1007 return captureRoutingEvents.call(this, originalJQueryOff, window.removeEventListener, eventString, fn, arguments);
1008 };
1009
1010 hasInitialized = true;
1011 }
1012 }
1013
1014 function captureRoutingEvents(originalJQueryFunction, nativeFunctionToCall, eventString, fn, originalArgs) {
1015 if (typeof eventString !== "string") {
1016 return originalJQueryFunction.apply(this, originalArgs);
1017 }
1018
1019 var eventNames = eventString.split(/\s+/);
1020 eventNames.forEach(function (eventName) {
1021 if (routingEventsListeningTo.indexOf(eventName) >= 0) {
1022 nativeFunctionToCall(eventName, fn);
1023 eventString = eventString.replace(eventName, "");
1024 }
1025 });
1026
1027 if (eventString.trim() === "") {
1028 return this;
1029 } else {
1030 return originalJQueryFunction.apply(this, originalArgs);
1031 }
1032 }
1033
1034 var appsToUnload = {};
1035 function toUnloadPromise(app) {
1036 return Promise.resolve().then(function () {
1037 var unloadInfo = appsToUnload[toName(app)];
1038
1039 if (!unloadInfo) {
1040 /* No one has called unloadApplication for this app,
1041 */
1042 return app;
1043 }
1044
1045 if (app.status === NOT_LOADED) {
1046 /* This app is already unloaded. We just need to clean up
1047 * anything that still thinks we need to unload the app.
1048 */
1049 finishUnloadingApp(app, unloadInfo);
1050 return app;
1051 }
1052
1053 if (app.status === UNLOADING) {
1054 /* Both unloadApplication and reroute want to unload this app.
1055 * It only needs to be done once, though.
1056 */
1057 return unloadInfo.promise.then(function () {
1058 return app;
1059 });
1060 }
1061
1062 if (app.status !== NOT_MOUNTED) {
1063 /* The app cannot be unloaded until it is unmounted.
1064 */
1065 return app;
1066 }
1067
1068 app.status = UNLOADING;
1069 return reasonableTime(app, "unload").then(function () {
1070 finishUnloadingApp(app, unloadInfo);
1071 return app;
1072 }).catch(function (err) {
1073 errorUnloadingApp(app, unloadInfo, err);
1074 return app;
1075 });
1076 });
1077 }
1078
1079 function finishUnloadingApp(app, unloadInfo) {
1080 delete appsToUnload[toName(app)]; // Unloaded apps don't have lifecycles
1081
1082 delete app.bootstrap;
1083 delete app.mount;
1084 delete app.unmount;
1085 delete app.unload;
1086 app.status = NOT_LOADED;
1087 /* resolve the promise of whoever called unloadApplication.
1088 * This should be done after all other cleanup/bookkeeping
1089 */
1090
1091 unloadInfo.resolve();
1092 }
1093
1094 function errorUnloadingApp(app, unloadInfo, err) {
1095 delete appsToUnload[toName(app)]; // Unloaded apps don't have lifecycles
1096
1097 delete app.bootstrap;
1098 delete app.mount;
1099 delete app.unmount;
1100 delete app.unload;
1101 handleAppError(err, app);
1102 app.status = SKIP_BECAUSE_BROKEN;
1103 unloadInfo.reject(err);
1104 }
1105
1106 function addAppToUnload(app, promiseGetter, resolve, reject) {
1107 appsToUnload[toName(app)] = {
1108 app: app,
1109 resolve: resolve,
1110 reject: reject
1111 };
1112 Object.defineProperty(appsToUnload[toName(app)], "promise", {
1113 get: promiseGetter
1114 });
1115 }
1116 function getAppUnloadInfo(appName) {
1117 return appsToUnload[appName];
1118 }
1119 function getAppsToUnload() {
1120 return Object.keys(appsToUnload).map(function (appName) {
1121 return appsToUnload[appName].app;
1122 }).filter(isntActive);
1123 }
1124
1125 var apps = [];
1126 function getMountedApps() {
1127 return apps.filter(isActive).map(toName);
1128 }
1129 function getAppNames() {
1130 return apps.map(toName);
1131 } // used in devtools, not (currently) exposed as a single-spa API
1132
1133 function getRawAppData() {
1134 return [].concat(apps);
1135 }
1136 function getAppStatus(appName) {
1137 var app = find(apps, function (app) {
1138 return toName(app) === appName;
1139 });
1140 return app ? app.status : null;
1141 }
1142 function registerApplication(appName, applicationOrLoadingFn, activityFn) {
1143 var customProps = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
1144 if (typeof appName !== "string" || appName.length === 0) throw Error(formatErrorMessage(20, "The first argument to registerApplication must be a non-empty string 'appName'"));
1145 if (getAppNames().indexOf(appName) !== -1) throw Error(formatErrorMessage(21, "There is already an app declared with name ".concat(appName), appName));
1146 if (_typeof(customProps) !== "object" || Array.isArray(customProps)) throw Error(formatErrorMessage(22, "customProps must be an object"));
1147 if (!applicationOrLoadingFn) throw Error(formatErrorMessage(23, "The application or loading function is required"));
1148 var loadImpl;
1149
1150 if (typeof applicationOrLoadingFn !== "function") {
1151 // applicationOrLoadingFn is an application
1152 loadImpl = function loadImpl() {
1153 return Promise.resolve(applicationOrLoadingFn);
1154 };
1155 } else {
1156 // applicationOrLoadingFn is a loadingFn
1157 loadImpl = applicationOrLoadingFn;
1158 }
1159
1160 if (typeof activityFn !== "function") throw Error(formatErrorMessage(24, "The activityFunction argument must be a function"));
1161 apps.push({
1162 loadErrorTime: null,
1163 name: appName,
1164 loadImpl: loadImpl,
1165 activeWhen: activityFn,
1166 status: NOT_LOADED,
1167 parcels: {},
1168 devtools: {
1169 overlays: {
1170 options: {},
1171 selectors: []
1172 }
1173 },
1174 customProps: customProps
1175 });
1176 ensureJQuerySupport();
1177 reroute();
1178 }
1179 function checkActivityFunctions(location) {
1180 return apps.filter(function (app) {
1181 return app.activeWhen(location);
1182 }).map(toName);
1183 }
1184 function getAppsToLoad() {
1185 return apps.filter(notSkipped).filter(withoutLoadErrors).filter(isntLoaded).filter(shouldBeActive);
1186 }
1187 function getAppsToUnmount() {
1188 return apps.filter(notSkipped).filter(isActive).filter(shouldntBeActive);
1189 }
1190 function getAppsToMount() {
1191 return apps.filter(notSkipped).filter(isntActive).filter(isLoaded).filter(shouldBeActive);
1192 }
1193 function unregisterApplication(appName) {
1194 if (!apps.find(function (app) {
1195 return toName(app) === appName;
1196 })) {
1197 throw Error(formatErrorMessage(25, "Cannot unregister application '".concat(appName, "' because no such application has been registered"), appName));
1198 }
1199
1200 return unloadApplication(appName).then(function () {
1201 var appIndex = apps.findIndex(function (app) {
1202 return toName(app) === appName;
1203 });
1204 apps.splice(appIndex, 1);
1205 });
1206 }
1207 function unloadApplication(appName) {
1208 var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
1209 waitForUnmount: false
1210 };
1211
1212 if (typeof appName !== "string") {
1213 throw Error(formatErrorMessage(26, "unloadApplication requires a string 'appName'"));
1214 }
1215
1216 var app = find(apps, function (App) {
1217 return toName(App) === appName;
1218 });
1219
1220 if (!app) {
1221 throw Error(formatErrorMessage(27, "Could not unload application '".concat(appName, "' because no such application has been registered"), appName));
1222 }
1223
1224 var appUnloadInfo = getAppUnloadInfo(toName(app));
1225
1226 if (opts && opts.waitForUnmount) {
1227 // We need to wait for unmount before unloading the app
1228 if (appUnloadInfo) {
1229 // Someone else is already waiting for this, too
1230 return appUnloadInfo.promise;
1231 } else {
1232 // We're the first ones wanting the app to be resolved.
1233 var promise = new Promise(function (resolve, reject) {
1234 addAppToUnload(app, function () {
1235 return promise;
1236 }, resolve, reject);
1237 });
1238 return promise;
1239 }
1240 } else {
1241 /* We should unmount the app, unload it, and remount it immediately.
1242 */
1243 var resultPromise;
1244
1245 if (appUnloadInfo) {
1246 // Someone else is already waiting for this app to unload
1247 resultPromise = appUnloadInfo.promise;
1248 immediatelyUnloadApp(app, appUnloadInfo.resolve, appUnloadInfo.reject);
1249 } else {
1250 // We're the first ones wanting the app to be resolved.
1251 resultPromise = new Promise(function (resolve, reject) {
1252 addAppToUnload(app, function () {
1253 return resultPromise;
1254 }, resolve, reject);
1255 immediatelyUnloadApp(app, resolve, reject);
1256 });
1257 }
1258
1259 return resultPromise;
1260 }
1261 }
1262
1263 function immediatelyUnloadApp(app, resolve, reject) {
1264 toUnmountPromise(app).then(toUnloadPromise).then(function () {
1265 resolve();
1266 setTimeout(function () {
1267 // reroute, but the unload promise is done
1268 reroute();
1269 });
1270 }).catch(reject);
1271 }
1272
1273 var appChangeUnderway = false,
1274 peopleWaitingOnAppChange = [];
1275 function triggerAppChange() {
1276 // Call reroute with no arguments, intentionally
1277 return reroute();
1278 }
1279 function reroute() {
1280 var pendingPromises = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
1281 var eventArguments = arguments.length > 1 ? arguments[1] : undefined;
1282
1283 if (appChangeUnderway) {
1284 return new Promise(function (resolve, reject) {
1285 peopleWaitingOnAppChange.push({
1286 resolve: resolve,
1287 reject: reject,
1288 eventArguments: eventArguments
1289 });
1290 });
1291 }
1292
1293 appChangeUnderway = true;
1294 var wasNoOp = true;
1295
1296 if (isStarted()) {
1297 return performAppChanges();
1298 } else {
1299 return loadApps();
1300 }
1301
1302 function loadApps() {
1303 return Promise.resolve().then(function () {
1304 var loadPromises = getAppsToLoad().map(toLoadPromise);
1305
1306 if (loadPromises.length > 0) {
1307 wasNoOp = false;
1308 }
1309
1310 return Promise.all(loadPromises).then(finishUpAndReturn).catch(function (err) {
1311 callAllEventListeners();
1312 throw err;
1313 });
1314 });
1315 }
1316
1317 function performAppChanges() {
1318 return Promise.resolve().then(function () {
1319 window.dispatchEvent(new customEvent("single-spa:before-routing-event", getCustomEventDetail()));
1320 var unloadPromises = getAppsToUnload().map(toUnloadPromise);
1321 var unmountUnloadPromises = getAppsToUnmount().map(toUnmountPromise).map(function (unmountPromise) {
1322 return unmountPromise.then(toUnloadPromise);
1323 });
1324 var allUnmountPromises = unmountUnloadPromises.concat(unloadPromises);
1325
1326 if (allUnmountPromises.length > 0) {
1327 wasNoOp = false;
1328 }
1329
1330 var unmountAllPromise = Promise.all(allUnmountPromises);
1331 var appsToLoad = getAppsToLoad();
1332 /* We load and bootstrap apps while other apps are unmounting, but we
1333 * wait to mount the app until all apps are finishing unmounting
1334 */
1335
1336 var loadThenMountPromises = appsToLoad.map(function (app) {
1337 return toLoadPromise(app).then(toBootstrapPromise).then(function (app) {
1338 return unmountAllPromise.then(function () {
1339 return toMountPromise(app);
1340 });
1341 });
1342 });
1343
1344 if (loadThenMountPromises.length > 0) {
1345 wasNoOp = false;
1346 }
1347 /* These are the apps that are already bootstrapped and just need
1348 * to be mounted. They each wait for all unmounting apps to finish up
1349 * before they mount.
1350 */
1351
1352
1353 var mountPromises = getAppsToMount().filter(function (appToMount) {
1354 return appsToLoad.indexOf(appToMount) < 0;
1355 }).map(function (appToMount) {
1356 return toBootstrapPromise(appToMount).then(function () {
1357 return unmountAllPromise;
1358 }).then(function () {
1359 return toMountPromise(appToMount);
1360 });
1361 });
1362
1363 if (mountPromises.length > 0) {
1364 wasNoOp = false;
1365 }
1366
1367 return unmountAllPromise.catch(function (err) {
1368 callAllEventListeners();
1369 throw err;
1370 }).then(function () {
1371 /* Now that the apps that needed to be unmounted are unmounted, their DOM navigation
1372 * events (like hashchange or popstate) should have been cleaned up. So it's safe
1373 * to let the remaining captured event listeners to handle about the DOM event.
1374 */
1375 callAllEventListeners();
1376 return Promise.all(loadThenMountPromises.concat(mountPromises)).catch(function (err) {
1377 pendingPromises.forEach(function (promise) {
1378 return promise.reject(err);
1379 });
1380 throw err;
1381 }).then(function () {
1382 return finishUpAndReturn(false);
1383 });
1384 });
1385 });
1386 }
1387
1388 function finishUpAndReturn() {
1389 var callEventListeners = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
1390 var returnValue = getMountedApps();
1391
1392 if (callEventListeners) {
1393 callAllEventListeners();
1394 }
1395
1396 pendingPromises.forEach(function (promise) {
1397 return promise.resolve(returnValue);
1398 });
1399
1400 try {
1401 var appChangeEventName = wasNoOp ? "single-spa:no-app-change" : "single-spa:app-change";
1402 window.dispatchEvent(new customEvent(appChangeEventName, getCustomEventDetail()));
1403 window.dispatchEvent(new customEvent("single-spa:routing-event", getCustomEventDetail()));
1404 } catch (err) {
1405 /* We use a setTimeout because if someone else's event handler throws an error, single-spa
1406 * needs to carry on. If a listener to the event throws an error, it's their own fault, not
1407 * single-spa's.
1408 */
1409 setTimeout(function () {
1410 throw err;
1411 });
1412 }
1413 /* Setting this allows for subsequent calls to reroute() to actually perform
1414 * a reroute instead of just getting queued behind the current reroute call.
1415 * We want to do this after the mounting/unmounting is done but before we
1416 * resolve the promise for the `reroute` function.
1417 */
1418
1419
1420 appChangeUnderway = false;
1421
1422 if (peopleWaitingOnAppChange.length > 0) {
1423 /* While we were rerouting, someone else triggered another reroute that got queued.
1424 * So we need reroute again.
1425 */
1426 var nextPendingPromises = peopleWaitingOnAppChange;
1427 peopleWaitingOnAppChange = [];
1428 reroute(nextPendingPromises);
1429 }
1430
1431 return returnValue;
1432 }
1433 /* We need to call all event listeners that have been delayed because they were
1434 * waiting on single-spa. This includes haschange and popstate events for both
1435 * the current run of performAppChanges(), but also all of the queued event listeners.
1436 * We want to call the listeners in the same order as if they had not been delayed by
1437 * single-spa, which means queued ones first and then the most recent one.
1438 */
1439
1440
1441 function callAllEventListeners() {
1442 pendingPromises.forEach(function (pendingPromise) {
1443 callCapturedEventListeners(pendingPromise.eventArguments);
1444 });
1445 callCapturedEventListeners(eventArguments);
1446 }
1447
1448 function getCustomEventDetail() {
1449 var result = {
1450 detail: {}
1451 };
1452
1453 if (eventArguments && eventArguments[0]) {
1454 result.detail.originalEvent = eventArguments[0];
1455 }
1456
1457 return result;
1458 }
1459 }
1460
1461 var started = false;
1462 function start() {
1463 started = true;
1464 reroute();
1465 }
1466 function isStarted() {
1467 return started;
1468 }
1469 setTimeout(function () {
1470 if (!started) {
1471 console.warn(formatErrorMessage(1, "singleSpa.start() has not been called, 5000ms after single-spa was loaded. Before start() is called, apps can be declared and loaded, but not bootstrapped or mounted."));
1472 }
1473 }, 5000);
1474
1475 var devtools = {
1476 getRawAppData: getRawAppData,
1477 reroute: reroute,
1478 NOT_LOADED: NOT_LOADED,
1479 toLoadPromise: toLoadPromise,
1480 toBootstrapPromise: toBootstrapPromise,
1481 unregisterApplication: unregisterApplication
1482 };
1483
1484 if (window && window.__SINGLE_SPA_DEVTOOLS__) {
1485 window.__SINGLE_SPA_DEVTOOLS__.exposedMethods = devtools;
1486 }
1487
1488 }
1489 };
1490});
1491//# sourceMappingURL=single-spa.dev.js.map