crelte 0.4.7 → 0.5.0-alpha.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/Crelte.d.ts +7 -6
- package/dist/Crelte.d.ts.map +1 -1
- package/dist/Crelte.js +5 -13
- package/dist/CrelteRequest.d.ts +9 -0
- package/dist/CrelteRequest.d.ts.map +1 -1
- package/dist/CrelteRequest.js +16 -2
- package/dist/blocks/Blocks.svelte +2 -2
- package/dist/blocks/Blocks.svelte.d.ts +3 -19
- package/dist/blocks/Blocks.svelte.d.ts.map +1 -1
- package/dist/cookies/ClientCookies.d.ts +0 -1
- package/dist/cookies/ClientCookies.d.ts.map +1 -1
- package/dist/cookies/ClientCookies.js +0 -1
- package/dist/cookies/ServerCookies.d.ts +1 -2
- package/dist/cookies/ServerCookies.d.ts.map +1 -1
- package/dist/cookies/ServerCookies.js +2 -6
- package/dist/cookies/index.d.ts +0 -2
- package/dist/cookies/index.d.ts.map +1 -1
- package/dist/graphql/GraphQl.d.ts +2 -2
- package/dist/graphql/GraphQl.d.ts.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -6
- package/dist/init/InternalApp.d.ts +30 -0
- package/dist/init/InternalApp.d.ts.map +1 -0
- package/dist/init/InternalApp.js +71 -0
- package/dist/init/client.d.ts +0 -5
- package/dist/init/client.d.ts.map +1 -1
- package/dist/init/client.js +88 -75
- package/dist/init/crelte-vite-plugin.d.ts +5 -0
- package/dist/init/server.d.ts +0 -5
- package/dist/init/server.d.ts.map +1 -1
- package/dist/init/server.js +50 -20
- package/dist/init/shared.d.ts +7 -18
- package/dist/init/shared.d.ts.map +1 -1
- package/dist/init/shared.js +97 -154
- package/dist/init/svelteComponents.d.ts +3 -0
- package/dist/init/svelteComponents.d.ts.map +1 -0
- package/dist/init/svelteComponents.js +7 -0
- package/dist/loadData/Globals.d.ts +40 -33
- package/dist/loadData/Globals.d.ts.map +1 -1
- package/dist/loadData/Globals.js +99 -88
- package/dist/loadData/index.d.ts +3 -2
- package/dist/loadData/index.d.ts.map +1 -1
- package/dist/loadData/index.js +2 -0
- package/dist/plugins/Events.d.ts +11 -13
- package/dist/plugins/Events.d.ts.map +1 -1
- package/dist/plugins/Events.js +10 -3
- package/dist/routing/BaseRoute.d.ts +255 -0
- package/dist/routing/BaseRoute.d.ts.map +1 -0
- package/dist/routing/BaseRoute.js +349 -0
- package/dist/routing/BaseRouter.d.ts +210 -0
- package/dist/routing/BaseRouter.d.ts.map +1 -0
- package/dist/routing/BaseRouter.js +444 -0
- package/dist/routing/ClientRouter.d.ts +32 -0
- package/dist/routing/ClientRouter.d.ts.map +1 -0
- package/dist/routing/ClientRouter.js +259 -0
- package/dist/routing/LoadRunner.d.ts +39 -0
- package/dist/routing/LoadRunner.d.ts.map +1 -0
- package/dist/routing/{PageLoader.js → LoadRunner.js} +32 -20
- package/dist/routing/Request.d.ts +35 -3
- package/dist/routing/Request.d.ts.map +1 -1
- package/dist/routing/Request.js +64 -5
- package/dist/routing/Route.d.ts +24 -223
- package/dist/routing/Route.d.ts.map +1 -1
- package/dist/routing/Route.js +26 -315
- package/dist/routing/Router.d.ts +49 -73
- package/dist/routing/Router.d.ts.map +1 -1
- package/dist/routing/Router.js +85 -251
- package/dist/routing/ServerRouter.d.ts +23 -0
- package/dist/routing/ServerRouter.d.ts.map +1 -0
- package/dist/routing/ServerRouter.js +57 -0
- package/dist/routing/utils.d.ts +5 -0
- package/dist/routing/utils.d.ts.map +1 -1
- package/dist/routing/utils.js +39 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -0
- package/package.json +6 -5
- package/src/Crelte.ts +12 -18
- package/src/CrelteRequest.ts +21 -2
- package/src/cookies/ClientCookies.ts +0 -2
- package/src/cookies/ServerCookies.ts +2 -7
- package/src/cookies/index.ts +0 -3
- package/src/graphql/GraphQl.ts +2 -1
- package/src/index.ts +17 -9
- package/src/init/InternalApp.ts +134 -0
- package/src/init/client.ts +104 -93
- package/src/init/crelte-vite-plugin.d.ts +5 -0
- package/src/init/server.ts +70 -35
- package/src/init/shared.ts +107 -227
- package/src/init/svelteComponents.ts +12 -0
- package/src/loadData/Globals.ts +121 -102
- package/src/loadData/index.ts +3 -2
- package/src/plugins/Events.ts +40 -42
- package/src/routing/BaseRoute.ts +422 -0
- package/src/routing/BaseRouter.ts +528 -0
- package/src/routing/ClientRouter.ts +329 -0
- package/src/routing/{PageLoader.ts → LoadRunner.ts} +43 -30
- package/src/routing/Request.ts +97 -12
- package/src/routing/Route.ts +56 -376
- package/src/routing/Router.ts +100 -359
- package/src/routing/ServerRouter.ts +78 -0
- package/src/routing/utils.ts +53 -0
- package/src/utils.ts +4 -0
- package/dist/routing/InnerRouter.d.ts +0 -113
- package/dist/routing/InnerRouter.d.ts.map +0 -1
- package/dist/routing/InnerRouter.js +0 -417
- package/dist/routing/PageLoader.d.ts +0 -36
- package/dist/routing/PageLoader.d.ts.map +0 -1
- package/src/routing/InnerRouter.ts +0 -498
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import BaseRouter from './BaseRouter.js';
|
|
2
|
+
export default class ClientRouter extends BaseRouter {
|
|
3
|
+
scrollDebounceTimeout;
|
|
4
|
+
preloadOnMouseOver;
|
|
5
|
+
onError;
|
|
6
|
+
constructor(sites, opts) {
|
|
7
|
+
super(sites, opts);
|
|
8
|
+
this.scrollDebounceTimeout = null;
|
|
9
|
+
this.preloadOnMouseOver = opts.preloadOnMouseOver;
|
|
10
|
+
this.onError = () => { };
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* ## Throws
|
|
14
|
+
*/
|
|
15
|
+
async init() {
|
|
16
|
+
this.listen();
|
|
17
|
+
// let's first try to load from the state
|
|
18
|
+
const req = this.targetToRequest(window.location.href);
|
|
19
|
+
req._fillFromState(window.history.state);
|
|
20
|
+
req.origin = 'init';
|
|
21
|
+
window.history.scrollRestoration = 'manual';
|
|
22
|
+
return await this.handleRequest(req, () => { });
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Do not call this with origin push or replace
|
|
26
|
+
*/
|
|
27
|
+
async openRequest(req) {
|
|
28
|
+
if (['push', 'replace'].includes(req.origin)) {
|
|
29
|
+
throw new Error('Do not use open with push or replace');
|
|
30
|
+
}
|
|
31
|
+
const current = this.route.get();
|
|
32
|
+
// store scrollY
|
|
33
|
+
if (current) {
|
|
34
|
+
// if the scrollY would still be updated we clear the timeout
|
|
35
|
+
// since we should have the latest scrollY
|
|
36
|
+
if (this.scrollDebounceTimeout) {
|
|
37
|
+
clearTimeout(this.scrollDebounceTimeout);
|
|
38
|
+
this.scrollDebounceTimeout = null;
|
|
39
|
+
}
|
|
40
|
+
// store the scroll position
|
|
41
|
+
const scrollY = window.scrollY;
|
|
42
|
+
if (typeof scrollY === 'number') {
|
|
43
|
+
current.scrollY = scrollY;
|
|
44
|
+
window.history.replaceState(current._toState(), '');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// if the domain of the current site is different than the domain of the
|
|
48
|
+
// new site we need to do a window.location.href call
|
|
49
|
+
if ((current && current.url.origin !== req.url.origin) ||
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
import.meta.env.SSR) {
|
|
52
|
+
window.location.href = req.url.href;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
req.index = (current?.index ?? 0) + 1;
|
|
56
|
+
return await this.handleRequestAndError(req, route => {
|
|
57
|
+
const url = route.url;
|
|
58
|
+
window.history.pushState(route._toState(), '', url.pathname + url.search + url.hash);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async pushRequest(req, _opts = {}) {
|
|
62
|
+
const url = req.url;
|
|
63
|
+
// todo a push should also store the previous scrollY
|
|
64
|
+
let nReq = req;
|
|
65
|
+
if (req.scrollY === null) {
|
|
66
|
+
// if there is no scrollY stored we store the current scrollY
|
|
67
|
+
// since a push does not cause a scroll top
|
|
68
|
+
// todo: probably should refactor something probably
|
|
69
|
+
// should not be here
|
|
70
|
+
nReq = req.clone();
|
|
71
|
+
nReq.scrollY = window.scrollY;
|
|
72
|
+
// todo this does not work? nReq is never used
|
|
73
|
+
// why not assign it without a clone?
|
|
74
|
+
}
|
|
75
|
+
return await this.handleRequest(req, route => {
|
|
76
|
+
window.history.pushState(route._toState(), url.pathname + url.search + url.hash);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async replaceRequest(req, _opts = {}) {
|
|
80
|
+
const url = req.url;
|
|
81
|
+
let nReq = req;
|
|
82
|
+
if (req.scrollY === null) {
|
|
83
|
+
// if there is no scrollY stored we store the current scrollY
|
|
84
|
+
// since a replace does not cause a scrollTo and we wan't
|
|
85
|
+
// history back to work as intended
|
|
86
|
+
// todo: probably should refactor something probably
|
|
87
|
+
// should not be here
|
|
88
|
+
nReq = req.clone();
|
|
89
|
+
nReq.scrollY = window.scrollY;
|
|
90
|
+
// todo this does not work? nReq is never used
|
|
91
|
+
// why not assign it without a clone?
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
return await this.handleRequest(req, () => {
|
|
95
|
+
window.history.replaceState(req._toState(), url.pathname + url.search + url.hash);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
console.warn('replacing route failed', e);
|
|
100
|
+
throw e;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* This returns a route if it was handled else if an error occured
|
|
105
|
+
* or the request was cancelled returns void
|
|
106
|
+
*/
|
|
107
|
+
async handleRequestAndError(req, updateHistory) {
|
|
108
|
+
try {
|
|
109
|
+
return await this.handleRequest(req, updateHistory);
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
console.error('request failed', e);
|
|
113
|
+
this.onError(e);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
listen() {
|
|
117
|
+
window.addEventListener('click', async (e) => {
|
|
118
|
+
// @ts-ignore
|
|
119
|
+
const link = e.target.closest('a');
|
|
120
|
+
const openInNewTab = e.metaKey || e.ctrlKey || e.shiftKey;
|
|
121
|
+
const saveLink = e.altKey;
|
|
122
|
+
if (!link || !link.href || openInNewTab || saveLink)
|
|
123
|
+
return;
|
|
124
|
+
if (link.target.toLowerCase() === '_blank')
|
|
125
|
+
return;
|
|
126
|
+
if (!link.href.startsWith('http'))
|
|
127
|
+
return;
|
|
128
|
+
e.preventDefault();
|
|
129
|
+
const req = this.targetToRequest(link.href, { origin: 'click' });
|
|
130
|
+
const currRoute = this.route.get();
|
|
131
|
+
const routeEq = currRoute && currRoute.eqUrl(req) && currRoute.eqSearch(req);
|
|
132
|
+
// this means the route is the same maybe with a different hash
|
|
133
|
+
// so it is not necessary to load the data again
|
|
134
|
+
if (routeEq) {
|
|
135
|
+
req.disableLoadData = true;
|
|
136
|
+
}
|
|
137
|
+
this.openRequest(req);
|
|
138
|
+
});
|
|
139
|
+
if (this.preloadOnMouseOver) {
|
|
140
|
+
let currentMouseOver = null;
|
|
141
|
+
window.addEventListener('mouseover', e => {
|
|
142
|
+
// @ts-ignore
|
|
143
|
+
const link = e.target.closest('a');
|
|
144
|
+
if (currentMouseOver && link === currentMouseOver)
|
|
145
|
+
return;
|
|
146
|
+
if (link && link.target.toLowerCase() === '_blank')
|
|
147
|
+
return;
|
|
148
|
+
if (link &&
|
|
149
|
+
!link.hasAttribute('data-no-preload') &&
|
|
150
|
+
link.href) {
|
|
151
|
+
this.preload(link.href);
|
|
152
|
+
}
|
|
153
|
+
currentMouseOver = link;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// store the scrollY position every 200ms
|
|
157
|
+
// we can't do this at the time of the open call since the pop event
|
|
158
|
+
// has already changed to a new history state so we can't update our
|
|
159
|
+
// current/previous state
|
|
160
|
+
// eslint-disable-next-line no-constant-condition
|
|
161
|
+
if (true) {
|
|
162
|
+
window.addEventListener('scroll', () => {
|
|
163
|
+
const current = this.route.get();
|
|
164
|
+
if (!current)
|
|
165
|
+
return;
|
|
166
|
+
// store the scroll position
|
|
167
|
+
current.scrollY = window.scrollY;
|
|
168
|
+
if (this.scrollDebounceTimeout)
|
|
169
|
+
return;
|
|
170
|
+
// this might cause `Attempt to use history.replaceState() more than
|
|
171
|
+
// 100 times per 30 seconds` in safari
|
|
172
|
+
// since we wait a moment we should almost ever be fine
|
|
173
|
+
this.scrollDebounceTimeout = setTimeout(() => {
|
|
174
|
+
const routerRoute = this.route.get();
|
|
175
|
+
if (!routerRoute || !current.eq(routerRoute))
|
|
176
|
+
return;
|
|
177
|
+
// use the latest state
|
|
178
|
+
window.history.replaceState(routerRoute._toState(), '');
|
|
179
|
+
if (current.inLivePreview()) {
|
|
180
|
+
sessionStorage.setItem('live-preview-scroll',
|
|
181
|
+
// use the latest scrollY
|
|
182
|
+
routerRoute.scrollY + '');
|
|
183
|
+
}
|
|
184
|
+
this.scrollDebounceTimeout = null;
|
|
185
|
+
}, 280);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
window.addEventListener('popstate', async (e) => {
|
|
189
|
+
if (!e.state?.route)
|
|
190
|
+
return;
|
|
191
|
+
const req = this.targetToRequest(window.location.href);
|
|
192
|
+
req._fillFromState(e.state);
|
|
193
|
+
req.origin = 'pop';
|
|
194
|
+
// todo handle errors
|
|
195
|
+
this.handleRequest(req, () => { });
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
updateScroll(cr, _route) {
|
|
199
|
+
const req = cr.req;
|
|
200
|
+
if (req.disableScroll)
|
|
201
|
+
return;
|
|
202
|
+
// scroll to target
|
|
203
|
+
let scrollTo = null;
|
|
204
|
+
// if the route is a live preview init and we have a scrollY stored
|
|
205
|
+
// scroll to that
|
|
206
|
+
if (req.inLivePreview()) {
|
|
207
|
+
const scrollY = sessionStorage.getItem('live-preview-scroll');
|
|
208
|
+
if (scrollY) {
|
|
209
|
+
scrollTo = {
|
|
210
|
+
top: parseInt(scrollY),
|
|
211
|
+
behavior: 'instant',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// if we have a hash and the route was not visited
|
|
215
|
+
}
|
|
216
|
+
else if (req.hash &&
|
|
217
|
+
((req.origin === 'init' && typeof req.scrollY !== 'number') ||
|
|
218
|
+
req.origin === 'click')) {
|
|
219
|
+
const el = document.getElementById(req.hash.substring(1));
|
|
220
|
+
if (el) {
|
|
221
|
+
scrollTo = {
|
|
222
|
+
intoView: el,
|
|
223
|
+
behavior: 'smooth',
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// restore scroll position
|
|
228
|
+
if (!scrollTo &&
|
|
229
|
+
req.origin !== 'click' &&
|
|
230
|
+
typeof req.scrollY === 'number') {
|
|
231
|
+
scrollTo = {
|
|
232
|
+
top: req.scrollY,
|
|
233
|
+
behavior: 'instant',
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
// make sure push and replace don't cause a scroll if it is not intended
|
|
237
|
+
if (!scrollTo && (req.origin === 'push' || req.origin === 'replace'))
|
|
238
|
+
return;
|
|
239
|
+
// scroll to the top if nothing else matches
|
|
240
|
+
if (!scrollTo) {
|
|
241
|
+
scrollTo = {
|
|
242
|
+
top: 0,
|
|
243
|
+
behavior: 'instant',
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
if ('top' in scrollTo) {
|
|
247
|
+
window.scrollTo({
|
|
248
|
+
top: scrollTo.top,
|
|
249
|
+
behavior: scrollTo.behavior,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
scrollTo.intoView.scrollIntoView({
|
|
254
|
+
behavior: scrollTo.behavior,
|
|
255
|
+
block: 'start',
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type CrelteRequest } from '../index.js';
|
|
2
|
+
export type LoadRunnerOptions = {
|
|
3
|
+
debugTiming: boolean;
|
|
4
|
+
};
|
|
5
|
+
export type LoadFn = (cr: CrelteRequest, opts: LoadOptions) => Promise<void> | void;
|
|
6
|
+
export type LoadOptions = {
|
|
7
|
+
setProgress: (num: number) => void;
|
|
8
|
+
isCanceled: () => boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* The PageLoader which is responsible for loading page Data
|
|
12
|
+
*/
|
|
13
|
+
export default class LoadRunner {
|
|
14
|
+
private debugTiming;
|
|
15
|
+
private preloadedUrls;
|
|
16
|
+
private loadingVersion;
|
|
17
|
+
onProgress: (loading: boolean, progress?: number) => void;
|
|
18
|
+
loadFn: LoadFn;
|
|
19
|
+
/**
|
|
20
|
+
* Creates a new PageLoader
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} options `{debugTiming}`
|
|
23
|
+
*/
|
|
24
|
+
constructor(options: LoadRunnerOptions);
|
|
25
|
+
/**
|
|
26
|
+
* Discard the current page load if one is happening
|
|
27
|
+
*/
|
|
28
|
+
discard(): void;
|
|
29
|
+
/**
|
|
30
|
+
* @returns true if the load was completed
|
|
31
|
+
*
|
|
32
|
+
* ## Throws
|
|
33
|
+
* if there was an error but not if the request
|
|
34
|
+
* was cancelled before
|
|
35
|
+
*/
|
|
36
|
+
load(req: CrelteRequest): Promise<boolean>;
|
|
37
|
+
preload(cr: CrelteRequest): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=LoadRunner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoadRunner.d.ts","sourceRoot":"","sources":["../../src/routing/LoadRunner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG;IAC/B,WAAW,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG,CACpB,EAAE,EAAE,aAAa,EACjB,IAAI,EAAE,WAAW,KACb,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAE1B,MAAM,MAAM,WAAW,GAAG;IACzB,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,UAAU,EAAE,MAAM,OAAO,CAAC;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,UAAU;IAC9B,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,aAAa,CAAc;IAEnC,OAAO,CAAC,cAAc,CAAS;IAE/B,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,MAAM,EAAE,MAAM,CAAC;IAEf;;;;OAIG;gBACS,OAAO,EAAE,iBAAiB;IAUtC;;OAEG;IACH,OAAO;IAKP;;;;;;OAMG;IACG,IAAI,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAuC1C,OAAO,CAAC,EAAE,EAAE,aAAa;CAiB/B"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The PageLoader which is responsible for loading page Data
|
|
3
3
|
*/
|
|
4
|
-
export default class
|
|
4
|
+
export default class LoadRunner {
|
|
5
5
|
debugTiming;
|
|
6
6
|
preloadedUrls;
|
|
7
7
|
loadingVersion;
|
|
8
|
-
onLoaded;
|
|
9
8
|
onProgress;
|
|
10
9
|
loadFn;
|
|
11
10
|
/**
|
|
@@ -17,7 +16,6 @@ export default class PageLoader {
|
|
|
17
16
|
this.debugTiming = options.debugTiming;
|
|
18
17
|
this.preloadedUrls = new Set();
|
|
19
18
|
this.loadingVersion = 0;
|
|
20
|
-
this.onLoaded = () => null;
|
|
21
19
|
this.onProgress = () => null;
|
|
22
20
|
this.loadFn = () => null;
|
|
23
21
|
}
|
|
@@ -28,43 +26,57 @@ export default class PageLoader {
|
|
|
28
26
|
this.loadingVersion++;
|
|
29
27
|
this.onProgress(false);
|
|
30
28
|
}
|
|
31
|
-
|
|
29
|
+
/**
|
|
30
|
+
* @returns true if the load was completed
|
|
31
|
+
*
|
|
32
|
+
* ## Throws
|
|
33
|
+
* if there was an error but not if the request
|
|
34
|
+
* was cancelled before
|
|
35
|
+
*/
|
|
36
|
+
async load(req) {
|
|
32
37
|
this.onProgress(true);
|
|
33
38
|
const version = ++this.loadingVersion;
|
|
34
39
|
const startTime = this.debugTiming ? Date.now() : null;
|
|
40
|
+
const isCanceled = () => this.loadingVersion !== version;
|
|
35
41
|
const setProgress = (num) => {
|
|
36
|
-
if (
|
|
42
|
+
if (isCanceled())
|
|
37
43
|
return;
|
|
38
44
|
this.onProgress(true, num);
|
|
39
45
|
};
|
|
40
|
-
|
|
46
|
+
// a function which should return the response
|
|
47
|
+
let resp;
|
|
41
48
|
try {
|
|
42
|
-
|
|
43
|
-
resp
|
|
49
|
+
const data = await this.loadFn(req, { isCanceled, setProgress });
|
|
50
|
+
resp = () => data;
|
|
44
51
|
}
|
|
45
52
|
catch (e) {
|
|
46
|
-
resp
|
|
47
|
-
|
|
53
|
+
resp = () => {
|
|
54
|
+
throw e;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (isCanceled()) {
|
|
58
|
+
console.log('route changed quickly, ignoring response');
|
|
59
|
+
return false;
|
|
48
60
|
}
|
|
61
|
+
this.onProgress(false);
|
|
49
62
|
if (startTime)
|
|
50
63
|
console.log('page load took ' + (Date.now() - startTime) + 'ms');
|
|
51
|
-
|
|
52
|
-
if (this.loadingVersion !== version)
|
|
53
|
-
return console.log('route changed quickly, ignoring response');
|
|
54
|
-
this.onProgress(false);
|
|
55
|
-
this.onLoaded(resp, req, more);
|
|
64
|
+
return (resp(), true);
|
|
56
65
|
}
|
|
57
66
|
// you don't need to wait on this call
|
|
58
|
-
async preload(
|
|
59
|
-
const url = req.url.origin + req.url.pathname;
|
|
67
|
+
async preload(cr) {
|
|
68
|
+
const url = cr.req.url.origin + cr.req.url.pathname;
|
|
60
69
|
if (this.preloadedUrls.has(url))
|
|
61
70
|
return;
|
|
62
71
|
this.preloadedUrls.add(url);
|
|
63
72
|
try {
|
|
64
|
-
await this.loadFn(
|
|
73
|
+
await this.loadFn(cr, {
|
|
74
|
+
isCanceled: () => false,
|
|
75
|
+
setProgress: () => null,
|
|
76
|
+
});
|
|
65
77
|
}
|
|
66
|
-
catch (
|
|
67
|
-
console.log('preload failed');
|
|
78
|
+
catch (e) {
|
|
79
|
+
console.log('preload failed', e);
|
|
68
80
|
// retry at another time
|
|
69
81
|
this.preloadedUrls.delete(url);
|
|
70
82
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { Barrier } from 'crelte-std/sync';
|
|
2
|
-
import Route, { RouteOrigin } from './Route.js';
|
|
3
2
|
import Site from './Site.js';
|
|
3
|
+
import { Entry } from '../entry/index.js';
|
|
4
|
+
import BaseRoute, { RouteOrigin } from './BaseRoute.js';
|
|
5
|
+
import Route, { TemplateModule } from './Route.js';
|
|
4
6
|
/**
|
|
5
7
|
* Options to create a Request
|
|
6
8
|
*/
|
|
7
9
|
export type RequestOptions = {
|
|
10
|
+
entry?: Entry;
|
|
11
|
+
template?: TemplateModule;
|
|
12
|
+
loadedData?: Record<string, any>;
|
|
8
13
|
scrollY?: number;
|
|
9
14
|
index?: number;
|
|
10
15
|
origin?: RouteOrigin;
|
|
@@ -19,7 +24,19 @@ export type RequestOptions = {
|
|
|
19
24
|
* you get a Request from the onRequest event or
|
|
20
25
|
* in a loadGlobal function.
|
|
21
26
|
*/
|
|
22
|
-
export default class Request extends
|
|
27
|
+
export default class Request extends BaseRoute {
|
|
28
|
+
/**
|
|
29
|
+
* The entry of the request once loaded
|
|
30
|
+
*/
|
|
31
|
+
entry: Entry | null;
|
|
32
|
+
/**
|
|
33
|
+
* The template module of the request once loaded
|
|
34
|
+
*/
|
|
35
|
+
template: TemplateModule | null;
|
|
36
|
+
/**
|
|
37
|
+
* The loaded data of the request
|
|
38
|
+
*/
|
|
39
|
+
loadedData: Record<string, any> | null;
|
|
23
40
|
/**
|
|
24
41
|
* Disable scrolling for this request
|
|
25
42
|
* @default false
|
|
@@ -36,14 +53,19 @@ export default class Request extends Route {
|
|
|
36
53
|
statusCode: number | null;
|
|
37
54
|
/** @hidden */
|
|
38
55
|
_renderBarrier: RenderBarrier;
|
|
56
|
+
private _cancelled;
|
|
39
57
|
/**
|
|
40
58
|
* Create a new Request
|
|
41
59
|
*/
|
|
42
60
|
constructor(url: string | URL, site: Site, opts?: RequestOptions);
|
|
43
61
|
/**
|
|
44
62
|
* Create a Request from a Route
|
|
63
|
+
*
|
|
64
|
+
* Clears the entry, template, and loadedData
|
|
65
|
+
* todo should it?
|
|
45
66
|
*/
|
|
46
67
|
static fromRoute(route: Route, opts?: RequestOptions): Request;
|
|
68
|
+
get cancelled(): boolean;
|
|
47
69
|
/**
|
|
48
70
|
* With delayRender you can make sure that the render waits
|
|
49
71
|
* until you are ready. This is useful for building page transitions.
|
|
@@ -75,14 +97,24 @@ export default class Request extends Route {
|
|
|
75
97
|
delayRender(): DelayRender;
|
|
76
98
|
/**
|
|
77
99
|
* Create a copy of the request
|
|
100
|
+
*
|
|
101
|
+
* without including the entry, template, loadedData or cancelled state
|
|
102
|
+
*
|
|
103
|
+
* Is this what we wan't, maybe it should copy everything
|
|
78
104
|
*/
|
|
79
105
|
clone(): Request;
|
|
80
106
|
/**
|
|
81
107
|
* Create a Route from the Request
|
|
108
|
+
*
|
|
109
|
+
* ## Throws
|
|
110
|
+
* if the entry, template or loadedData is missing
|
|
111
|
+
* or the request was cancelled
|
|
82
112
|
*/
|
|
83
113
|
toRoute(): Route;
|
|
84
114
|
/** @hidden */
|
|
85
115
|
_updateOpts(opts?: RequestOptions): void;
|
|
116
|
+
/** @hidden */
|
|
117
|
+
_cancel(): void;
|
|
86
118
|
}
|
|
87
119
|
export declare function isRequest(req: any): req is Request;
|
|
88
120
|
declare class RenderBarrier {
|
|
@@ -95,7 +127,7 @@ declare class RenderBarrier {
|
|
|
95
127
|
/** @hidden */
|
|
96
128
|
cancel(): void;
|
|
97
129
|
/** @hidden */
|
|
98
|
-
ready(): Promise<boolean
|
|
130
|
+
ready(): Promise<boolean> | boolean;
|
|
99
131
|
}
|
|
100
132
|
/**
|
|
101
133
|
* DelayRender is returned by `Request.delayRender`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Request.d.ts","sourceRoot":"","sources":["../../src/routing/Request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,KAAK,EAAE,EAAE,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"Request.d.ts","sourceRoot":"","sources":["../../src/routing/Request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,SAAS,EAAE,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEnD;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,OAAQ,SAAQ,SAAS;IAC7C;;OAEG;IACH,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAEpB;;OAEG;IACH,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAEhC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAEvC;;;OAGG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,eAAe,EAAE,OAAO,CAAC;IAEzB;;OAEG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1B,cAAc;IACd,cAAc,EAAE,aAAa,CAAC;IAE9B,OAAO,CAAC,UAAU,CAAU;IAE5B;;OAEG;gBACS,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,GAAE,cAAmB;IAapE;;;;;OAKG;IACH,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,GAAE,cAAmB;IAWxD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,WAAW,IAAI,WAAW;IAI1B;;;;;;OAMG;IACH,KAAK;IAsBL;;;;;;OAMG;IACH,OAAO;IA8BP,cAAc;IACd,WAAW,CAAC,IAAI,GAAE,cAAmB;IAiBrC,cAAc;IACd,OAAO;CAIP;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,OAAO,CAElD;AAED,cAAM,aAAa;IAClB,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,WAAW,CAAC;;IAQlB,MAAM,IAAI,OAAO;IAIjB,GAAG,IAAI,WAAW;IAclB,cAAc;IACd,MAAM;IAWN,cAAc;IACd,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO;CAGnC;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACzB;;;;;OAKG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAE9B;;OAEG;IACH,MAAM,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC"}
|
package/dist/routing/Request.js
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
import { Barrier } from 'crelte-std/sync';
|
|
2
|
-
import Route from './Route.js';
|
|
3
2
|
import { objClone } from '../utils.js';
|
|
3
|
+
import BaseRoute from './BaseRoute.js';
|
|
4
|
+
import Route from './Route.js';
|
|
4
5
|
/**
|
|
5
6
|
* A Request is a Route with some extra options
|
|
6
7
|
* you get a Request from the onRequest event or
|
|
7
8
|
* in a loadGlobal function.
|
|
8
9
|
*/
|
|
9
|
-
export default class Request extends
|
|
10
|
+
export default class Request extends BaseRoute {
|
|
11
|
+
/**
|
|
12
|
+
* The entry of the request once loaded
|
|
13
|
+
*/
|
|
14
|
+
entry;
|
|
15
|
+
/**
|
|
16
|
+
* The template module of the request once loaded
|
|
17
|
+
*/
|
|
18
|
+
template;
|
|
19
|
+
/**
|
|
20
|
+
* The loaded data of the request
|
|
21
|
+
*/
|
|
22
|
+
loadedData;
|
|
10
23
|
/**
|
|
11
24
|
* Disable scrolling for this request
|
|
12
25
|
* @default false
|
|
@@ -23,18 +36,26 @@ export default class Request extends Route {
|
|
|
23
36
|
statusCode;
|
|
24
37
|
/** @hidden */
|
|
25
38
|
_renderBarrier;
|
|
39
|
+
_cancelled;
|
|
26
40
|
/**
|
|
27
41
|
* Create a new Request
|
|
28
42
|
*/
|
|
29
43
|
constructor(url, site, opts = {}) {
|
|
30
44
|
super(url, site, opts);
|
|
45
|
+
this.entry = opts.entry ?? null;
|
|
46
|
+
this.template = opts.template ?? null;
|
|
47
|
+
this.loadedData = opts.loadedData ?? null;
|
|
31
48
|
this.disableScroll = opts.disableScroll ?? false;
|
|
32
49
|
this.disableLoadData = opts.disableLoadData ?? false;
|
|
33
50
|
this.statusCode = opts.statusCode ?? null;
|
|
34
51
|
this._renderBarrier = new RenderBarrier();
|
|
52
|
+
this._cancelled = false;
|
|
35
53
|
}
|
|
36
54
|
/**
|
|
37
55
|
* Create a Request from a Route
|
|
56
|
+
*
|
|
57
|
+
* Clears the entry, template, and loadedData
|
|
58
|
+
* todo should it?
|
|
38
59
|
*/
|
|
39
60
|
static fromRoute(route, opts = {}) {
|
|
40
61
|
return new Request(route.url.href, route.site, {
|
|
@@ -46,6 +67,9 @@ export default class Request extends Route {
|
|
|
46
67
|
...opts,
|
|
47
68
|
});
|
|
48
69
|
}
|
|
70
|
+
get cancelled() {
|
|
71
|
+
return this._cancelled;
|
|
72
|
+
}
|
|
49
73
|
/**
|
|
50
74
|
* With delayRender you can make sure that the render waits
|
|
51
75
|
* until you are ready. This is useful for building page transitions.
|
|
@@ -79,9 +103,20 @@ export default class Request extends Route {
|
|
|
79
103
|
}
|
|
80
104
|
/**
|
|
81
105
|
* Create a copy of the request
|
|
106
|
+
*
|
|
107
|
+
* without including the entry, template, loadedData or cancelled state
|
|
108
|
+
*
|
|
109
|
+
* Is this what we wan't, maybe it should copy everything
|
|
82
110
|
*/
|
|
83
111
|
clone() {
|
|
84
|
-
|
|
112
|
+
// todo should this clone the entry
|
|
113
|
+
// the route for example does not do it
|
|
114
|
+
//
|
|
115
|
+
// todo should this clone keep the entry?
|
|
116
|
+
const req = new Request(this.url.href, this.site, {
|
|
117
|
+
// entry: objClone(this.entry),
|
|
118
|
+
// template: this.template ?? undefined,
|
|
119
|
+
// loadedData: objClone(this.loadedData),
|
|
85
120
|
scrollY: this.scrollY ?? undefined,
|
|
86
121
|
index: this.index,
|
|
87
122
|
origin: this.origin,
|
|
@@ -91,21 +126,37 @@ export default class Request extends Route {
|
|
|
91
126
|
disableLoadData: this.disableLoadData,
|
|
92
127
|
statusCode: this.statusCode ?? undefined,
|
|
93
128
|
});
|
|
129
|
+
return req;
|
|
94
130
|
}
|
|
95
131
|
/**
|
|
96
132
|
* Create a Route from the Request
|
|
133
|
+
*
|
|
134
|
+
* ## Throws
|
|
135
|
+
* if the entry, template or loadedData is missing
|
|
136
|
+
* or the request was cancelled
|
|
97
137
|
*/
|
|
98
138
|
toRoute() {
|
|
99
|
-
|
|
139
|
+
if (this.cancelled)
|
|
140
|
+
throw new Error('cannot create a new route because it was cancelled');
|
|
141
|
+
if (!this.entry || !this.template || !this.loadedData)
|
|
142
|
+
throw new Error('cannot create a new route because entry, template or loadedData is missing');
|
|
143
|
+
const route = new Route(this.url.href, this.site, this.entry, this.template, this.loadedData, {
|
|
100
144
|
scrollY: this.scrollY ?? undefined,
|
|
101
145
|
index: this.index,
|
|
102
146
|
origin: this.origin,
|
|
103
147
|
state: objClone(this._state),
|
|
104
148
|
context: this._context,
|
|
105
149
|
});
|
|
150
|
+
route.entryChanged = !this.disableLoadData;
|
|
151
|
+
return route;
|
|
106
152
|
}
|
|
107
153
|
/** @hidden */
|
|
108
154
|
_updateOpts(opts = {}) {
|
|
155
|
+
// todo maybe should check that if entry is updated
|
|
156
|
+
// template and loadedData is also updated
|
|
157
|
+
this.entry = opts.entry ?? this.entry;
|
|
158
|
+
this.template = opts.template ?? this.template;
|
|
159
|
+
this.loadedData = opts.loadedData ?? this.loadedData;
|
|
109
160
|
this.scrollY = opts.scrollY ?? this.scrollY;
|
|
110
161
|
this.index = opts.index ?? this.index;
|
|
111
162
|
this.origin = opts.origin ?? this.origin;
|
|
@@ -115,6 +166,11 @@ export default class Request extends Route {
|
|
|
115
166
|
this.disableLoadData = opts.disableLoadData ?? this.disableLoadData;
|
|
116
167
|
this.statusCode = opts.statusCode ?? this.statusCode;
|
|
117
168
|
}
|
|
169
|
+
/** @hidden */
|
|
170
|
+
_cancel() {
|
|
171
|
+
this._cancelled = true;
|
|
172
|
+
this._renderBarrier.cancel();
|
|
173
|
+
}
|
|
118
174
|
}
|
|
119
175
|
export function isRequest(req) {
|
|
120
176
|
return typeof req === 'object' && req !== null && req instanceof Request;
|
|
@@ -147,12 +203,15 @@ class RenderBarrier {
|
|
|
147
203
|
}
|
|
148
204
|
/** @hidden */
|
|
149
205
|
cancel() {
|
|
206
|
+
// if the barrier is already open
|
|
207
|
+
// we don't wan't to flag the render as cancelled
|
|
208
|
+
// because it already happened
|
|
150
209
|
if (this.inner.isOpen())
|
|
151
210
|
return;
|
|
152
211
|
this.cancelled = true;
|
|
153
212
|
this.root.remove();
|
|
154
213
|
}
|
|
155
|
-
// returns if the render was cancelled
|
|
214
|
+
// returns true if the render was cancelled
|
|
156
215
|
/** @hidden */
|
|
157
216
|
ready() {
|
|
158
217
|
return this.root.ready();
|