crelte 0.5.11 → 0.5.13

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.
Files changed (110) hide show
  1. package/dist/blocks/Blocks.d.ts +1 -1
  2. package/dist/blocks/Blocks.d.ts.map +1 -1
  3. package/dist/bodyClass/BodyClass.d.ts +56 -0
  4. package/dist/bodyClass/BodyClass.d.ts.map +1 -0
  5. package/dist/bodyClass/BodyClass.js +70 -0
  6. package/dist/bodyClass/ClientBodyClass.d.ts +17 -0
  7. package/dist/bodyClass/ClientBodyClass.d.ts.map +1 -0
  8. package/dist/bodyClass/ClientBodyClass.js +71 -0
  9. package/dist/bodyClass/ServerBodyClass.d.ts +18 -0
  10. package/dist/bodyClass/ServerBodyClass.d.ts.map +1 -0
  11. package/dist/bodyClass/ServerBodyClass.js +61 -0
  12. package/dist/bodyClass/index.d.ts +2 -0
  13. package/dist/bodyClass/index.d.ts.map +1 -0
  14. package/dist/bodyClass/index.js +1 -0
  15. package/dist/bodyClass/utils.d.ts +39 -0
  16. package/dist/bodyClass/utils.d.ts.map +1 -0
  17. package/dist/bodyClass/utils.js +84 -0
  18. package/dist/cookies/ClientCookies.d.ts +8 -3
  19. package/dist/cookies/ClientCookies.d.ts.map +1 -1
  20. package/dist/cookies/ClientCookies.js +31 -7
  21. package/dist/cookies/Cookies.d.ts +42 -0
  22. package/dist/cookies/Cookies.d.ts.map +1 -0
  23. package/dist/cookies/Cookies.js +44 -0
  24. package/dist/cookies/ServerCookies.d.ts +3 -2
  25. package/dist/cookies/ServerCookies.d.ts.map +1 -1
  26. package/dist/cookies/ServerCookies.js +6 -4
  27. package/dist/cookies/index.d.ts +1 -25
  28. package/dist/cookies/index.d.ts.map +1 -1
  29. package/dist/cookies/index.js +1 -1
  30. package/dist/crelte.d.ts +7 -1
  31. package/dist/crelte.d.ts.map +1 -1
  32. package/dist/crelte.js +4 -1
  33. package/dist/index.d.ts +13 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +9 -0
  36. package/dist/init/client.d.ts.map +1 -1
  37. package/dist/init/client.js +23 -14
  38. package/dist/init/server.d.ts.map +1 -1
  39. package/dist/init/server.js +12 -3
  40. package/dist/init/shared.d.ts +1 -0
  41. package/dist/init/shared.d.ts.map +1 -1
  42. package/dist/init/shared.js +19 -5
  43. package/dist/loadData/Globals.d.ts.map +1 -1
  44. package/dist/loadData/entry.d.ts +11 -2
  45. package/dist/loadData/entry.d.ts.map +1 -1
  46. package/dist/loadData/entry.js +11 -2
  47. package/dist/node/index.js +1 -1
  48. package/dist/plugins/Events.d.ts +6 -1
  49. package/dist/plugins/Events.d.ts.map +1 -1
  50. package/dist/plugins/Plugins.d.ts +36 -1
  51. package/dist/plugins/Plugins.d.ts.map +1 -1
  52. package/dist/plugins/Plugins.js +32 -0
  53. package/dist/queries/index.d.ts +1 -1
  54. package/dist/queries/index.d.ts.map +1 -1
  55. package/dist/routing/route/Request.d.ts +1 -1
  56. package/dist/routing/route/Request.d.ts.map +1 -1
  57. package/dist/routing/route/Request.js +7 -3
  58. package/dist/routing/router/ClientRouter.d.ts.map +1 -1
  59. package/dist/routing/router/ClientRouter.js +18 -11
  60. package/dist/routing/router/Router.d.ts.map +1 -1
  61. package/dist/routing/router/Router.js +8 -12
  62. package/dist/server/CrelteServer.d.ts +1 -0
  63. package/dist/server/CrelteServer.d.ts.map +1 -1
  64. package/dist/server/CrelteServer.js +5 -2
  65. package/dist/server/queries/QueryGqlRoute.d.ts.map +1 -1
  66. package/dist/server/queries/QueryGqlRoute.js +3 -3
  67. package/dist/server/queries/routes.d.ts +1 -1
  68. package/dist/server/queries/routes.d.ts.map +1 -1
  69. package/dist/std/stores/StagedWritable.d.ts +48 -0
  70. package/dist/std/stores/StagedWritable.d.ts.map +1 -0
  71. package/dist/std/stores/StagedWritable.js +84 -0
  72. package/dist/std/stores/index.d.ts +2 -1
  73. package/dist/std/stores/index.d.ts.map +1 -1
  74. package/dist/std/stores/index.js +2 -1
  75. package/dist/std/sync/Barrier.js +1 -1
  76. package/dist/utils.d.ts +9 -0
  77. package/dist/utils.d.ts.map +1 -1
  78. package/dist/utils.js +11 -0
  79. package/package.json +5 -1
  80. package/src/blocks/Blocks.ts +1 -1
  81. package/src/bodyClass/BodyClass.ts +94 -0
  82. package/src/bodyClass/ClientBodyClass.ts +79 -0
  83. package/src/bodyClass/ServerBodyClass.ts +86 -0
  84. package/src/bodyClass/index.ts +1 -0
  85. package/src/bodyClass/utils.ts +118 -0
  86. package/src/cookies/ClientCookies.ts +41 -10
  87. package/src/cookies/Cookies.ts +70 -0
  88. package/src/cookies/ServerCookies.ts +9 -6
  89. package/src/cookies/index.ts +5 -29
  90. package/src/crelte.ts +12 -0
  91. package/src/index.ts +15 -1
  92. package/src/init/client.ts +26 -14
  93. package/src/init/server.ts +12 -3
  94. package/src/init/shared.ts +21 -6
  95. package/src/loadData/Globals.ts +1 -1
  96. package/src/loadData/entry.ts +12 -2
  97. package/src/node/index.ts +1 -1
  98. package/src/plugins/Events.ts +12 -1
  99. package/src/plugins/Plugins.ts +66 -1
  100. package/src/queries/index.ts +5 -1
  101. package/src/routing/route/Request.ts +11 -4
  102. package/src/routing/router/ClientRouter.ts +23 -14
  103. package/src/routing/router/Router.ts +8 -10
  104. package/src/server/CrelteServer.ts +4 -2
  105. package/src/server/queries/QueryGqlRoute.ts +3 -2
  106. package/src/server/queries/routes.ts +1 -0
  107. package/src/std/stores/StagedWritable.ts +96 -0
  108. package/src/std/stores/index.ts +2 -1
  109. package/src/std/sync/Barrier.ts +1 -1
  110. package/src/utils.ts +15 -0
