crelte 0.1.3 → 0.2.1
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/dist/CrelteRequest.d.ts +11 -6
- package/dist/CrelteRequest.d.ts.map +1 -1
- package/dist/CrelteRequest.js +14 -14
- package/dist/graphql/GraphQl.d.ts +1 -1
- package/dist/graphql/GraphQl.d.ts.map +1 -1
- package/dist/graphql/GraphQl.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -3
- package/dist/init/client.d.ts.map +1 -1
- package/dist/init/client.js +11 -5
- package/dist/init/server.d.ts +2 -0
- package/dist/init/server.d.ts.map +1 -1
- package/dist/init/server.js +24 -10
- package/dist/init/shared.d.ts +3 -3
- package/dist/init/shared.d.ts.map +1 -1
- package/dist/init/shared.js +1 -1
- package/dist/loadData/index.d.ts +11 -5
- package/dist/loadData/index.d.ts.map +1 -1
- package/dist/loadData/index.js +15 -6
- package/dist/routing/History.d.ts +5 -3
- package/dist/routing/History.d.ts.map +1 -1
- package/dist/routing/History.js +9 -4
- package/dist/routing/InnerRouter.d.ts +31 -21
- package/dist/routing/InnerRouter.d.ts.map +1 -1
- package/dist/routing/InnerRouter.js +98 -76
- package/dist/routing/PageLoader.d.ts +4 -5
- package/dist/routing/PageLoader.d.ts.map +1 -1
- package/dist/routing/PageLoader.js +5 -5
- package/dist/routing/Request.d.ts +15 -2
- package/dist/routing/Request.d.ts.map +1 -1
- package/dist/routing/Request.js +22 -1
- package/dist/routing/Route.d.ts +61 -25
- package/dist/routing/Route.d.ts.map +1 -1
- package/dist/routing/Route.js +90 -41
- package/dist/routing/Router.d.ts +34 -13
- package/dist/routing/Router.d.ts.map +1 -1
- package/dist/routing/Router.js +102 -49
- package/package.json +3 -2
- package/src/CrelteRequest.ts +14 -19
- package/src/graphql/GraphQl.ts +5 -2
- package/src/index.ts +26 -3
- package/src/init/client.ts +14 -10
- package/src/init/server.ts +31 -11
- package/src/init/shared.ts +4 -4
- package/src/loadData/index.ts +47 -15
- package/src/routing/History.ts +12 -5
- package/src/routing/InnerRouter.ts +109 -82
- package/src/routing/PageLoader.ts +7 -17
- package/src/routing/Request.ts +28 -6
- package/src/routing/Route.ts +115 -52
- package/src/routing/Router.ts +123 -59
- package/LICENSE.md +0 -41
package/src/routing/Route.ts
CHANGED
|
@@ -13,23 +13,25 @@ export type RouteOptions = {
|
|
|
13
13
|
*
|
|
14
14
|
* - `'init'`: is set on the first page load
|
|
15
15
|
* - `'manual'`: is set when a route is triggered manually via `Router.open`
|
|
16
|
-
* - `'live-preview-init'`: is set on the first page load in live preview mode
|
|
17
16
|
* - `'click'`: is set when a route is triggered by a click event
|
|
18
17
|
* - `'pop'`: is set when a route is triggered by a popstate event (back/forward)
|
|
18
|
+
* - `'replace'`: is set when a route is replaced via `Router.replaceState`
|
|
19
|
+
* - `'push'`: is set when a route is pushed via `Router.pushState`
|
|
20
|
+
*
|
|
21
|
+
* ## Note
|
|
22
|
+
* `replace` and `push` will not call loadData
|
|
19
23
|
*/
|
|
20
24
|
export type RouteOrigin =
|
|
21
25
|
| 'init'
|
|
22
|
-
| 'live-preview-init'
|
|
23
26
|
| 'manual'
|
|
24
27
|
| 'click'
|
|
25
|
-
| 'pop'
|
|
28
|
+
| 'pop'
|
|
29
|
+
| 'replace'
|
|
30
|
+
| 'push';
|
|
26
31
|
|
|
27
32
|
/**
|
|
28
33
|
* A Route contains information about the current page for example the url and
|
|
29
|
-
* the site
|
|
30
|
-
*
|
|
31
|
-
* ## Note
|
|
32
|
-
* Never update the route directly, clone it before
|
|
34
|
+
* the site
|
|
33
35
|
*/
|
|
34
36
|
export default class Route {
|
|
35
37
|
/**
|
|
@@ -38,12 +40,26 @@ export default class Route {
|
|
|
38
40
|
url: URL;
|
|
39
41
|
|
|
40
42
|
/**
|
|
41
|
-
* The site of the route
|
|
43
|
+
* The site of the route
|
|
44
|
+
*
|
|
45
|
+
* ## Note
|
|
46
|
+
* The site might not always match with the current route
|
|
47
|
+
* but be the site default site or one that matches the
|
|
48
|
+
* users language.
|
|
49
|
+
*
|
|
50
|
+
* If that is important call `route.siteMatches()` to verify
|
|
42
51
|
*/
|
|
43
|
-
site: Site
|
|
52
|
+
site: Site;
|
|
44
53
|
|
|
45
54
|
/**
|
|
46
55
|
* The scroll position of the current route
|
|
56
|
+
*
|
|
57
|
+
* ## Note
|
|
58
|
+
* This does not have to represent the current scroll position
|
|
59
|
+
* should more be used internally.
|
|
60
|
+
*
|
|
61
|
+
* It might be useful for a new request to specify the wanted
|
|
62
|
+
* scroll position
|
|
47
63
|
*/
|
|
48
64
|
scrollY: number | null;
|
|
49
65
|
|
|
@@ -61,7 +77,7 @@ export default class Route {
|
|
|
61
77
|
/**
|
|
62
78
|
* Creates a new Route
|
|
63
79
|
*/
|
|
64
|
-
constructor(url: string | URL, site: Site
|
|
80
|
+
constructor(url: string | URL, site: Site, opts: RouteOptions = {}) {
|
|
65
81
|
this.url = new URL(url);
|
|
66
82
|
|
|
67
83
|
this.site = site;
|
|
@@ -77,17 +93,17 @@ export default class Route {
|
|
|
77
93
|
*
|
|
78
94
|
* ## Example
|
|
79
95
|
* ```
|
|
80
|
-
* const
|
|
81
|
-
*
|
|
96
|
+
* const site = _; // site with url https://example.com/fo
|
|
97
|
+
* const route = new Route('https://example.com/foo/bar/', site);
|
|
98
|
+
* console.log(route.uri); // '/bar'
|
|
82
99
|
*
|
|
83
|
-
* const
|
|
84
|
-
* const route2 = new Route('https://example.com/foo/bar/?a=1',
|
|
85
|
-
* console.log(route2.uri); // '/bar'
|
|
100
|
+
* const site2 = _; // site with url https://example.com/other
|
|
101
|
+
* const route2 = new Route('https://example.com/foo/bar/?a=1', site2);
|
|
102
|
+
* console.log(route2.uri); // '/foo/bar'
|
|
86
103
|
* ```
|
|
87
104
|
*/
|
|
88
105
|
get uri(): string {
|
|
89
|
-
|
|
90
|
-
if (this.site) {
|
|
106
|
+
if (this.siteMatches()) {
|
|
91
107
|
return trimSlashEnd(
|
|
92
108
|
this.url.pathname.substring(this.site.uri.length),
|
|
93
109
|
);
|
|
@@ -103,16 +119,17 @@ export default class Route {
|
|
|
103
119
|
*
|
|
104
120
|
* ## Example
|
|
105
121
|
* ```
|
|
122
|
+
* const site = _; // site with url https://example.com/foo
|
|
106
123
|
* const route = new Route('https://example.com/foo/bar/', null);
|
|
107
|
-
* console.log(route.baseUrl); // 'https://example.com'
|
|
124
|
+
* console.log(route.baseUrl); // 'https://example.com/foo'
|
|
108
125
|
*
|
|
109
|
-
* const
|
|
110
|
-
* const route2 = new Route('https://example.com/foo/bar/',
|
|
111
|
-
* console.log(route2.baseUrl); // 'https://example.com
|
|
126
|
+
* const site2 = _; // site with url https://example.com/other
|
|
127
|
+
* const route2 = new Route('https://example.com/foo/bar/', site2);
|
|
128
|
+
* console.log(route2.baseUrl); // 'https://example.com'
|
|
112
129
|
* ```
|
|
113
130
|
*/
|
|
114
131
|
get baseUrl(): string {
|
|
115
|
-
if (this.
|
|
132
|
+
if (this.siteMatches()) return trimSlashEnd(this.site.url.href);
|
|
116
133
|
|
|
117
134
|
return this.url.origin;
|
|
118
135
|
}
|
|
@@ -146,36 +163,6 @@ export default class Route {
|
|
|
146
163
|
return this.url.hash;
|
|
147
164
|
}
|
|
148
165
|
|
|
149
|
-
/**
|
|
150
|
-
* Checks if the route is equal to another route
|
|
151
|
-
*
|
|
152
|
-
* This checks all properties of the url but search params do not have to be
|
|
153
|
-
* in the same order
|
|
154
|
-
*/
|
|
155
|
-
eq(route: Route) {
|
|
156
|
-
const searchEq = (a: URLSearchParams, b: URLSearchParams) => {
|
|
157
|
-
if (a.size !== b.size) return false;
|
|
158
|
-
|
|
159
|
-
a.sort();
|
|
160
|
-
b.sort();
|
|
161
|
-
|
|
162
|
-
const aEntries = Array.from(a.entries());
|
|
163
|
-
const bEntries = Array.from(b.entries());
|
|
164
|
-
|
|
165
|
-
return aEntries
|
|
166
|
-
.map((a, i) => [a, bEntries[i]])
|
|
167
|
-
.every(([[ak, av], [bk, bv]]) => ak === bk && av === bv);
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
return (
|
|
171
|
-
route &&
|
|
172
|
-
this.url.pathname === route.url.pathname &&
|
|
173
|
-
this.url.origin === route.url.origin &&
|
|
174
|
-
searchEq(this.search, route.search) &&
|
|
175
|
-
this.hash === route.hash
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
166
|
/**
|
|
180
167
|
* Checks if there are previous routes which would allow it to go back
|
|
181
168
|
*/
|
|
@@ -217,6 +204,82 @@ export default class Route {
|
|
|
217
204
|
}
|
|
218
205
|
}
|
|
219
206
|
|
|
207
|
+
inLivePreview(): boolean {
|
|
208
|
+
return !!this.search.get('x-craft-live-preview');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Returns if the site matches the url
|
|
213
|
+
*/
|
|
214
|
+
siteMatches(): boolean {
|
|
215
|
+
if (this.url.origin !== this.site.url.origin) return false;
|
|
216
|
+
|
|
217
|
+
// now we need to validate the pathname we should make sure both end with a slash
|
|
218
|
+
// todo can we do this better?
|
|
219
|
+
|
|
220
|
+
// make sure that urls like pathname: /abcbc and site: /abc don't match
|
|
221
|
+
return (this.url.pathname + '/').startsWith(
|
|
222
|
+
// uri never returns a slash at the end
|
|
223
|
+
this.site.uri + '/',
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Checks if the route is equal to another route
|
|
229
|
+
*
|
|
230
|
+
* This checks all properties of the url but search params do not have to be
|
|
231
|
+
* in the same order
|
|
232
|
+
*/
|
|
233
|
+
eq(route: Route | null) {
|
|
234
|
+
return (
|
|
235
|
+
route &&
|
|
236
|
+
this.eqUrl(route) &&
|
|
237
|
+
this.eqSearch(route) &&
|
|
238
|
+
this.eqHash(route)
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Checks if the route is equal to another route
|
|
244
|
+
*
|
|
245
|
+
* This does not check the search params or hash
|
|
246
|
+
*/
|
|
247
|
+
eqUrl(route: Route | null) {
|
|
248
|
+
return (
|
|
249
|
+
route &&
|
|
250
|
+
this.url.pathname === route.url.pathname &&
|
|
251
|
+
this.url.origin === route.url.origin
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Checks if the search params are equal to another route
|
|
257
|
+
*/
|
|
258
|
+
eqSearch(route: Route | null) {
|
|
259
|
+
const searchEq = (a: URLSearchParams, b: URLSearchParams) => {
|
|
260
|
+
if (a.size !== b.size) return false;
|
|
261
|
+
|
|
262
|
+
a.sort();
|
|
263
|
+
b.sort();
|
|
264
|
+
|
|
265
|
+
const aEntries = Array.from(a.entries());
|
|
266
|
+
const bEntries = Array.from(b.entries());
|
|
267
|
+
|
|
268
|
+
return aEntries
|
|
269
|
+
.map((a, i) => [a, bEntries[i]])
|
|
270
|
+
.every(([[ak, av], [bk, bv]]) => ak === bk && av === bv);
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
return route && searchEq(this.search, route.search);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Checks if the hash is equal to another route
|
|
278
|
+
*/
|
|
279
|
+
eqHash(route: Route | null) {
|
|
280
|
+
return route && this.hash === route.hash;
|
|
281
|
+
}
|
|
282
|
+
|
|
220
283
|
/**
|
|
221
284
|
* Create a copy of the request
|
|
222
285
|
*/
|
package/src/routing/Router.ts
CHANGED
|
@@ -5,7 +5,7 @@ import PageLoader, { LoadFn, LoadResponse } from './PageLoader.js';
|
|
|
5
5
|
import { ServerHistory } from './History.js';
|
|
6
6
|
import { Readable, Writable } from 'crelte-std/stores';
|
|
7
7
|
import { Listeners } from 'crelte-std/sync';
|
|
8
|
-
import Request from './Request.js';
|
|
8
|
+
import Request, { RequestOptions } from './Request.js';
|
|
9
9
|
|
|
10
10
|
export type RouterOptions = {
|
|
11
11
|
preloadOnMouseOver?: boolean;
|
|
@@ -28,7 +28,6 @@ type Internal = {
|
|
|
28
28
|
onLoaded: (
|
|
29
29
|
success: boolean,
|
|
30
30
|
req: Request,
|
|
31
|
-
site: Site,
|
|
32
31
|
// call ready once your ready to update the dom
|
|
33
32
|
// this makes sure we trigger a route and site update
|
|
34
33
|
// almost at the same moment and probably the same tick
|
|
@@ -36,6 +35,17 @@ type Internal = {
|
|
|
36
35
|
ready: () => any,
|
|
37
36
|
) => void;
|
|
38
37
|
|
|
38
|
+
// onNothingLoaded get's called if the request did not load new Data
|
|
39
|
+
// since maybe a push or replace was called
|
|
40
|
+
onNothingLoaded: (
|
|
41
|
+
req: Request,
|
|
42
|
+
// call ready once your ready to update the dom
|
|
43
|
+
// this makes sure we trigger a route and site update
|
|
44
|
+
// almost at the same moment and probably the same tick
|
|
45
|
+
// to make sure we don't have any flickering
|
|
46
|
+
ready: () => void,
|
|
47
|
+
) => void;
|
|
48
|
+
|
|
39
49
|
onLoad: LoadFn;
|
|
40
50
|
|
|
41
51
|
domReady: (req: Request) => void;
|
|
@@ -50,7 +60,6 @@ type ServerInited = {
|
|
|
50
60
|
// redirect to the route url
|
|
51
61
|
redirect: boolean;
|
|
52
62
|
req: Request;
|
|
53
|
-
site: Site;
|
|
54
63
|
props: any;
|
|
55
64
|
};
|
|
56
65
|
|
|
@@ -80,9 +89,7 @@ export default class Router {
|
|
|
80
89
|
*/
|
|
81
90
|
private _loadingProgress: Writable<number>;
|
|
82
91
|
|
|
83
|
-
private
|
|
84
|
-
|
|
85
|
-
private _onRequest: Listeners<[Request, Site]>;
|
|
92
|
+
private _onRequest: Listeners<[Request]>;
|
|
86
93
|
|
|
87
94
|
/** @hidden */
|
|
88
95
|
_internal: Internal;
|
|
@@ -107,26 +114,25 @@ export default class Router {
|
|
|
107
114
|
this._loading = new Writable(false);
|
|
108
115
|
this._loadingProgress = new Writable(0);
|
|
109
116
|
|
|
110
|
-
this._onRouteEv = new Listeners();
|
|
111
|
-
|
|
112
117
|
this._onRequest = new Listeners();
|
|
113
118
|
|
|
114
119
|
this._internal = {
|
|
115
120
|
onLoaded: () => {},
|
|
121
|
+
onNothingLoaded: () => {},
|
|
116
122
|
onLoad: () => {},
|
|
117
123
|
domReady: req => this.inner.domReady(req),
|
|
118
124
|
initClient: () => this._initClient(),
|
|
119
125
|
initServer: (url, acceptLang) => this._initServer(url, acceptLang),
|
|
120
126
|
};
|
|
121
127
|
|
|
122
|
-
this.inner.onRoute = (route,
|
|
123
|
-
this._onRoute(route,
|
|
124
|
-
this.inner.onPreload =
|
|
128
|
+
this.inner.onRoute = (route, changeHistory) =>
|
|
129
|
+
this._onRoute(route, changeHistory);
|
|
130
|
+
this.inner.onPreload = route => this._onPreload(route);
|
|
125
131
|
|
|
126
|
-
this.pageLoader.onLoaded = (resp, req,
|
|
127
|
-
this._onLoaded(resp, req,
|
|
128
|
-
this.pageLoader.loadFn = (req,
|
|
129
|
-
this._internal.onLoad(req,
|
|
132
|
+
this.pageLoader.onLoaded = (resp, req, more) =>
|
|
133
|
+
this._onLoaded(resp, req, more);
|
|
134
|
+
this.pageLoader.loadFn = (req, opts) =>
|
|
135
|
+
this._internal.onLoad(req, opts);
|
|
130
136
|
this.pageLoader.onProgress = (loading, progress) =>
|
|
131
137
|
this._onProgress(loading, progress);
|
|
132
138
|
}
|
|
@@ -169,10 +175,13 @@ export default class Router {
|
|
|
169
175
|
/**
|
|
170
176
|
* Open a new route
|
|
171
177
|
*
|
|
172
|
-
* @param target the target to open can be an url or a
|
|
178
|
+
* @param target the target to open can be an url, a route or a request
|
|
173
179
|
* the url needs to start with http or with a / which will be considered as
|
|
174
180
|
* the site baseUrl
|
|
175
181
|
*
|
|
182
|
+
* ## Note
|
|
183
|
+
* The origin will always be set to 'manual'
|
|
184
|
+
*
|
|
176
185
|
* ## Example
|
|
177
186
|
* ```
|
|
178
187
|
* import { getRouter } from 'crelte';
|
|
@@ -184,16 +193,25 @@ export default class Router {
|
|
|
184
193
|
* // the following page will be opened https://example.com/de/foo/bar
|
|
185
194
|
* ```
|
|
186
195
|
*/
|
|
187
|
-
open(target: string | URL | Route) {
|
|
188
|
-
this.inner.
|
|
196
|
+
open(target: string | URL | Route | Request, opts: RequestOptions = {}) {
|
|
197
|
+
const req = this.inner.targetToRequest(target, {
|
|
198
|
+
...opts,
|
|
199
|
+
origin: 'manual',
|
|
200
|
+
});
|
|
201
|
+
this.inner.open(req);
|
|
189
202
|
}
|
|
190
203
|
|
|
191
204
|
/**
|
|
192
|
-
* This pushes the
|
|
205
|
+
* This pushes the new route without triggering a new pageload
|
|
193
206
|
*
|
|
194
207
|
* You can use this when using pagination for example change the route object
|
|
195
208
|
* (search argument) and then call pushState
|
|
196
209
|
*
|
|
210
|
+
* ## Note
|
|
211
|
+
* This will always set the origin to 'push'
|
|
212
|
+
* And will clear the scrollY value if you not provide a new one via the `opts`
|
|
213
|
+
* This will disableLoadData by default if you not provide an override via the `opts`
|
|
214
|
+
*
|
|
197
215
|
* ## Example
|
|
198
216
|
* ```
|
|
199
217
|
* import { getRouter } from 'crelte';
|
|
@@ -206,17 +224,37 @@ export default class Router {
|
|
|
206
224
|
* router.pushState(route);
|
|
207
225
|
* ```
|
|
208
226
|
*/
|
|
209
|
-
|
|
227
|
+
push(route: Route | Request, opts: RequestOptions = {}) {
|
|
228
|
+
// cancel previous request
|
|
210
229
|
this.pageLoader.discard();
|
|
211
|
-
this.inner.
|
|
230
|
+
const req = this.inner.targetToRequest(route, {
|
|
231
|
+
...opts,
|
|
232
|
+
origin: 'push',
|
|
233
|
+
scrollY: opts.scrollY ?? undefined,
|
|
234
|
+
disableLoadData: opts.disableLoadData ?? true,
|
|
235
|
+
});
|
|
236
|
+
this.inner.push(req);
|
|
237
|
+
this.destroyRequest();
|
|
212
238
|
this.setNewRoute(route);
|
|
213
239
|
}
|
|
214
240
|
|
|
241
|
+
/**
|
|
242
|
+
* @deprecated use push instead
|
|
243
|
+
*/
|
|
244
|
+
pushState(route: Route | Request) {
|
|
245
|
+
this.push(route);
|
|
246
|
+
}
|
|
247
|
+
|
|
215
248
|
/**
|
|
216
249
|
* This replaces the state of the route without triggering an event
|
|
217
250
|
*
|
|
218
251
|
* You can use this when using some filters for example a search filter
|
|
219
252
|
*
|
|
253
|
+
* ## Note
|
|
254
|
+
* This will always set the origin to 'replace'
|
|
255
|
+
* And will clear the scrollY value if you not provide a new one via the `opts`
|
|
256
|
+
* This will disableLoadData by default if you not provide an override via the `opts`
|
|
257
|
+
*
|
|
220
258
|
* ## Example
|
|
221
259
|
* ```
|
|
222
260
|
* import { getRouter } from 'crelte';
|
|
@@ -229,10 +267,24 @@ export default class Router {
|
|
|
229
267
|
* router.replaceState(route);
|
|
230
268
|
* ```
|
|
231
269
|
*/
|
|
232
|
-
|
|
270
|
+
replace(route: Route | Request, opts: RequestOptions = {}) {
|
|
271
|
+
// cancel previous request
|
|
233
272
|
this.pageLoader.discard();
|
|
234
|
-
this.inner.
|
|
235
|
-
|
|
273
|
+
const req = this.inner.targetToRequest(route, {
|
|
274
|
+
origin: 'replace',
|
|
275
|
+
scrollY: opts.scrollY ?? undefined,
|
|
276
|
+
disableLoadData: opts.disableLoadData ?? true,
|
|
277
|
+
});
|
|
278
|
+
this.inner.replace(req);
|
|
279
|
+
this.destroyRequest();
|
|
280
|
+
this.setNewRoute(req);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @deprecated use replace instead
|
|
285
|
+
*/
|
|
286
|
+
replaceState(route: Route | Request) {
|
|
287
|
+
this.replace(route);
|
|
236
288
|
}
|
|
237
289
|
|
|
238
290
|
/**
|
|
@@ -259,13 +311,13 @@ export default class Router {
|
|
|
259
311
|
/**
|
|
260
312
|
* Add a listener for the onRoute event
|
|
261
313
|
*
|
|
262
|
-
* This
|
|
263
|
-
*
|
|
314
|
+
* This will trigger every time a new route is set
|
|
315
|
+
* and is equivalent to router.route.subscribe(fn)
|
|
264
316
|
*
|
|
265
317
|
* @returns a function to remove the listener
|
|
266
318
|
*/
|
|
267
|
-
onRoute(fn: (route: Route
|
|
268
|
-
return this.
|
|
319
|
+
onRoute(fn: (route: Route) => void): () => void {
|
|
320
|
+
return this.route.subscribe(fn);
|
|
269
321
|
}
|
|
270
322
|
|
|
271
323
|
/**
|
|
@@ -275,17 +327,16 @@ export default class Router {
|
|
|
275
327
|
*
|
|
276
328
|
* @returns a function to remove the listener
|
|
277
329
|
*/
|
|
278
|
-
onRequest(fn: (req: Request
|
|
330
|
+
onRequest(fn: (req: Request) => void): () => void {
|
|
279
331
|
return this._onRequest.add(fn);
|
|
280
332
|
}
|
|
281
333
|
|
|
282
334
|
private setNewRoute(route: Route) {
|
|
283
|
-
this.destroyRequest();
|
|
284
|
-
|
|
285
335
|
this._route.setSilent(route);
|
|
286
|
-
|
|
336
|
+
const siteChanged = this.site.get()?.id !== route.site.id;
|
|
337
|
+
this._site.setSilent(route.site);
|
|
287
338
|
this._route.notify();
|
|
288
|
-
if (
|
|
339
|
+
if (siteChanged) this._site.notify();
|
|
289
340
|
}
|
|
290
341
|
|
|
291
342
|
private async _initClient() {
|
|
@@ -298,8 +349,12 @@ export default class Router {
|
|
|
298
349
|
): Promise<ServerInited> {
|
|
299
350
|
this.inner.initServer();
|
|
300
351
|
|
|
352
|
+
this._internal.onNothingLoaded = (_req, ready) => {
|
|
353
|
+
ready();
|
|
354
|
+
};
|
|
355
|
+
|
|
301
356
|
const prom: Promise<ServerInited> = new Promise(resolve => {
|
|
302
|
-
this._internal.onLoaded = (success, req,
|
|
357
|
+
this._internal.onLoaded = (success, req, ready) => {
|
|
303
358
|
const props = ready();
|
|
304
359
|
this._internal.onLoaded = () => {};
|
|
305
360
|
|
|
@@ -307,7 +362,6 @@ export default class Router {
|
|
|
307
362
|
success,
|
|
308
363
|
redirect: false,
|
|
309
364
|
req,
|
|
310
|
-
site,
|
|
311
365
|
props,
|
|
312
366
|
});
|
|
313
367
|
};
|
|
@@ -318,14 +372,13 @@ export default class Router {
|
|
|
318
372
|
|
|
319
373
|
// let's see if the url matches any route and site
|
|
320
374
|
// if not let's redirect to the site which matches the acceptLang
|
|
321
|
-
if (!route.
|
|
375
|
+
if (!route.siteMatches()) {
|
|
322
376
|
const site = this.inner.siteByAcceptLang(acceptLang);
|
|
323
377
|
|
|
324
378
|
return {
|
|
325
379
|
success: true,
|
|
326
380
|
redirect: true,
|
|
327
381
|
req: new Request(site.url, site),
|
|
328
|
-
site,
|
|
329
382
|
props: {},
|
|
330
383
|
};
|
|
331
384
|
}
|
|
@@ -335,14 +388,13 @@ export default class Router {
|
|
|
335
388
|
const resp = await prom;
|
|
336
389
|
|
|
337
390
|
const hist = this.inner.history as ServerHistory;
|
|
338
|
-
if (hist.url) {
|
|
339
|
-
const
|
|
340
|
-
if (!route.eq(
|
|
391
|
+
if (hist.url || hist.req) {
|
|
392
|
+
const nReq = this.inner.targetToRequest(hist.req ?? hist.url!);
|
|
393
|
+
if (!route.eq(nReq)) {
|
|
341
394
|
return {
|
|
342
395
|
success: true,
|
|
343
396
|
redirect: true,
|
|
344
|
-
req:
|
|
345
|
-
site: route.site!,
|
|
397
|
+
req: nReq,
|
|
346
398
|
props: {},
|
|
347
399
|
};
|
|
348
400
|
}
|
|
@@ -351,7 +403,7 @@ export default class Router {
|
|
|
351
403
|
return resp;
|
|
352
404
|
}
|
|
353
405
|
|
|
354
|
-
private _onRoute(req: Request,
|
|
406
|
+
private _onRoute(req: Request, changeHistory: () => void) {
|
|
355
407
|
this.destroyRequest();
|
|
356
408
|
|
|
357
409
|
this._request = req;
|
|
@@ -361,10 +413,14 @@ export default class Router {
|
|
|
361
413
|
throw new Error('render barrier is already open');
|
|
362
414
|
}
|
|
363
415
|
|
|
364
|
-
this._onRequest.trigger(req
|
|
416
|
+
this._onRequest.trigger(req);
|
|
365
417
|
|
|
366
418
|
// route prepared
|
|
367
|
-
|
|
419
|
+
if (!req.disableLoadData) {
|
|
420
|
+
this.pageLoader.load(req, { changeHistory });
|
|
421
|
+
} else {
|
|
422
|
+
this._onNothingLoaded(req, { changeHistory });
|
|
423
|
+
}
|
|
368
424
|
}
|
|
369
425
|
|
|
370
426
|
private destroyRequest() {
|
|
@@ -374,14 +430,13 @@ export default class Router {
|
|
|
374
430
|
this._request = null;
|
|
375
431
|
}
|
|
376
432
|
|
|
377
|
-
private _onPreload(req: Request
|
|
378
|
-
this.pageLoader.preload(req
|
|
433
|
+
private _onPreload(req: Request) {
|
|
434
|
+
this.pageLoader.preload(req);
|
|
379
435
|
}
|
|
380
436
|
|
|
381
437
|
private async _onLoaded(
|
|
382
438
|
resp: LoadResponse,
|
|
383
439
|
req: Request,
|
|
384
|
-
site: Site,
|
|
385
440
|
more: LoadedMore,
|
|
386
441
|
) {
|
|
387
442
|
// check if the render was cancelled
|
|
@@ -394,19 +449,28 @@ export default class Router {
|
|
|
394
449
|
|
|
395
450
|
const route = req.toRoute();
|
|
396
451
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
452
|
+
// call the client or server saying we are ready for a new render
|
|
453
|
+
this._internal.onLoaded(resp.success, req, () => {
|
|
454
|
+
this.setNewRoute(route);
|
|
455
|
+
return resp.data;
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
private async _onNothingLoaded(req: Request, more: LoadedMore) {
|
|
460
|
+
// check if the render was cancelled
|
|
461
|
+
if (await req._renderBarrier.ready()) return;
|
|
403
462
|
|
|
404
|
-
|
|
405
|
-
|
|
463
|
+
// when the data is loaded let's update the route of the inner
|
|
464
|
+
// this is will only happen if no other route has been requested
|
|
465
|
+
// in the meantime
|
|
466
|
+
more.changeHistory();
|
|
406
467
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
468
|
+
const route = req.toRoute();
|
|
469
|
+
|
|
470
|
+
// call the client or server saying there was an update in the route
|
|
471
|
+
// but no new data was loaded so no render should happen
|
|
472
|
+
this._internal.onNothingLoaded(req, () => {
|
|
473
|
+
this.setNewRoute(route);
|
|
410
474
|
});
|
|
411
475
|
}
|
|
412
476
|
|
package/LICENSE.md
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
Copyright © Dunkel
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Permission is hereby granted to any person obtaining a copy of this software
|
|
5
|
-
(the “Software”) to use, copy, modify, merge, publish and/or distribute copies
|
|
6
|
-
of the Software, and to permit persons to whom the Software is furnished to do
|
|
7
|
-
so, subject to the following conditions:
|
|
8
|
-
|
|
9
|
-
1. **Don’t plagiarize.** The above copyright notice and this license shall be
|
|
10
|
-
included in all copies or substantial portions of the Software.
|
|
11
|
-
|
|
12
|
-
2. **Don’t use the same license on more than one project.** Each licensed copy
|
|
13
|
-
of the Software shall be actively installed in no more than one production
|
|
14
|
-
environment at a time.
|
|
15
|
-
|
|
16
|
-
3. **Don’t mess with the licensing features.** Software features related to
|
|
17
|
-
licensing shall not be altered or circumvented in any way, including (but
|
|
18
|
-
not limited to) license validation, payment prompts, feature restrictions,
|
|
19
|
-
and update eligibility.
|
|
20
|
-
|
|
21
|
-
4. **Pay up.** Payment shall be made immediately upon receipt of any notice,
|
|
22
|
-
prompt, reminder, or other message indicating that a payment is owed.
|
|
23
|
-
|
|
24
|
-
5. **Follow the law.** All use of the Software shall not violate any applicable
|
|
25
|
-
law or regulation, nor infringe the rights of any other person or entity.
|
|
26
|
-
|
|
27
|
-
Failure to comply with the foregoing conditions will automatically and
|
|
28
|
-
immediately result in termination of the permission granted hereby. This
|
|
29
|
-
license does not include any right to receive updates to the Software or
|
|
30
|
-
technical support. Licensees bear all risk related to the quality and
|
|
31
|
-
performance of the Software and any modifications made or obtained to it,
|
|
32
|
-
including liability for actual and consequential harm, such as loss or
|
|
33
|
-
corruption of data, and any necessary service, repair, or correction.
|
|
34
|
-
|
|
35
|
-
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
36
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
37
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
38
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
|
|
39
|
-
LIABILITY, INCLUDING SPECIAL, INCIDENTAL AND CONSEQUENTIAL DAMAGES, WHETHER IN
|
|
40
|
-
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
41
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|