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/loadData/index.ts
CHANGED
|
@@ -5,6 +5,20 @@ import type { Global } from './Globals.js';
|
|
|
5
5
|
|
|
6
6
|
export type { Globals, Global };
|
|
7
7
|
|
|
8
|
+
export interface LoadDataFn<A1 = any> {
|
|
9
|
+
(cr: CrelteRequest, entryOrBlock: A1, ...args: any[]): Promise<any> | any;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface LoadDataObj<A1 = any> {
|
|
13
|
+
[key: string]: LoadData<A1>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
17
|
+
export interface LoadDataArray<A1 = any> extends Array<LoadData<A1>> {}
|
|
18
|
+
|
|
19
|
+
// todo link to the real docs maybe instead of showing an example
|
|
20
|
+
// so we can add @type {import('crelte').LoadData}
|
|
21
|
+
// maybe enabling markdown might be enough: https://jsdoc.app/plugins-markdown
|
|
8
22
|
/**
|
|
9
23
|
* Load data function
|
|
10
24
|
*
|
|
@@ -15,8 +29,6 @@ export type { Globals, Global };
|
|
|
15
29
|
* Each property should be a loadData type, each one is called in parallel.
|
|
16
30
|
* And will be available to your component with the same name.
|
|
17
31
|
* ```
|
|
18
|
-
* export const loadData = {
|
|
19
|
-
*
|
|
20
32
|
* import entriesQuery from '@/queries/entries.graphql';
|
|
21
33
|
* import { loadData as headerLoadData } from '@/layout/header.svelte';
|
|
22
34
|
*
|
|
@@ -71,20 +83,30 @@ export type { Globals, Global };
|
|
|
71
83
|
* }
|
|
72
84
|
* ```
|
|
73
85
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
| ((cr: CrelteRequest, ...args: any[]) => Promise<T>)
|
|
86
|
+
export type LoadData<A1 = any> =
|
|
87
|
+
| LoadDataFn<A1>
|
|
77
88
|
| GraphQlQuery
|
|
78
|
-
|
|
|
89
|
+
| LoadDataObj<A1>
|
|
90
|
+
| LoadDataArray<A1>
|
|
91
|
+
| string
|
|
92
|
+
| number
|
|
93
|
+
| null
|
|
94
|
+
| undefined;
|
|
95
|
+
|
|
96
|
+
// export type LoadData<A1 = any, R = any> =
|
|
97
|
+
// | ((cr: CrelteRequest, entryOrBlock: A1, ...args: any[]) => Promise<R>)
|
|
98
|
+
// | GraphQlQuery
|
|
99
|
+
// | Record<string, LoadData<A1, R>>;
|
|
79
100
|
|
|
80
|
-
export async function callLoadData(
|
|
81
|
-
ld: LoadData<
|
|
101
|
+
export async function callLoadData<A1 = any>(
|
|
102
|
+
ld: LoadData<A1>,
|
|
82
103
|
cr: CrelteRequest,
|
|
104
|
+
arg1: A1,
|
|
83
105
|
...args: any[]
|
|
84
|
-
): Promise<
|
|
106
|
+
): Promise<any> {
|
|
85
107
|
// either we have a function
|
|
86
108
|
if (typeof ld === 'function') {
|
|
87
|
-
return await ld(cr, ...args);
|
|
109
|
+
return await ld(cr, arg1, ...args);
|
|
88
110
|
}
|
|
89
111
|
|
|
90
112
|
// or a graphql query
|
|
@@ -92,10 +114,16 @@ export async function callLoadData(
|
|
|
92
114
|
return await cr.query(ld);
|
|
93
115
|
}
|
|
94
116
|
|
|
117
|
+
if (ld === null || typeof ld === 'undefined') return null;
|
|
118
|
+
|
|
119
|
+
if (Array.isArray(ld)) {
|
|
120
|
+
return await mergeLoadData(...ld)(cr, arg1, ...args);
|
|
121
|
+
}
|
|
122
|
+
|
|
95
123
|
// or an object
|
|
96
|
-
if (typeof ld === 'object'
|
|
124
|
+
if (typeof ld === 'object') {
|
|
97
125
|
const data = await Promise.all(
|
|
98
|
-
Object.values(ld).map(nld => callLoadData(nld, cr, ...args)),
|
|
126
|
+
Object.values(ld).map(nld => callLoadData(nld, cr, arg1, ...args)),
|
|
99
127
|
);
|
|
100
128
|
|
|
101
129
|
return Object.fromEntries(
|
|
@@ -119,10 +147,14 @@ export async function callLoadData(
|
|
|
119
147
|
* );
|
|
120
148
|
* ```
|
|
121
149
|
*/
|
|
122
|
-
export function mergeLoadData
|
|
123
|
-
|
|
150
|
+
export function mergeLoadData<A1 = any>(
|
|
151
|
+
...lds: LoadData<A1>[]
|
|
152
|
+
): LoadDataFn<A1> {
|
|
153
|
+
return async (cr: CrelteRequest, arg1, ...args: any[]) => {
|
|
124
154
|
const datas = await Promise.all(
|
|
125
|
-
lds.map(
|
|
155
|
+
lds.map(
|
|
156
|
+
ld => callLoadData(ld, cr, arg1, ...args) as Promise<object>,
|
|
157
|
+
),
|
|
126
158
|
);
|
|
127
159
|
|
|
128
160
|
return datas.reduce((acc, data) => ({ ...acc, ...data }), {});
|
package/src/routing/History.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import Request from './Request.js';
|
|
2
|
+
|
|
1
3
|
export default interface History {
|
|
2
4
|
scrollY(): number | null;
|
|
3
5
|
replaceState(data: any, url?: string): void;
|
|
4
6
|
pushState(data: any, url: string): void;
|
|
5
|
-
open(
|
|
7
|
+
open(req: Request): void;
|
|
6
8
|
back(): void;
|
|
7
9
|
}
|
|
8
10
|
|
|
@@ -19,8 +21,8 @@ export class ClientHistory implements History {
|
|
|
19
21
|
history.pushState(data, '', url);
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
open(
|
|
23
|
-
window.location.href = url;
|
|
24
|
+
open(req: Request): void {
|
|
25
|
+
window.location.href = req.url.href;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
back(): void {
|
|
@@ -31,10 +33,12 @@ export class ClientHistory implements History {
|
|
|
31
33
|
export class ServerHistory implements History {
|
|
32
34
|
state: any | null;
|
|
33
35
|
url: string | null;
|
|
36
|
+
req: Request | null;
|
|
34
37
|
|
|
35
38
|
constructor() {
|
|
36
39
|
this.state = null;
|
|
37
40
|
this.url = null;
|
|
41
|
+
this.req = null;
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
scrollY(): number | null {
|
|
@@ -44,15 +48,18 @@ export class ServerHistory implements History {
|
|
|
44
48
|
replaceState(data: any, url?: string): void {
|
|
45
49
|
this.state = data;
|
|
46
50
|
this.url = url ?? null;
|
|
51
|
+
this.req = null;
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
pushState(data: any, url: string): void {
|
|
50
55
|
this.state = data;
|
|
51
56
|
this.url = url;
|
|
57
|
+
this.req = null;
|
|
52
58
|
}
|
|
53
59
|
|
|
54
|
-
open(
|
|
55
|
-
this.
|
|
60
|
+
open(req: Request): void {
|
|
61
|
+
this.req = req;
|
|
62
|
+
this.url = null;
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
back(): void {
|
|
@@ -1,7 +1,7 @@
|
|
|
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';
|
|
4
|
+
import Request, { isRequest, RequestOptions } from './Request.js';
|
|
5
5
|
import Route from './Route.js';
|
|
6
6
|
|
|
7
7
|
export type InnerRouterOpts = {
|
|
@@ -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,13 @@ export default class InnerRouter {
|
|
|
58
62
|
this.listen();
|
|
59
63
|
|
|
60
64
|
// let's first try to load from the state
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
route.origin = 'init';
|
|
65
|
-
|
|
66
|
-
if (route.search.get('x-craft-live-preview')) {
|
|
67
|
-
route.origin = 'live-preview-init';
|
|
68
|
-
}
|
|
65
|
+
const req = this.targetToRequest(window.location.href);
|
|
66
|
+
req._fillFromState(window.history.state);
|
|
69
67
|
|
|
68
|
+
req.origin = 'init';
|
|
70
69
|
window.history.scrollRestoration = 'manual';
|
|
71
70
|
|
|
72
|
-
this.
|
|
71
|
+
this.setRoute(req);
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
/**
|
|
@@ -80,8 +79,7 @@ export default class InnerRouter {
|
|
|
80
79
|
/**
|
|
81
80
|
* Get a site and if possible use the accept lang header.
|
|
82
81
|
*
|
|
83
|
-
* @param
|
|
84
|
-
* @return {Site}
|
|
82
|
+
* @param acceptLang Accept Language header.
|
|
85
83
|
*/
|
|
86
84
|
siteByAcceptLang(acceptLang: string | null = null): Site {
|
|
87
85
|
if (!acceptLang) return this.defaultSite();
|
|
@@ -125,7 +123,7 @@ export default class InnerRouter {
|
|
|
125
123
|
* Get the default site
|
|
126
124
|
*/
|
|
127
125
|
defaultSite(): Site {
|
|
128
|
-
return this.sites[0];
|
|
126
|
+
return this.sites.find(s => s.primary) ?? this.sites[0];
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
/**
|
|
@@ -136,15 +134,21 @@ export default class InnerRouter {
|
|
|
136
134
|
}
|
|
137
135
|
|
|
138
136
|
/**
|
|
139
|
-
* Resolve a url or Route and convert it to a
|
|
137
|
+
* Resolve a url or Route and convert it to a Request
|
|
140
138
|
*
|
|
141
139
|
* @param target
|
|
140
|
+
* @param opts, any option present will override the value in target
|
|
142
141
|
* @return Returns null if the url does not match our host (the protocol get's ignored)
|
|
143
142
|
*/
|
|
144
|
-
targetToRequest(
|
|
143
|
+
targetToRequest(
|
|
144
|
+
target: string | URL | Route | Request,
|
|
145
|
+
opts: RequestOptions = {},
|
|
146
|
+
): Request {
|
|
145
147
|
if (typeof target === 'string') {
|
|
146
148
|
if (target.startsWith('/')) {
|
|
147
|
-
|
|
149
|
+
// todo should we use the language matching or throw if the route does not
|
|
150
|
+
// exists
|
|
151
|
+
const site = this.route?.site ?? this.defaultSite();
|
|
148
152
|
target = new URL(site.uri + target, site.url);
|
|
149
153
|
} else {
|
|
150
154
|
target = new URL(target);
|
|
@@ -156,9 +160,10 @@ export default class InnerRouter {
|
|
|
156
160
|
}
|
|
157
161
|
|
|
158
162
|
if (!isRequest(target)) {
|
|
159
|
-
return Request.fromRoute(target);
|
|
163
|
+
return Request.fromRoute(target, opts);
|
|
160
164
|
}
|
|
161
165
|
|
|
166
|
+
target._updateOpts(opts);
|
|
162
167
|
return target;
|
|
163
168
|
}
|
|
164
169
|
|
|
@@ -170,7 +175,7 @@ export default class InnerRouter {
|
|
|
170
175
|
*/
|
|
171
176
|
routeFromUrl(fullUrl: URL): Route {
|
|
172
177
|
// strip stuff we dont need from url
|
|
173
|
-
const route = new Route(fullUrl, null);
|
|
178
|
+
const route = new Route(fullUrl, null!);
|
|
174
179
|
const url = route.url;
|
|
175
180
|
|
|
176
181
|
let site: Site | null = null;
|
|
@@ -193,7 +198,9 @@ export default class InnerRouter {
|
|
|
193
198
|
site = s;
|
|
194
199
|
}
|
|
195
200
|
|
|
196
|
-
|
|
201
|
+
// todo should we throw if we can't find a site
|
|
202
|
+
// or use the site which matches the language
|
|
203
|
+
route.site = site ?? this.defaultSite();
|
|
197
204
|
|
|
198
205
|
return route;
|
|
199
206
|
}
|
|
@@ -210,12 +217,20 @@ export default class InnerRouter {
|
|
|
210
217
|
|
|
211
218
|
e.preventDefault();
|
|
212
219
|
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
route
|
|
220
|
+
const req = this.targetToRequest(link.href, { origin: 'click' });
|
|
221
|
+
const routeEq =
|
|
222
|
+
this.route && this.route.eqUrl(req) && this.route.eqSearch(req);
|
|
223
|
+
// the route is the same don't do anything
|
|
224
|
+
// or maybe scroll the page to the hash? todo
|
|
225
|
+
if (routeEq && this.route?.eqHash(req)) return;
|
|
226
|
+
|
|
227
|
+
// this means the hash did not match, so we wan't to just scroll but not load
|
|
228
|
+
// data
|
|
229
|
+
if (routeEq) {
|
|
230
|
+
req.disableLoadData = true;
|
|
231
|
+
}
|
|
217
232
|
|
|
218
|
-
this.open(
|
|
233
|
+
this.open(req);
|
|
219
234
|
});
|
|
220
235
|
|
|
221
236
|
if (this.preloadOnMouseOver) {
|
|
@@ -259,7 +274,7 @@ export default class InnerRouter {
|
|
|
259
274
|
// use the latest state
|
|
260
275
|
this.history.replaceState(this.route._toState());
|
|
261
276
|
|
|
262
|
-
if (current.
|
|
277
|
+
if (current.inLivePreview()) {
|
|
263
278
|
sessionStorage.setItem(
|
|
264
279
|
'live-preview-scroll',
|
|
265
280
|
// use the latest scrollY
|
|
@@ -275,27 +290,28 @@ export default class InnerRouter {
|
|
|
275
290
|
window.addEventListener('popstate', async e => {
|
|
276
291
|
if (!('route' in e.state)) return;
|
|
277
292
|
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
293
|
+
const req = this.targetToRequest(window.location.href);
|
|
294
|
+
req._fillFromState(e.state);
|
|
295
|
+
req.origin = 'pop';
|
|
281
296
|
|
|
282
|
-
|
|
283
|
-
// for the scrollY in our open call so we just clear the current
|
|
284
|
-
// route since it is now already the new route
|
|
285
|
-
this.route = null;
|
|
286
|
-
|
|
287
|
-
this.open(route, false);
|
|
297
|
+
this.setRoute(req);
|
|
288
298
|
});
|
|
289
299
|
}
|
|
290
300
|
|
|
291
301
|
/**
|
|
292
|
-
* Open
|
|
302
|
+
* Open a new route
|
|
293
303
|
*
|
|
294
304
|
* @param route a route object or an url or uri, never input the same route object again
|
|
295
305
|
* @param pushState if true pushed the state to the window.history
|
|
306
|
+
*
|
|
307
|
+
* ## Important
|
|
308
|
+
* Make sure a req always has the correct origin,
|
|
309
|
+
* `push` and `replace` will cause this function to throw an error
|
|
296
310
|
*/
|
|
297
|
-
open(
|
|
298
|
-
|
|
311
|
+
open(req: Request) {
|
|
312
|
+
if (['push', 'replace'].includes(req.origin)) {
|
|
313
|
+
throw new Error('Do not use open with push or replace');
|
|
314
|
+
}
|
|
299
315
|
|
|
300
316
|
const current = this.route;
|
|
301
317
|
if (current) {
|
|
@@ -321,18 +337,14 @@ export default class InnerRouter {
|
|
|
321
337
|
// @ts-ignore
|
|
322
338
|
import.meta.env.SSR
|
|
323
339
|
) {
|
|
324
|
-
this.history.open(req
|
|
340
|
+
this.history.open(req);
|
|
325
341
|
return;
|
|
326
342
|
}
|
|
327
343
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
this.
|
|
331
|
-
|
|
332
|
-
});
|
|
333
|
-
} else {
|
|
334
|
-
this.setRoute(req);
|
|
335
|
-
}
|
|
344
|
+
req.index = (current?.index ?? 0) + 1;
|
|
345
|
+
this.onRoute(req, () => {
|
|
346
|
+
this.push(req, true);
|
|
347
|
+
});
|
|
336
348
|
}
|
|
337
349
|
|
|
338
350
|
/**
|
|
@@ -343,50 +355,70 @@ export default class InnerRouter {
|
|
|
343
355
|
*
|
|
344
356
|
* @param req
|
|
345
357
|
*/
|
|
346
|
-
setRoute(req: Request) {
|
|
358
|
+
setRoute(req: Request, preventOnRoute = false) {
|
|
347
359
|
this.route = req.toRoute();
|
|
348
|
-
if (req.site) this.site = req.site;
|
|
349
360
|
|
|
350
|
-
this.onRoute(req,
|
|
361
|
+
if (!preventOnRoute) this.onRoute(req, () => {});
|
|
351
362
|
}
|
|
352
363
|
|
|
353
364
|
/**
|
|
354
|
-
* This pushes
|
|
355
|
-
* or currentSiteId change
|
|
365
|
+
* This pushes a new route to the history
|
|
356
366
|
*
|
|
357
|
-
*
|
|
358
|
-
* (search argument) and then call pushState
|
|
367
|
+
* @param req, never input the same route object again
|
|
359
368
|
*
|
|
360
|
-
*
|
|
369
|
+
* ## Important
|
|
370
|
+
* Make sure the route has the correct origin
|
|
361
371
|
*/
|
|
362
|
-
|
|
363
|
-
const url =
|
|
372
|
+
push(req: Request, preventOnRoute = false) {
|
|
373
|
+
const url = req.url;
|
|
374
|
+
// todo a push should also store the previous scrollY
|
|
375
|
+
|
|
376
|
+
let nReq = req;
|
|
377
|
+
if (req.scrollY === null) {
|
|
378
|
+
// if there is no scrollY stored we store the current scrollY
|
|
379
|
+
// since a push does not cause a scroll top
|
|
380
|
+
// todo: probably should refactor something probably
|
|
381
|
+
// should not be here
|
|
382
|
+
nReq = req.clone();
|
|
383
|
+
nReq.scrollY = this.history.scrollY();
|
|
384
|
+
}
|
|
364
385
|
|
|
365
386
|
this.history.pushState(
|
|
366
|
-
|
|
387
|
+
nReq._toState(),
|
|
367
388
|
url.pathname + url.search + url.hash,
|
|
368
389
|
);
|
|
369
390
|
|
|
370
|
-
this.
|
|
371
|
-
if (route.site) this.site = route.site;
|
|
391
|
+
this.setRoute(req, preventOnRoute);
|
|
372
392
|
}
|
|
373
393
|
|
|
374
394
|
/**
|
|
375
|
-
* This replaces the
|
|
376
|
-
*
|
|
395
|
+
* This replaces the current route
|
|
396
|
+
*
|
|
397
|
+
* @param req, never input the same route object again
|
|
377
398
|
*
|
|
378
|
-
*
|
|
399
|
+
* ## Important
|
|
400
|
+
* Make sure the route has the correct origin
|
|
379
401
|
*/
|
|
380
|
-
|
|
381
|
-
const url =
|
|
402
|
+
replace(req: Request) {
|
|
403
|
+
const url = req.url;
|
|
404
|
+
|
|
405
|
+
let nReq = req;
|
|
406
|
+
if (req.scrollY === null) {
|
|
407
|
+
// if there is no scrollY stored we store the current scrollY
|
|
408
|
+
// since a replace does not cause a scrollTo and we wan't
|
|
409
|
+
// history back to work as intended
|
|
410
|
+
// todo: probably should refactor something probably
|
|
411
|
+
// should not be here
|
|
412
|
+
nReq = req.clone();
|
|
413
|
+
nReq.scrollY = this.history.scrollY();
|
|
414
|
+
}
|
|
382
415
|
|
|
383
416
|
this.history.replaceState(
|
|
384
|
-
|
|
417
|
+
nReq._toState(),
|
|
385
418
|
url.pathname + url.search + url.hash,
|
|
386
419
|
);
|
|
387
420
|
|
|
388
|
-
this.
|
|
389
|
-
if (route.site) this.site = route.site;
|
|
421
|
+
this.setRoute(req);
|
|
390
422
|
}
|
|
391
423
|
|
|
392
424
|
/**
|
|
@@ -398,21 +430,12 @@ export default class InnerRouter {
|
|
|
398
430
|
*/
|
|
399
431
|
preload(target: string | URL | Route | Request) {
|
|
400
432
|
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
433
|
const current = this.route;
|
|
410
|
-
const site = req.site ?? this.site;
|
|
411
434
|
|
|
412
435
|
// if the origin matches, the route will be able to be load
|
|
413
436
|
// so let's preload it
|
|
414
437
|
if (current && current.url.origin === req.url.origin) {
|
|
415
|
-
this.onPreload(req
|
|
438
|
+
this.onPreload(req);
|
|
416
439
|
}
|
|
417
440
|
}
|
|
418
441
|
|
|
@@ -427,7 +450,7 @@ export default class InnerRouter {
|
|
|
427
450
|
|
|
428
451
|
// if the route is a live preview init and we have a scrollY stored
|
|
429
452
|
// scroll to that
|
|
430
|
-
if (req.
|
|
453
|
+
if (req.inLivePreview()) {
|
|
431
454
|
const scrollY = sessionStorage.getItem('live-preview-scroll');
|
|
432
455
|
if (scrollY) {
|
|
433
456
|
scrollTo = {
|
|
@@ -462,6 +485,10 @@ export default class InnerRouter {
|
|
|
462
485
|
};
|
|
463
486
|
}
|
|
464
487
|
|
|
488
|
+
// make sure push and replace don't cause a scroll if it is not intended
|
|
489
|
+
if (!scrollTo && (req.origin === 'push' || req.origin === 'replace'))
|
|
490
|
+
return;
|
|
491
|
+
|
|
465
492
|
// scroll to the top if nothing else matches
|
|
466
493
|
if (!scrollTo) {
|
|
467
494
|
scrollTo = {
|
|
@@ -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,8 @@ export type RequestOptions = {
|
|
|
10
10
|
index?: number;
|
|
11
11
|
origin?: RouteOrigin;
|
|
12
12
|
disableScroll?: boolean;
|
|
13
|
+
disableLoadData?: boolean;
|
|
14
|
+
statusCode?: number;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
/**
|
|
@@ -24,31 +26,41 @@ export default class Request extends Route {
|
|
|
24
26
|
*/
|
|
25
27
|
disableScroll: boolean;
|
|
26
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Disable loading data
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
disableLoadData: boolean;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The Status code that should be returned for a redirect
|
|
37
|
+
*/
|
|
38
|
+
statusCode: number | null;
|
|
39
|
+
|
|
27
40
|
/** @hidden */
|
|
28
41
|
_renderBarrier: RenderBarrier;
|
|
29
42
|
|
|
30
43
|
/**
|
|
31
44
|
* Create a new Request
|
|
32
45
|
*/
|
|
33
|
-
constructor(
|
|
34
|
-
url: string | URL,
|
|
35
|
-
site: Site | null,
|
|
36
|
-
opts: RequestOptions = {},
|
|
37
|
-
) {
|
|
46
|
+
constructor(url: string | URL, site: Site, opts: RequestOptions = {}) {
|
|
38
47
|
super(url, site, opts);
|
|
39
48
|
|
|
40
49
|
this.disableScroll = opts.disableScroll ?? false;
|
|
50
|
+
this.disableLoadData = opts.disableLoadData ?? false;
|
|
51
|
+
this.statusCode = opts.statusCode ?? null;
|
|
41
52
|
this._renderBarrier = new RenderBarrier();
|
|
42
53
|
}
|
|
43
54
|
|
|
44
55
|
/**
|
|
45
56
|
* Create a Request from a Route
|
|
46
57
|
*/
|
|
47
|
-
static fromRoute(route: Route) {
|
|
58
|
+
static fromRoute(route: Route, opts: RequestOptions = {}) {
|
|
48
59
|
return new Request(route.url.href, route.site, {
|
|
49
60
|
scrollY: route.scrollY ?? undefined,
|
|
50
61
|
index: route.index,
|
|
51
62
|
origin: route.origin,
|
|
63
|
+
...opts,
|
|
52
64
|
});
|
|
53
65
|
}
|
|
54
66
|
|
|
@@ -93,6 +105,7 @@ export default class Request extends Route {
|
|
|
93
105
|
index: this.index,
|
|
94
106
|
origin: this.origin,
|
|
95
107
|
disableScroll: this.disableScroll,
|
|
108
|
+
statusCode: this.statusCode ?? undefined,
|
|
96
109
|
});
|
|
97
110
|
}
|
|
98
111
|
|
|
@@ -106,6 +119,15 @@ export default class Request extends Route {
|
|
|
106
119
|
origin: this.origin,
|
|
107
120
|
});
|
|
108
121
|
}
|
|
122
|
+
|
|
123
|
+
/** @hidden */
|
|
124
|
+
_updateOpts(opts: RequestOptions = {}) {
|
|
125
|
+
this.scrollY = opts.scrollY ?? this.scrollY;
|
|
126
|
+
this.index = opts.index ?? this.index;
|
|
127
|
+
this.origin = opts.origin ?? this.origin;
|
|
128
|
+
this.disableScroll = opts.disableScroll ?? this.disableScroll;
|
|
129
|
+
this.statusCode = opts.statusCode ?? this.statusCode;
|
|
130
|
+
}
|
|
109
131
|
}
|
|
110
132
|
|
|
111
133
|
export function isRequest(req: any): req is Request {
|