@@ -3,6 +3,7 @@ import {
3
3
  loadFn,
4
4
  newQueries,
5
5
  onNewCrelteRequest,
6
+ pluginsAfterRender,
6
7
  pluginsBeforeRender,
7
8
  pluginsBeforeRequest,
8
9
  setupPlugins,
@@ -19,6 +20,9 @@ import Plugins from '../plugins/Plugins.js';
19
20
  import Events from '../plugins/Events.js';
20
21
  import Globals from '../loadData/Globals.js';
21
22
  import { Writable } from '../std/stores/index.js';
23
+ import ClientBodyClass from '../bodyClass/ClientBodyClass.js';
24
+ import { BodyClass } from '../bodyClass/index.js';
25
+ import { Cookies } from '../cookies/index.js';
22
26
 
23
27
  /**
24
28
  * The main function to start the client side rendering
@@ -74,7 +78,6 @@ export async function main(data: MainData) {
74
78
  });
75
79
 
76
80
  const queries = newQueries(ssrCache, router.route.readonly(), config);
77
- const cookies = new ClientCookies();
78
81
 
79
82
  const crelte = newCrelte({
80
83
  config,
@@ -84,7 +87,8 @@ export async function main(data: MainData) {
84
87
  globals: new Globals(),
85
88
  router: new Router(router),
86
89
  queries,
87
- cookies,
90
+ cookies: new Cookies(new ClientCookies()),
91
+ bodyClass: new BodyClass(ClientBodyClass.fromSsrCache(ssrCache)),
88
92
  });
89
93
 
90
94
  const app = new InternalApp(data.app);
@@ -101,22 +105,22 @@ export async function main(data: MainData) {
101
105
 
102
106
  // render Space
103
107
 
104
- let appInstance: any;
105
- let routeProp: Writable<Route>;
108
+ let routeProp: Writable<Route> | null = null;
106
109
  const renderApp = (route: Route) => {
107
- if (appInstance) {
108
- routeProp!.set(route);
110
+ if (routeProp) {
111
+ routeProp.set(route);
109
112
  return;
110
113
  }
111
114
 
112
115
  routeProp = new Writable(route);
113
- appInstance = svelteMount(data.app.default, {
116
+ svelteMount(data.app.default, {
114
117
  target: document.body,
115
118
  props: { route: routeProp },
116
119
  context: new Map([['crelte', crelte]]),
117
120
  intro: config.playIntro,
118
121
  });
119
122
  };
123
+ const appMounted = () => !!routeProp;
120
124
 
121
125
  router.onError = (e, req) => {
122
126
  console.error('routing failed:', e, 'reloading trying to fix it');
@@ -126,9 +130,9 @@ export async function main(data: MainData) {
126
130
  };
127
131
 
128
132
  router.onRender = async (cr, readyForRoute, domUpdated) => {
129
- if (appInstance && cr.req.disableLoadData) {
133
+ if (appMounted() && cr.req.disableLoadData) {
130
134
  // if the app is already rendered and entry did not change
131
- // we just wan't to run domUpdated because we don't wan't to update anything
135
+ // we just wan't to run domUpdated because we don't want to update anything
132
136
 
133
137
  const route = readyForRoute();
134
138
  cr.router.z_requestCompleted();
@@ -140,6 +144,7 @@ export async function main(data: MainData) {
140
144
  await tick();
141
145
 
142
146
  domUpdated(cr, route);
147
+ pluginsAfterRender(cr, route);
143
148
 
144
149
  return route;
145
150
  }
@@ -149,9 +154,15 @@ export async function main(data: MainData) {
149
154
  let render = async () => {
150
155
  const route = readyForRoute();
151
156
  cr.router.z_requestCompleted();
152
- if (route.entryChanged) cr.globals.z_syncToStores();
153
- // we should trigger the route update here
154
- pluginsBeforeRender(cr, route);
157
+ // this is only important on the first render
158
+ // else we will catch an earlier branch in onRender
159
+ if (route.entryChanged) {
160
+ cr.globals.z_syncToStores();
161
+ pluginsBeforeRender(cr, route);
162
+ cr.cookies.z_render();
163
+ cr.bodyClass.z_render();
164
+ }
165
+
155
166
  renderApp(route);
156
167
 
157
168
  await tick();
@@ -163,6 +174,7 @@ export async function main(data: MainData) {
163
174
  }
164
175
 
165
176
  domUpdated(cr, route);
177
+ pluginsAfterRender(cr, route);
166
178
 
167
179
  return route;
168
180
  };
@@ -170,7 +182,7 @@ export async function main(data: MainData) {
170
182
  // render with view Transition if enabled and not in hydration
171
183
  if (
172
184
  config.viewTransition &&
173
- appInstance &&
185
+ appMounted() &&
174
186
  (document as any).startViewTransition
175
187
  ) {
176
188
  const prevRender = render;
@@ -211,8 +223,8 @@ function handleLoadError(e: any) {
211
223
 
212
224
  // Messages in different languages
213
225
  const messages: Record<string, string> = {
214
- en: 'An error has occurred. Please reload the page or try again later.',
215
226
  de: 'Leider ist ein Fehler aufgetreten. Laden Sie die Seite neu, oder versuchen Sie es später noch mal.',
227
+ en: 'An error has occurred. Please reload the page or try again later.',
216
228
  fr: 'Une erreur s’est produite. Veuillez recharger la page ou réessayer plus tard.',
217
229
  it: 'Si è verificato un errore. Ricarica la pagina o riprova più tardi.',
218
230
  nl: 'Er is een fout opgetreden. Herlaad de pagina of probeer het later opnieuw.',
@@ -23,6 +23,9 @@ import {
23
23
  RenderRequest,
24
24
  RenderResponse,
25
25
  } from '../server/shared.js';
26
+ import ServerBodyClass from '../bodyClass/ServerBodyClass.js';
27
+ import BodyClass from '../bodyClass/BodyClass.js';
28
+ import { Cookies } from '../cookies/index.js';
26
29
 
27
30
  export { type RenderRequest, type RenderResponse } from '../server/shared.js';
28
31
 
@@ -75,6 +78,7 @@ export async function main(data: MainData): Promise<RenderResponse> {
75
78
  ssrCache.set('FRONTEND_URL', data.serverData.frontend);
76
79
 
77
80
  const cookies = new ServerCookies(data.serverData.headers);
81
+ const bodyClass = new ServerBodyClass();
78
82
 
79
83
  ssrCache.set('crelteSites', data.serverData.sites);
80
84
  const router = new ServerRouter(
@@ -93,7 +97,8 @@ export async function main(data: MainData): Promise<RenderResponse> {
93
97
  globals: new Globals(),
94
98
  router: new Router(router),
95
99
  queries,
96
- cookies,
100
+ cookies: new Cookies(cookies),
101
+ bodyClass: new BodyClass(bodyClass),
97
102
  });
98
103
 
99
104
  const app = new InternalApp(data.app);
@@ -111,6 +116,8 @@ export async function main(data: MainData): Promise<RenderResponse> {
111
116
  cr.router.z_requestCompleted();
112
117
  cr.globals.z_syncToStores();
113
118
  pluginsBeforeRender(cr, route);
119
+ cr.cookies.z_render();
120
+ cr.bodyClass.z_render();
114
121
 
115
122
  return route;
116
123
  };
@@ -121,7 +128,7 @@ export async function main(data: MainData): Promise<RenderResponse> {
121
128
  // if redirect
122
129
  if (!route) {
123
130
  const headers = new Headers();
124
- (crelte.cookies as ServerCookies)._populateHeaders(headers);
131
+ cookies._populateHeaders(headers);
125
132
 
126
133
  return {
127
134
  status: req.statusCode ?? 302,
@@ -145,6 +152,7 @@ export async function main(data: MainData): Promise<RenderResponse> {
145
152
  context,
146
153
  });
147
154
 
155
+ bodyClass.z_populateSsrCache(ssrCache);
148
156
  head += ssrComponents.toHead(data.serverData.ssrManifest);
149
157
  head += crelte.ssrCache.z_exportToHead();
150
158
 
@@ -153,6 +161,7 @@ export async function main(data: MainData): Promise<RenderResponse> {
153
161
  '<!--page-lang-->',
154
162
  route.site.language,
155
163
  );
164
+ htmlTemplate = bodyClass.z_processHtmlTemplate(htmlTemplate);
156
165
 
157
166
  const finalHtml = htmlTemplate
158
167
  .replace('</head>', head + '\n\t</head>')
@@ -161,7 +170,7 @@ export async function main(data: MainData): Promise<RenderResponse> {
161
170
  const entry = route.entry;
162
171
 
163
172
  const headers = new Headers();
164
- (crelte.cookies as ServerCookies)._populateHeaders(headers);
173
+ cookies._populateHeaders(headers);
165
174
 
166
175
  return {
167
176
  status:
@@ -32,6 +32,11 @@ export function pluginsBeforeRequest(cr: CrelteRequest): Promise<void> | void {
32
32
 
33
33
  export function pluginsBeforeRender(cr: CrelteRequest, route: Route): void {
34
34
  cr.events.trigger('beforeRender', cr, route);
35
+ cr.plugins.z_render(cr, route);
36
+ }
37
+
38
+ export function pluginsAfterRender(cr: CrelteRequest, route: Route): void {
39
+ cr.events.trigger('afterRender', cr, route);
35
40
  }
36
41
 
37
42
  export function newQueries(
@@ -64,12 +69,18 @@ export function onNewCrelteRequest(
64
69
  ...crelte,
65
70
  router: crelte.router.z_toRequest(req),
66
71
  queries: crelte.queries.z_toRequest(req),
72
+ plugins: crelte.plugins.z_toRequest(req),
67
73
  globals: crelte.globals.z_toRequest(),
74
+ cookies: crelte.cookies.z_toRequest(),
75
+ bodyClass: crelte.bodyClass.z_toRequest(),
68
76
  };
77
+ // make sure helper funcitons link to the correct instances
78
+ nCrelte.getPlugin = name => nCrelte.plugins.get(name);
79
+ nCrelte.getGlobalStore = name => nCrelte.globals.getStore(name);
69
80
  return crelteToRequest(nCrelte, req);
70
81
  }
71
82
 
72
- async function pluginsLoadEntry(cr: CrelteRequest): Promise<Entry | null> {
83
+ async function eventsLoadEntry(cr: CrelteRequest): Promise<Entry | null> {
73
84
  const listeners = cr.events.getListeners('loadEntry');
74
85
 
75
86
  for (const loadEntry of listeners) {
@@ -123,7 +134,7 @@ export async function loadFn(
123
134
  // checked to be empty before doing it
124
135
  const entryProm = (async () => {
125
136
  // first let's try to call the plugin loadEntry
126
- let entry = await pluginsLoadEntry(cr);
137
+ let entry = await eventsLoadEntry(cr);
127
138
  if (isCanceled()) return [];
128
139
 
129
140
  // if no plugin provides an entry we load it from the app
@@ -152,7 +163,8 @@ export async function loadFn(
152
163
  return [entry, template] as [Entry, TemplateModule];
153
164
  })();
154
165
 
155
- const pluginsLoadGlobalData = cr.events.trigger('loadGlobalData', cr);
166
+ const eventsLoadGlobalData = cr.events.trigger('loadGlobalData', cr);
167
+ const pluginsLoadGlobalData = cr.plugins.z_loadGlobalData(cr);
156
168
 
157
169
  // loading progress is at 20%
158
170
  loadOpts?.setProgress(0.2);
@@ -160,6 +172,7 @@ export async function loadFn(
160
172
  const loadGlobalDataProm = Promise.all([
161
173
  globalProm,
162
174
  entryProm,
175
+ ...eventsLoadGlobalData,
163
176
  ...pluginsLoadGlobalData,
164
177
  ]);
165
178
 
@@ -167,7 +180,7 @@ export async function loadFn(
167
180
  // we force resolve them to prevent deadlocks
168
181
  if (
169
182
  import.meta.env.DEV &&
170
- !(await Promise.any([loadGlobalDataProm, timeout(2000)]))
183
+ !(await Promise.race([loadGlobalDataProm, timeout(2000)]))
171
184
  ) {
172
185
  console.error(
173
186
  'DEV: globals took longer than 2 seconds to load. ' +
@@ -184,8 +197,6 @@ export async function loadFn(
184
197
  // loading progress is at 60%
185
198
  loadOpts?.setProgress(0.6);
186
199
 
187
- const pluginsLoadData = cr.events.trigger('loadData', cr, entry);
188
-
189
200
  let loadDataProm = null;
190
201
  if (template.loadData) {
191
202
  loadDataProm = callLoadData(template.loadData, cr, entry);
@@ -196,9 +207,13 @@ export async function loadFn(
196
207
  entryDataProm = callLoadData(app.loadEntryData, cr, entry);
197
208
  }
198
209
 
210
+ const eventsLoadData = cr.events.trigger('loadData', cr, entry);
211
+ const pluginsLoadData = cr.plugins.z_loadData(cr);
212
+
199
213
  const [templateData, entryData] = await Promise.all([
200
214
  loadDataProm,
201
215
  entryDataProm,
216
+ ...eventsLoadData,
202
217
  ...pluginsLoadData,
203
218
  ]);
204
219
 
@@ -131,7 +131,7 @@ export default class Globals {
131
131
  * @hidden
132
132
  * call this before starting the loadGlobalData phase
133
133
  */
134
- z_toRequest() {
134
+ z_toRequest(): Globals {
135
135
  const nGlobals = new Globals(this.stores);
136
136
  nGlobals.waiters = new Map();
137
137
  nGlobals.newData = new Map();
@@ -1,5 +1,5 @@
1
1
  import { CrelteRequest } from '../index.js';
2
- import { Query } from '../queries/Queries.js';
2
+ import { Query, QueryOptions } from '../queries/Queries.js';
3
3
 
4
4
  export type Entry = {
5
5
  sectionHandle: string;
@@ -31,12 +31,22 @@ export function entryQueryVars(cr: CrelteRequest): EntryQueryVars {
31
31
  };
32
32
  }
33
33
 
34
+ /**
35
+ * ## Example
36
+ * `App.svelte`
37
+ * ```ts
38
+ * import entryQuery from '@/queries/entry.graphql';
39
+ *
40
+ * export const loadEntry => cr => queryEntry(cr, entryQuery, entryQueryVars(cr));
41
+ * ```
42
+ */
34
43
  export async function queryEntry(
35
44
  cr: CrelteRequest,
36
45
  entryQuery: Query,
37
46
  vars: EntryQueryVars,
47
+ opts?: QueryOptions,
38
48
  ): Promise<Entry> {
39
- const page = await cr.query(entryQuery, vars);
49
+ const page = await cr.query(entryQuery, vars, opts);
40
50
  return extractEntry(page) ?? ENTRY_ERROR_404;
41
51
  }
42
52
 
package/src/node/index.ts CHANGED
@@ -166,7 +166,7 @@ export default async function createServer(serverMod: any, buildTime: string) {
166
166
  } catch (e) {
167
167
  basicError(res, e);
168
168
  }
169
- }).listen(8080);
169
+ }).listen(process.env.PORT ?? 8080);
170
170
  }
