-
-
Notifications
You must be signed in to change notification settings - Fork 106
/
wrap.js
93 lines (82 loc) · 4.45 KB
/
wrap.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/**
* @typedef {Object} WrappedComponent Object returned by the `wrap` method
* @property {SvelteComponent} component - Component to load (this is always asynchronous)
* @property {RoutePrecondition[]} [conditions] - Route pre-conditions to validate
* @property {Object} [props] - Optional dictionary of static props
* @property {Object} [userData] - Optional user data dictionary
* @property {bool} _sveltesparouter - Internal flag; always set to true
*/
/**
* @callback AsyncSvelteComponent
* @returns {Promise<SvelteComponent>} Returns a Promise that resolves with a Svelte component
*/
/**
* @callback RoutePrecondition
* @param {RouteDetail} detail - Route detail object
* @returns {boolean|Promise<boolean>} If the callback returns a false-y value, it's interpreted as the precondition failed, so it aborts loading the component (and won't process other pre-condition callbacks)
*/
/**
* @typedef {Object} WrapOptions Options object for the call to `wrap`
* @property {SvelteComponent} [component] - Svelte component to load (this is incompatible with `asyncComponent`)
* @property {AsyncSvelteComponent} [asyncComponent] - Function that returns a Promise that fulfills with a Svelte component (e.g. `{asyncComponent: () => import('Foo.svelte')}`)
* @property {SvelteComponent} [loadingComponent] - Svelte component to be displayed while the async route is loading (as a placeholder); when unset or false-y, no component is shown while component
* @property {object} [loadingParams] - Optional dictionary passed to the `loadingComponent` component as params (for an exported prop called `params`)
* @property {object} [userData] - Optional object that will be passed to events such as `routeLoading`, `routeLoaded`, `conditionsFailed`
* @property {object} [props] - Optional key-value dictionary of static props that will be passed to the component. The props are expanded with {...props}, so the key in the dictionary becomes the name of the prop.
* @property {RoutePrecondition[]|RoutePrecondition} [conditions] - Route pre-conditions to add, which will be executed in order
*/
/**
* Wraps a component to enable multiple capabilities:
* 1. Using dynamically-imported component, with (e.g. `{asyncComponent: () => import('Foo.svelte')}`), which also allows bundlers to do code-splitting.
* 2. Adding route pre-conditions (e.g. `{conditions: [...]}`)
* 3. Adding static props that are passed to the component
* 4. Adding custom userData, which is passed to route events (e.g. route loaded events) or to route pre-conditions (e.g. `{userData: {foo: 'bar}}`)
*
* @param {WrapOptions} args - Arguments object
* @returns {WrappedComponent} Wrapped component
*/
export function wrap(args) {
if (!args) {
throw Error('Parameter args is required')
}
// We need to have one and only one of component and asyncComponent
// This does a "XNOR"
if (!args.component == !args.asyncComponent) {
throw Error('One and only one of component and asyncComponent is required')
}
// If the component is not async, wrap it into a function returning a Promise
if (args.component) {
args.asyncComponent = () => Promise.resolve(args.component)
}
// Parameter asyncComponent and each item of conditions must be functions
if (typeof args.asyncComponent != 'function') {
throw Error('Parameter asyncComponent must be a function')
}
if (args.conditions) {
// Ensure it's an array
if (!Array.isArray(args.conditions)) {
args.conditions = [args.conditions]
}
for (let i = 0; i < args.conditions.length; i++) {
if (!args.conditions[i] || typeof args.conditions[i] != 'function') {
throw Error('Invalid parameter conditions[' + i + ']')
}
}
}
// Check if we have a placeholder component
if (args.loadingComponent) {
args.asyncComponent.loading = args.loadingComponent
args.asyncComponent.loadingParams = args.loadingParams || undefined
}
// Returns an object that contains all the functions to execute too
// The _sveltesparouter flag is to confirm the object was created by this router
const obj = {
component: args.asyncComponent,
userData: args.userData,
conditions: (args.conditions && args.conditions.length) ? args.conditions : undefined,
props: (args.props && Object.keys(args.props).length) ? args.props : {},
_sveltesparouter: true
}
return obj
}
export default wrap