UNPKG

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