@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.
Files changed (97) hide show
  1. package/build/bundle/prod/lwr/navigation/es/modules/lwr/currentView/currentView.d.ts +28 -0
  2. package/build/bundle/prod/lwr/navigation/navigation.js +1 -1
  3. package/build/bundle/prod/lwr/router/es/modules/lwr/currentView/currentView.d.ts +28 -0
  4. package/build/bundle/prod/lwr/router/router.js +1 -1
  5. package/build/bundle/prod/lwr/routerContainer/es/modules/lwr/currentView/currentView.d.ts +28 -0
  6. package/build/bundle/prod/lwr/routerContainer/routerContainer.js +1 -1
  7. package/build/cjs/modules/lwr/outlet/outlet.cjs +4 -1
  8. package/build/es/modules/lwr/contextProvider/contextProvider.js +30 -0
  9. package/build/es/modules/lwr/contextUtils/contextInfo.js +93 -0
  10. package/build/es/modules/lwr/contextUtils/contextUtils.js +77 -0
  11. package/build/es/modules/lwr/contextUtils/navigationApiStore.js +46 -0
  12. package/build/{modules → es/modules}/lwr/currentPageReference/currentPageReference.d.ts +1 -1
  13. package/build/es/modules/lwr/currentPageReference/currentPageReference.js +14 -0
  14. package/build/{modules → es/modules}/lwr/currentView/currentView.d.ts +2 -2
  15. package/build/es/modules/lwr/currentView/currentView.js +62 -0
  16. package/build/{modules → es/modules}/lwr/domRouter/domRouter.d.ts +2 -2
  17. package/build/es/modules/lwr/domRouter/domRouter.js +441 -0
  18. package/build/es/modules/lwr/domRouterUtils/domRouterUtils.js +3 -0
  19. package/build/es/modules/lwr/domRouterUtils/historyUtils.js +30 -0
  20. package/build/{modules → es/modules}/lwr/domRouterUtils/types.d.ts +1 -1
  21. package/build/es/modules/lwr/domRouterUtils/types.js +2 -0
  22. package/build/es/modules/lwr/domRouterUtils/uriUtils.js +69 -0
  23. package/build/es/modules/lwr/historyRouter/historyRouter.js +88 -0
  24. package/build/es/modules/lwr/navigation/navigation.js +20 -0
  25. package/build/es/modules/lwr/navigation/navigationApi.js +27 -0
  26. package/build/es/modules/lwr/navigation/navigationMixin.js +76 -0
  27. package/build/{modules → es/modules}/lwr/navigationContext/navigationContext.d.ts +2 -2
  28. package/build/es/modules/lwr/navigationContext/navigationContext.js +10 -0
  29. package/build/es/modules/lwr/navigationMixinHacks/navigationMixinHacks.d.ts +7 -0
  30. package/build/es/modules/lwr/navigationMixinHacks/navigationMixinHacks.js +5 -0
  31. package/build/es/modules/lwr/observable/observable.js +71 -0
  32. package/build/{modules → es/modules}/lwr/outlet/outlet.d.ts +1 -0
  33. package/build/es/modules/lwr/outlet/outlet.js +69 -0
  34. package/build/es/modules/lwr/router/router.js +201 -0
  35. package/build/es/modules/lwr/routerBridge/routerBridge.js +85 -0
  36. package/build/es/modules/lwr/routerContainer/routerContainer.js +116 -0
  37. package/build/es/modules/lwr/routerContainer/utils.js +83 -0
  38. package/build/es/modules/lwr/routerErrors/routerErrors.js +154 -0
  39. package/build/es/modules/lwr/routerUtils/domUtils.js +3 -0
  40. package/build/{modules → es/modules}/lwr/routerUtils/filterUtils.d.ts +1 -1
  41. package/build/es/modules/lwr/routerUtils/filterUtils.js +74 -0
  42. package/build/es/modules/lwr/routerUtils/parseUtils.js +182 -0
  43. package/build/{modules → es/modules}/lwr/routerUtils/pathToRegexp.d.ts +5 -5
  44. package/build/es/modules/lwr/routerUtils/pathToRegexp.js +415 -0
  45. package/build/es/modules/lwr/routerUtils/routeDefUtils.js +204 -0
  46. package/build/es/modules/lwr/routerUtils/routeUtils.js +239 -0
  47. package/build/es/modules/lwr/routerUtils/routerUtils.js +19 -0
  48. package/build/es/modules/lwr/routerUtils/typeUtils.js +112 -0
  49. package/build/{modules → es/modules}/lwr/routerUtils/types.d.ts +23 -23
  50. package/build/es/modules/lwr/routerUtils/types.js +2 -0
  51. package/build/es/modules/lwr/routerUtils/uriUtils.js +134 -0
  52. package/build/modules/lwr/contextUtils/contextUtils.js +5 -0
  53. package/build/modules/lwr/contextUtils/navigationApiStore.js +7 -0
  54. package/build/modules/lwr/domRouter/domRouter.js +6 -0
  55. package/build/modules/lwr/domRouterUtils/historyUtils.js +0 -1
  56. package/build/modules/lwr/outlet/outlet.css +1 -1
  57. package/build/modules/lwr/outlet/outlet.html +2 -2
  58. package/build/modules/lwr/outlet/outlet.js +3 -4
  59. package/build/modules/lwr/router/router.js +3 -0
  60. package/build/modules/lwr/routerContainer/utils.js +1 -3
  61. package/build/modules/lwr/routerUtils/pathToRegexp.js +17 -0
  62. package/build/modules/lwr/routerUtils/typeUtils.js +0 -1
  63. package/package.json +17 -9
  64. package/pageObjects/outlet.cjs +24 -4
  65. package/pageObjects/outlet.d.ts +13 -2
  66. package/pageObjects/outlet.js +25 -5
  67. package/build/modules/lwr/navigationMixinHacks/navigationMixinHacks.d.ts +0 -7
  68. /package/build/{modules → es/modules}/lwr/contextProvider/contextProvider.d.ts +0 -0
  69. /package/build/{modules → es/modules}/lwr/contextUtils/contextInfo.d.ts +0 -0
  70. /package/build/{modules → es/modules}/lwr/contextUtils/contextUtils.d.ts +0 -0
  71. /package/build/{modules → es/modules}/lwr/contextUtils/navigationApiStore.d.ts +0 -0
  72. /package/build/{modules → es/modules}/lwr/domRouterUtils/domRouterUtils.d.ts +0 -0
  73. /package/build/{modules → es/modules}/lwr/domRouterUtils/historyUtils.d.ts +0 -0
  74. /package/build/{modules → es/modules}/lwr/domRouterUtils/uriUtils.d.ts +0 -0
  75. /package/build/{modules → es/modules}/lwr/historyRouter/historyRouter.d.ts +0 -0
  76. /package/build/{modules → es/modules}/lwr/navigation/navigation.d.ts +0 -0
  77. /package/build/{modules → es/modules}/lwr/navigation/navigationApi.d.ts +0 -0
  78. /package/build/{modules → es/modules}/lwr/navigation/navigationMixin.d.ts +0 -0
  79. /package/build/{modules → es/modules}/lwr/observable/observable.d.ts +0 -0
  80. /package/build/{modules → es/modules}/lwr/router/router.d.ts +0 -0
  81. /package/build/{modules → es/modules}/lwr/routerBridge/routerBridge.d.ts +0 -0
  82. /package/build/{modules → es/modules}/lwr/routerContainer/routerContainer.d.ts +0 -0
  83. /package/build/{modules → es/modules}/lwr/routerContainer/utils.d.ts +0 -0
  84. /package/build/{modules → es/modules}/lwr/routerErrors/routerErrors.d.ts +0 -0
  85. /package/build/{modules → es/modules}/lwr/routerUtils/domUtils.d.ts +0 -0
  86. /package/build/{modules → es/modules}/lwr/routerUtils/parseUtils.d.ts +0 -0
  87. /package/build/{modules → es/modules}/lwr/routerUtils/routeDefUtils.d.ts +0 -0
  88. /package/build/{modules → es/modules}/lwr/routerUtils/routeUtils.d.ts +0 -0
  89. /package/build/{modules → es/modules}/lwr/routerUtils/routerUtils.d.ts +0 -0
  90. /package/build/{modules → es/modules}/lwr/routerUtils/typeUtils.d.ts +0 -0
  91. /package/build/{modules → es/modules}/lwr/routerUtils/uriUtils.d.ts +0 -0
  92. /package/build/{services → es/services}/index.d.ts +0 -0
  93. /package/build/{services → es/services}/index.js +0 -0
  94. /package/build/{services → es/services}/module-provider/index.d.ts +0 -0
  95. /package/build/{services → es/services}/module-provider/index.js +0 -0
  96. /package/build/{services → es/services}/module-provider/utils.d.ts +0 -0
  97. /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 declare type ParentRouterEvent = CustomEvent<(router: RouterParent) => void>;
18
- declare type DomRoutingMatch = RoutingMatch & {
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,3 @@
1
+ export { getRelativeUrl } from './uriUtils';
2
+ export { set, replace } from './historyUtils';
3
+ //# sourceMappingURL=domRouterUtils.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 declare type NavigationHandler = (address: PageReference, replace?: boolean) => boolean;
6
+ export type NavigationHandler = (address: PageReference, replace?: boolean) => boolean;
7
7
  export interface DomRouterConfig {
8
8
  handleNavigation?: NavigationHandler;
9
9
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -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&param2=two&param3"
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