UNPKG

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