crelte 0.3.1 → 0.3.2
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/entry/EntryRouter.d.ts +30 -0
- package/dist/entry/EntryRouter.d.ts.map +1 -0
- package/dist/entry/EntryRouter.js +45 -0
- package/dist/entry/index.d.ts +32 -0
- package/dist/entry/index.d.ts.map +1 -0
- package/dist/entry/index.js +31 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/init/client.d.ts +1 -1
- package/dist/init/client.d.ts.map +1 -1
- package/dist/init/client.js +4 -3
- package/dist/init/server.d.ts.map +1 -1
- package/dist/init/server.js +3 -2
- package/dist/init/shared.d.ts +3 -1
- package/dist/init/shared.d.ts.map +1 -1
- package/dist/init/shared.js +47 -26
- package/dist/routing/Route.d.ts +11 -0
- package/dist/routing/Route.d.ts.map +1 -1
- package/dist/routing/Route.js +13 -0
- package/dist/routing/Router.d.ts +47 -10
- package/dist/routing/Router.d.ts.map +1 -1
- package/dist/routing/Router.js +74 -17
- package/dist/routing/index.d.ts +2 -2
- package/dist/routing/index.d.ts.map +1 -1
- package/package.json +9 -4
- package/src/entry/EntryRouter.ts +71 -0
- package/src/entry/index.ts +48 -0
- package/src/index.ts +2 -0
- package/src/init/client.ts +10 -3
- package/src/init/server.ts +9 -2
- package/src/init/shared.ts +78 -28
- package/src/routing/Route.ts +14 -0
- package/src/routing/Router.ts +102 -20
- package/src/routing/index.ts +2 -1
package/dist/routing/Router.js
CHANGED
|
@@ -54,6 +54,8 @@ export default class Router {
|
|
|
54
54
|
this._loading = new Writable(false);
|
|
55
55
|
this._loadingProgress = new Writable(0);
|
|
56
56
|
this._onRequest = new Listeners();
|
|
57
|
+
// these functions are exposed to the init "module"
|
|
58
|
+
// but should not be used by anybody else
|
|
57
59
|
this._internal = {
|
|
58
60
|
onLoaded: () => { },
|
|
59
61
|
onNothingLoaded: () => { },
|
|
@@ -130,10 +132,11 @@ export default class Router {
|
|
|
130
132
|
* ```
|
|
131
133
|
*/
|
|
132
134
|
open(target, opts = {}) {
|
|
133
|
-
const req = this.
|
|
134
|
-
...opts,
|
|
135
|
+
const req = this.targetOrUpdateToRequest(target, opts, {
|
|
135
136
|
origin: 'manual',
|
|
136
137
|
});
|
|
138
|
+
if (!req)
|
|
139
|
+
return;
|
|
137
140
|
this.inner.open(req);
|
|
138
141
|
}
|
|
139
142
|
/**
|
|
@@ -147,7 +150,17 @@ export default class Router {
|
|
|
147
150
|
* And will clear the scrollY value if you not provide a new one via the `opts`
|
|
148
151
|
* This will disableLoadData by default if you not provide an override via the `opts`
|
|
149
152
|
*
|
|
150
|
-
* ## Example
|
|
153
|
+
* ## Example using the update function
|
|
154
|
+
* ```
|
|
155
|
+
* import { getRouter } from 'crelte';
|
|
156
|
+
*
|
|
157
|
+
* const router = getRouter();
|
|
158
|
+
*
|
|
159
|
+
* const page = 1;
|
|
160
|
+
* router.push(req => req.setSearchParam('page', page || null));
|
|
161
|
+
* ```
|
|
162
|
+
*
|
|
163
|
+
* ## Example using the route object
|
|
151
164
|
* ```
|
|
152
165
|
* import { getRouter } from 'crelte';
|
|
153
166
|
*
|
|
@@ -160,14 +173,15 @@ export default class Router {
|
|
|
160
173
|
* ```
|
|
161
174
|
*/
|
|
162
175
|
push(route, opts = {}) {
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
const req = this.
|
|
166
|
-
...opts,
|
|
176
|
+
// theoretically string and URL also work but we might
|
|
177
|
+
// change that in the future
|
|
178
|
+
const req = this.targetOrUpdateToRequest(route, opts, {
|
|
167
179
|
origin: 'push',
|
|
168
180
|
scrollY: opts.scrollY ?? undefined,
|
|
169
181
|
disableLoadData: opts.disableLoadData ?? true,
|
|
170
182
|
});
|
|
183
|
+
if (!req)
|
|
184
|
+
return;
|
|
171
185
|
this.inner.push(req);
|
|
172
186
|
}
|
|
173
187
|
/**
|
|
@@ -178,16 +192,26 @@ export default class Router {
|
|
|
178
192
|
this.push(route);
|
|
179
193
|
}
|
|
180
194
|
/**
|
|
181
|
-
* This replaces the state of the route without triggering
|
|
195
|
+
* This replaces the state of the route without triggering a new pageload
|
|
182
196
|
*
|
|
183
197
|
* You can use this when using some filters for example a search filter
|
|
184
198
|
*
|
|
185
199
|
* ## Note
|
|
186
200
|
* This will always set the origin to 'replace'
|
|
187
|
-
* And will clear the scrollY value if you
|
|
188
|
-
* This will disableLoadData by default if you
|
|
201
|
+
* And will clear the scrollY value if you don't provide a new one via the `opts`
|
|
202
|
+
* This will disableLoadData by default if you don't provide an override via the `opts`
|
|
189
203
|
*
|
|
190
|
-
* ## Example
|
|
204
|
+
* ## Example using the update function
|
|
205
|
+
* ```
|
|
206
|
+
* import { getRouter } from 'crelte';
|
|
207
|
+
*
|
|
208
|
+
* const router = getRouter();
|
|
209
|
+
*
|
|
210
|
+
* const search = 'foo';
|
|
211
|
+
* router.replace(req => req.setSearchParam('search', search));
|
|
212
|
+
* ```
|
|
213
|
+
*
|
|
214
|
+
* ## Example using the route object
|
|
191
215
|
* ```
|
|
192
216
|
* import { getRouter } from 'crelte';
|
|
193
217
|
*
|
|
@@ -195,18 +219,20 @@ export default class Router {
|
|
|
195
219
|
*
|
|
196
220
|
* const search = 'foo';
|
|
197
221
|
* const route = router.route.get();
|
|
198
|
-
* route.setSearchParam('search', search
|
|
199
|
-
* router.
|
|
222
|
+
* route.setSearchParam('search', search);
|
|
223
|
+
* router.replace(route);
|
|
200
224
|
* ```
|
|
201
225
|
*/
|
|
202
226
|
replace(route, opts = {}) {
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
const req = this.
|
|
227
|
+
// theoretically string and URL also work but we might
|
|
228
|
+
// change that in the future
|
|
229
|
+
const req = this.targetOrUpdateToRequest(route, opts, {
|
|
206
230
|
origin: 'replace',
|
|
207
231
|
scrollY: opts.scrollY ?? undefined,
|
|
208
232
|
disableLoadData: opts.disableLoadData ?? true,
|
|
209
233
|
});
|
|
234
|
+
if (!req)
|
|
235
|
+
return;
|
|
210
236
|
this.inner.replace(req);
|
|
211
237
|
}
|
|
212
238
|
/**
|
|
@@ -325,6 +351,7 @@ export default class Router {
|
|
|
325
351
|
}
|
|
326
352
|
return resp;
|
|
327
353
|
}
|
|
354
|
+
// gets called by the InnerRouter when a new route is requested
|
|
328
355
|
_onRoute(req, changeHistory) {
|
|
329
356
|
this.destroyRequest();
|
|
330
357
|
this._request = req;
|
|
@@ -338,6 +365,7 @@ export default class Router {
|
|
|
338
365
|
this.pageLoader.load(req, { changeHistory });
|
|
339
366
|
}
|
|
340
367
|
else {
|
|
368
|
+
this.pageLoader.discard();
|
|
341
369
|
this._onNothingLoaded(req, { changeHistory });
|
|
342
370
|
}
|
|
343
371
|
}
|
|
@@ -350,12 +378,13 @@ export default class Router {
|
|
|
350
378
|
_onPreload(req) {
|
|
351
379
|
this.pageLoader.preload(req);
|
|
352
380
|
}
|
|
381
|
+
// gets called by the pageLoader when teh loadData completes
|
|
353
382
|
async _onLoaded(resp, req, more) {
|
|
354
383
|
// check if the render was cancelled
|
|
355
384
|
if (await req._renderBarrier.ready())
|
|
356
385
|
return;
|
|
357
386
|
// when the data is loaded let's update the route of the inner
|
|
358
|
-
// this
|
|
387
|
+
// this will only happen if no other route has been requested
|
|
359
388
|
// in the meantime
|
|
360
389
|
more.changeHistory();
|
|
361
390
|
const route = req.toRoute();
|
|
@@ -365,6 +394,7 @@ export default class Router {
|
|
|
365
394
|
return resp.data;
|
|
366
395
|
});
|
|
367
396
|
}
|
|
397
|
+
// this gets called if loadData is not called
|
|
368
398
|
async _onNothingLoaded(req, more) {
|
|
369
399
|
// check if the render was cancelled
|
|
370
400
|
if (await req._renderBarrier.ready())
|
|
@@ -380,10 +410,37 @@ export default class Router {
|
|
|
380
410
|
this.setNewRoute(route);
|
|
381
411
|
});
|
|
382
412
|
}
|
|
413
|
+
// this is called by the pageLoader if we get a progress update
|
|
383
414
|
_onProgress(loading, progress) {
|
|
384
415
|
if (this._loading.get() !== loading)
|
|
385
416
|
this._loading.set(loading);
|
|
386
417
|
if (typeof progress === 'number')
|
|
387
418
|
this._loadingProgress.set(progress);
|
|
388
419
|
}
|
|
420
|
+
/**
|
|
421
|
+
* Transforms a target to a request
|
|
422
|
+
*
|
|
423
|
+
* returns null if the request was canceled by the update request
|
|
424
|
+
*/
|
|
425
|
+
targetOrUpdateToRequest(target, opts = {}, forcedOpts = {}) {
|
|
426
|
+
// we have an update request
|
|
427
|
+
if (typeof target === 'function') {
|
|
428
|
+
const route = this.route.get();
|
|
429
|
+
if (!route) {
|
|
430
|
+
throw new Error('route to update missing in first loadData call');
|
|
431
|
+
}
|
|
432
|
+
// first get a req
|
|
433
|
+
const req = this.inner.targetToRequest(route, opts);
|
|
434
|
+
// check if the request was canceled by the update request
|
|
435
|
+
if (target(req) === false)
|
|
436
|
+
return null;
|
|
437
|
+
// now we add the forcedOpts
|
|
438
|
+
req._updateOpts(forcedOpts);
|
|
439
|
+
return req;
|
|
440
|
+
}
|
|
441
|
+
return this.inner.targetToRequest(target, {
|
|
442
|
+
...opts,
|
|
443
|
+
...forcedOpts,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
389
446
|
}
|
package/dist/routing/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import Router from './Router.js';
|
|
1
|
+
import Router, { type UpdateRequest } from './Router.js';
|
|
2
2
|
import Route, { type RouteOptions } from './Route.js';
|
|
3
3
|
import Request, { type RequestOptions, type DelayRender } from './Request.js';
|
|
4
4
|
import Site from './Site.js';
|
|
5
|
-
export { Router, Route, RouteOptions, Site, Request, DelayRender, RequestOptions, };
|
|
5
|
+
export { Router, UpdateRequest, Route, RouteOptions, Site, Request, DelayRender, RequestOptions, };
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/routing/index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/routing/index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,KAAK,EAAE,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,OAAO,EAAE,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EACN,MAAM,EACN,aAAa,EACb,KAAK,EACL,YAAY,EACZ,IAAI,EACJ,OAAO,EACP,WAAW,EACX,cAAc,GACd,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crelte",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"author": "Crelte <support@crelte.com>",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
"types": "./dist/routing/index.d.ts",
|
|
45
45
|
"default": "./dist/routing/index.js"
|
|
46
46
|
},
|
|
47
|
+
"./entry": {
|
|
48
|
+
"types": "./dist/entry/index.d.ts",
|
|
49
|
+
"default": "./dist/entry/index.js"
|
|
50
|
+
},
|
|
47
51
|
"./ssr": {
|
|
48
52
|
"types": "./dist/ssr/index.d.ts",
|
|
49
53
|
"default": "./dist/ssr/index.js"
|
|
@@ -71,14 +75,15 @@
|
|
|
71
75
|
},
|
|
72
76
|
"dependencies": {
|
|
73
77
|
"crelte-std": "^0.1.1",
|
|
74
|
-
"svelte": "^4.2.12"
|
|
78
|
+
"svelte": "^4.2.12",
|
|
79
|
+
"trouter": "^4.0.0"
|
|
75
80
|
},
|
|
76
81
|
"devDependencies": {
|
|
77
82
|
"@sveltejs/package": "^2.3.1",
|
|
78
83
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
|
79
84
|
"svelte-check": "^4.1.4",
|
|
80
85
|
"typescript-svelte-plugin": "^0.3.45",
|
|
81
|
-
"
|
|
82
|
-
"
|
|
86
|
+
"vite": "^5.0",
|
|
87
|
+
"vitest": "^2.0.0"
|
|
83
88
|
}
|
|
84
89
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Pattern, Trouter } from 'trouter';
|
|
2
|
+
import { Crelte, CrelteRequest, Entry, QueryOptions } from '../index.js';
|
|
3
|
+
import { CrelteEntryRequest, EntryRequest } from './index.js';
|
|
4
|
+
import { GraphQlQuery } from '../graphql/GraphQl.js';
|
|
5
|
+
|
|
6
|
+
export type EntryRouteHandler = (
|
|
7
|
+
cr: CrelteEntryRequest,
|
|
8
|
+
) => Promise<Entry | null | undefined> | Entry | null | undefined | void;
|
|
9
|
+
|
|
10
|
+
export type EntryRoutes = (router: EntryRouter) => Promise<void> | void;
|
|
11
|
+
|
|
12
|
+
export default class EntryRouter {
|
|
13
|
+
private _crelte: Crelte;
|
|
14
|
+
private inner: Trouter<EntryRouteHandler>;
|
|
15
|
+
|
|
16
|
+
constructor(crelte: Crelte) {
|
|
17
|
+
this._crelte = crelte;
|
|
18
|
+
this.inner = new Trouter();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
add(pattern: Pattern, ...handlers: EntryRouteHandler[]): this {
|
|
22
|
+
this.inner.add('GET', pattern, ...handlers);
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* returns an env variable from the craft/.env file.
|
|
28
|
+
*/
|
|
29
|
+
getEnv(name: 'ENDPOINT_URL'): string;
|
|
30
|
+
getEnv(name: 'CRAFT_WEB_URL'): string;
|
|
31
|
+
getEnv(name: string): string | null;
|
|
32
|
+
getEnv(name: string): string | null {
|
|
33
|
+
return this._crelte.getEnv(name);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Run a GraphQl Query
|
|
38
|
+
*
|
|
39
|
+
* @param query the default export from a graphql file or the gql`query {}`
|
|
40
|
+
* function
|
|
41
|
+
* @param variables variables that should be passed to the
|
|
42
|
+
* graphql query
|
|
43
|
+
*/
|
|
44
|
+
async query(
|
|
45
|
+
query: GraphQlQuery,
|
|
46
|
+
variables: Record<string, unknown> = {},
|
|
47
|
+
opts: QueryOptions = {},
|
|
48
|
+
): Promise<unknown> {
|
|
49
|
+
// this function is added as convenience
|
|
50
|
+
return this._crelte.graphQl.query(query, variables, opts);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** @hidden */
|
|
54
|
+
async _handle(cr: CrelteRequest): Promise<Entry | null> {
|
|
55
|
+
const { params, handlers } = this.inner.find('GET', cr.req.uri);
|
|
56
|
+
|
|
57
|
+
if (!handlers.length) return null;
|
|
58
|
+
|
|
59
|
+
const er = new EntryRequest(cr.req.url, cr.req.site, {
|
|
60
|
+
params: new Map(Object.entries(params)),
|
|
61
|
+
});
|
|
62
|
+
const cer = new CrelteEntryRequest(cr, er);
|
|
63
|
+
|
|
64
|
+
for (const handler of handlers) {
|
|
65
|
+
const res = await handler(cer);
|
|
66
|
+
if (res) return res;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Crelte, CrelteRequest } from '../index.js';
|
|
2
|
+
import { Request, RequestOptions, Site } from '../routing/index.js';
|
|
3
|
+
import EntryRouter, { EntryRouteHandler, EntryRoutes } from './EntryRouter.js';
|
|
4
|
+
|
|
5
|
+
export { EntryRouter, type EntryRouteHandler, type EntryRoutes };
|
|
6
|
+
|
|
7
|
+
export type Entry = {
|
|
8
|
+
sectionHandle: string;
|
|
9
|
+
typeHandle: string;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type EntryRequestOptions = RequestOptions & {
|
|
14
|
+
params?: Map<string, string>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export class EntryRequest extends Request {
|
|
18
|
+
private params: Map<string, string>;
|
|
19
|
+
|
|
20
|
+
constructor(url: string | URL, site: Site, opts: EntryRequestOptions = {}) {
|
|
21
|
+
super(url, site, opts);
|
|
22
|
+
|
|
23
|
+
this.params = opts.params ?? new Map();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* returns the url params from the request
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```js
|
|
31
|
+
* router.get('/blog/:slug', async (cs, req) => {
|
|
32
|
+
* return Response.json({ slug: cs.getParam('slug') });
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
getParam(name: string): string | null {
|
|
37
|
+
return this.params.get(name) ?? null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class CrelteEntryRequest extends CrelteRequest {
|
|
42
|
+
req: EntryRequest;
|
|
43
|
+
|
|
44
|
+
constructor(inner: Crelte, req: EntryRequest) {
|
|
45
|
+
super(inner, req);
|
|
46
|
+
this.req = req;
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
LoadDataFn,
|
|
16
16
|
LoadDataObj,
|
|
17
17
|
} from './loadData/index.js';
|
|
18
|
+
import { Entry } from './entry/index.js';
|
|
18
19
|
|
|
19
20
|
export {
|
|
20
21
|
Crelte,
|
|
@@ -25,6 +26,7 @@ export {
|
|
|
25
26
|
type LoadDataFn,
|
|
26
27
|
type LoadDataObj,
|
|
27
28
|
type LoadDataArray,
|
|
29
|
+
type Entry,
|
|
28
30
|
};
|
|
29
31
|
|
|
30
32
|
/**
|
package/src/init/client.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { CrelteBuilder } from '../Crelte.js';
|
|
|
2
2
|
import CrelteRequest from '../CrelteRequest.js';
|
|
3
3
|
import { GraphQlQuery } from '../graphql/GraphQl.js';
|
|
4
4
|
import { SiteFromGraphQl } from '../routing/Site.js';
|
|
5
|
-
import {
|
|
5
|
+
import { pluginsBeforeRender, prepareLoadFn, setupPlugins } from './shared.js';
|
|
6
6
|
import { tick } from 'svelte';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -49,7 +49,7 @@ const mainDataDefault = {
|
|
|
49
49
|
* });
|
|
50
50
|
* ```
|
|
51
51
|
*/
|
|
52
|
-
export function main(data: MainData) {
|
|
52
|
+
export async function main(data: MainData) {
|
|
53
53
|
data = { ...mainDataDefault, ...data };
|
|
54
54
|
|
|
55
55
|
// rendering steps
|
|
@@ -97,11 +97,18 @@ export function main(data: MainData) {
|
|
|
97
97
|
// setup plugins
|
|
98
98
|
setupPlugins(crelte, data.app.plugins ?? []);
|
|
99
99
|
|
|
100
|
+
const loadFn = await prepareLoadFn(
|
|
101
|
+
crelte,
|
|
102
|
+
data.app,
|
|
103
|
+
data.entryQuery,
|
|
104
|
+
data.globalQuery,
|
|
105
|
+
);
|
|
106
|
+
|
|
100
107
|
// setup load Data
|
|
101
108
|
|
|
102
109
|
crelte.router._internal.onLoad = (req, opts) => {
|
|
103
110
|
const cr = new CrelteRequest(crelte, req);
|
|
104
|
-
return loadFn(cr,
|
|
111
|
+
return loadFn(cr, opts);
|
|
105
112
|
};
|
|
106
113
|
|
|
107
114
|
// render Space
|
package/src/init/server.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CrelteBuilder } from '../Crelte.js';
|
|
2
2
|
import { SiteFromGraphQl } from '../routing/Site.js';
|
|
3
|
-
import {
|
|
3
|
+
import { pluginsBeforeRender, prepareLoadFn, setupPlugins } from './shared.js';
|
|
4
4
|
import SsrComponents from '../ssr/SsrComponents.js';
|
|
5
5
|
import SsrCache from '../ssr/SsrCache.js';
|
|
6
6
|
import ServerCookies from '../cookies/ServerCookies.js';
|
|
@@ -85,11 +85,18 @@ export async function main(data: MainData): Promise<{
|
|
|
85
85
|
// setup plugins
|
|
86
86
|
setupPlugins(crelte, data.app.plugins ?? []);
|
|
87
87
|
|
|
88
|
+
const loadFn = await prepareLoadFn(
|
|
89
|
+
crelte,
|
|
90
|
+
data.app,
|
|
91
|
+
data.entryQuery,
|
|
92
|
+
data.globalQuery,
|
|
93
|
+
);
|
|
94
|
+
|
|
88
95
|
// setup load Data
|
|
89
96
|
|
|
90
97
|
crelte.router._internal.onLoad = req => {
|
|
91
98
|
const cr = new CrelteRequest(crelte, req);
|
|
92
|
-
return loadFn(cr
|
|
99
|
+
return loadFn(cr);
|
|
93
100
|
};
|
|
94
101
|
|
|
95
102
|
const { success, redirect, req, props } =
|
package/src/init/shared.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Crelte from '../Crelte.js';
|
|
2
2
|
import CrelteRequest from '../CrelteRequest.js';
|
|
3
|
+
import EntryRouter, { EntryRoutes } from '../entry/EntryRouter.js';
|
|
3
4
|
import { GraphQlQuery } from '../graphql/GraphQl.js';
|
|
4
5
|
import { LoadData, callLoadData } from '../loadData/index.js';
|
|
5
6
|
import { PluginCreator } from '../plugins/Plugins.js';
|
|
@@ -12,6 +13,8 @@ interface App<E, T> {
|
|
|
12
13
|
loadEntryData?: LoadData<any>;
|
|
13
14
|
|
|
14
15
|
templates?: Record<string, LazyTemplateModule<E, T>>;
|
|
16
|
+
|
|
17
|
+
entryRoutes?: EntryRoutes;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
interface TemplateModule<E, T> {
|
|
@@ -58,14 +61,43 @@ export function getEntry(page: any): any {
|
|
|
58
61
|
};
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
|
|
64
|
+
// todo it would be nice to call this only once per server start
|
|
65
|
+
export async function prepareLoadFn<E, T>(
|
|
66
|
+
crelte: Crelte,
|
|
67
|
+
app: App<E, T>,
|
|
68
|
+
entryQuery: GraphQlQuery,
|
|
69
|
+
globalQuery?: GraphQlQuery,
|
|
70
|
+
): Promise<(cr: CrelteRequest, loadOpts?: LoadOptions) => Promise<any>> {
|
|
71
|
+
const templateModules = prepareTemplates(app.templates ?? {});
|
|
72
|
+
let entryRouter: EntryRouter | null = null;
|
|
73
|
+
if (app.entryRoutes) {
|
|
74
|
+
entryRouter = new EntryRouter(crelte);
|
|
75
|
+
await app.entryRoutes(entryRouter);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return async (cr, loadOpts) => {
|
|
79
|
+
return await loadFn(
|
|
80
|
+
cr,
|
|
81
|
+
app,
|
|
82
|
+
templateModules,
|
|
83
|
+
entryRouter,
|
|
84
|
+
entryQuery,
|
|
85
|
+
globalQuery,
|
|
86
|
+
loadOpts,
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function loadFn<E, T>(
|
|
62
92
|
cr: CrelteRequest,
|
|
63
93
|
app: App<E, T>,
|
|
94
|
+
templateModules: Map<string, LazyTemplateModule<E, T>>,
|
|
95
|
+
entryRouter: EntryRouter | null,
|
|
64
96
|
entryQuery: GraphQlQuery,
|
|
65
97
|
globalQuery?: GraphQlQuery,
|
|
66
98
|
loadOpts?: LoadOptions,
|
|
67
99
|
): Promise<any> {
|
|
68
|
-
let dataProm: Promise<
|
|
100
|
+
let dataProm: Promise<any> | null = null;
|
|
69
101
|
// @ts-ignore
|
|
70
102
|
if (app.loadData) {
|
|
71
103
|
throw new Error(
|
|
@@ -93,43 +125,29 @@ export async function loadFn<D, E, T>(
|
|
|
93
125
|
})();
|
|
94
126
|
}
|
|
95
127
|
|
|
96
|
-
|
|
97
|
-
if (cr.req.siteMatches()) {
|
|
98
|
-
let uri = decodeURI(cr.req.uri);
|
|
99
|
-
if (uri.startsWith('/')) uri = uri.substring(1);
|
|
100
|
-
if (uri === '' || uri === '/') uri = '__home__';
|
|
101
|
-
|
|
102
|
-
pageProm = cr.query(entryQuery, {
|
|
103
|
-
uri,
|
|
104
|
-
siteId: cr.site.id,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
128
|
+
const entryProm = queryEntry(cr, app, entryRouter, entryQuery);
|
|
107
129
|
|
|
108
130
|
const pluginsLoadGlobalData = cr.events.trigger('loadGlobalData', cr);
|
|
109
131
|
|
|
110
132
|
// loading progress is at 20%
|
|
111
133
|
loadOpts?.setProgress(0.2);
|
|
112
134
|
|
|
113
|
-
const [data, global,
|
|
135
|
+
const [data, global, entry] = await Promise.all([
|
|
114
136
|
dataProm,
|
|
115
137
|
globalProm,
|
|
116
|
-
|
|
138
|
+
entryProm,
|
|
117
139
|
...pluginsLoadGlobalData,
|
|
118
140
|
]);
|
|
119
141
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// so any waiters get's triggered
|
|
125
|
-
cr.globals._setData(cr.site.id, {});
|
|
142
|
+
// global is only set if !wasLoaded but we need to store something
|
|
143
|
+
// even if no globalQuery exists
|
|
144
|
+
if (global || !cr.globals._wasLoaded(cr.site.id)) {
|
|
145
|
+
cr.globals._setData(cr.site.id, global ?? {});
|
|
126
146
|
}
|
|
127
147
|
|
|
128
|
-
const entry = getEntry(page);
|
|
129
|
-
|
|
130
148
|
let template;
|
|
131
149
|
if (app.templates) {
|
|
132
|
-
template = await loadTemplate(
|
|
150
|
+
template = await loadTemplate(templateModules, entry);
|
|
133
151
|
} else {
|
|
134
152
|
throw new Error('App must have templates or loadTemplate method');
|
|
135
153
|
}
|
|
@@ -180,12 +198,39 @@ function parseFilename(path: string): [string, string] {
|
|
|
180
198
|
return [name, ext];
|
|
181
199
|
}
|
|
182
200
|
|
|
183
|
-
async function
|
|
201
|
+
async function queryEntry<E, T>(
|
|
202
|
+
cr: CrelteRequest,
|
|
203
|
+
app: App<E, T>,
|
|
204
|
+
entryRouter: EntryRouter | null,
|
|
205
|
+
entryQuery: GraphQlQuery,
|
|
206
|
+
): Promise<any | null> {
|
|
207
|
+
// check
|
|
208
|
+
if (entryRouter) {
|
|
209
|
+
const entry = await entryRouter._handle(cr);
|
|
210
|
+
if (entry) return entry;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (cr.req.siteMatches()) {
|
|
214
|
+
let uri = decodeURI(cr.req.uri);
|
|
215
|
+
if (uri.startsWith('/')) uri = uri.substring(1);
|
|
216
|
+
if (uri === '' || uri === '/') uri = '__home__';
|
|
217
|
+
|
|
218
|
+
const page = await cr.query(entryQuery, {
|
|
219
|
+
uri,
|
|
220
|
+
siteId: cr.site.id,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return getEntry(page);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function prepareTemplates<E, T>(
|
|
184
230
|
rawModules: Record<string, LazyTemplateModule<E, T>>,
|
|
185
|
-
|
|
186
|
-
): Promise<TemplateModule<E, T>> {
|
|
231
|
+
): Map<string, LazyTemplateModule<E, T>> {
|
|
187
232
|
// parse modules
|
|
188
|
-
|
|
233
|
+
return new Map(
|
|
189
234
|
Object.entries(rawModules)
|
|
190
235
|
.map(([path, mod]) => {
|
|
191
236
|
const [name, _ext] = parseFilename(path);
|
|
@@ -193,7 +238,12 @@ async function loadTemplate<E, T>(
|
|
|
193
238
|
})
|
|
194
239
|
.filter(([name, _mod]) => !!name),
|
|
195
240
|
);
|
|
241
|
+
}
|
|
196
242
|
|
|
243
|
+
async function loadTemplate<E, T>(
|
|
244
|
+
modules: Map<string, LazyTemplateModule<E, T>>,
|
|
245
|
+
entry: E,
|
|
246
|
+
): Promise<TemplateModule<E, T>> {
|
|
197
247
|
const entr = entry as any;
|
|
198
248
|
const handle = `${entr.sectionHandle}-${entr.typeHandle}`;
|
|
199
249
|
|
package/src/routing/Route.ts
CHANGED
|
@@ -187,6 +187,20 @@ export default class Route {
|
|
|
187
187
|
return this.url.hash;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Set the hash of the route
|
|
192
|
+
*
|
|
193
|
+
* ## Example
|
|
194
|
+
* ```
|
|
195
|
+
* const route = new Route('https://example.com/foo/bar/', null);
|
|
196
|
+
* route.hash = '#hash';
|
|
197
|
+
* console.log(route.url.href); // 'https://example.com/foo/bar/#hash'
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
set hash(hash: string) {
|
|
201
|
+
this.url.hash = hash;
|
|
202
|
+
}
|
|
203
|
+
|
|
190
204
|
/**
|
|
191
205
|
* Checks if there are previous routes which would allow it to go back
|
|
192
206
|
*/
|