@lwrjs/router 0.10.0-alpha.1 → 0.10.0-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/bundle/prod/lwr/navigation/es/modules/lwr/currentView/currentView.d.ts +28 -0
- package/build/bundle/prod/lwr/navigation/navigation.js +1 -1
- package/build/bundle/prod/lwr/router/es/modules/lwr/currentView/currentView.d.ts +28 -0
- package/build/bundle/prod/lwr/router/router.js +1 -1
- package/build/bundle/prod/lwr/routerContainer/es/modules/lwr/currentView/currentView.d.ts +28 -0
- package/build/bundle/prod/lwr/routerContainer/routerContainer.js +1 -1
- package/build/cjs/modules/lwr/outlet/outlet.cjs +4 -1
- package/build/es/modules/lwr/contextProvider/contextProvider.js +30 -0
- package/build/es/modules/lwr/contextUtils/contextInfo.js +93 -0
- package/build/es/modules/lwr/contextUtils/contextUtils.js +77 -0
- package/build/es/modules/lwr/contextUtils/navigationApiStore.js +46 -0
- package/build/{modules → es/modules}/lwr/currentPageReference/currentPageReference.d.ts +1 -1
- package/build/es/modules/lwr/currentPageReference/currentPageReference.js +14 -0
- package/build/{modules → es/modules}/lwr/currentView/currentView.d.ts +2 -2
- package/build/es/modules/lwr/currentView/currentView.js +62 -0
- package/build/{modules → es/modules}/lwr/domRouter/domRouter.d.ts +2 -2
- package/build/es/modules/lwr/domRouter/domRouter.js +441 -0
- package/build/es/modules/lwr/domRouterUtils/domRouterUtils.js +3 -0
- package/build/es/modules/lwr/domRouterUtils/historyUtils.js +30 -0
- package/build/{modules → es/modules}/lwr/domRouterUtils/types.d.ts +1 -1
- package/build/es/modules/lwr/domRouterUtils/types.js +2 -0
- package/build/es/modules/lwr/domRouterUtils/uriUtils.js +69 -0
- package/build/es/modules/lwr/historyRouter/historyRouter.js +88 -0
- package/build/es/modules/lwr/navigation/navigation.js +20 -0
- package/build/es/modules/lwr/navigation/navigationApi.js +27 -0
- package/build/es/modules/lwr/navigation/navigationMixin.js +76 -0
- package/build/{modules → es/modules}/lwr/navigationContext/navigationContext.d.ts +2 -2
- package/build/es/modules/lwr/navigationContext/navigationContext.js +10 -0
- package/build/es/modules/lwr/navigationMixinHacks/navigationMixinHacks.d.ts +7 -0
- package/build/es/modules/lwr/navigationMixinHacks/navigationMixinHacks.js +5 -0
- package/build/es/modules/lwr/observable/observable.js +71 -0
- package/build/{modules → es/modules}/lwr/outlet/outlet.d.ts +1 -0
- package/build/es/modules/lwr/outlet/outlet.js +69 -0
- package/build/es/modules/lwr/router/router.js +201 -0
- package/build/es/modules/lwr/routerBridge/routerBridge.js +85 -0
- package/build/es/modules/lwr/routerContainer/routerContainer.js +116 -0
- package/build/es/modules/lwr/routerContainer/utils.js +83 -0
- package/build/es/modules/lwr/routerErrors/routerErrors.js +154 -0
- package/build/es/modules/lwr/routerUtils/domUtils.js +3 -0
- package/build/{modules → es/modules}/lwr/routerUtils/filterUtils.d.ts +1 -1
- package/build/es/modules/lwr/routerUtils/filterUtils.js +74 -0
- package/build/es/modules/lwr/routerUtils/parseUtils.js +182 -0
- package/build/{modules → es/modules}/lwr/routerUtils/pathToRegexp.d.ts +5 -5
- package/build/es/modules/lwr/routerUtils/pathToRegexp.js +415 -0
- package/build/es/modules/lwr/routerUtils/routeDefUtils.js +204 -0
- package/build/es/modules/lwr/routerUtils/routeUtils.js +239 -0
- package/build/es/modules/lwr/routerUtils/routerUtils.js +19 -0
- package/build/es/modules/lwr/routerUtils/typeUtils.js +112 -0
- package/build/{modules → es/modules}/lwr/routerUtils/types.d.ts +23 -23
- package/build/es/modules/lwr/routerUtils/types.js +2 -0
- package/build/es/modules/lwr/routerUtils/uriUtils.js +134 -0
- package/build/modules/lwr/contextUtils/contextUtils.js +5 -0
- package/build/modules/lwr/contextUtils/navigationApiStore.js +7 -0
- package/build/modules/lwr/domRouter/domRouter.js +6 -0
- package/build/modules/lwr/domRouterUtils/historyUtils.js +0 -1
- package/build/modules/lwr/outlet/outlet.css +1 -1
- package/build/modules/lwr/outlet/outlet.html +2 -2
- package/build/modules/lwr/outlet/outlet.js +3 -4
- package/build/modules/lwr/router/router.js +3 -0
- package/build/modules/lwr/routerContainer/utils.js +1 -3
- package/build/modules/lwr/routerUtils/pathToRegexp.js +17 -0
- package/build/modules/lwr/routerUtils/typeUtils.js +0 -1
- package/package.json +17 -9
- package/pageObjects/outlet.cjs +24 -4
- package/pageObjects/outlet.d.ts +13 -2
- package/pageObjects/outlet.js +25 -5
- package/build/modules/lwr/navigationMixinHacks/navigationMixinHacks.d.ts +0 -7
- /package/build/{modules → es/modules}/lwr/contextProvider/contextProvider.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/contextUtils/contextInfo.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/contextUtils/contextUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/contextUtils/navigationApiStore.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/domRouterUtils/domRouterUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/domRouterUtils/historyUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/domRouterUtils/uriUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/historyRouter/historyRouter.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/navigation/navigation.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/navigation/navigationApi.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/navigation/navigationMixin.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/observable/observable.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/router/router.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerBridge/routerBridge.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerContainer/routerContainer.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerContainer/utils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerErrors/routerErrors.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/domUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/parseUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/routeDefUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/routeUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/routerUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/typeUtils.d.ts +0 -0
- /package/build/{modules → es/modules}/lwr/routerUtils/uriUtils.d.ts +0 -0
- /package/build/{services → es/services}/index.d.ts +0 -0
- /package/build/{services → es/services}/index.js +0 -0
- /package/build/{services → es/services}/module-provider/index.d.ts +0 -0
- /package/build/{services → es/services}/module-provider/index.js +0 -0
- /package/build/{services → es/services}/module-provider/utils.d.ts +0 -0
- /package/build/{services → es/services}/module-provider/utils.js +0 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2019, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
6
|
+
*/
|
|
7
|
+
import { generateContextualWireAdapter, ContextInfo } from 'lwr/contextUtils';
|
|
8
|
+
import { generateMessageObject, messages } from 'lwr/routerErrors';
|
|
9
|
+
const CURRENT_VIEW_CONTEXT = new ContextInfo(undefined);
|
|
10
|
+
/**
|
|
11
|
+
* Services @wire(CurrentView) requests.
|
|
12
|
+
* Hooks up to an Observable from the current navigation context.
|
|
13
|
+
*/
|
|
14
|
+
export const CurrentView = class CurrentView extends generateContextualWireAdapter(CURRENT_VIEW_CONTEXT) {
|
|
15
|
+
async update(config, context) {
|
|
16
|
+
if (context) {
|
|
17
|
+
const viewName = config && config.viewName ? config.viewName : 'default';
|
|
18
|
+
const viewEntry = context.viewset[viewName]; // either ViewInfo or an importer function
|
|
19
|
+
const viewInfo = viewEntry;
|
|
20
|
+
const viewImporter = (viewInfo && viewInfo.module) || viewEntry;
|
|
21
|
+
let importError;
|
|
22
|
+
if (viewImporter) {
|
|
23
|
+
try {
|
|
24
|
+
const viewModule = await viewImporter();
|
|
25
|
+
const newViewCtor = viewModule && viewModule.default;
|
|
26
|
+
if (newViewCtor && newViewCtor.constructor !== undefined) {
|
|
27
|
+
this._callback(newViewCtor);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// delegate to catch for consistent error handling
|
|
31
|
+
throw new Error('error occurred with view import');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
const error = e;
|
|
36
|
+
if (viewInfo.specifier) {
|
|
37
|
+
importError = generateMessageObject(messages.VIEW_IMPORT_FAILED_WITH_SPECIFIER, [
|
|
38
|
+
viewInfo.specifier,
|
|
39
|
+
viewName,
|
|
40
|
+
error.message,
|
|
41
|
+
error.stack || '',
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
importError = generateMessageObject(messages.VIEW_IMPORT_FAILED, [
|
|
46
|
+
viewName,
|
|
47
|
+
error.message,
|
|
48
|
+
error.stack || '',
|
|
49
|
+
]);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
importError = generateMessageObject(messages.VIEW_MISSING, [viewName]);
|
|
55
|
+
}
|
|
56
|
+
if (context.onComplete) {
|
|
57
|
+
context.onComplete(importError);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=currentView.js.map
|
|
@@ -14,8 +14,8 @@ export declare const PARENT_EVENT: string;
|
|
|
14
14
|
interface RouterParent {
|
|
15
15
|
addChild(child: DomRouter): Promise<void>;
|
|
16
16
|
}
|
|
17
|
-
export
|
|
18
|
-
|
|
17
|
+
export type ParentRouterEvent = CustomEvent<(router: RouterParent) => void>;
|
|
18
|
+
type DomRoutingMatch = RoutingMatch & {
|
|
19
19
|
url: string;
|
|
20
20
|
};
|
|
21
21
|
interface DomRouterNode extends RouterParent {
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2019, salesforce.com, inc.
|
|
4
|
+
* All rights reserved.
|
|
5
|
+
* SPDX-License-Identifier: MIT
|
|
6
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
7
|
+
*/
|
|
8
|
+
import { CurrentPageReference, CurrentView, NavigationContext, registerNavigationHelm } from 'lwr/navigation';
|
|
9
|
+
import { currentPageReferenceContextualizer, currentViewContextualizer, navigationContextContextualizer, provideContext, } from 'lwr/contextProvider';
|
|
10
|
+
import { generateMessageObject, invariant, messages } from 'lwr/routerErrors';
|
|
11
|
+
import { createFilterChain, guid, hasDocument } from 'lwr/routerUtils';
|
|
12
|
+
import { createObservable } from 'lwr/observable'; // @ts-ignore Need to import from client-modules package
|
|
13
|
+
import { ROUTER_ERROR, ROUTER_NAV, ROUTER_VIEW } from 'lwr/metrics'; // @ts-ignore
|
|
14
|
+
import { logOperationStart, logOperationEnd } from 'lwr/profiler';
|
|
15
|
+
import { CONTEXT_ID_BACKDOOR } from 'lwr/navigationMixinHacks';
|
|
16
|
+
// Event fired when a component calls navigate()
|
|
17
|
+
export const NAV_EVENT = `universalcontainernavigationevent${guid()}`;
|
|
18
|
+
// Event fired to find nearest parent
|
|
19
|
+
export const PARENT_EVENT = `universalcontainerparentevent${guid()}`;
|
|
20
|
+
export class DomRouterImpl {
|
|
21
|
+
/**
|
|
22
|
+
* Create and configure the Router.
|
|
23
|
+
*
|
|
24
|
+
* @param {object} config - The domRouter config object, all properties are optional
|
|
25
|
+
* @param {object} router - an optional Router
|
|
26
|
+
* @param {HTMLElement} target - DOM node to attach to
|
|
27
|
+
*/
|
|
28
|
+
constructor(config, router, target) {
|
|
29
|
+
this.pendingRoute = null;
|
|
30
|
+
this.committedRoute = null;
|
|
31
|
+
this.contextId = Object.freeze(() => undefined);
|
|
32
|
+
this.connected = false;
|
|
33
|
+
this.preNavFilters = createFilterChain();
|
|
34
|
+
this.errorNavFilters = createFilterChain();
|
|
35
|
+
/**
|
|
36
|
+
* Inspect a navigation event bubbling up from a descendent component.
|
|
37
|
+
* This node can choose to stop the event by returning false.
|
|
38
|
+
* If propagation is not stopped, and this node is the root (no parent),
|
|
39
|
+
* then begin the root -> leaf processing of this new route.
|
|
40
|
+
* This will update the navigation event subscribers in each DomRouter, top down.
|
|
41
|
+
*
|
|
42
|
+
* @param {Event} event - With detail: { url, options }
|
|
43
|
+
*/
|
|
44
|
+
this._handleNavigationEvent = (event) => {
|
|
45
|
+
const navigationEvent = event;
|
|
46
|
+
if (navigationEvent.detail && typeof navigationEvent.detail === 'object') {
|
|
47
|
+
const { url, replace, address } = navigationEvent.detail;
|
|
48
|
+
const continueNavigation = this.config.handleNavigation(address, replace);
|
|
49
|
+
if (!continueNavigation) {
|
|
50
|
+
navigationEvent.stopPropagation();
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
if (this.root && !url) {
|
|
54
|
+
// handle navigation can avoid the error message if it indicates navigation
|
|
55
|
+
// shouldn't continue, even if url is missing
|
|
56
|
+
this.root.processError(generateMessageObject(messages.MISSING_URL, [JSON.stringify(address)]));
|
|
57
|
+
}
|
|
58
|
+
else if (!this.parent) {
|
|
59
|
+
this.process(url, replace);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Be discovered as a parent for descendent components.
|
|
66
|
+
* Stop immediate propagation because we only want 1 parent to be found.
|
|
67
|
+
*
|
|
68
|
+
* @param {Event} event - With detail: callback
|
|
69
|
+
*/
|
|
70
|
+
this._handleParentEvent = (event) => {
|
|
71
|
+
event.stopImmediatePropagation();
|
|
72
|
+
const parentRouterEvent = event;
|
|
73
|
+
if (parentRouterEvent && parentRouterEvent.detail && typeof parentRouterEvent.detail === 'function') {
|
|
74
|
+
parentRouterEvent.detail(this);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
this.config = {
|
|
78
|
+
handleNavigation: config.handleNavigation || (() => true),
|
|
79
|
+
};
|
|
80
|
+
this.target = target || window;
|
|
81
|
+
this.router = router;
|
|
82
|
+
this.router.contextId = this.contextId; // give the underlying router access to the context
|
|
83
|
+
this.routeObservable = createObservable();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Search up the node chain until the root node is hit
|
|
87
|
+
*/
|
|
88
|
+
get root() {
|
|
89
|
+
if (!this.parent) {
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
let maybe = this.parent;
|
|
93
|
+
while (maybe) {
|
|
94
|
+
if (!maybe.parent) {
|
|
95
|
+
return maybe;
|
|
96
|
+
}
|
|
97
|
+
maybe = maybe.parent;
|
|
98
|
+
}
|
|
99
|
+
throw new Error('No root router could be found');
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Informs an wire listeners of routing changes/errors
|
|
103
|
+
*
|
|
104
|
+
* @param {RoutingResult} result - result object from navigation event
|
|
105
|
+
* @param {string} url - url from the page reference that triggered the navigation event
|
|
106
|
+
* @param {MessageObject} error - if an error occurred during the navigation
|
|
107
|
+
*/
|
|
108
|
+
updateWires(result, url, error) {
|
|
109
|
+
if (error) {
|
|
110
|
+
this.processError(error);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// This code block is called whenever CurrentView.update() executes successfully.
|
|
114
|
+
// CurrentView.update() is called:
|
|
115
|
+
// - Whenever a component with @wire(CurrentView) is mounted
|
|
116
|
+
// - Every time a nav event fires for EACH component with @wire(CurrentView)
|
|
117
|
+
// meaning every time a nav event fires, this function may be called several times.
|
|
118
|
+
// Since this function is post-processing for a nav event,
|
|
119
|
+
// it should ONLY run ONCE for each unique nav event.
|
|
120
|
+
// By doing a STRICT comparison between the incoming route and the committed route OBJECTS,
|
|
121
|
+
// it can be determined if the nav event is new or has already been post-processed.
|
|
122
|
+
if (this.committedRoute && result.route === this.committedRoute.route) {
|
|
123
|
+
// This nav event has already been processed, DO NOT do it again #957
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
logOperationEnd({ id: ROUTER_VIEW, specifier: this.eventId });
|
|
127
|
+
// if there's no pending route (the router was called to navigate independently of the DomRouter)
|
|
128
|
+
// then provide the result page reference -> url as the default
|
|
129
|
+
this.pendingRoute = this.pendingRoute || { ...result, url };
|
|
130
|
+
this.committedRoute = { ...this.pendingRoute, ...result };
|
|
131
|
+
CurrentPageReference.setContext(this.target, result.route.pageReference);
|
|
132
|
+
this.routeObservable.next({ ...this.committedRoute, viewset: result.viewset });
|
|
133
|
+
logOperationEnd({ id: ROUTER_NAV, specifier: this.eventId });
|
|
134
|
+
// kick off child node processing (if necessary)
|
|
135
|
+
if (this.child) {
|
|
136
|
+
this.child.process(this._stripUrlForChild(this.committedRoute.url));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Override to provide this router as a navigation context
|
|
142
|
+
*/
|
|
143
|
+
connect() {
|
|
144
|
+
// connect this node as a child to its closest ancestor
|
|
145
|
+
this._sendEvent(PARENT_EVENT, (router) => {
|
|
146
|
+
this.parent = router;
|
|
147
|
+
router.addChild(this);
|
|
148
|
+
});
|
|
149
|
+
const contextApi = {
|
|
150
|
+
navigate: (address, replace) => this.navigate(address, replace),
|
|
151
|
+
generateUrl: (address) => this.generateUrl(address),
|
|
152
|
+
subscribe: (callback, replay) => this.subscribe(callback, replay),
|
|
153
|
+
};
|
|
154
|
+
registerNavigationHelm(this.contextId, contextApi);
|
|
155
|
+
// have this router provide context to child wire users
|
|
156
|
+
provideContext(this.contextId, this.target, navigationContextContextualizer, NavigationContext);
|
|
157
|
+
provideContext(undefined, this.target, currentPageReferenceContextualizer, CurrentPageReference);
|
|
158
|
+
provideContext(undefined, this.target, currentViewContextualizer, CurrentView);
|
|
159
|
+
this.router.subscribe((result) => {
|
|
160
|
+
if (result.status === 404) {
|
|
161
|
+
// the route handler could not resolve the address
|
|
162
|
+
this.processError(generateMessageObject(messages.DESTINATION_NOT_FOUND));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (result.status && result.status >= 400) {
|
|
166
|
+
// the route handler encountered an error resolving the address
|
|
167
|
+
const error = result.error || new Error();
|
|
168
|
+
this.processError(generateMessageObject(messages.DESTINATION_ERROR, [
|
|
169
|
+
result.status.toString(),
|
|
170
|
+
error.message,
|
|
171
|
+
error.stack || '',
|
|
172
|
+
]));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const pageReference = result.route.pageReference || {};
|
|
176
|
+
const url = this.router.generateUrl(pageReference) || '';
|
|
177
|
+
logOperationStart({ id: ROUTER_VIEW, specifier: this.eventId });
|
|
178
|
+
if (result.viewset) {
|
|
179
|
+
// If result includes a viewset, wait to emit pageReference until the view is resolved.
|
|
180
|
+
// If the previous view subscribed to the pageReference wire, it may visibly update before outlet
|
|
181
|
+
// has rendered the incoming view. (W-8459372)
|
|
182
|
+
const currentViewContext = {
|
|
183
|
+
viewset: result.viewset,
|
|
184
|
+
onComplete: this.updateWires.bind(this, result, url),
|
|
185
|
+
};
|
|
186
|
+
CurrentView.setContext(this.target, currentViewContext);
|
|
187
|
+
}
|
|
188
|
+
else if (result.route.pageReference) {
|
|
189
|
+
this.updateWires(result, url);
|
|
190
|
+
}
|
|
191
|
+
}, true);
|
|
192
|
+
// add listener for navigation and parent events
|
|
193
|
+
if (hasDocument) {
|
|
194
|
+
this.target.addEventListener(NAV_EVENT, this._handleNavigationEvent);
|
|
195
|
+
this.target.addEventListener(PARENT_EVENT, this._handleParentEvent);
|
|
196
|
+
// Only used by NavigationMixin, which can't use `@wire` because it's part of the navigation API, and can't import anything from lwc.
|
|
197
|
+
this.target.addEventListener(CONTEXT_ID_BACKDOOR, (event) => {
|
|
198
|
+
const navCtxEvent = event;
|
|
199
|
+
if (navCtxEvent.detail.callback) {
|
|
200
|
+
navCtxEvent.detail.callback(this.contextId);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
this.connected = true;
|
|
205
|
+
}
|
|
206
|
+
disconnect() {
|
|
207
|
+
// remove event listeners
|
|
208
|
+
this.target.removeEventListener(NAV_EVENT, this._handleNavigationEvent);
|
|
209
|
+
this.target.removeEventListener(PARENT_EVENT, this._handleParentEvent);
|
|
210
|
+
// detach from parent
|
|
211
|
+
if (this.parent) {
|
|
212
|
+
this.parent.child = undefined;
|
|
213
|
+
}
|
|
214
|
+
this.parent = undefined;
|
|
215
|
+
// detach from child
|
|
216
|
+
if (this.child) {
|
|
217
|
+
this.child.parent = undefined;
|
|
218
|
+
}
|
|
219
|
+
this.child = undefined;
|
|
220
|
+
this.connected = false;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Add listeners to this router hook which run BEFORE a new URL is processed (root -> leaf).
|
|
224
|
+
*
|
|
225
|
+
* @param {object | object[]} filters
|
|
226
|
+
*/
|
|
227
|
+
addPreNavigate(filters) {
|
|
228
|
+
this.preNavFilters.add(filters);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Add listeners to this router hook which run when there is an error navigating.
|
|
232
|
+
*
|
|
233
|
+
* @param {object | object[]} filters
|
|
234
|
+
*/
|
|
235
|
+
addErrorNavigate(filters) {
|
|
236
|
+
this.errorNavFilters.add(filters);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Override parent implementation.
|
|
240
|
+
* Pass the current state down to a new child.
|
|
241
|
+
*
|
|
242
|
+
* @param {child} child - Child router
|
|
243
|
+
*/
|
|
244
|
+
async addChild(child) {
|
|
245
|
+
await new Promise((resolve) => {
|
|
246
|
+
// Temp fix for https://github.com/salesforce/lwc/issues/1894
|
|
247
|
+
/* eslint-disable-next-line */
|
|
248
|
+
setTimeout(() => {
|
|
249
|
+
invariant(!this.child, messages.MULTIPLE_CHILDREN);
|
|
250
|
+
this.child = child;
|
|
251
|
+
resolve();
|
|
252
|
+
}, 0);
|
|
253
|
+
});
|
|
254
|
+
if (this.child && this.committedRoute) {
|
|
255
|
+
const url = this._stripUrlForChild(this.committedRoute.url);
|
|
256
|
+
const canContinue = await this.child.preProcess(url);
|
|
257
|
+
if (canContinue) {
|
|
258
|
+
this.child.process(url);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Process the current URL passed down by the parent router.
|
|
264
|
+
* Stop propagation of the navigation event if a preNavigate filter returns false.
|
|
265
|
+
*
|
|
266
|
+
* Update the current path and route matches.
|
|
267
|
+
* Update the observable to hold the new route.
|
|
268
|
+
*
|
|
269
|
+
* After processing, delegate to a child router, if it exists.
|
|
270
|
+
*
|
|
271
|
+
* @param {string} url - Relative URL string to process
|
|
272
|
+
* @returns {boolean} - True if the processing was NOT blocked by a preNavigate listener
|
|
273
|
+
*/
|
|
274
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
275
|
+
async process(url, replace) {
|
|
276
|
+
// Mark the navigation event here instead of in navigate()
|
|
277
|
+
// This way, we catch ALL navigation events, since they all must go through process():
|
|
278
|
+
// 1. A component calls navigate()
|
|
279
|
+
// 2. The HistoryRouter catches a popstate event
|
|
280
|
+
// 3. Nested router processing
|
|
281
|
+
// Note: The < 1ms worth of logic in navigate() and _handleNavigationEvent() is excluded
|
|
282
|
+
this.eventId = new Date().getTime().toString();
|
|
283
|
+
logOperationStart({ id: ROUTER_NAV, specifier: this.eventId });
|
|
284
|
+
// Run the root -> leaf chain of pre navigate filters, if this is the root.
|
|
285
|
+
try {
|
|
286
|
+
if (!this.parent) {
|
|
287
|
+
await this.preProcess(url);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch (e) {
|
|
291
|
+
if (e.code) {
|
|
292
|
+
this.processError(e);
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
throw e;
|
|
296
|
+
}
|
|
297
|
+
// If there is a child, it will be subscribed to the router's navigate() output which will
|
|
298
|
+
// trigger the child to navigate afterwards
|
|
299
|
+
const address = this.router.parseUrl(url);
|
|
300
|
+
if (address) {
|
|
301
|
+
this.router.navigate(address);
|
|
302
|
+
}
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Run the preNavigate filters for this router.
|
|
307
|
+
* After processing, delegate to a child router, if it exists.
|
|
308
|
+
*
|
|
309
|
+
* @param {string} url - Relative URL string to process,
|
|
310
|
+
* cannot use a route since the processing is done in context
|
|
311
|
+
*
|
|
312
|
+
* @returns {Promise<boolean>} - Resolves to true if successful
|
|
313
|
+
*/
|
|
314
|
+
preProcess(url) {
|
|
315
|
+
const address = this.router.parseUrl(url);
|
|
316
|
+
const routingMatch = address && this.router.matchRoute(address);
|
|
317
|
+
// Check that the URL has a matching route, otherwise it is an error.
|
|
318
|
+
if (!routingMatch) {
|
|
319
|
+
return Promise.reject(generateMessageObject(messages.MISSING_ROUTE, [url]));
|
|
320
|
+
}
|
|
321
|
+
this.pendingRoute = { url, ...routingMatch };
|
|
322
|
+
// Compile this router's filters; continue with TRUE if there are no filters.
|
|
323
|
+
const canGo = this.preNavFilters.empty()
|
|
324
|
+
? Promise.resolve(true)
|
|
325
|
+
: this.preNavFilters.compile({
|
|
326
|
+
current: this.committedRoute || undefined,
|
|
327
|
+
next: this.pendingRoute,
|
|
328
|
+
});
|
|
329
|
+
// If the filters pass, run its child's filters.
|
|
330
|
+
return (canGo
|
|
331
|
+
.then((canContinue) => {
|
|
332
|
+
return canContinue && this.child
|
|
333
|
+
? this.child.preProcess(this._stripUrlForChild(url))
|
|
334
|
+
: canContinue;
|
|
335
|
+
})
|
|
336
|
+
// Craft an error message, if the filters have returned false.
|
|
337
|
+
.then((canContinue) => {
|
|
338
|
+
return (canContinue || Promise.reject(generateMessageObject(messages.PRENAV_FAILED, [url])));
|
|
339
|
+
}));
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Run the errorNavigate filters for this router.
|
|
343
|
+
* After processing, delegate to a child router, if it exists.
|
|
344
|
+
*
|
|
345
|
+
* @param {object} e - An error object to pass into the error hook listeners.
|
|
346
|
+
*/
|
|
347
|
+
processError(messageObject) {
|
|
348
|
+
logOperationStart({ id: ROUTER_ERROR });
|
|
349
|
+
this.errorNavFilters.compile(messageObject);
|
|
350
|
+
if (this.child) {
|
|
351
|
+
this.child.processError(messageObject);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* lightning/navigation
|
|
356
|
+
* Fire an event to send the navigation event up the DOM.
|
|
357
|
+
* The root router will be the last to catch the event if it is not stopped.
|
|
358
|
+
*
|
|
359
|
+
* @param {object} address - Address to navigate to
|
|
360
|
+
* @param {*} options - Usually a boolean; when true the previous browser history
|
|
361
|
+
* entry should be replaced by this one
|
|
362
|
+
*/
|
|
363
|
+
navigate(address, replace) {
|
|
364
|
+
// Ensure there is a string URL to pass to the navigation event.
|
|
365
|
+
let url = this.router.generateUrl(address);
|
|
366
|
+
if (url) {
|
|
367
|
+
// If this router is a child, we need to prepend the parent's matching portion
|
|
368
|
+
// of the url before sending the navigate event up
|
|
369
|
+
const parentPath = (this.parent && this.parent.committedRoute && this.parent.committedRoute.pathMatch) || '';
|
|
370
|
+
url = parentPath.concat(url);
|
|
371
|
+
}
|
|
372
|
+
// Fire event up the DOM with the original caller input
|
|
373
|
+
this._sendEvent(NAV_EVENT, { url, replace, address });
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* lightning/navigation
|
|
377
|
+
* Generate a URL based on the given route.
|
|
378
|
+
* Return a Promise containing the URL string.
|
|
379
|
+
*
|
|
380
|
+
* @param {object} route - Route to generate a url for
|
|
381
|
+
*
|
|
382
|
+
* @returns {Promise<string>}
|
|
383
|
+
*/
|
|
384
|
+
generateUrl(address) {
|
|
385
|
+
const url = this.router.generateUrl(address);
|
|
386
|
+
// Invalid addresses need to return null to indicate they are invalid
|
|
387
|
+
if (!url) {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
// If this router is a child, we need to prepend the parent's matching portion
|
|
391
|
+
// of the url to generate the complete url
|
|
392
|
+
const parentPath = (this.parent && this.parent.committedRoute && this.parent.committedRoute.pathMatch) || '';
|
|
393
|
+
return `${parentPath}${url}`;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* lightning/navigation
|
|
397
|
+
* Subscribe a callback to the Observable on the current route of this router.
|
|
398
|
+
*
|
|
399
|
+
* @param {function} callback - A callback function invoked when the navigation state changes
|
|
400
|
+
* callback(route, routeDef)
|
|
401
|
+
* @param {boolean} replay - Flag to determine if callback should be called with current route and data immediately
|
|
402
|
+
*/
|
|
403
|
+
subscribe(callback, replay) {
|
|
404
|
+
return this.routeObservable.subscribe({
|
|
405
|
+
next: callback,
|
|
406
|
+
error: () => {
|
|
407
|
+
// ignore
|
|
408
|
+
},
|
|
409
|
+
complete: () => {
|
|
410
|
+
// nothing to clean up
|
|
411
|
+
},
|
|
412
|
+
}, Boolean(replay));
|
|
413
|
+
}
|
|
414
|
+
_sendEvent(name, payload) {
|
|
415
|
+
hasDocument &&
|
|
416
|
+
this.target.dispatchEvent(new CustomEvent(name, {
|
|
417
|
+
bubbles: true,
|
|
418
|
+
composed: true,
|
|
419
|
+
detail: payload,
|
|
420
|
+
}));
|
|
421
|
+
}
|
|
422
|
+
_stripUrlForChild(url) {
|
|
423
|
+
if (this.pendingRoute && url.indexOf(this.pendingRoute.pathMatch) === 0) {
|
|
424
|
+
return url.replace(this.pendingRoute.pathMatch, '');
|
|
425
|
+
}
|
|
426
|
+
return url;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Create a new root Router, attach to the Window.
|
|
431
|
+
* This is the public, programmatic API for root router creation.
|
|
432
|
+
* An application can only have ONE root router.
|
|
433
|
+
*
|
|
434
|
+
* @param {object} config - The router config object
|
|
435
|
+
*
|
|
436
|
+
* @returns {object} - { addPreNavigate, addErrorNavigate, connect, disconnect }
|
|
437
|
+
*/
|
|
438
|
+
export function createDomRouter(config, router, target) {
|
|
439
|
+
return new DomRouterImpl(config, router, target);
|
|
440
|
+
}
|
|
441
|
+
//# sourceMappingURL=domRouter.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2019, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
6
|
+
*/
|
|
7
|
+
/*
|
|
8
|
+
* Manipulates the browser history on the window.
|
|
9
|
+
* Ths just uses the window.history functionality directly.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Sets a history state.
|
|
13
|
+
|
|
14
|
+
* @param {string} path - query to set
|
|
15
|
+
* @param {object} route - history state object
|
|
16
|
+
*/
|
|
17
|
+
export function set(path, route) {
|
|
18
|
+
const data = route || {};
|
|
19
|
+
window.history.pushState(data, '', path);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Replaces the current history state.
|
|
23
|
+
* @param {string} path - query to use as a replacement
|
|
24
|
+
* @param {object} route - history state object
|
|
25
|
+
*/
|
|
26
|
+
export function replace(path, route) {
|
|
27
|
+
const data = route || {};
|
|
28
|
+
window.history.replaceState(data, '', path);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=historyUtils.js.map
|
|
@@ -3,7 +3,7 @@ export interface RouteChange {
|
|
|
3
3
|
current?: RoutingMatch;
|
|
4
4
|
next: RoutingMatch;
|
|
5
5
|
}
|
|
6
|
-
export
|
|
6
|
+
export type NavigationHandler = (address: PageReference, replace?: boolean) => boolean;
|
|
7
7
|
export interface DomRouterConfig {
|
|
8
8
|
handleNavigation?: NavigationHandler;
|
|
9
9
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { decode } from 'lwr/routerUtils';
|
|
2
|
+
/**
|
|
3
|
+
* helper: f(url) -> new URL
|
|
4
|
+
* Query parameter values are URI decoded.
|
|
5
|
+
|
|
6
|
+
* @param {string} url - URL string to turn into a URL object
|
|
7
|
+
|
|
8
|
+
* @returns {object} { href, pathname, searchParams }
|
|
9
|
+
|
|
10
|
+
* @example
|
|
11
|
+
* {
|
|
12
|
+
* href: 'http://localhost:3001/products?lwr.mode=prod&lwr.locale=es#section',
|
|
13
|
+
* origin: 'http://localhost:3001',
|
|
14
|
+
* pathname: '/products',
|
|
15
|
+
* searchParams: {
|
|
16
|
+
* 'lwr.mode': 'prod',
|
|
17
|
+
* 'lwr.locale': 'es'
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
export function getUrlObject(url = '') {
|
|
22
|
+
// Ensure this is a full URL.
|
|
23
|
+
url = url || '';
|
|
24
|
+
if (url.indexOf('://') < 0) {
|
|
25
|
+
// get the current port string (or empty)
|
|
26
|
+
const port = window.location.port ? `:${window.location.port}` : '';
|
|
27
|
+
// construct the new base url using the original protocol, hostname, and port
|
|
28
|
+
const origin = `${window.location.protocol}//${window.location.hostname}${port}`;
|
|
29
|
+
// does the path start with a slash? If so do nothing
|
|
30
|
+
const prefix = url.charAt(0) === '/' ? '' : '/';
|
|
31
|
+
// construct the final URL
|
|
32
|
+
url = origin + prefix + url;
|
|
33
|
+
}
|
|
34
|
+
// Parse the URL using an anchor.
|
|
35
|
+
const searchParams = {};
|
|
36
|
+
const link = document.createElement('a');
|
|
37
|
+
link.href = url;
|
|
38
|
+
// Pull the search params out of the search query string.
|
|
39
|
+
const queryStr = link.search.substring(1);
|
|
40
|
+
if (queryStr) {
|
|
41
|
+
queryStr.split('&').forEach((pair) => {
|
|
42
|
+
const [key, value = ''] = pair.split('=');
|
|
43
|
+
searchParams[decode(key)] = decode(value);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
href: link.href,
|
|
48
|
+
origin: `${link.protocol}//${link.hostname}${link.port ? `:${link.port}` : ''}`,
|
|
49
|
+
pathname: link.pathname.replace(/(\/)?/, '/'),
|
|
50
|
+
searchParams,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* f(url) -> "/some/relative/path?param1=one¶m2=two¶m3"
|
|
55
|
+
|
|
56
|
+
* @param {string} url - URL string to make relative, may be a no-op
|
|
57
|
+
|
|
58
|
+
* @return {string}
|
|
59
|
+
*/
|
|
60
|
+
export function getRelativeUrl(url) {
|
|
61
|
+
const urlObj = getUrlObject(url);
|
|
62
|
+
// Remove port number from both href and origin before doing string replace.
|
|
63
|
+
// The port number gets included on the origin in IE11 with https but not
|
|
64
|
+
// href which breaks the string replace.
|
|
65
|
+
const href = urlObj.href.replace(/:\d+/, '');
|
|
66
|
+
const origin = urlObj.origin.replace(/:\d+/, '');
|
|
67
|
+
return href.replace(origin, '');
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=uriUtils.js.map
|