crelte 0.1.3 → 0.2.0
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 +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/init/client.d.ts.map +1 -1
- package/dist/init/client.js +5 -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 +13 -9
- package/dist/routing/InnerRouter.d.ts.map +1 -1
- package/dist/routing/InnerRouter.js +30 -35
- 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 +9 -2
- package/dist/routing/Request.d.ts.map +1 -1
- package/dist/routing/Request.js +16 -1
- package/dist/routing/Route.d.ts +25 -12
- package/dist/routing/Route.d.ts.map +1 -1
- package/dist/routing/Route.js +34 -13
- package/dist/routing/Router.d.ts +5 -6
- package/dist/routing/Router.d.ts.map +1 -1
- package/dist/routing/Router.js +26 -34
- package/package.json +3 -2
- package/src/CrelteRequest.ts +14 -19
- package/src/graphql/GraphQl.ts +5 -2
- package/src/index.ts +17 -3
- package/src/init/client.ts +5 -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 +42 -39
- package/src/routing/PageLoader.ts +7 -17
- package/src/routing/Request.ts +20 -6
- package/src/routing/Route.ts +39 -15
- package/src/routing/Router.ts +33 -42
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import Site, { SiteFromGraphQl } from './Site.js';
|
|
2
2
|
import History from './History.js';
|
|
3
3
|
import { ClientHistory, ServerHistory } from './History.js';
|
|
4
|
-
import Request, { isRequest } from './Request.js';
|
|
5
|
-
import Route from './Route.js';
|
|
4
|
+
import Request, { isRequest, RequestOptions } from './Request.js';
|
|
5
|
+
import Route, { RouteOptions } from './Route.js';
|
|
6
6
|
|
|
7
7
|
export type InnerRouterOpts = {
|
|
8
8
|
preloadOnMouseOver: boolean;
|
|
@@ -13,16 +13,21 @@ export type InnerRouterOpts = {
|
|
|
13
13
|
*/
|
|
14
14
|
export default class InnerRouter {
|
|
15
15
|
sites: Site[];
|
|
16
|
+
/**
|
|
17
|
+
* The current route
|
|
18
|
+
*
|
|
19
|
+
* ## Null
|
|
20
|
+
* It might be null on the first targetToRequest, open, and routeFromUrl call
|
|
21
|
+
*/
|
|
16
22
|
route: Route | null;
|
|
17
|
-
site: Site;
|
|
18
23
|
history: History;
|
|
19
24
|
preloadOnMouseOver: boolean;
|
|
20
25
|
/**
|
|
21
26
|
* @param changeHistory returns a function you need to call when you are ready to
|
|
22
27
|
update the window history (note do not call this after another onRoute call was made)
|
|
23
28
|
*/
|
|
24
|
-
onRoute: (route: Request,
|
|
25
|
-
onPreload: (route: Request
|
|
29
|
+
onRoute: (route: Request, changeHistory: () => void) => void;
|
|
30
|
+
onPreload: (route: Request) => void;
|
|
26
31
|
|
|
27
32
|
private scrollDebounceTimeout: any | null;
|
|
28
33
|
|
|
@@ -36,7 +41,6 @@ export default class InnerRouter {
|
|
|
36
41
|
this.sites = sites.map(s => new Site(s));
|
|
37
42
|
|
|
38
43
|
this.route = null;
|
|
39
|
-
this.site = this.defaultSite();
|
|
40
44
|
// @ts-ignore
|
|
41
45
|
this.history = import.meta.env.SSR
|
|
42
46
|
? new ServerHistory()
|
|
@@ -58,18 +62,18 @@ export default class InnerRouter {
|
|
|
58
62
|
this.listen();
|
|
59
63
|
|
|
60
64
|
// let's first try to load from the state
|
|
61
|
-
const
|
|
62
|
-
|
|
65
|
+
const req = this.targetToRequest(window.location.href);
|
|
66
|
+
req._fillFromState(window.history.state);
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
req.origin = 'init';
|
|
65
69
|
|
|
66
|
-
if (
|
|
67
|
-
|
|
70
|
+
if (req.search.get('x-craft-live-preview')) {
|
|
71
|
+
req.origin = 'live-preview-init';
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
window.history.scrollRestoration = 'manual';
|
|
71
75
|
|
|
72
|
-
this.open(
|
|
76
|
+
this.open(req, {}, false);
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
/**
|
|
@@ -80,8 +84,7 @@ export default class InnerRouter {
|
|
|
80
84
|
/**
|
|
81
85
|
* Get a site and if possible use the accept lang header.
|
|
82
86
|
*
|
|
83
|
-
* @param
|
|
84
|
-
* @return {Site}
|
|
87
|
+
* @param acceptLang Accept Language header.
|
|
85
88
|
*/
|
|
86
89
|
siteByAcceptLang(acceptLang: string | null = null): Site {
|
|
87
90
|
if (!acceptLang) return this.defaultSite();
|
|
@@ -125,7 +128,7 @@ export default class InnerRouter {
|
|
|
125
128
|
* Get the default site
|
|
126
129
|
*/
|
|
127
130
|
defaultSite(): Site {
|
|
128
|
-
return this.sites[0];
|
|
131
|
+
return this.sites.find(s => s.primary) ?? this.sites[0];
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
/**
|
|
@@ -141,10 +144,15 @@ export default class InnerRouter {
|
|
|
141
144
|
* @param target
|
|
142
145
|
* @return Returns null if the url does not match our host (the protocol get's ignored)
|
|
143
146
|
*/
|
|
144
|
-
targetToRequest(
|
|
147
|
+
targetToRequest(
|
|
148
|
+
target: string | URL | Route | Request,
|
|
149
|
+
opts: RequestOptions = {},
|
|
150
|
+
): Request {
|
|
145
151
|
if (typeof target === 'string') {
|
|
146
152
|
if (target.startsWith('/')) {
|
|
147
|
-
|
|
153
|
+
// todo should we use the language matching or throw if the route does not
|
|
154
|
+
// exists
|
|
155
|
+
const site = this.route?.site ?? this.defaultSite();
|
|
148
156
|
target = new URL(site.uri + target, site.url);
|
|
149
157
|
} else {
|
|
150
158
|
target = new URL(target);
|
|
@@ -156,9 +164,10 @@ export default class InnerRouter {
|
|
|
156
164
|
}
|
|
157
165
|
|
|
158
166
|
if (!isRequest(target)) {
|
|
159
|
-
return Request.fromRoute(target);
|
|
167
|
+
return Request.fromRoute(target, opts);
|
|
160
168
|
}
|
|
161
169
|
|
|
170
|
+
target._updateOpts(opts);
|
|
162
171
|
return target;
|
|
163
172
|
}
|
|
164
173
|
|
|
@@ -170,7 +179,7 @@ export default class InnerRouter {
|
|
|
170
179
|
*/
|
|
171
180
|
routeFromUrl(fullUrl: URL): Route {
|
|
172
181
|
// strip stuff we dont need from url
|
|
173
|
-
const route = new Route(fullUrl, null);
|
|
182
|
+
const route = new Route(fullUrl, null!);
|
|
174
183
|
const url = route.url;
|
|
175
184
|
|
|
176
185
|
let site: Site | null = null;
|
|
@@ -193,7 +202,9 @@ export default class InnerRouter {
|
|
|
193
202
|
site = s;
|
|
194
203
|
}
|
|
195
204
|
|
|
196
|
-
|
|
205
|
+
// todo should we throw if we can't find a site
|
|
206
|
+
// or use the site which matches the language
|
|
207
|
+
route.site = site ?? this.defaultSite();
|
|
197
208
|
|
|
198
209
|
return route;
|
|
199
210
|
}
|
|
@@ -284,7 +295,7 @@ export default class InnerRouter {
|
|
|
284
295
|
// route since it is now already the new route
|
|
285
296
|
this.route = null;
|
|
286
297
|
|
|
287
|
-
this.open(route, false);
|
|
298
|
+
this.open(route, {}, false);
|
|
288
299
|
});
|
|
289
300
|
}
|
|
290
301
|
|
|
@@ -294,8 +305,12 @@ export default class InnerRouter {
|
|
|
294
305
|
* @param route a route object or an url or uri, never input the same route object again
|
|
295
306
|
* @param pushState if true pushed the state to the window.history
|
|
296
307
|
*/
|
|
297
|
-
open(
|
|
298
|
-
|
|
308
|
+
open(
|
|
309
|
+
target: string | URL | Route | Request,
|
|
310
|
+
opts: RouteOptions = {},
|
|
311
|
+
pushState: boolean = true,
|
|
312
|
+
) {
|
|
313
|
+
const req = this.targetToRequest(target, opts);
|
|
299
314
|
|
|
300
315
|
const current = this.route;
|
|
301
316
|
if (current) {
|
|
@@ -321,13 +336,13 @@ export default class InnerRouter {
|
|
|
321
336
|
// @ts-ignore
|
|
322
337
|
import.meta.env.SSR
|
|
323
338
|
) {
|
|
324
|
-
this.history.open(req
|
|
339
|
+
this.history.open(req);
|
|
325
340
|
return;
|
|
326
341
|
}
|
|
327
342
|
|
|
328
343
|
if (pushState) {
|
|
329
344
|
req.index = (current?.index ?? 0) + 1;
|
|
330
|
-
this.onRoute(req,
|
|
345
|
+
this.onRoute(req, () => {
|
|
331
346
|
this.pushState(req.toRoute());
|
|
332
347
|
});
|
|
333
348
|
} else {
|
|
@@ -345,9 +360,8 @@ export default class InnerRouter {
|
|
|
345
360
|
*/
|
|
346
361
|
setRoute(req: Request) {
|
|
347
362
|
this.route = req.toRoute();
|
|
348
|
-
if (req.site) this.site = req.site;
|
|
349
363
|
|
|
350
|
-
this.onRoute(req,
|
|
364
|
+
this.onRoute(req, () => {});
|
|
351
365
|
}
|
|
352
366
|
|
|
353
367
|
/**
|
|
@@ -368,7 +382,6 @@ export default class InnerRouter {
|
|
|
368
382
|
);
|
|
369
383
|
|
|
370
384
|
this.route = route;
|
|
371
|
-
if (route.site) this.site = route.site;
|
|
372
385
|
}
|
|
373
386
|
|
|
374
387
|
/**
|
|
@@ -386,7 +399,6 @@ export default class InnerRouter {
|
|
|
386
399
|
);
|
|
387
400
|
|
|
388
401
|
this.route = route;
|
|
389
|
-
if (route.site) this.site = route.site;
|
|
390
402
|
}
|
|
391
403
|
|
|
392
404
|
/**
|
|
@@ -398,21 +410,12 @@ export default class InnerRouter {
|
|
|
398
410
|
*/
|
|
399
411
|
preload(target: string | URL | Route | Request) {
|
|
400
412
|
const req = this.targetToRequest(target);
|
|
401
|
-
|
|
402
|
-
// todo, don't think this makes any sense
|
|
403
|
-
// if the domain of the current site is different than the domain of the
|
|
404
|
-
// new site id does not make sense to preload
|
|
405
|
-
if (this.site.url.origin !== req.url.origin) {
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
413
|
const current = this.route;
|
|
410
|
-
const site = req.site ?? this.site;
|
|
411
414
|
|
|
412
415
|
// if the origin matches, the route will be able to be load
|
|
413
416
|
// so let's preload it
|
|
414
417
|
if (current && current.url.origin === req.url.origin) {
|
|
415
|
-
this.onPreload(req
|
|
418
|
+
this.onPreload(req);
|
|
416
419
|
}
|
|
417
420
|
}
|
|
418
421
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import Request from './Request.js';
|
|
2
|
-
import Site from './Site.js';
|
|
3
2
|
|
|
4
3
|
export type PageLoaderOptions = {
|
|
5
4
|
debugTiming: boolean;
|
|
@@ -10,11 +9,7 @@ export type LoadResponse = {
|
|
|
10
9
|
data: any;
|
|
11
10
|
};
|
|
12
11
|
|
|
13
|
-
export type LoadFn = (
|
|
14
|
-
req: Request,
|
|
15
|
-
site: Site,
|
|
16
|
-
opts: LoadOptions,
|
|
17
|
-
) => Promise<any> | any;
|
|
12
|
+
export type LoadFn = (req: Request, opts: LoadOptions) => Promise<any> | any;
|
|
18
13
|
|
|
19
14
|
export type LoadOptions = {
|
|
20
15
|
setProgress: (num: number) => void;
|
|
@@ -29,12 +24,7 @@ export default class PageLoader<More> {
|
|
|
29
24
|
|
|
30
25
|
private loadingVersion: number;
|
|
31
26
|
|
|
32
|
-
onLoaded: (
|
|
33
|
-
resp: LoadResponse,
|
|
34
|
-
req: Request,
|
|
35
|
-
site: Site,
|
|
36
|
-
more: More,
|
|
37
|
-
) => void;
|
|
27
|
+
onLoaded: (resp: LoadResponse, req: Request, more: More) => void;
|
|
38
28
|
onProgress: (loading: boolean, progress?: number) => void;
|
|
39
29
|
loadFn: LoadFn;
|
|
40
30
|
|
|
@@ -62,7 +52,7 @@ export default class PageLoader<More> {
|
|
|
62
52
|
this.onProgress(false);
|
|
63
53
|
}
|
|
64
54
|
|
|
65
|
-
async load(req: Request,
|
|
55
|
+
async load(req: Request, more: More) {
|
|
66
56
|
this.onProgress(true);
|
|
67
57
|
|
|
68
58
|
const version = ++this.loadingVersion;
|
|
@@ -76,7 +66,7 @@ export default class PageLoader<More> {
|
|
|
76
66
|
|
|
77
67
|
const resp: LoadResponse = { success: false, data: null };
|
|
78
68
|
try {
|
|
79
|
-
resp.data = await this.loadFn(req,
|
|
69
|
+
resp.data = await this.loadFn(req, { setProgress });
|
|
80
70
|
resp.success = true;
|
|
81
71
|
} catch (e) {
|
|
82
72
|
resp.success = false;
|
|
@@ -91,18 +81,18 @@ export default class PageLoader<More> {
|
|
|
91
81
|
return console.log('route changed quickly, ignoring response');
|
|
92
82
|
|
|
93
83
|
this.onProgress(false);
|
|
94
|
-
this.onLoaded(resp, req,
|
|
84
|
+
this.onLoaded(resp, req, more);
|
|
95
85
|
}
|
|
96
86
|
|
|
97
87
|
// you don't need to wait on this call
|
|
98
|
-
async preload(req: Request
|
|
88
|
+
async preload(req: Request) {
|
|
99
89
|
const url = req.url.origin + req.url.pathname;
|
|
100
90
|
if (this.preloadedUrls.has(url)) return;
|
|
101
91
|
|
|
102
92
|
this.preloadedUrls.add(url);
|
|
103
93
|
|
|
104
94
|
try {
|
|
105
|
-
await this.loadFn(req,
|
|
95
|
+
await this.loadFn(req, { setProgress: () => null });
|
|
106
96
|
} catch (_e) {
|
|
107
97
|
console.log('preload failed');
|
|
108
98
|
// retry at another time
|
package/src/routing/Request.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type RequestOptions = {
|
|
|
10
10
|
index?: number;
|
|
11
11
|
origin?: RouteOrigin;
|
|
12
12
|
disableScroll?: boolean;
|
|
13
|
+
statusCode?: number;
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -24,31 +25,34 @@ export default class Request extends Route {
|
|
|
24
25
|
*/
|
|
25
26
|
disableScroll: boolean;
|
|
26
27
|
|
|
28
|
+
/**
|
|
29
|
+
* The Status code that should be returned for a redirect
|
|
30
|
+
*/
|
|
31
|
+
statusCode: number | null;
|
|
32
|
+
|
|
27
33
|
/** @hidden */
|
|
28
34
|
_renderBarrier: RenderBarrier;
|
|
29
35
|
|
|
30
36
|
/**
|
|
31
37
|
* Create a new Request
|
|
32
38
|
*/
|
|
33
|
-
constructor(
|
|
34
|
-
url: string | URL,
|
|
35
|
-
site: Site | null,
|
|
36
|
-
opts: RequestOptions = {},
|
|
37
|
-
) {
|
|
39
|
+
constructor(url: string | URL, site: Site, opts: RequestOptions = {}) {
|
|
38
40
|
super(url, site, opts);
|
|
39
41
|
|
|
40
42
|
this.disableScroll = opts.disableScroll ?? false;
|
|
43
|
+
this.statusCode = opts.statusCode ?? null;
|
|
41
44
|
this._renderBarrier = new RenderBarrier();
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
/**
|
|
45
48
|
* Create a Request from a Route
|
|
46
49
|
*/
|
|
47
|
-
static fromRoute(route: Route) {
|
|
50
|
+
static fromRoute(route: Route, opts: RequestOptions = {}) {
|
|
48
51
|
return new Request(route.url.href, route.site, {
|
|
49
52
|
scrollY: route.scrollY ?? undefined,
|
|
50
53
|
index: route.index,
|
|
51
54
|
origin: route.origin,
|
|
55
|
+
...opts,
|
|
52
56
|
});
|
|
53
57
|
}
|
|
54
58
|
|
|
@@ -93,6 +97,7 @@ export default class Request extends Route {
|
|
|
93
97
|
index: this.index,
|
|
94
98
|
origin: this.origin,
|
|
95
99
|
disableScroll: this.disableScroll,
|
|
100
|
+
statusCode: this.statusCode ?? undefined,
|
|
96
101
|
});
|
|
97
102
|
}
|
|
98
103
|
|
|
@@ -106,6 +111,15 @@ export default class Request extends Route {
|
|
|
106
111
|
origin: this.origin,
|
|
107
112
|
});
|
|
108
113
|
}
|
|
114
|
+
|
|
115
|
+
/** @hidden */
|
|
116
|
+
_updateOpts(opts: RequestOptions = {}) {
|
|
117
|
+
this.scrollY = opts.scrollY ?? this.scrollY;
|
|
118
|
+
this.index = opts.index ?? this.index;
|
|
119
|
+
this.origin = opts.origin ?? this.origin;
|
|
120
|
+
this.disableScroll = opts.disableScroll ?? this.disableScroll;
|
|
121
|
+
this.statusCode = opts.statusCode ?? this.statusCode;
|
|
122
|
+
}
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
export function isRequest(req: any): req is Request {
|
package/src/routing/Route.ts
CHANGED
|
@@ -38,9 +38,16 @@ export default class Route {
|
|
|
38
38
|
url: URL;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* The site of the route
|
|
41
|
+
* The site of the route
|
|
42
|
+
*
|
|
43
|
+
* ## Note
|
|
44
|
+
* The site might not always match with the current route
|
|
45
|
+
* but be the site default site or one that matches the
|
|
46
|
+
* users language.
|
|
47
|
+
*
|
|
48
|
+
* If that is important call `route.siteMatches()` to verify
|
|
42
49
|
*/
|
|
43
|
-
site: Site
|
|
50
|
+
site: Site;
|
|
44
51
|
|
|
45
52
|
/**
|
|
46
53
|
* The scroll position of the current route
|
|
@@ -61,7 +68,7 @@ export default class Route {
|
|
|
61
68
|
/**
|
|
62
69
|
* Creates a new Route
|
|
63
70
|
*/
|
|
64
|
-
constructor(url: string | URL, site: Site
|
|
71
|
+
constructor(url: string | URL, site: Site, opts: RouteOptions = {}) {
|
|
65
72
|
this.url = new URL(url);
|
|
66
73
|
|
|
67
74
|
this.site = site;
|
|
@@ -77,17 +84,17 @@ export default class Route {
|
|
|
77
84
|
*
|
|
78
85
|
* ## Example
|
|
79
86
|
* ```
|
|
80
|
-
* const
|
|
81
|
-
*
|
|
87
|
+
* const site = _; // site with url https://example.com/fo
|
|
88
|
+
* const route = new Route('https://example.com/foo/bar/', site);
|
|
89
|
+
* console.log(route.uri); // '/bar'
|
|
82
90
|
*
|
|
83
|
-
* const
|
|
84
|
-
* const route2 = new Route('https://example.com/foo/bar/?a=1',
|
|
85
|
-
* console.log(route2.uri); // '/bar'
|
|
91
|
+
* const site2 = _; // site with url https://example.com/other
|
|
92
|
+
* const route2 = new Route('https://example.com/foo/bar/?a=1', site2);
|
|
93
|
+
* console.log(route2.uri); // '/foo/bar'
|
|
86
94
|
* ```
|
|
87
95
|
*/
|
|
88
96
|
get uri(): string {
|
|
89
|
-
|
|
90
|
-
if (this.site) {
|
|
97
|
+
if (this.siteMatches()) {
|
|
91
98
|
return trimSlashEnd(
|
|
92
99
|
this.url.pathname.substring(this.site.uri.length),
|
|
93
100
|
);
|
|
@@ -103,16 +110,17 @@ export default class Route {
|
|
|
103
110
|
*
|
|
104
111
|
* ## Example
|
|
105
112
|
* ```
|
|
113
|
+
* const site = _; // site with url https://example.com/foo
|
|
106
114
|
* const route = new Route('https://example.com/foo/bar/', null);
|
|
107
|
-
* console.log(route.baseUrl); // 'https://example.com'
|
|
115
|
+
* console.log(route.baseUrl); // 'https://example.com/foo'
|
|
108
116
|
*
|
|
109
|
-
* const
|
|
110
|
-
* const route2 = new Route('https://example.com/foo/bar/',
|
|
111
|
-
* console.log(route2.baseUrl); // 'https://example.com
|
|
117
|
+
* const site2 = _; // site with url https://example.com/other
|
|
118
|
+
* const route2 = new Route('https://example.com/foo/bar/', site2);
|
|
119
|
+
* console.log(route2.baseUrl); // 'https://example.com'
|
|
112
120
|
* ```
|
|
113
121
|
*/
|
|
114
122
|
get baseUrl(): string {
|
|
115
|
-
if (this.
|
|
123
|
+
if (this.siteMatches()) return trimSlashEnd(this.site.url.href);
|
|
116
124
|
|
|
117
125
|
return this.url.origin;
|
|
118
126
|
}
|
|
@@ -146,6 +154,22 @@ export default class Route {
|
|
|
146
154
|
return this.url.hash;
|
|
147
155
|
}
|
|
148
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Returns if the site matches the url
|
|
159
|
+
*/
|
|
160
|
+
siteMatches(): boolean {
|
|
161
|
+
if (this.url.origin !== this.site.url.origin) return false;
|
|
162
|
+
|
|
163
|
+
// now we need to validate the pathname we should make sure both end with a slash
|
|
164
|
+
// todo can we do this better?
|
|
165
|
+
|
|
166
|
+
// make sure that urls like pathname: /abcbc and site: /abc don't match
|
|
167
|
+
return (this.url.pathname + '/').startsWith(
|
|
168
|
+
// uri never returns a slash at the end
|
|
169
|
+
this.site.uri + '/',
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
149
173
|
/**
|
|
150
174
|
* Checks if the route is equal to another route
|
|
151
175
|
*
|
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
|
|
@@ -50,7 +49,6 @@ type ServerInited = {
|
|
|
50
49
|
// redirect to the route url
|
|
51
50
|
redirect: boolean;
|
|
52
51
|
req: Request;
|
|
53
|
-
site: Site;
|
|
54
52
|
props: any;
|
|
55
53
|
};
|
|
56
54
|
|
|
@@ -80,9 +78,9 @@ export default class Router {
|
|
|
80
78
|
*/
|
|
81
79
|
private _loadingProgress: Writable<number>;
|
|
82
80
|
|
|
83
|
-
private _onRouteEv: Listeners<[Route
|
|
81
|
+
private _onRouteEv: Listeners<[Route]>;
|
|
84
82
|
|
|
85
|
-
private _onRequest: Listeners<[Request
|
|
83
|
+
private _onRequest: Listeners<[Request]>;
|
|
86
84
|
|
|
87
85
|
/** @hidden */
|
|
88
86
|
_internal: Internal;
|
|
@@ -119,14 +117,14 @@ export default class Router {
|
|
|
119
117
|
initServer: (url, acceptLang) => this._initServer(url, acceptLang),
|
|
120
118
|
};
|
|
121
119
|
|
|
122
|
-
this.inner.onRoute = (route,
|
|
123
|
-
this._onRoute(route,
|
|
124
|
-
this.inner.onPreload =
|
|
120
|
+
this.inner.onRoute = (route, changeHistory) =>
|
|
121
|
+
this._onRoute(route, changeHistory);
|
|
122
|
+
this.inner.onPreload = route => this._onPreload(route);
|
|
125
123
|
|
|
126
|
-
this.pageLoader.onLoaded = (resp, req,
|
|
127
|
-
this._onLoaded(resp, req,
|
|
128
|
-
this.pageLoader.loadFn = (req,
|
|
129
|
-
this._internal.onLoad(req,
|
|
124
|
+
this.pageLoader.onLoaded = (resp, req, more) =>
|
|
125
|
+
this._onLoaded(resp, req, more);
|
|
126
|
+
this.pageLoader.loadFn = (req, opts) =>
|
|
127
|
+
this._internal.onLoad(req, opts);
|
|
130
128
|
this.pageLoader.onProgress = (loading, progress) =>
|
|
131
129
|
this._onProgress(loading, progress);
|
|
132
130
|
}
|
|
@@ -184,8 +182,8 @@ export default class Router {
|
|
|
184
182
|
* // the following page will be opened https://example.com/de/foo/bar
|
|
185
183
|
* ```
|
|
186
184
|
*/
|
|
187
|
-
open(target: string | URL | Route) {
|
|
188
|
-
this.inner.open(target);
|
|
185
|
+
open(target: string | URL | Route, opts: RequestOptions = {}) {
|
|
186
|
+
this.inner.open(target, opts);
|
|
189
187
|
}
|
|
190
188
|
|
|
191
189
|
/**
|
|
@@ -209,6 +207,7 @@ export default class Router {
|
|
|
209
207
|
pushState(route: Route) {
|
|
210
208
|
this.pageLoader.discard();
|
|
211
209
|
this.inner.pushState(route);
|
|
210
|
+
this.destroyRequest();
|
|
212
211
|
this.setNewRoute(route);
|
|
213
212
|
}
|
|
214
213
|
|
|
@@ -232,6 +231,7 @@ export default class Router {
|
|
|
232
231
|
replaceState(route: Route) {
|
|
233
232
|
this.pageLoader.discard();
|
|
234
233
|
this.inner.replaceState(route);
|
|
234
|
+
this.destroyRequest();
|
|
235
235
|
this.setNewRoute(route);
|
|
236
236
|
}
|
|
237
237
|
|
|
@@ -264,7 +264,7 @@ export default class Router {
|
|
|
264
264
|
*
|
|
265
265
|
* @returns a function to remove the listener
|
|
266
266
|
*/
|
|
267
|
-
onRoute(fn: (route: Route
|
|
267
|
+
onRoute(fn: (route: Route) => void): () => void {
|
|
268
268
|
return this._onRouteEv.add(fn);
|
|
269
269
|
}
|
|
270
270
|
|
|
@@ -275,17 +275,16 @@ export default class Router {
|
|
|
275
275
|
*
|
|
276
276
|
* @returns a function to remove the listener
|
|
277
277
|
*/
|
|
278
|
-
onRequest(fn: (req: Request
|
|
278
|
+
onRequest(fn: (req: Request) => void): () => void {
|
|
279
279
|
return this._onRequest.add(fn);
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
private setNewRoute(route: Route) {
|
|
283
|
-
this.destroyRequest();
|
|
284
|
-
|
|
285
283
|
this._route.setSilent(route);
|
|
286
|
-
|
|
284
|
+
const siteChanged = this.site.get()?.id !== route.site.id;
|
|
285
|
+
this._site.setSilent(route.site);
|
|
287
286
|
this._route.notify();
|
|
288
|
-
if (
|
|
287
|
+
if (siteChanged) this._site.notify();
|
|
289
288
|
}
|
|
290
289
|
|
|
291
290
|
private async _initClient() {
|
|
@@ -299,7 +298,7 @@ export default class Router {
|
|
|
299
298
|
this.inner.initServer();
|
|
300
299
|
|
|
301
300
|
const prom: Promise<ServerInited> = new Promise(resolve => {
|
|
302
|
-
this._internal.onLoaded = (success, req,
|
|
301
|
+
this._internal.onLoaded = (success, req, ready) => {
|
|
303
302
|
const props = ready();
|
|
304
303
|
this._internal.onLoaded = () => {};
|
|
305
304
|
|
|
@@ -307,7 +306,6 @@ export default class Router {
|
|
|
307
306
|
success,
|
|
308
307
|
redirect: false,
|
|
309
308
|
req,
|
|
310
|
-
site,
|
|
311
309
|
props,
|
|
312
310
|
});
|
|
313
311
|
};
|
|
@@ -318,14 +316,13 @@ export default class Router {
|
|
|
318
316
|
|
|
319
317
|
// let's see if the url matches any route and site
|
|
320
318
|
// if not let's redirect to the site which matches the acceptLang
|
|
321
|
-
if (!route.
|
|
319
|
+
if (!route.siteMatches()) {
|
|
322
320
|
const site = this.inner.siteByAcceptLang(acceptLang);
|
|
323
321
|
|
|
324
322
|
return {
|
|
325
323
|
success: true,
|
|
326
324
|
redirect: true,
|
|
327
325
|
req: new Request(site.url, site),
|
|
328
|
-
site,
|
|
329
326
|
props: {},
|
|
330
327
|
};
|
|
331
328
|
}
|
|
@@ -335,14 +332,13 @@ export default class Router {
|
|
|
335
332
|
const resp = await prom;
|
|
336
333
|
|
|
337
334
|
const hist = this.inner.history as ServerHistory;
|
|
338
|
-
if (hist.url) {
|
|
339
|
-
const
|
|
340
|
-
if (!route.eq(
|
|
335
|
+
if (hist.url || hist.req) {
|
|
336
|
+
const nReq = this.inner.targetToRequest(hist.req ?? hist.url!);
|
|
337
|
+
if (!route.eq(nReq)) {
|
|
341
338
|
return {
|
|
342
339
|
success: true,
|
|
343
340
|
redirect: true,
|
|
344
|
-
req:
|
|
345
|
-
site: route.site!,
|
|
341
|
+
req: nReq,
|
|
346
342
|
props: {},
|
|
347
343
|
};
|
|
348
344
|
}
|
|
@@ -351,7 +347,7 @@ export default class Router {
|
|
|
351
347
|
return resp;
|
|
352
348
|
}
|
|
353
349
|
|
|
354
|
-
private _onRoute(req: Request,
|
|
350
|
+
private _onRoute(req: Request, changeHistory: () => void) {
|
|
355
351
|
this.destroyRequest();
|
|
356
352
|
|
|
357
353
|
this._request = req;
|
|
@@ -361,10 +357,10 @@ export default class Router {
|
|
|
361
357
|
throw new Error('render barrier is already open');
|
|
362
358
|
}
|
|
363
359
|
|
|
364
|
-
this._onRequest.trigger(req
|
|
360
|
+
this._onRequest.trigger(req);
|
|
365
361
|
|
|
366
362
|
// route prepared
|
|
367
|
-
this.pageLoader.load(req,
|
|
363
|
+
this.pageLoader.load(req, { changeHistory });
|
|
368
364
|
}
|
|
369
365
|
|
|
370
366
|
private destroyRequest() {
|
|
@@ -374,14 +370,13 @@ export default class Router {
|
|
|
374
370
|
this._request = null;
|
|
375
371
|
}
|
|
376
372
|
|
|
377
|
-
private _onPreload(req: Request
|
|
378
|
-
this.pageLoader.preload(req
|
|
373
|
+
private _onPreload(req: Request) {
|
|
374
|
+
this.pageLoader.preload(req);
|
|
379
375
|
}
|
|
380
376
|
|
|
381
377
|
private async _onLoaded(
|
|
382
378
|
resp: LoadResponse,
|
|
383
379
|
req: Request,
|
|
384
|
-
site: Site,
|
|
385
380
|
more: LoadedMore,
|
|
386
381
|
) {
|
|
387
382
|
// check if the render was cancelled
|
|
@@ -395,16 +390,12 @@ export default class Router {
|
|
|
395
390
|
const route = req.toRoute();
|
|
396
391
|
|
|
397
392
|
const updateRoute = () => {
|
|
398
|
-
this.
|
|
399
|
-
const siteChanged = this.site.get()?.id !== site.id;
|
|
400
|
-
this._site.setSilent(site);
|
|
401
|
-
this._route.notify();
|
|
402
|
-
if (siteChanged) this._site.notify();
|
|
393
|
+
this.setNewRoute(route);
|
|
403
394
|
|
|
404
|
-
this._onRouteEv.trigger(route.clone()
|
|
395
|
+
this._onRouteEv.trigger(route.clone());
|
|
405
396
|
};
|
|
406
397
|
|
|
407
|
-
this._internal.onLoaded(resp.success, req,
|
|
398
|
+
this._internal.onLoaded(resp.success, req, () => {
|
|
408
399
|
updateRoute();
|
|
409
400
|
return resp.data;
|
|
410
401
|
});
|