crelte 0.1.2 → 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 +35 -14
- package/dist/routing/Router.d.ts +5 -7
- package/dist/routing/Router.d.ts.map +1 -1
- package/dist/routing/Router.js +26 -37
- package/dist/routing/Site.js +1 -1
- package/dist/routing/utils.d.ts +2 -0
- package/dist/routing/utils.d.ts.map +1 -0
- package/dist/routing/utils.js +3 -0
- 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 +40 -16
- package/src/routing/Router.ts +33 -46
- package/src/routing/Site.ts +1 -1
- package/src/routing/utils.ts +3 -0
|
@@ -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
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { trimSlashEnd } from './Router.js';
|
|
2
1
|
import Site from './Site.js';
|
|
2
|
+
import { trimSlashEnd } from './utils.js';
|
|
3
3
|
|
|
4
4
|
export type RouteOptions = {
|
|
5
5
|
scrollY?: number;
|
|
@@ -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,14 +49,9 @@ 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
|
|
|
57
|
-
export function trimSlashEnd(str: string) {
|
|
58
|
-
return str.endsWith('/') ? str.substring(0, str.length - 1) : str;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
55
|
// Make sure route and nextRoute are not the same object as _inner.route
|
|
62
56
|
export default class Router {
|
|
63
57
|
/**
|
|
@@ -84,9 +78,9 @@ export default class Router {
|
|
|
84
78
|
*/
|
|
85
79
|
private _loadingProgress: Writable<number>;
|
|
86
80
|
|
|
87
|
-
private _onRouteEv: Listeners<[Route
|
|
81
|
+
private _onRouteEv: Listeners<[Route]>;
|
|
88
82
|
|
|
89
|
-
private _onRequest: Listeners<[Request
|
|
83
|
+
private _onRequest: Listeners<[Request]>;
|
|
90
84
|
|
|
91
85
|
/** @hidden */
|
|
92
86
|
_internal: Internal;
|
|
@@ -123,14 +117,14 @@ export default class Router {
|
|
|
123
117
|
initServer: (url, acceptLang) => this._initServer(url, acceptLang),
|
|
124
118
|
};
|
|
125
119
|
|
|
126
|
-
this.inner.onRoute = (route,
|
|
127
|
-
this._onRoute(route,
|
|
128
|
-
this.inner.onPreload =
|
|
120
|
+
this.inner.onRoute = (route, changeHistory) =>
|
|
121
|
+
this._onRoute(route, changeHistory);
|
|
122
|
+
this.inner.onPreload = route => this._onPreload(route);
|
|
129
123
|
|
|
130
|
-
this.pageLoader.onLoaded = (resp, req,
|
|
131
|
-
this._onLoaded(resp, req,
|
|
132
|
-
this.pageLoader.loadFn = (req,
|
|
133
|
-
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);
|
|
134
128
|
this.pageLoader.onProgress = (loading, progress) =>
|
|
135
129
|
this._onProgress(loading, progress);
|
|
136
130
|
}
|
|
@@ -188,8 +182,8 @@ export default class Router {
|
|
|
188
182
|
* // the following page will be opened https://example.com/de/foo/bar
|
|
189
183
|
* ```
|
|
190
184
|
*/
|
|
191
|
-
open(target: string | URL | Route) {
|
|
192
|
-
this.inner.open(target);
|
|
185
|
+
open(target: string | URL | Route, opts: RequestOptions = {}) {
|
|
186
|
+
this.inner.open(target, opts);
|
|
193
187
|
}
|
|
194
188
|
|
|
195
189
|
/**
|
|
@@ -213,6 +207,7 @@ export default class Router {
|
|
|
213
207
|
pushState(route: Route) {
|
|
214
208
|
this.pageLoader.discard();
|
|
215
209
|
this.inner.pushState(route);
|
|
210
|
+
this.destroyRequest();
|
|
216
211
|
this.setNewRoute(route);
|
|
217
212
|
}
|
|
218
213
|
|
|
@@ -236,6 +231,7 @@ export default class Router {
|
|
|
236
231
|
replaceState(route: Route) {
|
|
237
232
|
this.pageLoader.discard();
|
|
238
233
|
this.inner.replaceState(route);
|
|
234
|
+
this.destroyRequest();
|
|
239
235
|
this.setNewRoute(route);
|
|
240
236
|
}
|
|
241
237
|
|
|
@@ -268,7 +264,7 @@ export default class Router {
|
|
|
268
264
|
*
|
|
269
265
|
* @returns a function to remove the listener
|
|
270
266
|
*/
|
|
271
|
-
onRoute(fn: (route: Route
|
|
267
|
+
onRoute(fn: (route: Route) => void): () => void {
|
|
272
268
|
return this._onRouteEv.add(fn);
|
|
273
269
|
}
|
|
274
270
|
|
|
@@ -279,17 +275,16 @@ export default class Router {
|
|
|
279
275
|
*
|
|
280
276
|
* @returns a function to remove the listener
|
|
281
277
|
*/
|
|
282
|
-
onRequest(fn: (req: Request
|
|
278
|
+
onRequest(fn: (req: Request) => void): () => void {
|
|
283
279
|
return this._onRequest.add(fn);
|
|
284
280
|
}
|
|
285
281
|
|
|
286
282
|
private setNewRoute(route: Route) {
|
|
287
|
-
this.destroyRequest();
|
|
288
|
-
|
|
289
283
|
this._route.setSilent(route);
|
|
290
|
-
|
|
284
|
+
const siteChanged = this.site.get()?.id !== route.site.id;
|
|
285
|
+
this._site.setSilent(route.site);
|
|
291
286
|
this._route.notify();
|
|
292
|
-
if (
|
|
287
|
+
if (siteChanged) this._site.notify();
|
|
293
288
|
}
|
|
294
289
|
|
|
295
290
|
private async _initClient() {
|
|
@@ -303,7 +298,7 @@ export default class Router {
|
|
|
303
298
|
this.inner.initServer();
|
|
304
299
|
|
|
305
300
|
const prom: Promise<ServerInited> = new Promise(resolve => {
|
|
306
|
-
this._internal.onLoaded = (success, req,
|
|
301
|
+
this._internal.onLoaded = (success, req, ready) => {
|
|
307
302
|
const props = ready();
|
|
308
303
|
this._internal.onLoaded = () => {};
|
|
309
304
|
|
|
@@ -311,7 +306,6 @@ export default class Router {
|
|
|
311
306
|
success,
|
|
312
307
|
redirect: false,
|
|
313
308
|
req,
|
|
314
|
-
site,
|
|
315
309
|
props,
|
|
316
310
|
});
|
|
317
311
|
};
|
|
@@ -322,14 +316,13 @@ export default class Router {
|
|
|
322
316
|
|
|
323
317
|
// let's see if the url matches any route and site
|
|
324
318
|
// if not let's redirect to the site which matches the acceptLang
|
|
325
|
-
if (!route.
|
|
319
|
+
if (!route.siteMatches()) {
|
|
326
320
|
const site = this.inner.siteByAcceptLang(acceptLang);
|
|
327
321
|
|
|
328
322
|
return {
|
|
329
323
|
success: true,
|
|
330
324
|
redirect: true,
|
|
331
325
|
req: new Request(site.url, site),
|
|
332
|
-
site,
|
|
333
326
|
props: {},
|
|
334
327
|
};
|
|
335
328
|
}
|
|
@@ -339,14 +332,13 @@ export default class Router {
|
|
|
339
332
|
const resp = await prom;
|
|
340
333
|
|
|
341
334
|
const hist = this.inner.history as ServerHistory;
|
|
342
|
-
if (hist.url) {
|
|
343
|
-
const
|
|
344
|
-
if (!route.eq(
|
|
335
|
+
if (hist.url || hist.req) {
|
|
336
|
+
const nReq = this.inner.targetToRequest(hist.req ?? hist.url!);
|
|
337
|
+
if (!route.eq(nReq)) {
|
|
345
338
|
return {
|
|
346
339
|
success: true,
|
|
347
340
|
redirect: true,
|
|
348
|
-
req:
|
|
349
|
-
site: route.site!,
|
|
341
|
+
req: nReq,
|
|
350
342
|
props: {},
|
|
351
343
|
};
|
|
352
344
|
}
|
|
@@ -355,7 +347,7 @@ export default class Router {
|
|
|
355
347
|
return resp;
|
|
356
348
|
}
|
|
357
349
|
|
|
358
|
-
private _onRoute(req: Request,
|
|
350
|
+
private _onRoute(req: Request, changeHistory: () => void) {
|
|
359
351
|
this.destroyRequest();
|
|
360
352
|
|
|
361
353
|
this._request = req;
|
|
@@ -365,10 +357,10 @@ export default class Router {
|
|
|
365
357
|
throw new Error('render barrier is already open');
|
|
366
358
|
}
|
|
367
359
|
|
|
368
|
-
this._onRequest.trigger(req
|
|
360
|
+
this._onRequest.trigger(req);
|
|
369
361
|
|
|
370
362
|
// route prepared
|
|
371
|
-
this.pageLoader.load(req,
|
|
363
|
+
this.pageLoader.load(req, { changeHistory });
|
|
372
364
|
}
|
|
373
365
|
|
|
374
366
|
private destroyRequest() {
|
|
@@ -378,14 +370,13 @@ export default class Router {
|
|
|
378
370
|
this._request = null;
|
|
379
371
|
}
|
|
380
372
|
|
|
381
|
-
private _onPreload(req: Request
|
|
382
|
-
this.pageLoader.preload(req
|
|
373
|
+
private _onPreload(req: Request) {
|
|
374
|
+
this.pageLoader.preload(req);
|
|
383
375
|
}
|
|
384
376
|
|
|
385
377
|
private async _onLoaded(
|
|
386
378
|
resp: LoadResponse,
|
|
387
379
|
req: Request,
|
|
388
|
-
site: Site,
|
|
389
380
|
more: LoadedMore,
|
|
390
381
|
) {
|
|
391
382
|
// check if the render was cancelled
|
|
@@ -399,16 +390,12 @@ export default class Router {
|
|
|
399
390
|
const route = req.toRoute();
|
|
400
391
|
|
|
401
392
|
const updateRoute = () => {
|
|
402
|
-
this.
|
|
403
|
-
const siteChanged = this.site.get()?.id !== site.id;
|
|
404
|
-
this._site.setSilent(site);
|
|
405
|
-
this._route.notify();
|
|
406
|
-
if (siteChanged) this._site.notify();
|
|
393
|
+
this.setNewRoute(route);
|
|
407
394
|
|
|
408
|
-
this._onRouteEv.trigger(route.clone()
|
|
395
|
+
this._onRouteEv.trigger(route.clone());
|
|
409
396
|
};
|
|
410
397
|
|
|
411
|
-
this._internal.onLoaded(resp.success, req,
|
|
398
|
+
this._internal.onLoaded(resp.success, req, () => {
|
|
412
399
|
updateRoute();
|
|
413
400
|
return resp.data;
|
|
414
401
|
});
|