171
171
 
172
172
  function basicError(res: ServerResponse, err: any) {
@@ -32,6 +32,9 @@ export default class Events {
32
32
  * Will be executed in preload as well.
33
33
  *
34
34
  * @returns a function to remove the listener
35
+ *
36
+ * #### afterRender
37
+ * Note this will also be executed when disableLoadData is true
35
38
  */
36
39
  // override this function to add your own function signatures
37
40
  on(
@@ -58,7 +61,14 @@ export default class Events {
58
61
  ev: 'loadData',
59
62
  fn: (cr: CrelteRequest, entry: Entry) => Promise<any> | any,
60
63
  ): () => void;
61
- on(ev: 'beforeRender', fn: (cr: CrelteRequest) => void): () => void;
64
+ on(
65
+ ev: 'beforeRender',
66
+ fn: (cr: CrelteRequest, route: Route) => void,
67
+ ): () => void;
68
+ on(
69
+ ev: 'afterRender',
70
+ fn: (cr: CrelteRequest, route: Route) => void,
71
+ ): () => void;
62
72
  on(ev: string, fn: (...args: any[]) => any): () => void {
63
73
  let set = this.inner.get(ev);
64
74
  if (!set) {
@@ -108,6 +118,7 @@ export default class Events {
108
118
  entry: Entry,
109
119
  ): (Promise<any> | any)[];
110
120
  trigger(ev: 'beforeRender', cr: CrelteRequest, route: Route): void[];
121
+ trigger(ev: 'afterRender', cr: CrelteRequest, route: Route): void[];
111
122
  trigger(ev: string, ...args: any[]): any[] {
112
123
  const set = this.inner.get(ev);
113
124
  if (!set) return [];
@@ -1,10 +1,32 @@
1
- import { Crelte } from '../crelte.js';
1
+ import { Crelte, CrelteRequest } from '../crelte.js';
2
+ import { Request, Route } from '../routing/index.js';
2
3
 
3
4
  /**
4
5
  * A plugin
5
6
  */
6
7
  export interface Plugin {
7
8
  name: string;
9
+ /**
10
+ * The returned value will be used inside of CrelteRequest
11
+ */
12
+ toRequest?: (req: Request) => Plugin;
13
+
14
+ /**
15
+ * This will be called during the loadGlobalData phase.
16
+ */
17
+ loadGlobalData?: (cr: CrelteRequest) => Promise<void> | void;
18
+
19
+ /**
20
+ * This will be called during the loadData phase.
21
+ */
22
+ loadData?: (cr: CrelteRequest) => Promise<void> | void;
23
+
24
+ /**
25
+ * This will be called before the dom gets updated.
26
+ *
27
+ * At this point you can update variables or stores.
28
+ */
29
+ render?: (cr: CrelteRequest, route: Route) => void;
8
30
  }
9
31
 
10
32
  /**
@@ -46,4 +68,47 @@ export default class Plugins {
46
68
  get(name: string): Plugin | null {
47
69
  return this.plugins.get(name) ?? null;
48
70
  }
71
+
72
+ /**
73
+ * @hidden
74
+ */
75
+ z_toRequest(req: Request): Plugins {
76
+ const nPlugins = new Plugins();
77
+
78
+ for (let plugin of this.plugins.values()) {
79
+ if (typeof plugin.toRequest === 'function')
80
+ plugin = plugin.toRequest(req);
81
+
82
+ nPlugins.add(plugin);
83
+ }
84
+
85
+ return nPlugins;
86
+ }
87
+
88
+ /**
89
+ * @hidden
90
+ */
91
+ z_loadGlobalData(cr: CrelteRequest): (Promise<void> | void | undefined)[] {
92
+ return Array.from(this.plugins.values()).map(plugin =>
93
+ plugin.loadGlobalData?.(cr),
94
+ );
95
+ }
96
+
97
+ /**
98
+ * @hidden
99
+ */
100
+ z_loadData(cr: CrelteRequest): (Promise<void> | void | undefined)[] {
101
+ return Array.from(this.plugins.values()).map(plugin =>
102
+ plugin.loadData?.(cr),
103
+ );
104
+ }
105
+
106
+ /**
107
+ * @hidden
108
+ */
109
+ z_render(cr: CrelteRequest, route: Route): void {
110
+ for (const plugin of this.plugins.values()) {
111
+ plugin.render?.(cr, route);
112
+ }
113
+ }
49
114
  }
@@ -81,7 +81,11 @@ export type TransformFn<
81
81
  export type Transform<
82
82
  T extends Record<string, QueryVar<any>> = Record<string, QueryVar<any>>,
83
83
  F extends TransformFn<T> = TransformFn<T>,
84
- > = (response: any, vars: InferVariableTypes<T>) => Awaited<ReturnType<F>>;
84
+ > = (
85
+ response: any,
86
+ vars: InferVariableTypes<T>,
87
+ csr: CrelteServerRequest,
88
+ ) => Awaited<ReturnType<F>>;
85
89
 
86
90
  /** use {@link Handle} */
87
91
  export type HandleFn<
@@ -1,5 +1,5 @@
1
1
  import Site from '../Site.js';
2
- import { objClone } from '../../utils.js';
2
+ import { objClone, promiseThen } from '../../utils.js';
3
3
  import BaseRoute, { RouteOrigin } from './BaseRoute.js';
4
4
  import Route, { TemplateModule } from './Route.js';
5
5
  import { Entry } from '../../loadData/index.js';
@@ -254,8 +254,15 @@ class RenderBarrier {
254
254
  const action = this.inner.add();
255
255
 
256
256
  return {
257
- ready: async () => {
258
- if (!this.inner.isOpen()) await action.ready(null);
257
+ ready: () => {
258
+ if (!this.inner.isOpen())
259
+ return promiseThen(
260
+ // wait for action.ready
261
+ action.ready(null),
262
+ // then return if it was cancelled
263
+ () => this.cancelled,
264
+ );
265
+
259
266
  return this.cancelled;
260
267
  },
261
268
  remove: () => {
@@ -292,7 +299,7 @@ export type DelayRender = {
292
299
  *
293
300
  * @returns if the render was cancelled
294
301
  */
295
- ready: () => Promise<boolean>;
302
+ ready: () => Promise<boolean> | boolean;
296
303
 
297
304
  /**
298
305
  * If youre not interested when the render happens anymore
@@ -107,7 +107,7 @@ export default class ClientRouter extends BaseRouter {
107
107
  async pushRequest(req: Request, _opts: RequestOptions = {}) {
108
108
  const url = req.url;
109
109
 
110
- return await this.handleRequest(req, route => {
110
+ return this.handleRequest(req, route => {
111
111
  window.history.pushState(
112
112
  route.z_toState(),
113
113
  '',
@@ -119,18 +119,13 @@ export default class ClientRouter extends BaseRouter {
119
119
  async replaceRequest(req: Request, _opts: RequestOptions = {}) {
120
120
  const url = req.url;
121
121
 
122
- try {
123
- return await this.handleRequest(req, () => {
124
- window.history.replaceState(
125
- req.z_toState(),
126
- '',
127
- url.pathname + url.search + url.hash,
128
- );
129
- });
130
- } catch (e) {
131
- console.warn('replacing route failed', e);
132
- throw e;
133
- }
122
+ return this.handleRequest(req, () => {
123
+ window.history.replaceState(
124
+ req.z_toState(),
125
+ '',
126
+ url.pathname + url.search + url.hash,
127
+ );
128
+ });
134
129
  }
135
130
 
136
131
  back(): void {
@@ -164,6 +159,7 @@ export default class ClientRouter extends BaseRouter {
164
159
  const req = this.targetToRequest(link.href, {
165
160
  origin: 'click',
166
161
  context: { ...link.dataset },
162
+ disableScroll: attributeToBool(link, 'data-disable-scroll'),
167
163
  });
168
164
  const currRoute = this.route.get();
169
165
  const routeEq =
@@ -189,7 +185,9 @@ export default class ClientRouter extends BaseRouter {
189
185
 
190
186
  if (
191
187
  link &&
192
- !link.hasAttribute('data-no-preload') &&
188
+ // todo remove data-no-preload
189
+ !attributeToBool(link, 'data-no-preload') &&
190
+ !attributeToBool(link, 'data-disable-preload') &&
193
191
  link.href
194
192
  ) {
195
193
  this.preload(link.href);
@@ -311,3 +309,14 @@ export default class ClientRouter extends BaseRouter {
311
309
  }
312
310
  }
313
311
  }
312
+
313
+ function attributeToBool(el: HTMLElement, attr: string): boolean {
314
+ switch (el.getAttribute(attr)) {
315
+ case '':
316
+ case 'true':
317
+ return true;
318
+ case 'false':
319
+ default:
320
+ return false;
321
+ }
322
+ }
@@ -226,19 +226,18 @@ export default class Router {
226
226
  });
227
227
  if (!req) return;
228
228
 
229
- try {
230
- return await this.inner.pushRequest(req, opts);
231
- } catch (e) {
229
+ return this.inner.pushRequest(req, opts).catch(e => {
232
230
  console.warn('pushing route failed', e);
233
231
  throw e;
234
- }
232
+ });
235
233
  }
236
234
 
237
235
  /**
238
236
  * @deprecated use push instead
239
237
  */
240
238
  pushState(route: Route | Request) {
241
- console.warn('pushState is deprecated, use push instead');
239
+ if (import.meta.env.DEV)
240
+ console.warn('pushState is deprecated, use push instead');
242
241
  this.push(route);
243
242
  }
244
243
 
@@ -287,19 +286,18 @@ export default class Router {
287
286
  });
288
287
  if (!req) return;
289
288
 
290
- try {
291
- return await this.inner.replaceRequest(req, opts);
292
- } catch (e) {
289
+ return this.inner.replaceRequest(req, opts).catch(e => {
293
290
  console.warn('replacing route failed', e);
294
291
  throw e;
295
- }
292
+ });
296
293
  }
297
294
 
298
295
  /**
299
296
  * @deprecated use replace instead
300
297
  */
301
298
  replaceState(route: Route | Request) {
302
- console.warn('replaceState is deprecated, use replace instead');
299
+ if (import.meta.env.DEV)
300
+ console.warn('replaceState is deprecated, use replace instead');
303
301
  this.replace(route);
304
302
  }
305
303
 
@@ -24,6 +24,7 @@ export default class CrelteServerRequest {
24
24
  private _sites: Site[];
25
25
  private _langs: string[];
26
26
  private _queries: Queries;
27
+ private _scookies: ServerCookies;
27
28
  private _cookies: Cookies;
28
29
 
29
30
  constructor(req: ServerRequest, opts: CrelteServerRequestOptions) {
@@ -37,7 +38,8 @@ export default class CrelteServerRequest {
37
38
  this._queries = opts.queries.z_toRequest(
38
39
  new Request(new URL(req.url), req.site),
39
40
  );
40
- this._cookies = new ServerCookies(req.headers);
41
+ this._scookies = new ServerCookies(req.headers);
42
+ this._cookies = new Cookies(this._scookies);
41
43
  }
42
44
 
43
45
  /**
@@ -145,6 +147,6 @@ export default class CrelteServerRequest {
145
147
 
146
148
  /** @hidden */
147
149
  z_finishResponse(resp: Response) {
148
- (this.cookies as ServerCookies)._populateHeaders(resp.headers);
150
+ this._scookies._populateHeaders(resp.headers);
149
151
  }
150
152
  }
@@ -97,10 +97,11 @@ export default class QueryGqlRoute {
97
97
  private async transform(
98
98
  jsonResp: Record<string, any>,
99
99
  vars: Record<string, any>,
100
+ csr: CrelteServerRequest,
100
101
  ): Promise<void> {
101
102
  if (!this.transformFn || !jsonResp.data) return;
102
103
 
103
- const transformed = await this.transformFn(jsonResp.data, vars);
104
+ const transformed = await this.transformFn(jsonResp.data, vars, csr);
104
105
  if (typeof transformed !== 'undefined') jsonResp.data = transformed;
105
106
  }
106
107
 
@@ -195,7 +196,7 @@ export default class QueryGqlRoute {
195
196
  jsonResp = await resp.json();
196
197
  if (!jsonResp || typeof jsonResp !== 'object')
197
198
  throw new Error('invalid json response');
198
- await this.transform(jsonResp, vars);
199
+ await this.transform(jsonResp, vars, csr);
199
200
  } catch (e) {
200
201
  return newError(e, 500);
201
202
  }
@@ -11,6 +11,7 @@ export type CacheIfFn = (response: any, vars: Record<string, any>) => boolean;
11
11
  export type TransformFn = (
12
12
  response: any,
13
13
  vars: Record<string, any>,
14
+ csr: CrelteServerRequest,
14
15
  ) => void | any | Promise<void | any>;
15
16
 
16
17
  export type HandleFn = (