@alepha/react 0.11.5 → 0.11.6
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/index.browser.js +17 -9
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +58 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -20
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
- package/src/hooks/useAction.ts +1 -1
- package/src/hooks/useRouterState.ts +1 -1
- package/src/index.ts +1 -1
- package/src/providers/ReactBrowserProvider.ts +29 -6
- package/src/providers/ReactBrowserRouterProvider.ts +2 -2
- package/src/providers/ReactServerProvider.ts +49 -25
- package/src/services/ReactRouter.ts +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alepha/react",
|
|
3
3
|
"description": "Build server-side rendered (SSR) or single-page React applications.",
|
|
4
|
-
"version": "0.11.
|
|
4
|
+
"version": "0.11.6",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=22.0.0"
|
|
@@ -17,25 +17,25 @@
|
|
|
17
17
|
"src"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@alepha/core": "0.11.
|
|
21
|
-
"@alepha/datetime": "0.11.
|
|
22
|
-
"@alepha/logger": "0.11.
|
|
23
|
-
"@alepha/router": "0.11.
|
|
24
|
-
"@alepha/server": "0.11.
|
|
25
|
-
"@alepha/server-cache": "0.11.
|
|
26
|
-
"@alepha/server-links": "0.11.
|
|
27
|
-
"@alepha/server-static": "0.11.
|
|
20
|
+
"@alepha/core": "0.11.6",
|
|
21
|
+
"@alepha/datetime": "0.11.6",
|
|
22
|
+
"@alepha/logger": "0.11.6",
|
|
23
|
+
"@alepha/router": "0.11.6",
|
|
24
|
+
"@alepha/server": "0.11.6",
|
|
25
|
+
"@alepha/server-cache": "0.11.6",
|
|
26
|
+
"@alepha/server-links": "0.11.6",
|
|
27
|
+
"@alepha/server-static": "0.11.6"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@alepha/testing": "0.11.
|
|
31
|
-
"@biomejs/biome": "^2.3.
|
|
30
|
+
"@alepha/testing": "0.11.6",
|
|
31
|
+
"@biomejs/biome": "^2.3.4",
|
|
32
32
|
"@types/react": "^19.2.2",
|
|
33
33
|
"@types/react-dom": "^19.2.2",
|
|
34
34
|
"react": "^19.2.0",
|
|
35
35
|
"react-dom": "^19.2.0",
|
|
36
|
-
"tsdown": "^0.16.
|
|
36
|
+
"tsdown": "^0.16.1",
|
|
37
37
|
"typescript": "^5.9.3",
|
|
38
|
-
"vitest": "^4.0.
|
|
38
|
+
"vitest": "^4.0.8"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": "*",
|
package/src/hooks/useAction.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { ReactRouterState } from "../providers/ReactPageProvider.ts";
|
|
|
3
3
|
import { useStore } from "./useStore.ts";
|
|
4
4
|
|
|
5
5
|
export const useRouterState = (): ReactRouterState => {
|
|
6
|
-
const [state] = useStore("react.router.state");
|
|
6
|
+
const [state] = useStore("alepha.react.router.state");
|
|
7
7
|
if (!state) {
|
|
8
8
|
throw new AlephaError("Missing react router state");
|
|
9
9
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
+
$atom,
|
|
2
3
|
$env,
|
|
3
4
|
$hook,
|
|
4
5
|
$inject,
|
|
6
|
+
$use,
|
|
5
7
|
Alepha,
|
|
6
8
|
type State,
|
|
7
9
|
type Static,
|
|
@@ -17,6 +19,8 @@ import type {
|
|
|
17
19
|
TransitionOptions,
|
|
18
20
|
} from "./ReactPageProvider.ts";
|
|
19
21
|
|
|
22
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
23
|
+
|
|
20
24
|
const envSchema = t.object({
|
|
21
25
|
REACT_ROOT_ID: t.text({ default: "root" }),
|
|
22
26
|
});
|
|
@@ -25,10 +29,31 @@ declare module "@alepha/core" {
|
|
|
25
29
|
interface Env extends Partial<Static<typeof envSchema>> {}
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
/**
|
|
33
|
+
* React browser renderer configuration atom
|
|
34
|
+
*/
|
|
35
|
+
export const reactBrowserOptions = $atom({
|
|
36
|
+
name: "alepha.react.browser.options",
|
|
37
|
+
schema: t.object({
|
|
38
|
+
scrollRestoration: t.enum(["top", "manual"]),
|
|
39
|
+
}),
|
|
40
|
+
default: {
|
|
41
|
+
scrollRestoration: "top" as const,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export type ReactBrowserRendererOptions = Static<
|
|
46
|
+
typeof reactBrowserOptions.schema
|
|
47
|
+
>;
|
|
48
|
+
|
|
49
|
+
declare module "@alepha/core" {
|
|
50
|
+
interface State {
|
|
51
|
+
[reactBrowserOptions.key]: ReactBrowserRendererOptions;
|
|
52
|
+
}
|
|
30
53
|
}
|
|
31
54
|
|
|
55
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
56
|
+
|
|
32
57
|
export class ReactBrowserProvider {
|
|
33
58
|
protected readonly env = $env(envSchema);
|
|
34
59
|
protected readonly log = $logger();
|
|
@@ -37,9 +62,7 @@ export class ReactBrowserProvider {
|
|
|
37
62
|
protected readonly router = $inject(ReactBrowserRouterProvider);
|
|
38
63
|
protected readonly dateTimeProvider = $inject(DateTimeProvider);
|
|
39
64
|
|
|
40
|
-
|
|
41
|
-
scrollRestoration: "top",
|
|
42
|
-
};
|
|
65
|
+
protected readonly options = $use(reactBrowserOptions);
|
|
43
66
|
|
|
44
67
|
protected getRootElement() {
|
|
45
68
|
const root = this.document.getElementById(this.env.REACT_ROOT_ID);
|
|
@@ -61,7 +84,7 @@ export class ReactBrowserProvider {
|
|
|
61
84
|
};
|
|
62
85
|
|
|
63
86
|
public get state(): ReactRouterState {
|
|
64
|
-
return this.alepha.state.get("react.router.state")!;
|
|
87
|
+
return this.alepha.state.get("alepha.react.router.state")!;
|
|
65
88
|
}
|
|
66
89
|
|
|
67
90
|
/**
|
|
@@ -63,7 +63,7 @@ export class ReactBrowserRouterProvider extends RouterProvider<BrowserRoute> {
|
|
|
63
63
|
type: "transition",
|
|
64
64
|
});
|
|
65
65
|
await this.alepha.events.emit("react:transition:begin", {
|
|
66
|
-
previous: this.alepha.state.get("react.router.state")!,
|
|
66
|
+
previous: this.alepha.state.get("alepha.react.router.state")!,
|
|
67
67
|
state,
|
|
68
68
|
});
|
|
69
69
|
|
|
@@ -135,7 +135,7 @@ export class ReactBrowserRouterProvider extends RouterProvider<BrowserRoute> {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
this.alepha.state.set("react.router.state", state);
|
|
138
|
+
this.alepha.state.set("alepha.react.router.state", state);
|
|
139
139
|
|
|
140
140
|
await this.alepha.events.emit("react:action:end", {
|
|
141
141
|
type: "transition",
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import {
|
|
4
|
+
$atom,
|
|
4
5
|
$env,
|
|
5
6
|
$hook,
|
|
6
7
|
$inject,
|
|
8
|
+
$use,
|
|
7
9
|
Alepha,
|
|
8
10
|
AlephaError,
|
|
9
|
-
type Configurable,
|
|
10
11
|
type Static,
|
|
11
12
|
t,
|
|
12
13
|
} from "@alepha/core";
|
|
@@ -18,10 +19,7 @@ import {
|
|
|
18
19
|
ServerTimingProvider,
|
|
19
20
|
} from "@alepha/server";
|
|
20
21
|
import { ServerLinksProvider } from "@alepha/server-links";
|
|
21
|
-
import {
|
|
22
|
-
type ServeDescriptorOptions,
|
|
23
|
-
ServerStaticProvider,
|
|
24
|
-
} from "@alepha/server-static";
|
|
22
|
+
import { ServerStaticProvider } from "@alepha/server-static";
|
|
25
23
|
import { renderToString } from "react-dom/server";
|
|
26
24
|
import {
|
|
27
25
|
$page,
|
|
@@ -36,9 +34,9 @@ import {
|
|
|
36
34
|
type ReactRouterState,
|
|
37
35
|
} from "./ReactPageProvider.ts";
|
|
38
36
|
|
|
37
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
38
|
+
|
|
39
39
|
const envSchema = t.object({
|
|
40
|
-
REACT_SERVER_DIST: t.text({ default: "public" }),
|
|
41
|
-
REACT_SERVER_PREFIX: t.text({ default: "" }),
|
|
42
40
|
REACT_SSR_ENABLED: t.optional(t.boolean()),
|
|
43
41
|
REACT_ROOT_ID: t.text({ default: "root" }), // TODO: move to ReactPageProvider.options?
|
|
44
42
|
REACT_SERVER_TEMPLATE: t.optional(
|
|
@@ -51,19 +49,46 @@ const envSchema = t.object({
|
|
|
51
49
|
declare module "@alepha/core" {
|
|
52
50
|
interface Env extends Partial<Static<typeof envSchema>> {}
|
|
53
51
|
interface State {
|
|
54
|
-
"react.server.ssr"?: boolean;
|
|
52
|
+
"alepha.react.server.ssr"?: boolean;
|
|
55
53
|
}
|
|
56
54
|
}
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
/**
|
|
57
|
+
* React server provider configuration atom
|
|
58
|
+
*/
|
|
59
|
+
export const reactServerOptions = $atom({
|
|
60
|
+
name: "alepha.react.server.options",
|
|
61
|
+
schema: t.object({
|
|
62
|
+
publicDir: t.string(),
|
|
63
|
+
staticServer: t.object({
|
|
64
|
+
disabled: t.boolean(),
|
|
65
|
+
path: t.string({
|
|
66
|
+
description: "URL path where static files will be served.",
|
|
67
|
+
}),
|
|
68
|
+
}),
|
|
69
|
+
}),
|
|
70
|
+
default: {
|
|
71
|
+
publicDir: "public",
|
|
72
|
+
staticServer: {
|
|
73
|
+
disabled: false,
|
|
74
|
+
path: "/",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export type ReactServerProviderOptions = Static<
|
|
80
|
+
typeof reactServerOptions.schema
|
|
81
|
+
>;
|
|
82
|
+
|
|
83
|
+
declare module "@alepha/core" {
|
|
84
|
+
interface State {
|
|
85
|
+
[reactServerOptions.key]: ReactServerProviderOptions;
|
|
86
|
+
}
|
|
64
87
|
}
|
|
65
88
|
|
|
66
|
-
|
|
89
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
export class ReactServerProvider {
|
|
67
92
|
protected readonly log = $logger();
|
|
68
93
|
protected readonly alepha = $inject(Alepha);
|
|
69
94
|
protected readonly env = $env(envSchema);
|
|
@@ -79,7 +104,7 @@ export class ReactServerProvider implements Configurable {
|
|
|
79
104
|
);
|
|
80
105
|
protected preprocessedTemplate: PreprocessedTemplate | null = null;
|
|
81
106
|
|
|
82
|
-
|
|
107
|
+
protected readonly options = $use(reactServerOptions);
|
|
83
108
|
|
|
84
109
|
/**
|
|
85
110
|
* Configure the React server provider.
|
|
@@ -92,7 +117,7 @@ export class ReactServerProvider implements Configurable {
|
|
|
92
117
|
const ssrEnabled =
|
|
93
118
|
pages.length > 0 && this.env.REACT_SSR_ENABLED !== false;
|
|
94
119
|
|
|
95
|
-
this.alepha.state.set("react.server.ssr", ssrEnabled);
|
|
120
|
+
this.alepha.state.set("alepha.react.server.ssr", ssrEnabled);
|
|
96
121
|
|
|
97
122
|
// development mode
|
|
98
123
|
if (this.alepha.isViteDev()) {
|
|
@@ -180,8 +205,8 @@ export class ReactServerProvider implements Configurable {
|
|
|
180
205
|
*/
|
|
181
206
|
protected getPublicDirectory(): string {
|
|
182
207
|
const maybe = [
|
|
183
|
-
join(process.cwd(), `dist/${this.
|
|
184
|
-
join(process.cwd(), this.
|
|
208
|
+
join(process.cwd(), `dist/${this.options.publicDir}`),
|
|
209
|
+
join(process.cwd(), this.options.publicDir),
|
|
185
210
|
];
|
|
186
211
|
|
|
187
212
|
for (const it of maybe) {
|
|
@@ -199,12 +224,11 @@ export class ReactServerProvider implements Configurable {
|
|
|
199
224
|
protected async configureStaticServer(root: string) {
|
|
200
225
|
await this.serverStaticProvider.createStaticServer({
|
|
201
226
|
root,
|
|
202
|
-
path: this.env.REACT_SERVER_PREFIX,
|
|
203
227
|
cacheControl: {
|
|
204
228
|
maxAge: 3600,
|
|
205
229
|
immutable: true,
|
|
206
230
|
},
|
|
207
|
-
...this.options.
|
|
231
|
+
...this.options.staticServer,
|
|
208
232
|
});
|
|
209
233
|
}
|
|
210
234
|
|
|
@@ -265,7 +289,7 @@ export class ReactServerProvider implements Configurable {
|
|
|
265
289
|
}
|
|
266
290
|
|
|
267
291
|
if (!options.html) {
|
|
268
|
-
this.alepha.state.set("react.router.state", state);
|
|
292
|
+
this.alepha.state.set("alepha.react.router.state", state);
|
|
269
293
|
|
|
270
294
|
return {
|
|
271
295
|
state,
|
|
@@ -317,7 +341,7 @@ export class ReactServerProvider implements Configurable {
|
|
|
317
341
|
|
|
318
342
|
if (this.alepha.has(ServerLinksProvider)) {
|
|
319
343
|
this.alepha.state.set(
|
|
320
|
-
"
|
|
344
|
+
"alepha.server.request.apiLinks",
|
|
321
345
|
await this.alepha.inject(ServerLinksProvider).getUserApiLinks({
|
|
322
346
|
user: (serverRequest as any).user, // TODO: fix type
|
|
323
347
|
authorization: serverRequest.headers.authorization,
|
|
@@ -407,7 +431,7 @@ export class ReactServerProvider implements Configurable {
|
|
|
407
431
|
const element = this.pageApi.root(state);
|
|
408
432
|
|
|
409
433
|
// attach react router state to the http request context
|
|
410
|
-
this.alepha.state.set("react.router.state", state);
|
|
434
|
+
this.alepha.state.set("alepha.react.router.state", state);
|
|
411
435
|
|
|
412
436
|
this.serverTimingProvider.beginTiming("renderToString");
|
|
413
437
|
let app = "";
|
|
@@ -440,7 +464,7 @@ export class ReactServerProvider implements Configurable {
|
|
|
440
464
|
const hydrationData: ReactHydrationState = {
|
|
441
465
|
...store,
|
|
442
466
|
// map react.router.state to the hydration state
|
|
443
|
-
"react.router.state": undefined,
|
|
467
|
+
"alepha.react.router.state": undefined,
|
|
444
468
|
layers: state.layers.map((it) => ({
|
|
445
469
|
...it,
|
|
446
470
|
error: it.error
|
|
@@ -15,7 +15,7 @@ export class ReactRouter<T extends object> {
|
|
|
15
15
|
protected readonly pageApi = $inject(ReactPageProvider);
|
|
16
16
|
|
|
17
17
|
public get state(): ReactRouterState {
|
|
18
|
-
return this.alepha.state.get("react.router.state")!;
|
|
18
|
+
return this.alepha.state.get("alepha.react.router.state")!;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
public get pages() {
|