single-spa
Advanced tools
Comparing version 4.0.0 to 4.0.1
# Registered applications | ||
A single-spa registered application is everything that a normal SPA is, except that it doesn't have an HTML page. | ||
In a single-spa world, your SPA contains many registered applications, where each has its own framework. | ||
registered applications have their own client-side routing and their own frameworks/libraries. | ||
They render their own html and have full freedom to do whatever they want, whenever they are *mounted*. | ||
The concept of being *mounted* refers to whether a registered application is putting content on the DOM or not. | ||
What determines if a registered application is mounted is its [activity function](/docs/single-spa-config.md#activity-function). | ||
Whenever a registered application is *not mounted*, it should remain completely dormant until mounted. | ||
## Creating a registered application | ||
To create a registered application, first | ||
[register the application with single-spa](/docs/single-spa-config.md#registering-applications). | ||
Once registered, the registered application must correctly implement **all** of the following lifecycle functions | ||
inside of its main entry point. | ||
## Registered application lifecycle | ||
During the course of a single-spa page, registered applications are loaded, initialized (bootstrapped), mounted, unmounted, and unloaded. | ||
single-spa provides hooks into each phase via `lifecycles`. | ||
A lifecycle function is a function or array of functions that single-spa will call on a registered application. | ||
Single-spa calls these by finding exported functions from the registered application's main file. | ||
Notes: | ||
- Lifecycle functions are called with a `props` argument, which is an object with a `name` string, a `mountParcel` [function]('/docs/parcel-api#mountParcel') and all other properties you want to pass from your root-application to your child app. See [Passing custom properties to applications](/docs/applications.md#passing-custom-properties-to-applications) | ||
- Implementing `bootstrap`, `mount`, and `unmount` is required. But implementing `unload` is optional. | ||
- Each lifecycle function must either return a `Promise` or be an `async function`. | ||
- If an array of functions is exported (instead of just one function), the functions will be called | ||
one-after-the-other, waiting for the resolution of one function's promise before calling the next. | ||
- If single-spa is [not started](/docs/single-spa-api.md#start), applications will be loaded, | ||
but will not be bootstrapped, mounted or unmounted. | ||
## Lifecyle props | ||
Lifecycle functions are called with a `props` argument, which is an object with some guaranteed information and also some custom information. | ||
Example: | ||
```js | ||
function bootstrap(props) { | ||
console.log(props) // will log appName, the singleSpa instance, and custom props | ||
return Promise.resolve() | ||
} | ||
``` | ||
#### Built-in props | ||
Each lifecycle function is guranteed to be called with the following props: | ||
- `appName`: The string name that was registered to single-spa. | ||
- `singleSpa`: A reference to the singleSpa instance, itself. This is intended to allow applications and helper libraries to call singleSpa | ||
APIs without having to import it. This is useful in situations where there are multiple webpack configs that are not set up to ensure | ||
that only one instance of singleSpa is loaded. | ||
#### Custom props | ||
In addition to the built-in props that are provided by single-spa, you may optionally specify custom props to be passed to an application. | ||
This is done by passing a fourth argument to registerApplication, which will be the customProps passed to each lifecycle method. | ||
Example: | ||
```js | ||
// root-application.js | ||
singleSpa.registerApplication('app1', () => {}, () => {}, {authToken: "d83jD63UdZ6RS6f70D0"}); | ||
``` | ||
```js | ||
// app1.js | ||
export function mount(props) { | ||
console.log(props.customProps.authToken); // do something with the common authToken in app1 | ||
return reactLifecycles.mount(props); | ||
} | ||
``` | ||
The usecases may include: | ||
- share common access Tokens with all child apps | ||
- pass down some initialization information like the rendering target | ||
- pass a reference to a common event bus so each app may talk to each other | ||
Note that when no customProps are provided during registration, `props.customProps` defaults to an empty object. | ||
### Lifecycle helpers | ||
Helper libraries that helps implement lifecycle functions for specific frameworks, libraries, and applications | ||
is available for many popular technologies. See [the ecosystem docs](/docs/single-spa-ecosystem.md) for details. | ||
### Load | ||
When registered applications are being lazily loaded, this refers to when the code for a registered application | ||
is fetched from the server and executed. This will happen once the registered application's [activity function](/docs/single-spa-config.md#activity-function) | ||
returns a truthy value for the first time. It is best practice to do as little as possible / nothing at all | ||
during `load`, but instead to wait until the bootstrap lifecycle function to do anything. | ||
If you need to do something during `load`, simply put the code into a registered application's main entry point, | ||
but not inside of an exported function. | ||
For example: | ||
```js | ||
console.log("The registered application has been loaded!"); | ||
export async function bootstrap(props) {...} | ||
export async function mount(props) {...} | ||
export async function unmount(props) {...} | ||
``` | ||
### Bootstrap | ||
This lifecycle function will be called once, right before the registered application is | ||
mounted for the first time. | ||
```js | ||
export function bootstrap(props) { | ||
return Promise | ||
.resolve() | ||
.then(() => { | ||
// This is where you do one-time initialization | ||
console.log('bootstrapped!') | ||
}); | ||
} | ||
``` | ||
### Mount | ||
This lifecycle function will be called whenever the registered application is not mounted, but its | ||
[activity function](/docs/single-spa-config.md#activity-function) returns a truthy value. When | ||
called, this function should look at the URL to determine the active route and then create | ||
DOM elements, DOM event listeners, etc. to render content to the user. Any subsequent routing | ||
events (such as `hashchange` and `popstate`) will **not** trigger more calls to `mount`, but | ||
instead should be handled by the application itself. | ||
```js | ||
export function mount(props) { | ||
return Promise | ||
.resolve() | ||
.then(() => { | ||
// This is where you tell a framework (e.g., React) to render some ui to the dom | ||
console.log('mounted!') | ||
}); | ||
} | ||
``` | ||
### Unmount | ||
This lifecycle function will be called whenever the registered application is mounted, but its | ||
[activity function](/docs/single-spa-config.md#activity-function) returns a falsy value. When | ||
called, this function should clean up all DOM elements, DOM event listeners, leaked memory, globals, | ||
observable subscriptions, etc. that were created at any point when the registered application was mounted. | ||
```js | ||
export function unmount(props) { | ||
return Promise | ||
.resolve() | ||
.then(() => { | ||
// This is where you tell a framework (e.g., React) to unrender some ui from the dom | ||
console.log('unmounted!'); | ||
}); | ||
} | ||
``` | ||
### Unload | ||
The `unload` lifecycle is an optionally implemented lifecycle function. It will be called whenever an application should be | ||
`unloaded`. This will not ever happen unless someone calls the [`unloadApplication`](/docs/single-spa-api.md#unload-application) API. | ||
If a registered application does not implement the unload lifecycle, then it assumed that unloading the app is a no-op. | ||
The purpose of the `unload` lifecycle is to perform logic right before a single-spa application is unloaded. Once | ||
the application is unloaded, the application status will be NOT_LOADED and the application will be re-bootstrapped. | ||
The motivation for `unload` was to implement the hot-loading of entire registered applications, but it is useful in other | ||
scenarios as well when you want to re-bootstrap applications, but perform some logic before applications are re-bootstrapped. | ||
```js | ||
export function unload(props) { | ||
return Promise | ||
.resolve() | ||
.then(() => { | ||
// This is where you would normally do whatever it is you need to hot reload the code. | ||
console.log('unloaded!'); | ||
}); | ||
} | ||
``` | ||
## Passing custom properties to applications | ||
Each lifecycle method has a `props` property which contains an object with a `name` string, a `mountParcel` [function]('/docs/parcel-api#mountParcel'), and all custom props that were passed to the application. | ||
The usecases may include: | ||
- share common access Tokens with all applications | ||
- pass down some initialization information like the rendering target | ||
- pass a reference to a common event bus so each app may talk to each other | ||
Here is a simple example on how to pass a authToken to a child app when the app is beeing mounted: | ||
```js | ||
// root-application.js | ||
singleSpa.registerApplication('app1', () => {}, () => {}, {authToken: "d83jD63UdZ6RS6f70D0"}); | ||
``` | ||
```js | ||
// app1.js | ||
export function mount(props) { | ||
console.log(props.authToken); // do something with the common authToken in app1 | ||
return reactLifecycles.mount(props); | ||
} | ||
``` | ||
## Timeouts | ||
By default, registered applications obey the [global dieOnTimeout configuration](/docs/single-spa-api.md#dieontimeout), | ||
but can override that behavior for their specific application. This is done by exporting a `timeouts` object | ||
from the main entry point of the registered application. Example: | ||
```js | ||
// app-1.main-entry.js | ||
export function bootstrap(props) {...} | ||
export function mount(props) {...} | ||
export function unmount(props) {...} | ||
export const timeouts = { | ||
bootstrap: { | ||
millis: 5000, | ||
dieOnTimeout: true, | ||
}, | ||
mount: { | ||
millis: 5000, | ||
dieOnTimeout: false, | ||
}, | ||
unmount: { | ||
millis: 5000, | ||
dieOnTimeout: true, | ||
}, | ||
unload: { | ||
millis: 5000, | ||
dieOnTimeout: true, | ||
}, | ||
}; | ||
``` | ||
## Transitioning between applications | ||
If you find yourself wanting to add transitions as applications are mounted and unmounted, then you'll probably want to tie into the `bootstrap`, `mount`, and `unmount` lifecycle methods. [This repo](https://github.com/frehner/singlespa-transitions) is a small proof-of-concept of how you can tie into these lifecycle methods to add transitions as your apps mount and unmount. | ||
Transitions for pages within a mounted application can be handled entirely by the application itself, for example using [react-transition-group](https://github.com/reactjs/react-transition-group) for React-based projects. | ||
This page has moved. Find ["Building Applications"](https://single-spa.js.org/docs/building-applications.html) on the single-spa website. |
# Migrating existing SPAs | ||
If you're interested in migrating existing SPAs into a single-spa, you'll | ||
need to do three things: | ||
1. Create a [single spa config](/docs/single-spa-config.md) | ||
1. [Convert your SPA or SPAs to be registered applications](#converting-spas-into-registered-applications) | ||
1. Adjust your html file so that your single spa config is the new boss in town. | ||
See [docs](/docs/single-spa-config.md#indexhtml-file). | ||
## Converting SPAs into registered applications | ||
Your existing SPAs, whether they be Angular, React, or something else, probably are | ||
not used to unmounting themselves from the DOM. Also, they probably have had the luxury | ||
of controlling the entire html page themselves, instead of being purely javascript applications | ||
that don't have sole control over `<script>` tags and `<link>` tags. So in order to convert them | ||
into single-spa registered applications, they will need to overcome those obstacles while implementing | ||
lifecycle functions. | ||
### (1) Implementing lifecycle functions | ||
See [the registered application lifecycle docs](/docs/applications.md) to see what you need to do. | ||
The hardest part will almost certainly be the `unmount` lifecycle, since most SPAs aren't accustomed | ||
to going dormant and unmounting themselves from the DOM. When implementing your lifecycle functions, first check out the [ecosystem](/docs/single-spa-ecosystem.md) | ||
docs before reinventing the wheel yourself. If that doesn't have everything you need, you'll have to make sure that your | ||
SPA can clean up its DOM, DOM event listeners (all of them, but *especially* hashchange and popstate), | ||
and memory. | ||
### (2) Getting the CSS, fonts, `<script>` dependencies to work | ||
Since existing SPAs are used to having an index.html file for their css, fonts, | ||
third party script-tags, etc., it's likely that you'll have to do some work | ||
to make sure all of those keep on working when your SPA becomes an html-less [ | ||
application](/docs/applications.md). It is best to try to put all that | ||
you can into the javascript bundle, but your escape hatch is to put the things | ||
you need into your [single spa config](/docs/single-spa-config.md). | ||
This page has moved. Find ["Migrating existing SPAs"](https://single-spa.js.org/docs/migrating-existing-spas.html) on the single-spa website. |
# Parcel API | ||
Most parcel methods will be called on the parcel itself, with the exception being around mounting. | ||
## mounting | ||
Both mount methods take a [parcelConfig](/docs/parcels.md#parcel-configuration) and [additional props](/docs/parcel-api.md#parcel-props). | ||
They both return a [parcel object](/docs/parcel-api.md#parcel-object). The parcel object contains all additional exposed methods. | ||
### Parcel Props | ||
When mounting a parcel the second argument is props, a javascript object of properties to be passed to the parcel. This object must have a domElement prop, which is the dom node that the parcel will mount into. | ||
```js | ||
const parcelProps = { | ||
customerId: 7, | ||
numberOfTasks: 42, | ||
domElement: document.createElement('div') | ||
} | ||
``` | ||
### mountParcel | ||
`applicationProps.mountParcel(parcelConfig, parcelProps)`. Each application is provided a mountParcel function. | ||
The main advantage to using an applications `mountParcel` function is that parcels mounted via an | ||
applications `mountParcel` will be automatically unmounted when the application is unmounted. | ||
### mountRootParcel | ||
The [mountRootParcel](/docs/single-spa-api.md#mountrootparcel) method will mount the parcel but `unmount` must be called manually. | ||
## Parcel Object | ||
The parcel object contains the following functions and methods: | ||
- [mount](/docs/parcel-api.md#mount) | ||
- [unmount](/docs/parcel-api.md#unmount) | ||
- [getStatus](/docs/parcel-api.md#getstatus) | ||
- [loadPromise](/docs/parcel-api.md#loadpromise) | ||
- [bootstrapPromise](/docs/parcel-api.md#bootstrappromise) | ||
- [mountPromise](/docs/parcel-api.md#mountpromise) | ||
- [unmountPromise](/docs/parcel-api.md#unmountpromise) | ||
### unmount | ||
`parcel.unmount()` returns a promise that resolves once the parcel is successfully unmounted. The promise may throw an error which needs to be handled. | ||
### mount | ||
`parcel.mount()` returns a promise that resolves once the parcel is successfully mounted. The promise can throw an error which needs to be handled. | ||
### getStatus | ||
`parcel.getStatus()` retuns a string of that parcels status. The string status is one of the following: | ||
- `NOT_BOOTSTRAPPED`: The parcel has not been bootstrapped | ||
- `BOOTSTRAPPING`: The parcel is bootstrapping but has not finished | ||
- `NOT_MOUNTED`: The parcel has bootstrapped, but is not mounted | ||
- `MOUNTED`: The parcel is currently active and mounted to the DOM | ||
- `UNMOUNTING`: The parcel is unmounting, but has not finished | ||
- `UPDATING`: The parcel is currently being updated, but has not finished | ||
- `SKIP_BECAUSE_BROKEN`: The parcel threw an error during bootstrap, mount, unmount, or update. Other parcels may continue normally, but this one will be skipped. | ||
### loadPromise | ||
`parcel.loadPromise()` returns a promise that will resolve once the parcel has been loaded. | ||
### bootstrapPromise | ||
`parcel.bootstrapPromise()` returns a promise that will resolve once the parcel has been bootstrapped. | ||
### mountPromise | ||
`parcel.mountPromise()` returns a promise that will resolve once the parcel has been mounted. This is helpful for knowing exactly when a parcel has been appended to the DOM | ||
### unmountPromise | ||
`parcel.unmountPromise()` returns a promise that will resolve once the parcel has been unmounted. | ||
This page has moved. Find the [Single-spa Parcels API](https://single-spa.js.org/docs/parcels-api.html) documentation on the single-spa website. |
# Parcels | ||
A single-spa parcel is a bundle of functionality (like an application or component) meant to be mounted manually | ||
by an application. Parcels use similar methodology as applications but are mounted by a manual function call rather than the activity function. | ||
A parcel can be as large as an application or as small as an component and written in | ||
any language as long as it exports the correct lifecycle events. In a single-spa world, your SPA contains | ||
many registered applications and potentially many parcels. Typically we recommend you mount a parcel within | ||
the context of an application because the parcel will be unmounted with the application. | ||
## Quick Example | ||
```js | ||
// The parcel implementation | ||
const parcelConfig = { | ||
bootstrap() { | ||
// one time initialization | ||
return Promise.resolve() | ||
}, | ||
mount() { | ||
// use a framework to create dom nodes and mount the parcel | ||
return Promise.resolve() | ||
}, | ||
unmount() { | ||
// use a framework to unmount dom nodes and perform other cleanup | ||
return Promise.resolve() | ||
} | ||
} | ||
// How to mount the parcel | ||
const domElement = document.getElementById('place-in-dom-to-mount-parcel') | ||
const parcelProps = {domElement, customProp1: 'foo'} | ||
const parcel = singleSpa.mountRootParcel(parcelConfig, parcelProps) | ||
// The parcel is being mounted. We can wait for it to finish with the mountPromise. | ||
parcel.mountPromise.then(() => { | ||
console.log('finished mounting parcel!') | ||
// If we want to re-render the parcel, we can call the update lifecycle method, which returns a promise | ||
parcelProps.customProp1 = 'bar' | ||
return parcel.update(parcelProps) | ||
}) | ||
.then(() => { | ||
// Call the unmount lifecycle when we need the parcel to unmount. This function also returns a promise | ||
return parcel.unmount() | ||
}) | ||
``` | ||
## Parcel configuration | ||
A parcel is just an object with 3 or 4 functions on it. When mounting a parcel, you can provided either the object itself or a loading function that asynchronously downloads the parcel object. | ||
Each function on a parcel object is a lifecycle method, which is a function that returns a promise. Parcels have three required lifecycle methods (bootstrap, mount, and unmount) and one optional lifecycle method (update). | ||
When implementing a parcel, it's strongly recommended that you use the [lifecycle helper methods](/docs/single-spa-ecosystem.md#help-for-frameworks). | ||
An example of a parcel written in React would look like this: | ||
```js | ||
// myParcel.js | ||
import React from 'react' | ||
import ReactDom from 'react-dom' | ||
import singleSpaReact from 'single-spa-react' | ||
import MyParcelComponent from './my-parcel-component.component.js' | ||
export const MyParcel = singleSpaReact({ | ||
React, | ||
ReactDom, | ||
rootComponent: MyParcelComponent | ||
}) | ||
// in this case singleSpaReact is taking our inputs and generating an object with the required lifecycles. | ||
``` | ||
Then to use the parcel you just created all you need to do is use the `Parcel` component provided in [single-spa-react](https://github.com/CanopyTax/single-spa-react#parcels) | ||
```jsx | ||
// mycomponent.js | ||
import { Parcel } from 'single-spa-react' | ||
import MyParcel from './myparcel.js' | ||
export class myComponent extends React.Component { | ||
render () { | ||
return ( | ||
<Parcel | ||
config={MyParcel} | ||
{ /* optional props */ } | ||
{ /* and any extra props you want here */ } | ||
/> | ||
) | ||
} | ||
} | ||
``` | ||
Note that in some cases the optional props are required [(see additional examples)](https://github.com/CanopyTax/single-spa-react#examples). | ||
## Parcel Lifecycles | ||
Start with [applications](/docs/applications.md#registered-application-lifecycle) to learn more about the functionality of single-spa's lifecycle methods. | ||
### Bootstrap | ||
This lifecycle function will be called once, right before the parcel is | ||
mounted for the first time. | ||
```js | ||
function bootstrap(props) { | ||
return Promise | ||
.resolve() | ||
.then(() => { | ||
// This is where you do one-time initialization | ||
console.log('bootstrapped!') | ||
}); | ||
} | ||
``` | ||
### Mount | ||
If the parcel is not mounted this lifecycle function is called when ever `mountParcel` is called. When | ||
called, this function should create DOM elements, DOM event listeners, etc. to render content to the user. | ||
```js | ||
function mount(props) { | ||
return Promise | ||
.resolve() | ||
.then(() => { | ||
// This is where you tell a framework (e.g., React) to render some ui to the dom | ||
console.log('mounted!') | ||
}); | ||
} | ||
``` | ||
### Unmount | ||
This lifecycle function will be called whenever the parcel is mounted and one of the following cases is true: | ||
- `unmount()` is called | ||
- The parent parcel or application is unmounted | ||
When called, this function should clean up all DOM elements, DOM event listeners, leaked memory, globals, | ||
observable subscriptions, etc. that were created at any point when the parcel was mounted. | ||
```js | ||
function unmount(props) { | ||
return Promise | ||
.resolve() | ||
.then(() => { | ||
// This is where you tell a framework (e.g., React) to unrender some ui from the dom | ||
console.log('unmounted!'); | ||
}); | ||
} | ||
``` | ||
### Update (optional) | ||
The update lifecycle function will be called whenever the user of the parcel calls `parcel.update()`. | ||
Single this lifecycle is optional, the user of a parcel needs to check whether the parcel has implemented the update lifecycle before attempting to make the call. | ||
## Example use cases | ||
### Modals | ||
`App1` handles everything related to contacts (highly cohesive) but somewhere in `App2` we need to create a contact. | ||
We could do any number of things to share the functionality between application 1 and 2: | ||
- If both are written in the same framework we could export/import components. | ||
- We could reimplement creating a contact (loss of cohesion) | ||
- We could use single-spa parcels. | ||
Exporting a parcel from `App1` that wraps the createContact modal component gives us the ability to share components and behavior across disparate frameworks, without losing application cohesion. | ||
App1 can export a modal as a single-spa parcel and App2 can import the parcel and use it easily. One major advantage is that in the below example | ||
the parcel/modal from App1 that is being used by App2 will also be unmounted, without unmounting/mounting of App1. | ||
```js | ||
// App1 | ||
export const AddContactParcel = { | ||
bootstrap: bootstrapFn, | ||
mount: mountFn, | ||
unmount: unmountFn, | ||
} | ||
// App2 | ||
// get the parcel configuration in this case I'm using systemJS and react | ||
... | ||
componentDidMount() { | ||
SystemJS.import('App1').then(App1 => { | ||
const domElement = document.body | ||
App2MountProps.mountParcel(App1.AddContactParcel, {domElement}) | ||
}) | ||
} | ||
... | ||
``` | ||
This page has moved. Find the [Single-spa Parcels](https://single-spa.js.org/docs/parcels-overview.html) documentation on the single-spa website. |
# single-spa documentation | ||
- [Registering applications](/docs/root-application.md) | ||
- [Building applications](/docs/applications.md) | ||
- [API reference](/docs/single-spa-api.md) | ||
- [Separating applications out](/docs/separating-applications.md) | ||
- [Ecosystem](/docs/single-spa-ecosystem.md) | ||
- [Migrating existing apps](/docs/migrating-existing-spas.md) | ||
This page has moved. Find the single-spa documentation on the [single-spa website](https://single-spa.js.org/docs/getting-started-overview.html) | ||
# Root application | ||
Root application has been renamed to single-spa-config, to make it clear that the root application/single-spa-config is very different from single-spa-applications | ||
## Single-spa-config | ||
[single-spa-config](/docs/single-spa-config.md) | ||
[single-spa-config](https://single-spa.js.org/docs/configuration.html) | ||
## Single-spa-applications | ||
[applications](/docs/applications.md) | ||
[applications](https://single-spa.js.org/docs/building-applications.html) | ||
# Separating out applications | ||
In a large, microserviced system, your root single-spa configuration and each of the applications | ||
should probably have its own git repository. How to do that in a javascript project isn't necessarily clear, | ||
so some options are listed below. | ||
Since single-spa is a framework that helps with organizational scaling, it is | ||
important to figure out how to split out and separate applications from each other | ||
so that developers and teams can work on the applications without stepping on each others' toes. | ||
Most interpretations of microservice architecture encourage separate code repositories, builds, and | ||
deployments. Although **single-spa does not solve how code is hosted, built, or deployed**, | ||
these are relevant to many users of single-spa, so some strategies for doing so are discussed here. | ||
#### Option 1: One code repo, one build | ||
The simplest approach for using single-spa is to have one code repository with everything in it. | ||
Typically, you would have a single package.json with a single webpack config that produces a bundle | ||
that can be included in an html file with a `<script>` tag. | ||
Advantages: | ||
- Simplest to set up | ||
- [monolithic version control has some advantages](https://danluu.com/monorepo/) | ||
Disadvantages: | ||
- One master webpack config and package.json means less flexibility / freedom for individual projects | ||
- Slow build times once your project gets large | ||
- Builds and deployments are all tied together, which can necessitate fixed release schedules instead of ad hoc releases. | ||
#### Option 2: NPM packages | ||
Create a root application that npm installs each of the single-spa applications. Each child application | ||
is in a separate code repository and is responsible for publishing a new version everytime that it updates. | ||
The root application should reinstall, rebuild, and redeploy whenever a single-spa application changes. | ||
Typically, the single-spa applications compile themselves separately with babel and/or webpack. | ||
Note that you can also use the [monorepo methodology](https://medium.com/netscape/the-case-for-monorepos-907c1361708a) which | ||
allows for separate builds without having separate code repositories. | ||
Advantages: | ||
- npm install is familiar and easy to set up | ||
- Separate npm packages means each application can build itself separately before publishing to npm | ||
Disadvantages: | ||
- The root application must reinstall the child applications in order to rebuild/redeploy | ||
- Medium difficulty to set up | ||
#### Option 3: Dynamic Module Loading | ||
Create a root application which can allow single-spa applications to deploy themselves separately. To do so, | ||
create a manifest file that the single-spa applications update during their deployment process, which controls | ||
which versions of the single-spa applications are "live". Then change which javascript file is loaded based on the manifest. | ||
Changing which javascript file is loaded for each child application can be done in many ways. | ||
1) Web server: have your webserver create a dynamic script tag for the "live" version of each single-spa application. | ||
2) Use a [module loader](https://www.jvandemo.com/a-10-minute-primer-to-javascript-modules-module-formats-module-loaders-and-module-bundlers/) | ||
such as [SystemJS](https://github.com/systemjs/systemjs) that can download and execute javascript code in the browser | ||
from dynamic urls. | ||
#### Comparison | ||
| | Separate code repositories | Separate builds | Separate deployments | Difficulty to set up | Example repo | | ||
| -------------- | -------------------------- | --------------- | -------------------- | -------------------- | ------------ | | ||
| One code repo | | | | Easy | [simple-webpack-example](https://github.com/joeldenning/simple-single-spa-webpack-example) and [single-spa-examples](https://github.com/CanopyTax/single-spa-examples) | | ||
| NPM modules | [x] | [x] | | Medium | (No example repo, yet -- contributions accepted!) | | ||
| Module loading | [x] | [x] | [x] | Hard | [single-spa-portal-example](https://github.com/me-12/single-spa-portal-example) | | ||
This page has moved. Find the [Separating Applications](https://single-spa.js.org/docs/separating-applications.html) documentation on the single-spa website. |
# single-spa API | ||
The single-spa library does not `export default`, but instead exports named functions and variables. | ||
What this means is you can use the api in two ways: | ||
```js | ||
import * as singleSpa from 'single-spa'; | ||
// OR | ||
import {registerApplication, start} from 'single-spa'; | ||
``` | ||
## registerApplication | ||
`registerApplication(name, whereToGetApplication, activeWhen)` is the most important api your single spa config will use. | ||
It is described in detail inside of the [single-spa-config.md docs](/docs/single-spa-config.md#registering-applications) | ||
## declareChildApplication | ||
`declareChildApplication` is deprecated use `registerApplication` instead | ||
## start | ||
`start()` is a function that must be called by your single spa config. Before `start` is called, | ||
applications will be loaded, but will never be bootstrapped, mounted or unmounted. The reason for `start` | ||
is to give you control over the performance of your single page application. For example, you may want to declare registered applications | ||
immediately (to start downloading the code for the active ones), but not actually mount the registered applications | ||
until an initial AJAX request (maybe to get information about the logged in user) has been completed. In that case, | ||
the best performance is achieved by calling `registerApplication` immediately, but calling `start` after | ||
the AJAX request is completed. | ||
## triggerAppChange | ||
`triggerAppChange()` takes in no arguments and returns a Promise that will resolve/reject when all apps have mounted. | ||
## navigateToUrl | ||
`navigateToUrl(obj)` takes in one optional argument and returns no value. It is a utility function that | ||
allows for easy url navigation between registered applications, without needing to deal with `event.preventDefault()`, | ||
`pushState`, `triggerAppChange()`, etc. It can be called with one of the following: | ||
- a string parameter `url` | ||
- a context / thisArg that has an `href` property (useful for `singleSpaNavigate.call(anchorElement)` case). | ||
- a DOMEvent object for a click event on a DOMElement that has an `href` attribute | ||
(ideal for the `<a onclick="singleSpaNavigate"></a>` use case). | ||
This function is exposed onto the window as `window.singleSpaNavigate`, for convenience and use inside of `<button onclick="singleSpaNavigate('url')">` or `<a href="/url" onclick="singleSpaNavigate">` | ||
## getMountedApps | ||
`getMountedApps()` returns an array of strings, where each string is the name of the registered application, | ||
as defined in the call to `registerApplication`. | ||
## getAppNames | ||
`getAppNames()` returns an array of strings, where each string is the name of the registered application. NOTE: this returns all declared registered applications regardless of app status. | ||
## getAppStatus | ||
`getAppStatus(appName)` takes in one string parameter and returns either a string (when the app exists) | ||
or `null` (when the app doesn't exist). The string status is one of the following: | ||
- `NOT_LOADED`: the app has been registered with single-spa, but the app itself has not yet been loaded. | ||
- `LOADING_SOURCE_CODE`: the app's source code is being fetched. | ||
- `NOT_BOOTSTRAPPED`: the app has been loaded, but not yet bootstrapped. | ||
- `BOOTSTRAPPING`: the `bootstrap` lifecycle function has been called, but has not yet finished. | ||
- `NOT_MOUNTED`: the app has been loaded and bootstrapped, but is not currently mounted. | ||
- `MOUNTING`: the app is being mounted, but has not finished. | ||
- `MOUNTED`: the app is currently active and is mounted to the DOM. | ||
- `UNMOUNTING`: the app is currently being unmounted, but has not yet finished. | ||
- `UNLOADING`: the app is currently being unloaded, but has not yet finished. | ||
- `SKIP_BECAUSE_BROKEN`: the app threw an error during load, bootstrap, mount, or unmount and has been | ||
siloed because it is misbehaving. Other apps may continue on normally, but this one will be skipped. | ||
## unloadApplication | ||
`unloadApplication(appName, opts)` takes in a string parameter `appName` and (optionally) an `opts` object. It returns | ||
a promise that is resolved when the registered application has been successfully resolved. The `opts` parameter is an object with the | ||
following property: | ||
- `waitForUnmount`: a boolean that decides when to unload the application. Defaults to false. | ||
Examples: | ||
```js | ||
unloadApplication('app1', {waitForUnmount: false}); | ||
unloadApplication('app1'); // This is the same as providing `{waitForUnmount: false}` | ||
unloadApplication('app1', {waitForUnmount: true}); | ||
``` | ||
The purpose of unloading a registered application is to set it back to to a NOT_LOADED status, which means that | ||
it will be re-bootstrapped the next time it needs to mount. The motivation for this was to allow for | ||
the hot-reloading of entire registered applications, but `unload` can be useful whenever you want to re-bootstrap | ||
your application. | ||
Single-spa performs the following steps when unloadApplication is called. | ||
1. Call the [unload lifecyle](/docs/applications.md#unload) on the registered application that is being unloaded. | ||
2. Set the app status to NOT_LOADED | ||
3. Trigger a reroute, during which single-spa will potentially mount the application that was just unloaded. | ||
Because a registered application might be mounted when `unloadApplication` is called, you can specify whether you want to immediately | ||
unload or if you want to wait until the application is no longer mounted. This is done with the `waitForUnmount` option. If `false`, | ||
single-spa immediately unloads the specified registered application even if the app is currently mounted. If `true`, single-spa will unload | ||
the registered application as soon as it is safe to do so (when the app status is not `MOUNTED`). | ||
## unloadChildApplication | ||
`unloadChildApplication` is deprecated use `unloadApplication` instead | ||
## checkActivityFunctions | ||
`checkActivityFunctions(mockWindowLocation)` takes in a mock of the `window.location`. It returns an array of | ||
`applicationName` strings. This API will call every app's activity function with the provided mockWindowLocation | ||
## addErrorHandler | ||
`addErrorHandler(fn)` adds a handler that will be called every time an application throws an error during a lifecycle function or an | ||
activity function. When there are no error handlers, single-spa throws the error to the window, but when there is at least one handler, | ||
errors will no longer be thrown on the window. | ||
The `fn` must be a function. It will be called with one argument, `err`, which is an Error object that has a `message` and `name` property. | ||
## removeErrorHandler | ||
`removeErrorHandler(fn)` removes an error handler. Returns true if the error handler was removed, and false if it was not. | ||
## mountRootParcel | ||
`mountRootParcel(parcelConfig, parcelProps)` will create and mount a parcel. | ||
This function takes a [parcelConfig](/docs/parcels.md#parcel-configuration) and [parcelProps](/docs/parcel-api.md#parcel-props). | ||
The mounted parcel will not be automatically unmounted and unmounting will need to be triggered manually. | ||
## before routing event | ||
single-spa fires an event `single-spa:before-routing-event` on the window every time before a routing event occurs. | ||
This event will get fired after each hashchange, popstate, or triggerAppChange, even if no changes | ||
to registered applications were necessary. Sample usage of this event might look like this: | ||
```js | ||
window.addEventListener('single-spa:before-routing-event', () => { | ||
console.log('before routing event occurred!'); | ||
}) | ||
``` | ||
## routing event | ||
single-spa fires an event `single-spa:routing-event` on the window every time that a routing event has occurred in which | ||
single-spa verified that all apps were correctly loaded, bootstrapped, mounted, and unmounted. | ||
This event will get fired after each hashchange, popstate, or triggerAppChange, even if no changes | ||
to registered applications were necessary. Sample usage of this event might look like this: | ||
```js | ||
window.addEventListener('single-spa:routing-event', () => { | ||
console.log('routing event occurred!'); | ||
}) | ||
``` | ||
## app-change event | ||
single-spa fires an event `single-spa:app-change` on the window every time that one or more apps were loaded, bootstrapped, | ||
mounted, unmounted, or unloaded. It is similar to `single-spa:routing-event` except that it will not fire unless | ||
one or more apps were actually loaded, bootstrapped, mounted, or unmounted. A hashchange, popstate, or triggerAppChange | ||
that does not result in one of those changes will not cause the event to be fired. | ||
Sample usage of this event might look like this: | ||
```js | ||
window.addEventListener('single-spa:app-change', () => { | ||
console.log(singleSpa.getMountedApps()) | ||
}) | ||
``` | ||
## no-app-change event | ||
When no apps were loaded, bootstrapped, mounted, unmounted, or unloaded, single-spa fires a `single-spa:no-app-change` event. | ||
This is the converse of the `single-spa:app-change` event -- only one will be fired for each routing event. | ||
```js | ||
window.addEventListener('single-spa:no-app-change', () => { | ||
console.log(singleSpa.getMountedApps()) | ||
}) | ||
``` | ||
## before-first-mount | ||
Right before the first time that any app is mounted, single-spa fires a `single-spa:before-first-mount` event. This will happen | ||
after the app is already loaded, but before it is mounted. This event will only get fired once, ever. It does *not* get fired for each | ||
app's first mount, but rather for the first time that any of the apps is mounted. | ||
```js | ||
window.addEventListener('single-spa:before-first-mount', () => { | ||
console.log('Suggested use case: remove a loader bar that the user is seeing right before the first app will be mounted'); | ||
}); | ||
``` | ||
## first-mount | ||
Right after the first time that any app is mounted, single-spa fires a `single-spa:first-mount` event. This event will only get fired once, ever. | ||
It does *not* get fired for each app's first mount, but rather for the first time that any of the apps is mounted. | ||
```js | ||
window.addEventListener('single-spa:first-mount', () => { | ||
console.log('Suggested use case: log the time it took before the user sees any of the apps mounted'); | ||
}); | ||
``` | ||
## ensureJQuerySupport | ||
`ensureJQuerySupport(jQuery)`: Since jquery does event delegation, single-spa | ||
has to specifically monkey patch each version of jQuery that you're using. single-spa tries to do | ||
this automatically as much as possible for looking for window.jQuery or window.$, but if you want | ||
to give your version of jQuery to single-spa manually, call ensureJQuerySupport(jQuery). The | ||
jQuery argument is actually optional and will default to window.jQuery. | ||
## setBootstrapMaxTime | ||
`setBootstrapMaxTime(millis, dieOnTimeout = false)` takes in a number of milliseconds and a boolean dieOnTimeout | ||
that defaults to false. It sets the global configuration for bootstrap timeouts and does not return any value. | ||
See dieOnTimeout section below for details. | ||
## setMountMaxTime | ||
`setMountMaxTime(millis, dieOnTimeout = false)` takes in a number of milliseconds and a boolean dieOnTimeout | ||
that defaults to false. It sets the global configuration for mount timeouts and does not return any value. | ||
See dieOnTimeout section below for details. | ||
## setUnmountMaxTime | ||
`setUnmountMaxTime(millis, dieOnTimeout = false)` takes in a number of milliseconds and a boolean dieOnTimeout | ||
that defaults to false. It sets the global configuration for unmount timeouts and does not return any value. | ||
See dieOnTimeout section below for details. | ||
## dieOnTimeout | ||
`dieOnTimeout` refers to configuration of what should happen when registered applications take longer than expected | ||
to load, bootstrap, mount, or unmount. There is both a global configuration applicable to all registered applications, and also | ||
the ability for each registered application to override this behavior for itself. See [registered application configuration | ||
for timeouts](/docs/applications.md#timeouts) for details on registered application overrides of the global | ||
behavior. | ||
If `dieOnTimeout` is false (which is the default), registered applications that are slowing things down will cause | ||
nothing more than some warnings in the console up until `millis` is reached. | ||
If `dieOnTimeout` is true, registered applications that are slowing things down will be siloed into a SKIP_BECAUSE_BROKEN | ||
status where they will never again be given the chance to break everything. | ||
This page has moved. Find the [single-spa API](https://single-spa.js.org/docs/api.html) documentation on the single-spa website. |
# Single spa config | ||
The single spa config consists of all code that is not part of a | ||
[registered application](/docs/applications.md). Ideally, this only includes an html file | ||
and a javascript file that registers single-spa applications. It is best practice to keep your | ||
single spa config as small as possible and to simply defer to single-spa to manage | ||
all of the applications. The single spa config should not be doing client-side html | ||
rendering nor should it be responding to routing events such as `hashchange` or `popstate`. | ||
Instead, all of that functionality should be taken care of either by single-spa itself or by | ||
a single-spa application. | ||
## Index.html file | ||
The main thing that you should be doing in your html file is executing your single spa config. For your | ||
use case, this could mean something like `<script src="/single-spa-config.js"></script>`. | ||
Example: | ||
```js | ||
<html> | ||
<body> | ||
<script src="/single-spa-config.js"></script> | ||
</body> | ||
</html> | ||
``` | ||
## Registering applications | ||
You must register [applications](/docs/applications.md) with single-spa so it knows how and when to | ||
initiate, load, mount, and unmount. Registration most commonly occurs inside of the single spa config, but | ||
does not have to. Note that if an application is registered from within another application, that no hierarchy | ||
will be maintained between the applications. Instead, the applications will be siblings and will be mounted | ||
and unmounted according to their own activity functions. | ||
In order to register an application, call the `registerApplication(name, howToLoad, activityFunction)` api. Example: | ||
```js | ||
// single-spa-config.js | ||
import { registerApplication, start } from 'single-spa'; | ||
registerApplication("applicationName", loadingFunction, activityFunction); | ||
start(); | ||
function loadingFunction() { | ||
return import("src/app1/main.js"); | ||
} | ||
function activityFunction(location) { | ||
return location.pathname.indexOf("/app1/") === 0; | ||
} | ||
``` | ||
### Application name | ||
The first argument to `registerApplication` must be a string name. | ||
### Loading Function or Application | ||
The second argument to `registerApplication` must be either a function that returns a promise [loading function](/docs/single-spa-config.md#loading-function) or the resolved Application. | ||
#### Application as second argument | ||
Optionally for the second argument you can use the resolved Application, consisting of an object with the lifecycle methods. | ||
This allows you import the Application from another file or define applications inline in your single-spa-config | ||
```js | ||
const application = { | ||
bootstrap: () => Promise.resolve(), //bootstrap function | ||
mount: () => Promise.resolve(), //mount function | ||
unmount: () => Promise.resolve(), //unmount function | ||
} | ||
registerApplication('applicatonName', application, activityFunction) | ||
``` | ||
#### Loading function | ||
The second argument to `registerApplication` must be a function that returns a promise (or an ["async function"](https://ponyfoo.com/articles/understanding-javascript-async-await)). | ||
The function will be called with no arguments when it's time to load the application for the first time. The returned | ||
promise must be resolved with the application. The most common implementation of a loading function is an import call: | ||
`() => import('/path/to/application.js')` | ||
### Activity function | ||
The third argument to `registerApplication` must be a pure function, the function is provided `window.location` as the first argument, and returns a truthy | ||
value whenever the application should be active. Most commonly, the activity function determines if an application | ||
is active by looking at `window.location`/the first param. | ||
Another way of looking at this is that single-spa is a top-level router that has a lot of applications that have their own sub-router. | ||
single-spa will call each application's activity function under the following scenarios: | ||
- `hashchange` or `popstate` event | ||
- `pushState` or `replaceState` is called | ||
- [`triggerAppChange`](/docs/single-spa-api.md#triggerappchange) api is called on single-spa | ||
- Whenever the `checkActivityFunctions` method is called | ||
## Calling singleSpa.start() | ||
The [`start()` api](/docs/single-spa-api.md#start) **must** be called by your single spa config in order for | ||
applications to actually be mounted. Before `start` is called, applications will be loaded, but not bootstrapped/mounted/unmounted. | ||
The reason for `start` is to give you control over performance. For example, you may want to register applications | ||
immediately (to start downloading the code for the active ones), but not actually mount the applications | ||
until an initial AJAX request (maybe to get information about the logged in user) has been completed. In that case, | ||
the best performance is achieved by calling `registerApplication` immediately, but calling `start` after | ||
the AJAX request is completed. | ||
```js | ||
//single-spa-config.js | ||
import { start } from 'single-spa'; | ||
/* Calling start before registering apps means that single-spa can immediately mount apps, without | ||
* waiting for any initial setup of the single page app. | ||
*/ | ||
start(); | ||
// Register applications.... | ||
``` | ||
## Two registered applications simultaneously?? | ||
Yep, it's possible. And it's actually not that scary if you do it right. And once you do, | ||
it's really really powerful. One approach to do this is to create a `<div id="app-name"></div>` for each app, | ||
so that they never try to modify the same DOM at the same time. | ||
This page has moved. Find the [single-spa config](https://single-spa.js.org/docs/configuration.html) documentation on the single-spa website. |
# Single-spa ecosystem | ||
The single-spa ecosystem is quickly growing to support as many frameworks and build tools as possible. | ||
## Help for frameworks | ||
There is a growing number of projects that help you bootstrap, mount, | ||
and unmount your applications that are written with popular frameworks. Feel free | ||
to contribute to this list with your own project: | ||
- [single-spa-angular1](https://github.com/CanopyTax/single-spa-angular1) | ||
- [single-spa-react](https://github.com/CanopyTax/single-spa-react) | ||
- [single-spa-angular-cli](https://github.com/PlaceMe-SAS/single-spa-angular-cli) (useful for angular-cli applications) | ||
- [single-spa-angular2](https://github.com/CanopyTax/single-spa-angular2) (works for angular 2, 3, 4, 5+, useful if you don't use angular-cli) | ||
- [single-spa-vue](https://github.com/CanopyTax/single-spa-vue) | ||
- [single-spa-ember](https://github.com/CanopyTax/single-spa-ember) | ||
- [single-spa-preact](https://github.com/CanopyTax/single-spa-preact) | ||
- [single-spa-inferno](https://github.com/CanopyTax/single-spa-inferno) | ||
- [single-spa-svelte](https://github.com/CanopyTax/single-spa-svelte) | ||
- [single-spa-cycle](https://github.com/pcmnac/single-spa-cycle) | ||
## Webpack 2+ | ||
With webpack 2+, we can take advantage of its support for [code splitting](https://webpack.js.org/guides/code-splitting/) with [import()](https://webpack.js.org/api/module-methods/#import) | ||
in order to easily lazy-load registered applications when they are needed. When registering | ||
registered applications from inside of your single spa config, try the following for your | ||
[loading functions](/docs/single-spa-config.md#loading-function). | ||
```js | ||
import {registerApplication} from 'single-spa'; | ||
registerApplication('app-name', () => import('./my-app.js'), activeWhen); | ||
function activeWhen() { | ||
return window.location.pathname.indexOf('/my-app') === 0; | ||
} | ||
``` | ||
## SystemJS | ||
Since SystemJS is a Promise-based [loader](https://whatwg.github.io/loader), the way to | ||
lazy load your registered applications is straightforward: | ||
```js | ||
import {registerApplication} from 'single-spa'; | ||
// Import the registered application with a SystemJS.import call | ||
registerApplication('app-name-1', () => SystemJS.import('./my-app.js'), activeWhen); | ||
// Alternatively, use the more out-of-date System.import (instead of SystemJS.import) | ||
registerApplication('app-name-2', () => System.import('./my-other-app.js'), activeWhen); | ||
function activeWhen() { | ||
return window.location.pathname.indexOf('/my-app') === 0; | ||
} | ||
``` | ||
## Webpack 1 | ||
With webpack 1, there is no support for Promise-based code splitting. Instead, we have to either wrap | ||
a require.ensure in a Promise, or just give up on lazy loading completely. | ||
```js | ||
import {registerApplication} from 'single-spa'; | ||
import app1 from './app1'; // Not lazy loading with code splitting :( | ||
// Giving up on lazy loading and code splitting :( | ||
registerApplication('app-1', () => Promise.resolve(app1), activeWhen); | ||
// Still doing code splitting! But verbose :( | ||
registerApplication('app-2', app2InPromise, activeWhen); | ||
/* Unfortunately, this logic cannot be abstracted into a generic | ||
* function that handles wrapping require.ensure in a promise for | ||
* any dynamically imported module. This is because webpack needs to | ||
* be able to statically analyze the code and find all of the require.ensure | ||
* calls at build-time, so you can't pass variables into require.ensure. | ||
*/ | ||
function app2InPromise() { | ||
return new Promise((resolve, reject) => { | ||
require.ensure(['./app-2.js'], require => { | ||
try { | ||
resolve(require('./app-2.js')); | ||
} catch(err) { | ||
reject(err); | ||
} | ||
}); | ||
}); | ||
} | ||
function activeWhen() { | ||
return window.location.pathname.indexOf('/my-app') === 0; | ||
} | ||
``` | ||
This page has moved. Find the [single-spa ecosystem](https://single-spa.js.org/docs/ecosystem.html) documentation on the single-spa website. |
@@ -1,2 +0,2 @@ | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("singleSpa",[],t):"object"==typeof exports?exports.singleSpa=t():e.singleSpa=t()}(window,function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=21)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isActive=s,t.isntActive=function(e){return!s(e)},t.isLoaded=l,t.isntLoaded=function(e){return!l(e)},t.shouldBeActive=function(e){try{return e.activeWhen(window.location)}catch(t){(0,r.handleAppError)(t,e),e.status=c}},t.shouldntBeActive=function(e){try{return!e.activeWhen(window.location)}catch(t){(0,r.handleAppError)(t,e),e.status=c}},t.notBootstrapped=function(e){return e.status!==u},t.notSkipped=function(e){return e!==c&&(!e||e.status!==c)},t.toName=function(e){return e.name},t.SKIP_BECAUSE_BROKEN=t.UNLOADING=t.UNMOUNTING=t.UPDATING=t.MOUNTED=t.MOUNTING=t.NOT_MOUNTED=t.BOOTSTRAPPING=t.NOT_BOOTSTRAPPED=t.LOADING_SOURCE_CODE=t.NOT_LOADED=void 0;var r=n(1);!function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}t.default=e}(n(17));var o="NOT_LOADED";t.NOT_LOADED=o;var i="LOADING_SOURCE_CODE";t.LOADING_SOURCE_CODE=i;var u="NOT_BOOTSTRAPPED";t.NOT_BOOTSTRAPPED=u;t.BOOTSTRAPPING="BOOTSTRAPPING";t.NOT_MOUNTED="NOT_MOUNTED";t.MOUNTING="MOUNTING";var a="MOUNTED";t.MOUNTED=a;t.UPDATING="UPDATING";t.UNMOUNTING="UNMOUNTING";t.UNLOADING="UNLOADING";var c="SKIP_BECAUSE_BROKEN";function s(e){return e.status===a}function l(e){return e.status!==o&&e.status!==i}t.SKIP_BECAUSE_BROKEN=c},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.handleAppError=function(e,t){var n=i(e,t);o.length?o.forEach(function(e){return e(n)}):setTimeout(function(){throw n})},t.addErrorHandler=function(e){if("function"!=typeof e)throw new Error("a single-spa error handler must be a function");o.push(e)},t.removeErrorHandler=function(e){if("function"!=typeof e)throw new Error("a single-spa error handler must be a function");var t=!1;return o=o.filter(function(n){var r=n===e;return t=t||r,!r}),t},t.transformErr=i;var r;(r=n(8))&&r.__esModule;var o=[];function i(e,t){var n,r=t.unmountThisParcel?"Parcel":"Application",o="".concat(r," '").concat(t.name,"' died in status ").concat(t.status,": ");if(e instanceof Error){try{e.message=o+e.message}catch(e){}n=e}else{console.warn("While ".concat(t.status,", '").concat(t.name,"' rejected its lifecycle function promise with a non-Error. This will cause stack traces to not be accurate."));try{n=new Error(o+JSON.stringify(e))}catch(t){n=e}}return n.appName=t.name,n.name=t.name,n}},function(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}Object.defineProperty(t,"__esModule",{value:!0}),t.setBootstrapMaxTime=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if("number"!=typeof e||e<=0)throw new Error("bootstrap max time must be a positive integer number of milliseconds");o.bootstrap={millis:e,dieOnTimeout:t}},t.setMountMaxTime=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if("number"!=typeof e||e<=0)throw new Error("mount max time must be a positive integer number of milliseconds");o.mount={millis:e,dieOnTimeout:t}},t.setUnmountMaxTime=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if("number"!=typeof e||e<=0)throw new Error("unmount max time must be a positive integer number of milliseconds");o.unmount={millis:e,dieOnTimeout:t}},t.setUnloadMaxTime=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if("number"!=typeof e||e<=0)throw new Error("unload max time must be a positive integer number of milliseconds");o.unload={millis:e,dieOnTimeout:t}},t.reasonableTime=function(e,t,n){var r=1e3;return new Promise(function(o,i){var u=!1,a=!1;function c(e){if(!u)if(!0===e)a=!0,n.dieOnTimeout?i("".concat(t," did not resolve or reject for ").concat(n.millis," milliseconds")):console.error("".concat(t," did not resolve or reject for ").concat(n.millis," milliseconds -- we're no longer going to warn you about it."));else if(!a){var o=e,s=o*r;console.warn("".concat(t," did not resolve or reject within ").concat(s," milliseconds")),s+r<n.millis&&setTimeout(function(){return c(o+1)},r)}}e.then(function(e){u=!0,o(e)}).catch(function(e){u=!0,i(e)}),setTimeout(function(){return c(1)},r),setTimeout(function(){return c(!0)},n.millis)})},t.ensureValidAppTimeouts=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},o=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),o.forEach(function(t){r(e,t,n[t])})}return e}({},o,e)};var o={bootstrap:{millis:4e3,dieOnTimeout:!1},mount:{millis:3e3,dieOnTimeout:!1},unmount:{millis:3e3,dieOnTimeout:!1},unload:{millis:3e3,dieOnTimeout:!1}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getProps=function(e){var t=function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){o(e,t,n[t])})}return e}({},e.customProps,{name:e.name,mountParcel:r.mountParcel.bind(e)});e.unmountThisParcel&&(t.unmountSelf=e.unmountThisParcel);return t};var r=n(14);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.find=function(e,t){for(var n=0;n<e.length;n++)if(t(e[n]))return e[n];return null}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.reroute=function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];var n=arguments.length>1?arguments[1]:void 0;if(d)return new Promise(function(e,t){m.push({resolve:e,reject:t,eventArguments:n})});d=!0;var r=!0;return(0,i.isStarted)()?Promise.resolve().then(function(){var e;n&&n[0]&&(e={detail:n[0]}),window.dispatchEvent(new o.default("single-spa:before-routing-event",e));var i=(0,p.getAppsToUnload)().map(p.toUnloadPromise),f=(0,l.getAppsToUnmount)().map(s.toUnmountPromise).map(function(e){return e.then(p.toUnloadPromise)}),d=f.concat(i);d.length>0&&(r=!1);var m=Promise.all(d),O=(0,l.getAppsToLoad)(),b=O.map(function(e){return(0,u.toLoadPromise)(e).then(a.toBootstrapPromise).then(function(e){return m.then(function(){return(0,c.toMountPromise)(e)})})});b.length>0&&(r=!1);var y=(0,l.getAppsToMount)().filter(function(e){return O.indexOf(e)<0}).map(function(e){return(0,a.toBootstrapPromise)(e).then(function(){return m}).then(function(){return(0,c.toMountPromise)(e)})});return y.length>0&&(r=!1),m.catch(function(e){throw v(),e}).then(function(){return v(),Promise.all(b.concat(y)).catch(function(e){throw t.forEach(function(t){return t.reject(e)}),e}).then(function(){return h(!1)})})}):Promise.resolve().then(function(){var e=(0,l.getAppsToLoad)().map(u.toLoadPromise);return e.length>0&&(r=!1),Promise.all(e).then(h).catch(function(e){throw v(),e})});function h(){var n=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],i=(0,l.getMountedApps)();n&&v(),t.forEach(function(e){return e.resolve(i)});try{var u=r?"single-spa:no-app-change":"single-spa:app-change";window.dispatchEvent(new o.default(u)),window.dispatchEvent(new o.default("single-spa:routing-event"))}catch(e){setTimeout(function(){throw e})}if(d=!1,m.length>0){var a=m;m=[],e(a)}return i}function v(){t.forEach(function(e){(0,f.callCapturedEventListeners)(e.eventArguments)}),(0,f.callCapturedEventListeners)(n)}};var r,o=(r=n(8))&&r.__esModule?r:{default:r},i=n(16),u=n(19),a=n(13),c=n(12),s=n(7),l=n(11),f=(n(0),n(6)),p=n(9);var d=!1,m=[]},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.navigateToUrl=a,t.callCapturedEventListeners=function(e){var t=this;if(e){var n=e[0].type;u.indexOf(n)>=0&&i[n].forEach(function(n){n.apply(t,e)})}},t.routingEventsListeningTo=void 0;var r=n(5),o=n(4),i={hashchange:[],popstate:[]},u=["hashchange","popstate"];function a(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("string"==typeof e)t=e;else if(this&&this.href)t=this.href;else{if(!(e&&e.currentTarget&&e.currentTarget.href&&e.preventDefault))throw new Error("singleSpaNavigate 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");t=e.currentTarget.href,e.preventDefault()}var r=d(window.location.href),o=d(t);if(0===t.indexOf("#"))window.location.hash="#"+o.anchor;else if(r.host!==o.host&&o.host){if(n.isTestingEnv)return{wouldHaveReloadedThePage:!0};window.location.href=t}else!function(e,t){return t===e||t==="/"+e}(o.path,r.path)?window.history.pushState(null,null,t):window.location.hash="#"+o.anchor}function c(){(0,r.reroute)([],arguments)}t.routingEventsListeningTo=u,window.addEventListener("hashchange",c),window.addEventListener("popstate",c);var s=window.addEventListener,l=window.removeEventListener;window.addEventListener=function(e,t){if(!("function"==typeof t&&u.indexOf(e)>=0)||(0,o.find)(i[e],function(e){return e===t}))return s.apply(this,arguments);i[e].push(t)},window.removeEventListener=function(e,t){if(!("function"==typeof t&&u.indexOf(e)>=0))return l.apply(this,arguments);i[e]=i[e].filter(function(e){return e!==t})};var f=window.history.pushState;window.history.pushState=function(e){var t=f.apply(this,arguments);return(0,r.reroute)(),t};var p=window.history.replaceState;function d(e){for(var t={strictMode:!0,key:["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],q:{name:"queryKey",parser:/(?:^|&)([^&=]*)=?([^&]*)/g},parser:{strict:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}},n=t.parser[t.strictMode?"strict":"loose"].exec(e),r={},o=14;o--;)r[t.key[o]]=n[o]||"";return r[t.q.name]={},r[t.key[12]].replace(t.q.parser,function(e,n,o){n&&(r[t.q.name][n]=o)}),r}window.history.replaceState=function(){var e=p.apply(this,arguments);return(0,r.reroute)(),e},window.singleSpaNavigate=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toUnmountPromise=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return Promise.resolve().then(function(){if(e.status!==r.MOUNTED)return e;e.status=r.UNMOUNTING;var n,a=Object.keys(e.parcels).map(function(t){return e.parcels[t].unmountThisParcel()});return Promise.all(a).then(c,function(n){return c().then(function(){var i=new Error(n.message);if(t){var u=(0,o.transformErr)(i,e);throw e.status=r.SKIP_BECAUSE_BROKEN,u}(0,o.handleAppError)(i,e),e.status=r.SKIP_BECAUSE_BROKEN})}).then(function(){return e});function c(){return(0,i.reasonableTime)(e.unmount((0,u.getProps)(e)),"Unmounting application ".concat(e.name,"'"),e.timeouts.unmount).then(function(){n||(e.status=r.NOT_MOUNTED)}).catch(function(n){if(t){var i=(0,o.transformErr)(n,e);throw e.status=r.SKIP_BECAUSE_BROKEN,i}(0,o.handleAppError)(n,e),e.status=r.SKIP_BECAUSE_BROKEN})}})};var r=n(0),o=n(1),i=n(2),u=n(3)},function(e,t,n){(function(t){var n=t.CustomEvent;e.exports=function(){try{var e=new n("cat",{detail:{foo:"bar"}});return"cat"===e.type&&"bar"===e.detail.foo}catch(e){}return!1}()?n:"undefined"!=typeof document&&"function"==typeof document.createEvent?function(e,t){var n=document.createEvent("CustomEvent");return t?n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail):n.initCustomEvent(e,!1,!1,void 0),n}:function(e,t){var n=document.createEventObject();return n.type=e,t?(n.bubbles=Boolean(t.bubbles),n.cancelable=Boolean(t.cancelable),n.detail=t.detail):(n.bubbles=!1,n.cancelable=!1,n.detail=void 0),n}}).call(this,n(20))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toUnloadPromise=function(e){return Promise.resolve().then(function(){var t=a[e.name];return t?e.status===r.NOT_LOADED?(c(e,t),e):e.status===r.UNLOADING?t.promise.then(function(){return e}):e.status!==r.NOT_MOUNTED?e:(e.status=r.UNLOADING,(0,i.reasonableTime)(e.unload((0,u.getProps)(e)),"Unloading application '".concat(e.name,"'"),e.timeouts.unload).then(function(){return c(e,t),e}).catch(function(n){return function(e,t,n){delete a[e.name],delete e.bootstrap,delete e.mount,delete e.unmount,delete e.unload,(0,o.handleAppError)(n,e),e.status=r.SKIP_BECAUSE_BROKEN,t.reject(n)}(e,t,n),e})):e})},t.addAppToUnload=function(e,t,n,r){a[e.name]={app:e,resolve:n,reject:r},Object.defineProperty(a[e.name],"promise",{get:t})},t.getAppUnloadInfo=function(e){return a[e]},t.getAppsToUnload=function(){return Object.keys(a).map(function(e){return a[e].app}).filter(r.isntActive)};var r=n(0),o=n(1),i=n(2),u=n(3),a={};function c(e,t){delete a[e.name],delete e.bootstrap,delete e.mount,delete e.unmount,delete e.unload,e.status=r.NOT_LOADED,t.resolve()}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ensureJQuerySupport=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:window.jQuery;e||window.$&&window.$.fn&&window.$.fn.jquery&&(e=window.$);if(e&&!o){var t=e.fn.on,n=e.fn.off;e.fn.on=function(e,n){return i.call(this,t,window.addEventListener,e,n,arguments)},e.fn.off=function(e,t){return i.call(this,n,window.removeEventListener,e,t,arguments)},o=!0}};var r=n(6),o=!1;function i(e,t,n,o,i){return"string"!=typeof n?e.apply(this,i):(n.split(/\s+/).forEach(function(e){r.routingEventsListeningTo.indexOf(e)>=0&&(t(e,o),n=n.replace(e,""))}),""===n.trim()?this:e.apply(this,i))}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getMountedApps=function(){return l.filter(o.isActive).map(o.toName)},t.getAppNames=f,t.getAppStatus=function(e){var t=(0,u.find)(l,function(t){return t.name===e});return t?t.status:null},t.declareChildApplication=function(e,t,n){return console.warn('declareChildApplication is deprecated and will be removed in the next major version, use "registerApplication" instead'),p(e,t,n)},t.registerApplication=p,t.checkActivityFunctions=function(e){for(var t=[],n=0;n<l.length;n++)l[n].activeWhen(e)&&t.push(l[n].name);return t},t.getAppsToLoad=function(){return l.filter(o.notSkipped).filter(o.isntLoaded).filter(o.shouldBeActive)},t.getAppsToUnmount=function(){return l.filter(o.notSkipped).filter(o.isActive).filter(o.shouldntBeActive)},t.getAppsToMount=function(){return l.filter(o.notSkipped).filter(o.isntActive).filter(o.isLoaded).filter(o.shouldBeActive)},t.unloadChildApplication=function(e,t){return console.warn('unloadChildApplication is deprecated and will be removed in the next major version, use "unloadApplication" instead'),d(e,t)},t.unloadApplication=d;var r=n(10),o=n(0),i=n(5),u=n(4),a=n(7),c=n(9);function s(e){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var l=[];function f(){return l.map(o.toName)}function p(e,t,n){var u,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};if("string"!=typeof e||0===e.length)throw new Error("The first argument must be a non-empty string 'appName'");if(-1!==f().indexOf(e))throw new Error("There is already an app declared with name ".concat(e));if("object"!==s(a)||Array.isArray(a))throw new Error("customProps must be an object");if(!t)throw new Error("The application or loading function is required");if(u="function"!=typeof t?function(){return Promise.resolve(t)}:t,"function"!=typeof n)throw new Error("The activeWhen argument must be a function");l.push({name:e,loadImpl:u,activeWhen:n,status:o.NOT_LOADED,parcels:{},customProps:a}),(0,r.ensureJQuerySupport)(),(0,i.reroute)()}function d(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{waitForUnmount:!1};if("string"!=typeof e)throw new Error("unloadApplication requires a string 'appName'");var n=(0,u.find)(l,function(t){return t.name===e});if(!n)throw new Error("Could not unload application '".concat(e,"' because no such application has been declared"));var r,o=(0,c.getAppUnloadInfo)(n.name);if(t&&t.waitForUnmount){if(o)return o.promise;var i=new Promise(function(e,t){(0,c.addAppToUnload)(n,function(){return i},e,t)});return i}return o?(r=o.promise,m(n,o.resolve,o.reject)):r=new Promise(function(e,t){(0,c.addAppToUnload)(n,function(){return r},e,t),m(n,e,t)}),r}function m(e,t,n){(0,a.toUnmountPromise)(e).then(c.toUnloadPromise).then(function(){t(),setTimeout(function(){(0,i.reroute)()})}).catch(n)}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toMountPromise=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return Promise.resolve().then(function(){return e.status!==o.NOT_MOUNTED?e:(s||(window.dispatchEvent(new a.default("single-spa:before-first-mount")),s=!0),(0,u.reasonableTime)(e.mount((0,c.getProps)(e)),"Mounting application '".concat(e.name,"'"),e.timeouts.mount).then(function(){return e.status=o.MOUNTED,l||(window.dispatchEvent(new a.default("single-spa:first-mount")),l=!0),e}).catch(function(n){if(t){var r=(0,i.transformErr)(n,e);throw e.status=o.SKIP_BECAUSE_BROKEN,r}return(0,i.handleAppError)(n,e),e.status=o.SKIP_BECAUSE_BROKEN,e}))})};var r,o=n(0),i=n(1),u=n(2),a=(r=n(8))&&r.__esModule?r:{default:r},c=n(3);var s=!1,l=!1},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toBootstrapPromise=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return Promise.resolve().then(function(){return e.status!==r.NOT_BOOTSTRAPPED?e:(e.status=r.BOOTSTRAPPING,(0,o.reasonableTime)(e.bootstrap((0,u.getProps)(e)),"Bootstrapping appOrParcel '".concat(e.name,"'"),e.timeouts.bootstrap).then(function(){return e.status=r.NOT_MOUNTED,e}).catch(function(n){if(e.status=r.SKIP_BECAUSE_BROKEN,t){var o=(0,i.transformErr)(n,e);throw o}return(0,i.handleAppError)(n,e),e}))})};var r=n(0),o=n(2),i=n(1),u=n(3)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.mountRootParcel=function(){return d.apply(p,arguments)},t.mountParcel=d;var r=n(15),o=n(0),i=n(13),u=n(12),a=n(18),c=n(7),s=n(2);n(1);function l(e){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var f=0,p={parcels:{}};function d(e,t){var n=this;if(!e||"object"!==l(e)&&"function"!=typeof e)throw new Error("Cannot mount parcel without a config object or config loading function");if(e.name&&"string"!=typeof e.name)throw new Error("Parcel name must be a string, if provided");if("object"!==l(t))throw new Error("Parcel ".concat(name," has invalid customProps -- must be an object"));if(!t.domElement)throw new Error("Parcel ".concat(name," cannot be mounted without a domElement provided as a prop"));var p,d=f++,h="function"==typeof e,v=h?e:function(){return Promise.resolve(e)},O={id:d,parcels:{},status:h?o.LOADING_SOURCE_CODE:o.NOT_BOOTSTRAPPED,customProps:t,owningAppOrParcel:n,unmountThisParcel:function(){if(O.status!==o.MOUNTED)throw new Error("Cannot unmount parcel '".concat(name,"' -- it is in a ").concat(O.status," status"));return(0,c.toUnmountPromise)(O,!0).then(function(e){return O.owningAppOrParcel&&delete O.owningAppOrParcel.parcels[O.id],e}).then(function(e){return y(e),e}).catch(function(e){throw O.status=o.SKIP_BECAUSE_BROKEN,P(e),e})}};n.parcels[d]=O;var b=v();if(!b||"function"!=typeof b.then)throw new Error("When mounting a parcel, the config loading function must return a promise that resolves with the parcel config");var y,P,g=(b=b.then(function(e){if(!e)throw new Error("When mounting a parcel, the config loading function returned a promise that did not resolve with a parcel config");var t=e.name||"parcel-".concat(d);if(!(0,r.validLifecycleFn)(e.bootstrap))throw new Error("Parcel ".concat(t," must have a valid bootstrap function"));if(!(0,r.validLifecycleFn)(e.mount))throw new Error("Parcel ".concat(t," must have a valid mount function"));if(!(0,r.validLifecycleFn)(e.unmount))throw new Error("Parcel ".concat(t," must have a valid unmount function"));if(e.update&&!(0,r.validLifecycleFn)(e.update))throw new Error("Parcel ".concat(t," provided an invalid update function"));var n=(0,r.flattenFnArray)(e.bootstrap),i=(0,r.flattenFnArray)(e.mount),u=(0,r.flattenFnArray)(e.unmount);O.status=o.NOT_BOOTSTRAPPED,O.name=t,O.bootstrap=n,O.mount=i,O.unmount=u,O.timeouts=(0,s.ensureValidAppTimeouts)(O),e.update&&(O.update=(0,r.flattenFnArray)(e.update),p.update=function(e){return O.customProps=e,m((0,a.toUpdatePromise)(O))})})).then(function(){return(0,i.toBootstrapPromise)(O,!0)}),E=g.then(function(){return(0,u.toMountPromise)(O,!0)}),w=new Promise(function(e,t){y=e,P=t});return p={mount:function(){return m(Promise.resolve().then(function(){if(O.status!==o.NOT_MOUNTED)throw new Error("Cannot mount parcel '".concat(name,"' -- it is in a ").concat(O.status," status"));return n.parcels[d]=O,(0,u.toMountPromise)(O)}))},unmount:function(){return m(O.unmountThisParcel())},getStatus:function(){return O.status},loadPromise:m(b),bootstrapPromise:m(g),mountPromise:m(E),unmountPromise:m(w)}}function m(e){return e.then(function(){return null})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validLifecycleFn=function(e){return e&&("function"==typeof e||(t=e,Array.isArray(t)&&!(0,r.find)(t,function(e){return"function"!=typeof e})));var t},t.flattenFnArray=function(e,t){0===(e=Array.isArray(e)?e:[e]).length&&(e=[function(){return Promise.resolve()}]);return function(n){return new Promise(function(r,i){!function u(a){var c=e[a](n);o(c)?c.then(function(){a===e.length-1?r():u(a+1)}).catch(i):i("".concat(t," at index ").concat(a," did not return a promise"))}(0)})}},t.smellsLikeAPromise=o;var r=n(4);function o(e){return e&&"function"==typeof e.then&&"function"==typeof e.catch}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.start=function(){t.started=o=!0,(0,r.reroute)()},t.isStarted=function(){return o},t.started=void 0;var r=n(5),o=!1;t.started=o;setTimeout(function(){o||console.warn("singleSpa.start() has not been called, ".concat(5e3,"ms after single-spa was loaded. Before start() is called, apps can be declared and loaded, but not bootstrapped or mounted. See https://github.com/CanopyTax/single-spa/blob/master/docs/single-spa-api.md#start"))},5e3)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"start",{enumerable:!0,get:function(){return r.start}}),Object.defineProperty(t,"ensureJQuerySupport",{enumerable:!0,get:function(){return o.ensureJQuerySupport}}),Object.defineProperty(t,"setBootstrapMaxTime",{enumerable:!0,get:function(){return i.setBootstrapMaxTime}}),Object.defineProperty(t,"setMountMaxTime",{enumerable:!0,get:function(){return i.setMountMaxTime}}),Object.defineProperty(t,"setUnmountMaxTime",{enumerable:!0,get:function(){return i.setUnmountMaxTime}}),Object.defineProperty(t,"setUnloadMaxTime",{enumerable:!0,get:function(){return i.setUnloadMaxTime}}),Object.defineProperty(t,"registerApplication",{enumerable:!0,get:function(){return u.registerApplication}}),Object.defineProperty(t,"getMountedApps",{enumerable:!0,get:function(){return u.getMountedApps}}),Object.defineProperty(t,"getAppStatus",{enumerable:!0,get:function(){return u.getAppStatus}}),Object.defineProperty(t,"unloadApplication",{enumerable:!0,get:function(){return u.unloadApplication}}),Object.defineProperty(t,"checkActivityFunctions",{enumerable:!0,get:function(){return u.checkActivityFunctions}}),Object.defineProperty(t,"getAppNames",{enumerable:!0,get:function(){return u.getAppNames}}),Object.defineProperty(t,"declareChildApplication",{enumerable:!0,get:function(){return u.declareChildApplication}}),Object.defineProperty(t,"unloadChildApplication",{enumerable:!0,get:function(){return u.unloadChildApplication}}),Object.defineProperty(t,"navigateToUrl",{enumerable:!0,get:function(){return a.navigateToUrl}}),Object.defineProperty(t,"triggerAppChange",{enumerable:!0,get:function(){return c.reroute}}),Object.defineProperty(t,"addErrorHandler",{enumerable:!0,get:function(){return s.addErrorHandler}}),Object.defineProperty(t,"removeErrorHandler",{enumerable:!0,get:function(){return s.removeErrorHandler}}),Object.defineProperty(t,"mountRootParcel",{enumerable:!0,get:function(){return l.mountRootParcel}}),Object.defineProperty(t,"NOT_LOADED",{enumerable:!0,get:function(){return f.NOT_LOADED}}),Object.defineProperty(t,"LOADING_SOURCE_CODE",{enumerable:!0,get:function(){return f.LOADING_SOURCE_CODE}}),Object.defineProperty(t,"NOT_BOOTSTRAPPED",{enumerable:!0,get:function(){return f.NOT_BOOTSTRAPPED}}),Object.defineProperty(t,"BOOTSTRAPPING",{enumerable:!0,get:function(){return f.BOOTSTRAPPING}}),Object.defineProperty(t,"NOT_MOUNTED",{enumerable:!0,get:function(){return f.NOT_MOUNTED}}),Object.defineProperty(t,"MOUNTING",{enumerable:!0,get:function(){return f.MOUNTING}}),Object.defineProperty(t,"UPDATING",{enumerable:!0,get:function(){return f.UPDATING}}),Object.defineProperty(t,"MOUNTED",{enumerable:!0,get:function(){return f.MOUNTED}}),Object.defineProperty(t,"UNMOUNTING",{enumerable:!0,get:function(){return f.UNMOUNTING}}),Object.defineProperty(t,"SKIP_BECAUSE_BROKEN",{enumerable:!0,get:function(){return f.SKIP_BECAUSE_BROKEN}});var r=n(16),o=n(10),i=n(2),u=n(11),a=n(6),c=n(5),s=n(1),l=n(14),f=n(0)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toUpdatePromise=function(e){return Promise.resolve().then(function(){if(e.status!==r.MOUNTED)throw new Error("Cannot update parcel '".concat(e.name,"' because it is not mounted"));return e.status=r.UPDATING,(0,i.reasonableTime)(e.update((0,u.getProps)(e)),"Updating parcel '".concat(e.name,"'"),e.timeouts.mount).then(function(){return e.status=r.MOUNTED,e}).catch(function(t){var n=(0,o.transformErr)(t,e);throw e.status=r.SKIP_BECAUSE_BROKEN,n})})};var r=n(0),o=n(1),i=n(2),u=n(3)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toLoadPromise=function(e){return Promise.resolve().then(function(){return e.status!==r.NOT_LOADED?e:(e.status=r.LOADING_SOURCE_CODE,Promise.resolve().then(function(){var n=e.loadImpl((0,a.getProps)(e));if(!(0,u.smellsLikeAPromise)(n))throw new Error("single-spa loading function did not return a promise. Check the second argument to registerApplication('".concat(e.name,"', loadingFunction, activityFunction)"));return n.then(function(n){var a;return"object"!==c(t=n)&&(a="does not export anything"),(0,u.validLifecycleFn)(t.bootstrap)||(a="does not export a bootstrap function or array of functions"),(0,u.validLifecycleFn)(t.mount)||(a="does not export a mount function or array of functions"),(0,u.validLifecycleFn)(t.unmount)||(a="does not export an unmount function or array of functions"),a?((0,i.handleAppError)(a,e),e.status=r.SKIP_BECAUSE_BROKEN,e):(e.status=r.NOT_BOOTSTRAPPED,e.bootstrap=(0,u.flattenFnArray)(t.bootstrap,"App '".concat(e.name,"' bootstrap function")),e.mount=(0,u.flattenFnArray)(t.mount,"App '".concat(e.name,"' mount function")),e.unmount=(0,u.flattenFnArray)(t.unmount,"App '".concat(e.name,"' unmount function")),e.unload=(0,u.flattenFnArray)(t.unload||[],"App '".concat(e.name,"' unload function")),e.timeouts=(0,o.ensureValidAppTimeouts)(t.timeouts),e)})}).catch(function(t){return(0,i.handleAppError)(t,e),e.status=r.SKIP_BECAUSE_BROKEN,e}));var t})};var r=n(0),o=n(2),i=n(1),u=(n(4),n(15)),a=n(3);function c(e){return(c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}},function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){e.exports=n(17)}])}); | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("singleSpa",[],t):"object"==typeof exports?exports.singleSpa=t():e.singleSpa=t()}(window,function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=21)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isActive=s,t.isntActive=function(e){return!s(e)},t.isLoaded=l,t.isntLoaded=function(e){return!l(e)},t.shouldBeActive=function(e){try{return e.activeWhen(window.location)}catch(t){(0,r.handleAppError)(t,e),e.status=c}},t.shouldntBeActive=function(e){try{return!e.activeWhen(window.location)}catch(t){(0,r.handleAppError)(t,e),e.status=c}},t.notBootstrapped=function(e){return e.status!==u},t.notSkipped=function(e){return e!==c&&(!e||e.status!==c)},t.toName=function(e){return e.name},t.SKIP_BECAUSE_BROKEN=t.UNLOADING=t.UNMOUNTING=t.UPDATING=t.MOUNTED=t.MOUNTING=t.NOT_MOUNTED=t.BOOTSTRAPPING=t.NOT_BOOTSTRAPPED=t.LOADING_SOURCE_CODE=t.NOT_LOADED=void 0;var r=n(1);!function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}t.default=e}(n(9));var o="NOT_LOADED";t.NOT_LOADED=o;var i="LOADING_SOURCE_CODE";t.LOADING_SOURCE_CODE=i;var u="NOT_BOOTSTRAPPED";t.NOT_BOOTSTRAPPED=u;t.BOOTSTRAPPING="BOOTSTRAPPING";t.NOT_MOUNTED="NOT_MOUNTED";t.MOUNTING="MOUNTING";var a="MOUNTED";t.MOUNTED=a;t.UPDATING="UPDATING";t.UNMOUNTING="UNMOUNTING";t.UNLOADING="UNLOADING";var c="SKIP_BECAUSE_BROKEN";function s(e){return e.status===a}function l(e){return e.status!==o&&e.status!==i}t.SKIP_BECAUSE_BROKEN=c},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.handleAppError=function(e,t){var n=i(e,t);o.length?o.forEach(function(e){return e(n)}):setTimeout(function(){throw n})},t.addErrorHandler=function(e){if("function"!=typeof e)throw new Error("a single-spa error handler must be a function");o.push(e)},t.removeErrorHandler=function(e){if("function"!=typeof e)throw new Error("a single-spa error handler must be a function");var t=!1;return o=o.filter(function(n){var r=n===e;return t=t||r,!r}),t},t.transformErr=i;var r;(r=n(8))&&r.__esModule;var o=[];function i(e,t){var n,r=t.unmountThisParcel?"Parcel":"Application",o="".concat(r," '").concat(t.name,"' died in status ").concat(t.status,": ");if(e instanceof Error){try{e.message=o+e.message}catch(e){}n=e}else{console.warn("While ".concat(t.status,", '").concat(t.name,"' rejected its lifecycle function promise with a non-Error. This will cause stack traces to not be accurate."));try{n=new Error(o+JSON.stringify(e))}catch(t){n=e}}return n.appName=t.name,n.name=t.name,n}},function(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}Object.defineProperty(t,"__esModule",{value:!0}),t.setBootstrapMaxTime=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if("number"!=typeof e||e<=0)throw new Error("bootstrap max time must be a positive integer number of milliseconds");o.bootstrap={millis:e,dieOnTimeout:t}},t.setMountMaxTime=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if("number"!=typeof e||e<=0)throw new Error("mount max time must be a positive integer number of milliseconds");o.mount={millis:e,dieOnTimeout:t}},t.setUnmountMaxTime=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if("number"!=typeof e||e<=0)throw new Error("unmount max time must be a positive integer number of milliseconds");o.unmount={millis:e,dieOnTimeout:t}},t.setUnloadMaxTime=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if("number"!=typeof e||e<=0)throw new Error("unload max time must be a positive integer number of milliseconds");o.unload={millis:e,dieOnTimeout:t}},t.reasonableTime=function(e,t,n){var r=1e3;return new Promise(function(o,i){var u=!1,a=!1;function c(e){if(!u)if(!0===e)a=!0,n.dieOnTimeout?i("".concat(t," did not resolve or reject for ").concat(n.millis," milliseconds")):console.error("".concat(t," did not resolve or reject for ").concat(n.millis," milliseconds -- we're no longer going to warn you about it."));else if(!a){var o=e,s=o*r;console.warn("".concat(t," did not resolve or reject within ").concat(s," milliseconds")),s+r<n.millis&&setTimeout(function(){return c(o+1)},r)}}e.then(function(e){u=!0,o(e)}).catch(function(e){u=!0,i(e)}),setTimeout(function(){return c(1)},r),setTimeout(function(){return c(!0)},n.millis)})},t.ensureValidAppTimeouts=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},o=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),o.forEach(function(t){r(e,t,n[t])})}return e}({},o,e)};var o={bootstrap:{millis:4e3,dieOnTimeout:!1},mount:{millis:3e3,dieOnTimeout:!1},unmount:{millis:3e3,dieOnTimeout:!1},unload:{millis:3e3,dieOnTimeout:!1}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getProps=function(e){var t=function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){i(e,t,n[t])})}return e}({},e.customProps,{name:e.name,mountParcel:o.mountParcel.bind(e),singleSpa:r});e.unmountThisParcel&&(t.unmountSelf=e.unmountThisParcel);return t};var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(9)),o=n(15);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.find=function(e,t){for(var n=0;n<e.length;n++)if(t(e[n]))return e[n];return null}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.reroute=function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];var n=arguments.length>1?arguments[1]:void 0;if(d)return new Promise(function(e,t){m.push({resolve:e,reject:t,eventArguments:n})});d=!0;var r=!0;return(0,i.isStarted)()?Promise.resolve().then(function(){var e;n&&n[0]&&(e={detail:n[0]}),window.dispatchEvent(new o.default("single-spa:before-routing-event",e));var i=(0,p.getAppsToUnload)().map(p.toUnloadPromise),f=(0,l.getAppsToUnmount)().map(s.toUnmountPromise).map(function(e){return e.then(p.toUnloadPromise)}),d=f.concat(i);d.length>0&&(r=!1);var m=Promise.all(d),O=(0,l.getAppsToLoad)(),b=O.map(function(e){return(0,u.toLoadPromise)(e).then(a.toBootstrapPromise).then(function(e){return m.then(function(){return(0,c.toMountPromise)(e)})})});b.length>0&&(r=!1);var y=(0,l.getAppsToMount)().filter(function(e){return O.indexOf(e)<0}).map(function(e){return(0,a.toBootstrapPromise)(e).then(function(){return m}).then(function(){return(0,c.toMountPromise)(e)})});return y.length>0&&(r=!1),m.catch(function(e){throw v(),e}).then(function(){return v(),Promise.all(b.concat(y)).catch(function(e){throw t.forEach(function(t){return t.reject(e)}),e}).then(function(){return h(!1)})})}):Promise.resolve().then(function(){var e=(0,l.getAppsToLoad)().map(u.toLoadPromise);return e.length>0&&(r=!1),Promise.all(e).then(h).catch(function(e){throw v(),e})});function h(){var n=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],i=(0,l.getMountedApps)();n&&v(),t.forEach(function(e){return e.resolve(i)});try{var u=r?"single-spa:no-app-change":"single-spa:app-change";window.dispatchEvent(new o.default(u)),window.dispatchEvent(new o.default("single-spa:routing-event"))}catch(e){setTimeout(function(){throw e})}if(d=!1,m.length>0){var a=m;m=[],e(a)}return i}function v(){t.forEach(function(e){(0,f.callCapturedEventListeners)(e.eventArguments)}),(0,f.callCapturedEventListeners)(n)}};var r,o=(r=n(8))&&r.__esModule?r:{default:r},i=n(17),u=n(19),a=n(14),c=n(13),s=n(7),l=n(12),f=(n(0),n(6)),p=n(10);var d=!1,m=[]},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.navigateToUrl=a,t.callCapturedEventListeners=function(e){var t=this;if(e){var n=e[0].type;u.indexOf(n)>=0&&i[n].forEach(function(n){n.apply(t,e)})}},t.routingEventsListeningTo=void 0;var r=n(5),o=n(4),i={hashchange:[],popstate:[]},u=["hashchange","popstate"];function a(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("string"==typeof e)t=e;else if(this&&this.href)t=this.href;else{if(!(e&&e.currentTarget&&e.currentTarget.href&&e.preventDefault))throw new Error("singleSpaNavigate 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");t=e.currentTarget.href,e.preventDefault()}var r=d(window.location.href),o=d(t);if(0===t.indexOf("#"))window.location.hash="#"+o.anchor;else if(r.host!==o.host&&o.host){if(n.isTestingEnv)return{wouldHaveReloadedThePage:!0};window.location.href=t}else!function(e,t){return t===e||t==="/"+e}(o.path,r.path)?window.history.pushState(null,null,t):window.location.hash="#"+o.anchor}function c(){(0,r.reroute)([],arguments)}t.routingEventsListeningTo=u,window.addEventListener("hashchange",c),window.addEventListener("popstate",c);var s=window.addEventListener,l=window.removeEventListener;window.addEventListener=function(e,t){if(!("function"==typeof t&&u.indexOf(e)>=0)||(0,o.find)(i[e],function(e){return e===t}))return s.apply(this,arguments);i[e].push(t)},window.removeEventListener=function(e,t){if(!("function"==typeof t&&u.indexOf(e)>=0))return l.apply(this,arguments);i[e]=i[e].filter(function(e){return e!==t})};var f=window.history.pushState;window.history.pushState=function(e){var t=f.apply(this,arguments);return(0,r.reroute)(),t};var p=window.history.replaceState;function d(e){for(var t={strictMode:!0,key:["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],q:{name:"queryKey",parser:/(?:^|&)([^&=]*)=?([^&]*)/g},parser:{strict:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}},n=t.parser[t.strictMode?"strict":"loose"].exec(e),r={},o=14;o--;)r[t.key[o]]=n[o]||"";return r[t.q.name]={},r[t.key[12]].replace(t.q.parser,function(e,n,o){n&&(r[t.q.name][n]=o)}),r}window.history.replaceState=function(){var e=p.apply(this,arguments);return(0,r.reroute)(),e},window.singleSpaNavigate=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toUnmountPromise=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return Promise.resolve().then(function(){if(e.status!==r.MOUNTED)return e;e.status=r.UNMOUNTING;var n,a=Object.keys(e.parcels).map(function(t){return e.parcels[t].unmountThisParcel()});return Promise.all(a).then(c,function(n){return c().then(function(){var i=new Error(n.message);if(t){var u=(0,o.transformErr)(i,e);throw e.status=r.SKIP_BECAUSE_BROKEN,u}(0,o.handleAppError)(i,e),e.status=r.SKIP_BECAUSE_BROKEN})}).then(function(){return e});function c(){return(0,i.reasonableTime)(e.unmount((0,u.getProps)(e)),"Unmounting application ".concat(e.name,"'"),e.timeouts.unmount).then(function(){n||(e.status=r.NOT_MOUNTED)}).catch(function(n){if(t){var i=(0,o.transformErr)(n,e);throw e.status=r.SKIP_BECAUSE_BROKEN,i}(0,o.handleAppError)(n,e),e.status=r.SKIP_BECAUSE_BROKEN})}})};var r=n(0),o=n(1),i=n(2),u=n(3)},function(e,t,n){(function(t){var n=t.CustomEvent;e.exports=function(){try{var e=new n("cat",{detail:{foo:"bar"}});return"cat"===e.type&&"bar"===e.detail.foo}catch(e){}return!1}()?n:"undefined"!=typeof document&&"function"==typeof document.createEvent?function(e,t){var n=document.createEvent("CustomEvent");return t?n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail):n.initCustomEvent(e,!1,!1,void 0),n}:function(e,t){var n=document.createEventObject();return n.type=e,t?(n.bubbles=Boolean(t.bubbles),n.cancelable=Boolean(t.cancelable),n.detail=t.detail):(n.bubbles=!1,n.cancelable=!1,n.detail=void 0),n}}).call(this,n(20))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"start",{enumerable:!0,get:function(){return r.start}}),Object.defineProperty(t,"ensureJQuerySupport",{enumerable:!0,get:function(){return o.ensureJQuerySupport}}),Object.defineProperty(t,"setBootstrapMaxTime",{enumerable:!0,get:function(){return i.setBootstrapMaxTime}}),Object.defineProperty(t,"setMountMaxTime",{enumerable:!0,get:function(){return i.setMountMaxTime}}),Object.defineProperty(t,"setUnmountMaxTime",{enumerable:!0,get:function(){return i.setUnmountMaxTime}}),Object.defineProperty(t,"setUnloadMaxTime",{enumerable:!0,get:function(){return i.setUnloadMaxTime}}),Object.defineProperty(t,"registerApplication",{enumerable:!0,get:function(){return u.registerApplication}}),Object.defineProperty(t,"getMountedApps",{enumerable:!0,get:function(){return u.getMountedApps}}),Object.defineProperty(t,"getAppStatus",{enumerable:!0,get:function(){return u.getAppStatus}}),Object.defineProperty(t,"unloadApplication",{enumerable:!0,get:function(){return u.unloadApplication}}),Object.defineProperty(t,"checkActivityFunctions",{enumerable:!0,get:function(){return u.checkActivityFunctions}}),Object.defineProperty(t,"getAppNames",{enumerable:!0,get:function(){return u.getAppNames}}),Object.defineProperty(t,"declareChildApplication",{enumerable:!0,get:function(){return u.declareChildApplication}}),Object.defineProperty(t,"unloadChildApplication",{enumerable:!0,get:function(){return u.unloadChildApplication}}),Object.defineProperty(t,"navigateToUrl",{enumerable:!0,get:function(){return a.navigateToUrl}}),Object.defineProperty(t,"triggerAppChange",{enumerable:!0,get:function(){return c.reroute}}),Object.defineProperty(t,"addErrorHandler",{enumerable:!0,get:function(){return s.addErrorHandler}}),Object.defineProperty(t,"removeErrorHandler",{enumerable:!0,get:function(){return s.removeErrorHandler}}),Object.defineProperty(t,"mountRootParcel",{enumerable:!0,get:function(){return l.mountRootParcel}}),Object.defineProperty(t,"NOT_LOADED",{enumerable:!0,get:function(){return f.NOT_LOADED}}),Object.defineProperty(t,"LOADING_SOURCE_CODE",{enumerable:!0,get:function(){return f.LOADING_SOURCE_CODE}}),Object.defineProperty(t,"NOT_BOOTSTRAPPED",{enumerable:!0,get:function(){return f.NOT_BOOTSTRAPPED}}),Object.defineProperty(t,"BOOTSTRAPPING",{enumerable:!0,get:function(){return f.BOOTSTRAPPING}}),Object.defineProperty(t,"NOT_MOUNTED",{enumerable:!0,get:function(){return f.NOT_MOUNTED}}),Object.defineProperty(t,"MOUNTING",{enumerable:!0,get:function(){return f.MOUNTING}}),Object.defineProperty(t,"UPDATING",{enumerable:!0,get:function(){return f.UPDATING}}),Object.defineProperty(t,"MOUNTED",{enumerable:!0,get:function(){return f.MOUNTED}}),Object.defineProperty(t,"UNMOUNTING",{enumerable:!0,get:function(){return f.UNMOUNTING}}),Object.defineProperty(t,"SKIP_BECAUSE_BROKEN",{enumerable:!0,get:function(){return f.SKIP_BECAUSE_BROKEN}});var r=n(17),o=n(11),i=n(2),u=n(12),a=n(6),c=n(5),s=n(1),l=n(15),f=n(0)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toUnloadPromise=function(e){return Promise.resolve().then(function(){var t=a[e.name];return t?e.status===r.NOT_LOADED?(c(e,t),e):e.status===r.UNLOADING?t.promise.then(function(){return e}):e.status!==r.NOT_MOUNTED?e:(e.status=r.UNLOADING,(0,i.reasonableTime)(e.unload((0,u.getProps)(e)),"Unloading application '".concat(e.name,"'"),e.timeouts.unload).then(function(){return c(e,t),e}).catch(function(n){return function(e,t,n){delete a[e.name],delete e.bootstrap,delete e.mount,delete e.unmount,delete e.unload,(0,o.handleAppError)(n,e),e.status=r.SKIP_BECAUSE_BROKEN,t.reject(n)}(e,t,n),e})):e})},t.addAppToUnload=function(e,t,n,r){a[e.name]={app:e,resolve:n,reject:r},Object.defineProperty(a[e.name],"promise",{get:t})},t.getAppUnloadInfo=function(e){return a[e]},t.getAppsToUnload=function(){return Object.keys(a).map(function(e){return a[e].app}).filter(r.isntActive)};var r=n(0),o=n(1),i=n(2),u=n(3),a={};function c(e,t){delete a[e.name],delete e.bootstrap,delete e.mount,delete e.unmount,delete e.unload,e.status=r.NOT_LOADED,t.resolve()}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ensureJQuerySupport=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:window.jQuery;e||window.$&&window.$.fn&&window.$.fn.jquery&&(e=window.$);if(e&&!o){var t=e.fn.on,n=e.fn.off;e.fn.on=function(e,n){return i.call(this,t,window.addEventListener,e,n,arguments)},e.fn.off=function(e,t){return i.call(this,n,window.removeEventListener,e,t,arguments)},o=!0}};var r=n(6),o=!1;function i(e,t,n,o,i){return"string"!=typeof n?e.apply(this,i):(n.split(/\s+/).forEach(function(e){r.routingEventsListeningTo.indexOf(e)>=0&&(t(e,o),n=n.replace(e,""))}),""===n.trim()?this:e.apply(this,i))}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getMountedApps=function(){return l.filter(o.isActive).map(o.toName)},t.getAppNames=f,t.getAppStatus=function(e){var t=(0,u.find)(l,function(t){return t.name===e});return t?t.status:null},t.declareChildApplication=function(e,t,n){return console.warn('declareChildApplication is deprecated and will be removed in the next major version, use "registerApplication" instead'),p(e,t,n)},t.registerApplication=p,t.checkActivityFunctions=function(e){for(var t=[],n=0;n<l.length;n++)l[n].activeWhen(e)&&t.push(l[n].name);return t},t.getAppsToLoad=function(){return l.filter(o.notSkipped).filter(o.isntLoaded).filter(o.shouldBeActive)},t.getAppsToUnmount=function(){return l.filter(o.notSkipped).filter(o.isActive).filter(o.shouldntBeActive)},t.getAppsToMount=function(){return l.filter(o.notSkipped).filter(o.isntActive).filter(o.isLoaded).filter(o.shouldBeActive)},t.unloadChildApplication=function(e,t){return console.warn('unloadChildApplication is deprecated and will be removed in the next major version, use "unloadApplication" instead'),d(e,t)},t.unloadApplication=d;var r=n(11),o=n(0),i=n(5),u=n(4),a=n(7),c=n(10);function s(e){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var l=[];function f(){return l.map(o.toName)}function p(e,t,n){var u,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};if("string"!=typeof e||0===e.length)throw new Error("The first argument must be a non-empty string 'appName'");if(-1!==f().indexOf(e))throw new Error("There is already an app declared with name ".concat(e));if("object"!==s(a)||Array.isArray(a))throw new Error("customProps must be an object");if(!t)throw new Error("The application or loading function is required");if(u="function"!=typeof t?function(){return Promise.resolve(t)}:t,"function"!=typeof n)throw new Error("The activeWhen argument must be a function");l.push({name:e,loadImpl:u,activeWhen:n,status:o.NOT_LOADED,parcels:{},customProps:a}),(0,r.ensureJQuerySupport)(),(0,i.reroute)()}function d(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{waitForUnmount:!1};if("string"!=typeof e)throw new Error("unloadApplication requires a string 'appName'");var n=(0,u.find)(l,function(t){return t.name===e});if(!n)throw new Error("Could not unload application '".concat(e,"' because no such application has been declared"));var r,o=(0,c.getAppUnloadInfo)(n.name);if(t&&t.waitForUnmount){if(o)return o.promise;var i=new Promise(function(e,t){(0,c.addAppToUnload)(n,function(){return i},e,t)});return i}return o?(r=o.promise,m(n,o.resolve,o.reject)):r=new Promise(function(e,t){(0,c.addAppToUnload)(n,function(){return r},e,t),m(n,e,t)}),r}function m(e,t,n){(0,a.toUnmountPromise)(e).then(c.toUnloadPromise).then(function(){t(),setTimeout(function(){(0,i.reroute)()})}).catch(n)}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toMountPromise=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return Promise.resolve().then(function(){return e.status!==o.NOT_MOUNTED?e:(s||(window.dispatchEvent(new a.default("single-spa:before-first-mount")),s=!0),(0,u.reasonableTime)(e.mount((0,c.getProps)(e)),"Mounting application '".concat(e.name,"'"),e.timeouts.mount).then(function(){return e.status=o.MOUNTED,l||(window.dispatchEvent(new a.default("single-spa:first-mount")),l=!0),e}).catch(function(n){if(t){var r=(0,i.transformErr)(n,e);throw e.status=o.SKIP_BECAUSE_BROKEN,r}return(0,i.handleAppError)(n,e),e.status=o.SKIP_BECAUSE_BROKEN,e}))})};var r,o=n(0),i=n(1),u=n(2),a=(r=n(8))&&r.__esModule?r:{default:r},c=n(3);var s=!1,l=!1},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toBootstrapPromise=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return Promise.resolve().then(function(){return e.status!==r.NOT_BOOTSTRAPPED?e:(e.status=r.BOOTSTRAPPING,(0,o.reasonableTime)(e.bootstrap((0,u.getProps)(e)),"Bootstrapping appOrParcel '".concat(e.name,"'"),e.timeouts.bootstrap).then(function(){return e.status=r.NOT_MOUNTED,e}).catch(function(n){if(e.status=r.SKIP_BECAUSE_BROKEN,t){var o=(0,i.transformErr)(n,e);throw o}return(0,i.handleAppError)(n,e),e}))})};var r=n(0),o=n(2),i=n(1),u=n(3)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.mountRootParcel=function(){return d.apply(p,arguments)},t.mountParcel=d;var r=n(16),o=n(0),i=n(14),u=n(13),a=n(18),c=n(7),s=n(2);n(1);function l(e){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var f=0,p={parcels:{}};function d(e,t){var n=this;if(!e||"object"!==l(e)&&"function"!=typeof e)throw new Error("Cannot mount parcel without a config object or config loading function");if(e.name&&"string"!=typeof e.name)throw new Error("Parcel name must be a string, if provided");if("object"!==l(t))throw new Error("Parcel ".concat(name," has invalid customProps -- must be an object"));if(!t.domElement)throw new Error("Parcel ".concat(name," cannot be mounted without a domElement provided as a prop"));var p,d=f++,h="function"==typeof e,v=h?e:function(){return Promise.resolve(e)},O={id:d,parcels:{},status:h?o.LOADING_SOURCE_CODE:o.NOT_BOOTSTRAPPED,customProps:t,owningAppOrParcel:n,unmountThisParcel:function(){if(O.status!==o.MOUNTED)throw new Error("Cannot unmount parcel '".concat(name,"' -- it is in a ").concat(O.status," status"));return(0,c.toUnmountPromise)(O,!0).then(function(e){return O.owningAppOrParcel&&delete O.owningAppOrParcel.parcels[O.id],e}).then(function(e){return y(e),e}).catch(function(e){throw O.status=o.SKIP_BECAUSE_BROKEN,P(e),e})}};n.parcels[d]=O;var b=v();if(!b||"function"!=typeof b.then)throw new Error("When mounting a parcel, the config loading function must return a promise that resolves with the parcel config");var y,P,g=(b=b.then(function(e){if(!e)throw new Error("When mounting a parcel, the config loading function returned a promise that did not resolve with a parcel config");var t=e.name||"parcel-".concat(d);if(!(0,r.validLifecycleFn)(e.bootstrap))throw new Error("Parcel ".concat(t," must have a valid bootstrap function"));if(!(0,r.validLifecycleFn)(e.mount))throw new Error("Parcel ".concat(t," must have a valid mount function"));if(!(0,r.validLifecycleFn)(e.unmount))throw new Error("Parcel ".concat(t," must have a valid unmount function"));if(e.update&&!(0,r.validLifecycleFn)(e.update))throw new Error("Parcel ".concat(t," provided an invalid update function"));var n=(0,r.flattenFnArray)(e.bootstrap),i=(0,r.flattenFnArray)(e.mount),u=(0,r.flattenFnArray)(e.unmount);O.status=o.NOT_BOOTSTRAPPED,O.name=t,O.bootstrap=n,O.mount=i,O.unmount=u,O.timeouts=(0,s.ensureValidAppTimeouts)(O),e.update&&(O.update=(0,r.flattenFnArray)(e.update),p.update=function(e){return O.customProps=e,m((0,a.toUpdatePromise)(O))})})).then(function(){return(0,i.toBootstrapPromise)(O,!0)}),E=g.then(function(){return(0,u.toMountPromise)(O,!0)}),w=new Promise(function(e,t){y=e,P=t});return p={mount:function(){return m(Promise.resolve().then(function(){if(O.status!==o.NOT_MOUNTED)throw new Error("Cannot mount parcel '".concat(name,"' -- it is in a ").concat(O.status," status"));return n.parcels[d]=O,(0,u.toMountPromise)(O)}))},unmount:function(){return m(O.unmountThisParcel())},getStatus:function(){return O.status},loadPromise:m(b),bootstrapPromise:m(g),mountPromise:m(E),unmountPromise:m(w)}}function m(e){return e.then(function(){return null})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validLifecycleFn=function(e){return e&&("function"==typeof e||(t=e,Array.isArray(t)&&!(0,r.find)(t,function(e){return"function"!=typeof e})));var t},t.flattenFnArray=function(e,t){0===(e=Array.isArray(e)?e:[e]).length&&(e=[function(){return Promise.resolve()}]);return function(n){return new Promise(function(r,i){!function u(a){var c=e[a](n);o(c)?c.then(function(){a===e.length-1?r():u(a+1)}).catch(i):i("".concat(t," at index ").concat(a," did not return a promise"))}(0)})}},t.smellsLikeAPromise=o;var r=n(4);function o(e){return e&&"function"==typeof e.then&&"function"==typeof e.catch}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.start=function(){t.started=o=!0,(0,r.reroute)()},t.isStarted=function(){return o},t.started=void 0;var r=n(5),o=!1;t.started=o;setTimeout(function(){o||console.warn("singleSpa.start() has not been called, ".concat(5e3,"ms after single-spa was loaded. Before start() is called, apps can be declared and loaded, but not bootstrapped or mounted. See https://github.com/CanopyTax/single-spa/blob/master/docs/single-spa-api.md#start"))},5e3)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toUpdatePromise=function(e){return Promise.resolve().then(function(){if(e.status!==r.MOUNTED)throw new Error("Cannot update parcel '".concat(e.name,"' because it is not mounted"));return e.status=r.UPDATING,(0,i.reasonableTime)(e.update((0,u.getProps)(e)),"Updating parcel '".concat(e.name,"'"),e.timeouts.mount).then(function(){return e.status=r.MOUNTED,e}).catch(function(t){var n=(0,o.transformErr)(t,e);throw e.status=r.SKIP_BECAUSE_BROKEN,n})})};var r=n(0),o=n(1),i=n(2),u=n(3)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toLoadPromise=function(e){return Promise.resolve().then(function(){return e.status!==r.NOT_LOADED?e:(e.status=r.LOADING_SOURCE_CODE,Promise.resolve().then(function(){var n=e.loadImpl((0,a.getProps)(e));if(!(0,u.smellsLikeAPromise)(n))throw new Error("single-spa loading function did not return a promise. Check the second argument to registerApplication('".concat(e.name,"', loadingFunction, activityFunction)"));return n.then(function(n){var a;return"object"!==c(t=n)&&(a="does not export anything"),(0,u.validLifecycleFn)(t.bootstrap)||(a="does not export a bootstrap function or array of functions"),(0,u.validLifecycleFn)(t.mount)||(a="does not export a mount function or array of functions"),(0,u.validLifecycleFn)(t.unmount)||(a="does not export an unmount function or array of functions"),a?((0,i.handleAppError)(a,e),e.status=r.SKIP_BECAUSE_BROKEN,e):(e.status=r.NOT_BOOTSTRAPPED,e.bootstrap=(0,u.flattenFnArray)(t.bootstrap,"App '".concat(e.name,"' bootstrap function")),e.mount=(0,u.flattenFnArray)(t.mount,"App '".concat(e.name,"' mount function")),e.unmount=(0,u.flattenFnArray)(t.unmount,"App '".concat(e.name,"' unmount function")),e.unload=(0,u.flattenFnArray)(t.unload||[],"App '".concat(e.name,"' unload function")),e.timeouts=(0,o.ensureValidAppTimeouts)(t.timeouts),e)})}).catch(function(t){return(0,i.handleAppError)(t,e),e.status=r.SKIP_BECAUSE_BROKEN,e}));var t})};var r=n(0),o=n(2),i=n(1),u=(n(4),n(16)),a=n(3);function c(e){return(c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}},function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){e.exports=n(9)}])}); | ||
//# sourceMappingURL=single-spa.js.map |
{ | ||
"name": "single-spa", | ||
"version": "4.0.0", | ||
"version": "4.0.1", | ||
"description": "Multiple applications, one page", | ||
@@ -5,0 +5,0 @@ "main": "lib/single-spa.js", |
122
README.md
@@ -1,3 +0,4 @@ | ||
# single-spa | ||
<img src="https://single-spa.js.org/img/logo-white-bgblue.svg" width="50" height="50"> | ||
[![npm version](https://img.shields.io/npm/v/single-spa.svg?style=flat-square)](https://www.npmjs.org/package/single-spa) | ||
@@ -8,2 +9,4 @@ [![Build Status](https://img.shields.io/travis/CanopyTax/single-spa/master.svg?style=flat-square)](https://travis-ci.org/CanopyTax/single-spa) | ||
# single-spa | ||
[Join the chat on Slack](https://join.slack.com/t/single-spa/shared_invite/enQtMzIwMTcxNTU3ODQyLTM1Y2U1OWMzNTNjOWYyZDBlMDJhN2VkYzk3MDI2NzQ2Nzg0MzMzNjVhNWE2YjVhMTcxNjFkOWYzMjllMmUxMjk) | ||
@@ -19,116 +22,37 @@ | ||
## Demo and examples | ||
A [live demo](https://single-spa.surge.sh) is available and the source code for that demo is available in the [single-spa-examples](https://github.com/CanopyTax/single-spa-examples) repository. | ||
Also, you can check out [a simple webpack starter project](https://github.com/joeldenning/simple-single-spa-webpack-example) which is simpler and hopefully easier to get started with. | ||
## Architectural Overview | ||
Single-spa takes inspiration from React component lifecycles by applying lifecycles to entire applications. | ||
It started out of a desire to use React + react-router instead of being forever stuck with our AngularJS + ui-router application, but now single-spa supports almost any framework coexisting with any other. Since Javascript is notorious for the short-life of its many frameworks, we decided to make it easy to use whichever frameworks you want. | ||
Apps built with single-spa are made up of the following pieces: | ||
1. Many [applications](/docs/applications.md), each of which is sort of like an entire SPA itself. Applications respond to url routing events and must know how to bootstrap, mount, and unmount themselves from the DOM. The main difference between a SPA and an application is that applications must coexist together and do not each have their own html page. | ||
For example, your React or Angular applications are applications which are either active or dormant. When active, they listen to url routing events and put content on the DOM. When dormant, they do not listen to url routing events and are totally removed from the DOM. | ||
2. A [single-spa-config](/docs/single-spa-config.md). The single-spa-config is the html page, plus the javascript that registers applications with single-spa. Each application is registered with three things: | ||
1. A name | ||
2. A function to load the application's code | ||
3. A function that determines when the application is active/dormant. | ||
## How hard will it be to use single-spa? | ||
single-spa works with es5, es6+, typescript, webpack, systemjs, gulp, grunt, bower, ember-cli, or really any build system you can think of. You can npm install it, jspm install it, or even just use a `<script>` tag if you prefer. If you're not starting your application from scratch, you'll have to [migrate your SPA](/docs/migrating-existing-spas.md) to become a single-spa application. | ||
single-spa works in Chrome, Firefox, Safari, IE11, and Edge. | ||
## Isn't single-spa sort of a redundant name? | ||
Yep | ||
## Documentation | ||
See the [docs](/docs). If you're looking for help with specific frameworks or build systems (React, Angular, Webpack, Ember, etc), check out the [ecosystem wiki](https://github.com/CanopyTax/single-spa/blob/master/docs/single-spa-ecosystem.md) | ||
Also, check out [this step by step guide](https://medium.com/@joeldenning/a-step-by-step-guide-to-single-spa-abbbcb1bedc6). | ||
You can find the single-spa documentation [on the website](https://single-spa.js.org/). | ||
## Simple Usage | ||
For a full example, check out [this simple webpack example](https://github.com/joeldenning/simple-single-spa-webpack-example). | ||
Check out the [Getting Started](https://single-spa.js.org/docs/getting-started-overview.html) page for a quick overview. | ||
To create a single-spa application, you will need to do three things: | ||
## Demo and examples | ||
1. Create an html file: | ||
```html | ||
<html> | ||
<body> | ||
<script src="single-spa-config.js"></script> | ||
</body> | ||
</html> | ||
``` | ||
A [live demo](https://single-spa.surge.sh) is available and the source code for that demo is available in the [single-spa-examples](https://github.com/CanopyTax/single-spa-examples) repository. | ||
2. Create a single-spa-config. Check out the [docs](https://github.com/CanopyTax/single-spa/blob/master/docs/single-spa-config.md) for more detail. | ||
Also, you can check out [a simple webpack starter project](https://github.com/joeldenning/simple-single-spa-webpack-example) which is simpler and hopefully easier to get started with. | ||
```js | ||
// single-spa-config.js | ||
import * as singleSpa from 'single-spa'; | ||
## Want to help? | ||
const appName = 'app1'; | ||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our | ||
guidelines for [contributing](https://single-spa.js.org/docs/contributing-overview.html) on the [single-spa website](https://single-spa.js.org). | ||
/* The loading function is a function that returns a promise that resolves with the javascript application module. | ||
* The purpose of it is to facilitate lazy loading -- single-spa will not download the code for a application until it needs to. | ||
* In this example, import() is supported in webpack and returns a Promise, but single-spa works with any loading function that returns a Promise. | ||
*/ | ||
const loadingFunction = () => import('./app1/app1.js'); | ||
## Project roadmap | ||
/* Single-spa does some top-level routing to determine which application is active for any url. You can implement this routing any way you'd like. | ||
* One useful convention might be to prefix the url with the name of the app that is active, to keep your top-level routing simple. | ||
*/ | ||
const activityFunction = location => location.pathname.startsWith('/app1'); | ||
We're trying out github's Projects feature ([here](https://github.com/CanopyTax/single-spa/projects)) and are keeping it up-to-date with the fancy things in the works for single-spa. | ||
singleSpa.registerApplication(appName, loadingFunction, activityFunction); | ||
singleSpa.start(); | ||
``` | ||
- [CanopyTax](https://www.canopytax.com) | ||
- [Dealer Socket](https://dealersocket.com/) | ||
- [Beamery](https://beamery.com/) | ||
3. Create an application. Check out the [docs](https://github.com/CanopyTax/single-spa/blob/master/docs/applications.md) for more detail. | ||
```js | ||
//app1.js | ||
## Contributing | ||
let domEl; | ||
The main purpose of this repository is to continue to evolve single-spa, making it better and easier to use. Development of single-spa, and the [single-spa ecosystem](https://single-spa.js.org/docs/ecosystem.html) happens in the open on GitHub, and we are grateful to the community for contributing bugfixes and improvements. Read below to learn how you can take part in improving single-spa. | ||
export function bootstrap(props) { | ||
return Promise | ||
.resolve() | ||
.then(() => { | ||
domEl = document.createElement('div'); | ||
domEl.id = 'app1'; | ||
document.body.appendChild(domEl); | ||
}); | ||
} | ||
### [Code of Conduct](https://single-spa.js.org/docs/code-of-conduct.html) | ||
export function mount(props) { | ||
return Promise | ||
.resolve() | ||
.then(() => { | ||
// This is where you would normally use a framework to mount some ui to the dom. See https://github.com/CanopyTax/single-spa/blob/master/docs/single-spa-ecosystem.md. | ||
domEl.textContent = 'App 1 is mounted!' | ||
}); | ||
} | ||
Single-spa has adopted a Code of Conduct that we expect project participants to adhere to. Please read [the full text](https://single-spa.js.org/docs/code-of-conduct.html) so that you can understand what actions will and will not be tolerated. | ||
export function unmount(props) { | ||
return Promise | ||
.resolve() | ||
.then(() => { | ||
// This is normally where you would tell the framework to unmount the ui from the dom. See https://github.com/CanopyTax/single-spa/blob/master/docs/single-spa-ecosystem.md | ||
domEl.textContent = ''; | ||
}) | ||
} | ||
``` | ||
### [Contributing Guide](https://reactjs.org/contributing/how-to-contribute.html) | ||
## Project roadmap | ||
We're trying out github's Projects feature ([here](https://github.com/CanopyTax/single-spa/projects)) and are keeping it up-to-date with the fancy things in the works for single-spa. | ||
## API | ||
See [single-spa api](/docs/single-spa-api.md) and [application api](/docs/applications.md#application-lifecycle). | ||
## Who's Using This? | ||
Please submit a P.R. to this section if you start using single-spa. | ||
- [CanopyTax](https://www.canopytax.com) | ||
- [Dealer Socket](https://dealersocket.com/) | ||
- [Beamery](https://beamery.com/) | ||
Read our [contributing guide](https://reactjs.org/contributing/how-to-contribute.html) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to single-spa. |
@@ -0,1 +1,2 @@ | ||
import * as singleSpa from 'src/single-spa.js' | ||
import { mountParcel } from 'src/parcels/mount-parcel.js'; | ||
@@ -8,2 +9,3 @@ | ||
mountParcel: mountParcel.bind(appOrParcel), | ||
singleSpa | ||
}; | ||
@@ -10,0 +12,0 @@ |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
46
1465
0
164434
57