UNPKG

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