@alepha/react 0.7.5 → 0.7.7

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.
@@ -12,13 +12,16 @@ import {
12
12
  import {
13
13
  apiLinksResponseSchema,
14
14
  type ServerHandler,
15
- ServerLinksProvider,
16
15
  ServerRouterProvider,
17
16
  ServerTimingProvider,
18
17
  } from "@alepha/server";
18
+ import { ServerLinksProvider } from "@alepha/server-links";
19
19
  import { ServerStaticProvider } from "@alepha/server-static";
20
20
  import { renderToString } from "react-dom/server";
21
- import { $page } from "../descriptors/$page.ts";
21
+ import {
22
+ $page,
23
+ type PageDescriptorRenderOptions,
24
+ } from "../descriptors/$page.ts";
22
25
  import {
23
26
  PageDescriptorProvider,
24
27
  type PageReactContext,
@@ -27,7 +30,6 @@ import {
27
30
  type RouterState,
28
31
  } from "./PageDescriptorProvider.ts";
29
32
  import type { ReactHydrationState } from "./ReactBrowserProvider.ts";
30
- import { ServerHeadProvider } from "./ServerHeadProvider.ts";
31
33
 
32
34
  const envSchema = t.object({
33
35
  REACT_SERVER_DIST: t.string({ default: "public" }),
@@ -50,7 +52,6 @@ export class ReactServerProvider {
50
52
  protected readonly pageDescriptorProvider = $inject(PageDescriptorProvider);
51
53
  protected readonly serverStaticProvider = $inject(ServerStaticProvider);
52
54
  protected readonly serverRouterProvider = $inject(ServerRouterProvider);
53
- protected readonly headProvider = $inject(ServerHeadProvider);
54
55
  protected readonly serverTimingProvider = $inject(ServerTimingProvider);
55
56
  protected readonly env = $inject(envSchema);
56
57
  protected readonly ROOT_DIV_REGEX = new RegExp(
@@ -71,11 +72,7 @@ export class ReactServerProvider {
71
72
  for (const { key, instance, value } of pages) {
72
73
  const name = value[OPTIONS].name ?? key;
73
74
 
74
- instance[key].prerender = this.createRenderFunction(name, true);
75
-
76
- if (this.alepha.isTest()) {
77
- instance[key].render = this.createRenderFunction(name);
78
- }
75
+ instance[key].render = this.createRenderFunction(name);
79
76
  }
80
77
 
81
78
  // development mode
@@ -129,7 +126,10 @@ export class ReactServerProvider {
129
126
  });
130
127
 
131
128
  public get template() {
132
- return this.alepha.state("ReactServerProvider.template");
129
+ return (
130
+ this.alepha.state("ReactServerProvider.template") ??
131
+ "<!DOCTYPE html><html lang='en'><head></head><body></body></html>"
132
+ );
133
133
  }
134
134
 
135
135
  protected async registerPages(templateLoader: TemplateLoader) {
@@ -193,12 +193,7 @@ export class ReactServerProvider {
193
193
  * For testing purposes, creates a render function that can be used.
194
194
  */
195
195
  protected createRenderFunction(name: string, withIndex = false) {
196
- return async (
197
- options: {
198
- params?: Record<string, string>;
199
- query?: Record<string, string>;
200
- } = {},
201
- ) => {
196
+ return async (options: PageDescriptorRenderOptions = {}) => {
202
197
  const page = this.pageDescriptorProvider.page(name);
203
198
  const url = new URL(this.pageDescriptorProvider.url(name, options));
204
199
  const context: PageRequest = {
@@ -209,12 +204,16 @@ export class ReactServerProvider {
209
204
  onError: () => null,
210
205
  };
211
206
 
207
+ await this.alepha.emit("react:server:render:begin", {
208
+ context,
209
+ });
210
+
212
211
  const state = await this.pageDescriptorProvider.createLayers(
213
212
  page,
214
213
  context,
215
214
  );
216
215
 
217
- if (!withIndex) {
216
+ if (!withIndex && !options.html) {
218
217
  return {
219
218
  context,
220
219
  html: renderToString(
@@ -223,10 +222,22 @@ export class ReactServerProvider {
223
222
  };
224
223
  }
225
224
 
226
- return {
225
+ const html = this.renderToHtml(
226
+ this.template ?? "",
227
+ state,
227
228
  context,
228
- html: this.renderToHtml(this.template ?? "", state, context),
229
+ options.hydration,
230
+ );
231
+
232
+ const result = {
233
+ context,
234
+ state,
235
+ html,
229
236
  };
237
+
238
+ await this.alepha.emit("react:server:render:end", result);
239
+
240
+ return result;
230
241
  };
231
242
  }
232
243
 
@@ -287,16 +298,10 @@ export class ReactServerProvider {
287
298
  // return;
288
299
  // }
289
300
 
290
- await this.alepha.emit(
291
- "react:server:render",
292
- {
293
- request: serverRequest,
294
- pageRequest: context,
295
- },
296
- {
297
- log: false,
298
- },
299
- );
301
+ await this.alepha.emit("react:server:render:begin", {
302
+ request: serverRequest,
303
+ context,
304
+ });
300
305
 
301
306
  this.serverTimingProvider.beginTiming("createLayers");
302
307
 
@@ -327,6 +332,13 @@ export class ReactServerProvider {
327
332
 
328
333
  const html = this.renderToHtml(template, state, context);
329
334
 
335
+ await this.alepha.emit("react:server:render:end", {
336
+ request: serverRequest,
337
+ context,
338
+ state,
339
+ html,
340
+ });
341
+
330
342
  page.afterHandler?.(serverRequest);
331
343
 
332
344
  return html;
@@ -337,6 +349,7 @@ export class ReactServerProvider {
337
349
  template: string,
338
350
  state: RouterState,
339
351
  context: PageReactContext,
352
+ hydration = true,
340
353
  ) {
341
354
  const element = this.pageDescriptorProvider.root(state, context);
342
355
 
@@ -352,41 +365,36 @@ export class ReactServerProvider {
352
365
 
353
366
  this.serverTimingProvider.endTiming("renderToString");
354
367
 
355
- const hydrationData: ReactHydrationState = {
356
- links: context.links,
357
- layers: state.layers.map((it) => ({
358
- ...it,
359
- error: it.error
360
- ? {
361
- ...it.error,
362
- name: it.error.name,
363
- message: it.error.message,
364
- stack: it.error.stack, // TODO: Hide stack in production ?
365
- }
366
- : undefined,
367
- index: undefined,
368
- path: undefined,
369
- element: undefined,
370
- })),
371
- };
372
-
373
- // create hydration data
374
- const script = `<script>window.__ssr=${JSON.stringify(hydrationData)}</script>`;
375
-
376
368
  const response = {
377
369
  html: template,
378
370
  };
379
371
 
380
- // inject app into template
381
- this.fillTemplate(response, app, script);
372
+ if (hydration) {
373
+ const hydrationData: ReactHydrationState = {
374
+ links: context.links,
375
+ layers: state.layers.map((it) => ({
376
+ ...it,
377
+ error: it.error
378
+ ? {
379
+ ...it.error,
380
+ name: it.error.name,
381
+ message: it.error.message,
382
+ stack: it.error.stack, // TODO: Hide stack in production ?
383
+ }
384
+ : undefined,
385
+ index: undefined,
386
+ path: undefined,
387
+ element: undefined,
388
+ route: undefined,
389
+ })),
390
+ };
382
391
 
383
- // inject head meta
384
- if (context.head) {
385
- response.html = this.headProvider.renderHead(response.html, context.head);
386
- }
392
+ // create hydration data
393
+ const script = `<script>window.__ssr=${JSON.stringify(hydrationData)}</script>`;
387
394
 
388
- // TODO: hook for plugins "react:server:template"
389
- // { response: { html: string }, request, state }
395
+ // inject app into template
396
+ this.fillTemplate(response, app, script);
397
+ }
390
398
 
391
399
  return response.html;
392
400
  }
@@ -1,75 +0,0 @@
1
- 'use strict';
2
-
3
- var core = require('@alepha/core');
4
- var useRouterState = require('./useRouterState-BTmuHxkM.cjs');
5
- var client = require('react-dom/client');
6
- require('react/jsx-runtime');
7
- require('react');
8
- require('@alepha/server');
9
- require('@alepha/router');
10
-
11
- const envSchema = core.t.object({
12
- REACT_ROOT_ID: core.t.string({ default: "root" })
13
- });
14
- class ReactBrowserRenderer {
15
- browserProvider = core.$inject(useRouterState.ReactBrowserProvider);
16
- browserRouterProvider = core.$inject(useRouterState.BrowserRouterProvider);
17
- env = core.$inject(envSchema);
18
- log = core.$logger();
19
- root;
20
- getRootElement() {
21
- const root = this.browserProvider.document.getElementById(
22
- this.env.REACT_ROOT_ID
23
- );
24
- if (root) {
25
- return root;
26
- }
27
- const div = this.browserProvider.document.createElement("div");
28
- div.id = this.env.REACT_ROOT_ID;
29
- this.browserProvider.document.body.prepend(div);
30
- return div;
31
- }
32
- ready = core.$hook({
33
- name: "react:browser:render",
34
- handler: async ({ state, context, hydration }) => {
35
- const element = this.browserRouterProvider.root(state, context);
36
- if (hydration?.layers) {
37
- this.root = client.hydrateRoot(this.getRootElement(), element);
38
- this.log.info("Hydrated root element");
39
- } else {
40
- this.root ??= client.createRoot(this.getRootElement());
41
- this.root.render(element);
42
- this.log.info("Created root element");
43
- }
44
- }
45
- });
46
- }
47
-
48
- class AlephaReact {
49
- name = "alepha.react";
50
- $services = (alepha) => alepha.with(useRouterState.PageDescriptorProvider).with(useRouterState.ReactBrowserProvider).with(useRouterState.BrowserRouterProvider).with(ReactBrowserRenderer);
51
- }
52
- core.__bind(useRouterState.$page, AlephaReact);
53
-
54
- exports.$page = useRouterState.$page;
55
- exports.BrowserRouterProvider = useRouterState.BrowserRouterProvider;
56
- exports.ClientOnly = useRouterState.ClientOnly;
57
- exports.ErrorBoundary = useRouterState.ErrorBoundary;
58
- exports.Link = useRouterState.Link;
59
- exports.NestedView = useRouterState.NestedView;
60
- exports.PageDescriptorProvider = useRouterState.PageDescriptorProvider;
61
- exports.ReactBrowserProvider = useRouterState.ReactBrowserProvider;
62
- exports.RedirectionError = useRouterState.RedirectionError;
63
- exports.RouterContext = useRouterState.RouterContext;
64
- exports.RouterHookApi = useRouterState.RouterHookApi;
65
- exports.RouterLayerContext = useRouterState.RouterLayerContext;
66
- exports.isPageRoute = useRouterState.isPageRoute;
67
- exports.useActive = useRouterState.useActive;
68
- exports.useAlepha = useRouterState.useAlepha;
69
- exports.useClient = useRouterState.useClient;
70
- exports.useInject = useRouterState.useInject;
71
- exports.useQueryParams = useRouterState.useQueryParams;
72
- exports.useRouter = useRouterState.useRouter;
73
- exports.useRouterEvents = useRouterState.useRouterEvents;
74
- exports.useRouterState = useRouterState.useRouterState;
75
- exports.AlephaReact = AlephaReact;