UNPKG

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