@alepha/react 0.8.0 → 0.8.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/README.md +153 -1
- package/dist/index.browser.js +47 -14
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cjs +162 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +163 -29
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +157 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +167 -19
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
- package/src/components/NestedView.tsx +3 -1
- package/src/descriptors/$page.ts +40 -40
- package/src/hooks/RouterHookApi.ts +17 -0
- package/src/hooks/useRouter.ts +1 -0
- package/src/index.shared.ts +1 -0
- package/src/index.ts +125 -3
- package/src/providers/PageDescriptorProvider.ts +18 -13
- package/src/providers/ReactBrowserProvider.ts +37 -4
- package/src/providers/ReactBrowserRenderer.ts +20 -0
- package/src/providers/ReactServerProvider.ts +4 -4
package/src/descriptors/$page.ts
CHANGED
|
@@ -15,6 +15,46 @@ import type { PageReactContext } from "../providers/PageDescriptorProvider.ts";
|
|
|
15
15
|
|
|
16
16
|
const KEY = "PAGE";
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Main descriptor for defining a React route in the application.
|
|
20
|
+
*/
|
|
21
|
+
export const $page = <
|
|
22
|
+
TConfig extends PageConfigSchema = PageConfigSchema,
|
|
23
|
+
TProps extends object = TPropsDefault,
|
|
24
|
+
TPropsParent extends object = TPropsParentDefault,
|
|
25
|
+
>(
|
|
26
|
+
options: PageDescriptorOptions<TConfig, TProps, TPropsParent>,
|
|
27
|
+
): PageDescriptor<TConfig, TProps, TPropsParent> => {
|
|
28
|
+
__descriptor(KEY);
|
|
29
|
+
|
|
30
|
+
// if (options.children) {
|
|
31
|
+
// for (const child of options.children) {
|
|
32
|
+
// child[OPTIONS].parent = {
|
|
33
|
+
// [OPTIONS]: options as PageDescriptorOptions<any, any, any>,
|
|
34
|
+
// };
|
|
35
|
+
// }
|
|
36
|
+
// }
|
|
37
|
+
|
|
38
|
+
// if (options.parent) {
|
|
39
|
+
// options.parent[OPTIONS].children ??= [];
|
|
40
|
+
// options.parent[OPTIONS].children.push({
|
|
41
|
+
// [OPTIONS]: options as PageDescriptorOptions<any, any, any>,
|
|
42
|
+
// });
|
|
43
|
+
// }
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
[KIND]: KEY,
|
|
47
|
+
[OPTIONS]: options,
|
|
48
|
+
render: () => {
|
|
49
|
+
throw new NotImplementedError(KEY);
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
$page[KIND] = KEY;
|
|
55
|
+
|
|
56
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
57
|
+
|
|
18
58
|
export interface PageConfigSchema {
|
|
19
59
|
query?: TSchema;
|
|
20
60
|
params?: TSchema;
|
|
@@ -145,46 +185,6 @@ export interface PageDescriptor<
|
|
|
145
185
|
) => Promise<PageDescriptorRenderResult>;
|
|
146
186
|
}
|
|
147
187
|
|
|
148
|
-
/**
|
|
149
|
-
* Main descriptor for defining a React route in the application.
|
|
150
|
-
*/
|
|
151
|
-
export const $page = <
|
|
152
|
-
TConfig extends PageConfigSchema = PageConfigSchema,
|
|
153
|
-
TProps extends object = TPropsDefault,
|
|
154
|
-
TPropsParent extends object = TPropsParentDefault,
|
|
155
|
-
>(
|
|
156
|
-
options: PageDescriptorOptions<TConfig, TProps, TPropsParent>,
|
|
157
|
-
): PageDescriptor<TConfig, TProps, TPropsParent> => {
|
|
158
|
-
__descriptor(KEY);
|
|
159
|
-
|
|
160
|
-
// if (options.children) {
|
|
161
|
-
// for (const child of options.children) {
|
|
162
|
-
// child[OPTIONS].parent = {
|
|
163
|
-
// [OPTIONS]: options as PageDescriptorOptions<any, any, any>,
|
|
164
|
-
// };
|
|
165
|
-
// }
|
|
166
|
-
// }
|
|
167
|
-
|
|
168
|
-
// if (options.parent) {
|
|
169
|
-
// options.parent[OPTIONS].children ??= [];
|
|
170
|
-
// options.parent[OPTIONS].children.push({
|
|
171
|
-
// [OPTIONS]: options as PageDescriptorOptions<any, any, any>,
|
|
172
|
-
// });
|
|
173
|
-
// }
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
[KIND]: KEY,
|
|
177
|
-
[OPTIONS]: options,
|
|
178
|
-
render: () => {
|
|
179
|
-
throw new NotImplementedError(KEY);
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
$page[KIND] = KEY;
|
|
185
|
-
|
|
186
|
-
// ---------------------------------------------------------------------------------------------------------------------
|
|
187
|
-
|
|
188
188
|
export interface PageDescriptorRenderOptions {
|
|
189
189
|
params?: Record<string, string>;
|
|
190
190
|
query?: Record<string, string>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PageDescriptor } from "../descriptors/$page.ts";
|
|
2
2
|
import type {
|
|
3
3
|
AnchorProps,
|
|
4
|
+
PageReactContext,
|
|
4
5
|
PageRoute,
|
|
5
6
|
RouterState,
|
|
6
7
|
} from "../providers/PageDescriptorProvider.ts";
|
|
@@ -12,6 +13,7 @@ import type {
|
|
|
12
13
|
export class RouterHookApi {
|
|
13
14
|
constructor(
|
|
14
15
|
private readonly pages: PageRoute[],
|
|
16
|
+
private readonly context: PageReactContext,
|
|
15
17
|
private readonly state: RouterState,
|
|
16
18
|
private readonly layer: {
|
|
17
19
|
path: string;
|
|
@@ -19,6 +21,21 @@ export class RouterHookApi {
|
|
|
19
21
|
private readonly browser?: ReactBrowserProvider,
|
|
20
22
|
) {}
|
|
21
23
|
|
|
24
|
+
public getURL(): URL {
|
|
25
|
+
if (!this.browser) {
|
|
26
|
+
return this.context.url;
|
|
27
|
+
}
|
|
28
|
+
return new URL(this.location.href);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public get location(): Location {
|
|
32
|
+
if (!this.browser) {
|
|
33
|
+
throw new Error("Browser is required");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return this.browser.location;
|
|
37
|
+
}
|
|
38
|
+
|
|
22
39
|
public get current(): RouterState {
|
|
23
40
|
return this.state;
|
|
24
41
|
}
|
package/src/hooks/useRouter.ts
CHANGED
package/src/index.shared.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { default as ErrorBoundary } from "./components/ErrorBoundary.tsx";
|
|
|
3
3
|
export * from "./components/ErrorViewer.tsx";
|
|
4
4
|
export { default as Link } from "./components/Link.tsx";
|
|
5
5
|
export { default as NestedView } from "./components/NestedView.tsx";
|
|
6
|
+
export { default as NotFound } from "./components/NotFound.tsx";
|
|
6
7
|
export * from "./contexts/RouterContext.ts";
|
|
7
8
|
export * from "./contexts/RouterLayerContext.ts";
|
|
8
9
|
export * from "./descriptors/$page.ts";
|
package/src/index.ts
CHANGED
|
@@ -66,10 +66,132 @@ declare module "@alepha/core" {
|
|
|
66
66
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
69
|
+
* Provides full-stack React development with declarative routing, server-side rendering, and client-side hydration.
|
|
70
70
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
71
|
+
* The React module enables building modern React applications using the `$page` descriptor on class properties.
|
|
72
|
+
* It delivers seamless server-side rendering, automatic code splitting, and client-side navigation with full
|
|
73
|
+
* type safety and schema validation for route parameters and data.
|
|
74
|
+
*
|
|
75
|
+
* **Key Features:**
|
|
76
|
+
* - Declarative page definition with `$page` descriptor
|
|
77
|
+
* - Server-side rendering (SSR) with automatic hydration
|
|
78
|
+
* - Type-safe routing with parameter validation
|
|
79
|
+
* - Schema-based data resolution and validation
|
|
80
|
+
* - SEO-friendly meta tag management
|
|
81
|
+
* - Automatic code splitting and lazy loading
|
|
82
|
+
* - Client-side navigation with browser history
|
|
83
|
+
*
|
|
84
|
+
* **Basic Usage:**
|
|
85
|
+
* ```ts
|
|
86
|
+
* import { Alepha, run, t } from "alepha";
|
|
87
|
+
* import { AlephaReact, $page } from "alepha/react";
|
|
88
|
+
*
|
|
89
|
+
* class AppRoutes {
|
|
90
|
+
* // Home page
|
|
91
|
+
* home = $page({
|
|
92
|
+
* path: "/",
|
|
93
|
+
* component: () => (
|
|
94
|
+
* <div>
|
|
95
|
+
* <h1>Welcome to Alepha</h1>
|
|
96
|
+
* <p>Build amazing React applications!</p>
|
|
97
|
+
* </div>
|
|
98
|
+
* ),
|
|
99
|
+
* });
|
|
100
|
+
*
|
|
101
|
+
* // About page with meta tags
|
|
102
|
+
* about = $page({
|
|
103
|
+
* path: "/about",
|
|
104
|
+
* head: {
|
|
105
|
+
* title: "About Us",
|
|
106
|
+
* description: "Learn more about our mission",
|
|
107
|
+
* },
|
|
108
|
+
* component: () => (
|
|
109
|
+
* <div>
|
|
110
|
+
* <h1>About Us</h1>
|
|
111
|
+
* <p>Learn more about our mission.</p>
|
|
112
|
+
* </div>
|
|
113
|
+
* ),
|
|
114
|
+
* });
|
|
115
|
+
* }
|
|
116
|
+
*
|
|
117
|
+
* const alepha = Alepha.create()
|
|
118
|
+
* .with(AlephaReact)
|
|
119
|
+
* .with(AppRoutes);
|
|
120
|
+
*
|
|
121
|
+
* run(alepha);
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* **Dynamic Routes with Parameters:**
|
|
125
|
+
* ```tsx
|
|
126
|
+
* class UserRoutes {
|
|
127
|
+
* userProfile = $page({
|
|
128
|
+
* path: "/users/:id",
|
|
129
|
+
* schema: {
|
|
130
|
+
* params: t.object({
|
|
131
|
+
* id: t.string(),
|
|
132
|
+
* }),
|
|
133
|
+
* },
|
|
134
|
+
* resolve: async ({ params }) => {
|
|
135
|
+
* // Fetch user data server-side
|
|
136
|
+
* const user = await getUserById(params.id);
|
|
137
|
+
* return { user };
|
|
138
|
+
* },
|
|
139
|
+
* head: ({ user }) => ({
|
|
140
|
+
* title: `${user.name} - Profile`,
|
|
141
|
+
* description: `View ${user.name}'s profile`,
|
|
142
|
+
* }),
|
|
143
|
+
* component: ({ user }) => (
|
|
144
|
+
* <div>
|
|
145
|
+
* <h1>{user.name}</h1>
|
|
146
|
+
* <p>Email: {user.email}</p>
|
|
147
|
+
* </div>
|
|
148
|
+
* ),
|
|
149
|
+
* });
|
|
150
|
+
*
|
|
151
|
+
* userSettings = $page({
|
|
152
|
+
* path: "/users/:id/settings",
|
|
153
|
+
* schema: {
|
|
154
|
+
* params: t.object({
|
|
155
|
+
* id: t.string(),
|
|
156
|
+
* }),
|
|
157
|
+
* },
|
|
158
|
+
* component: ({ params }) => (
|
|
159
|
+
* <UserSettings userId={params.id} />
|
|
160
|
+
* ),
|
|
161
|
+
* });
|
|
162
|
+
* }
|
|
163
|
+
* ```
|
|
164
|
+
*
|
|
165
|
+
* **Static Generation:**
|
|
166
|
+
* ```tsx
|
|
167
|
+
* class BlogRoutes {
|
|
168
|
+
* blogPost = $page({
|
|
169
|
+
* path: "/blog/:slug",
|
|
170
|
+
* schema: {
|
|
171
|
+
* params: t.object({
|
|
172
|
+
* slug: t.string(),
|
|
173
|
+
* }),
|
|
174
|
+
* },
|
|
175
|
+
* static: {
|
|
176
|
+
* entries: [
|
|
177
|
+
* { params: { slug: "getting-started" } },
|
|
178
|
+
* { params: { slug: "advanced-features" } },
|
|
179
|
+
* { params: { slug: "deployment" } },
|
|
180
|
+
* ],
|
|
181
|
+
* },
|
|
182
|
+
* resolve: ({ params }) => {
|
|
183
|
+
* const post = getBlogPost(params.slug);
|
|
184
|
+
* return { post };
|
|
185
|
+
* },
|
|
186
|
+
* component: ({ post }) => (
|
|
187
|
+
* <article>
|
|
188
|
+
* <h1>{post.title}</h1>
|
|
189
|
+
* <div>{post.content}</div>
|
|
190
|
+
* </article>
|
|
191
|
+
* ),
|
|
192
|
+
* });
|
|
193
|
+
* }
|
|
194
|
+
* ```
|
|
73
195
|
*
|
|
74
196
|
* @see {@link $page}
|
|
75
197
|
* @module alepha.react
|
|
@@ -109,7 +109,7 @@ export class PageDescriptorProvider {
|
|
|
109
109
|
try {
|
|
110
110
|
config.query = route.schema?.query
|
|
111
111
|
? this.alepha.parse(route.schema.query, request.query)
|
|
112
|
-
:
|
|
112
|
+
: {};
|
|
113
113
|
} catch (e) {
|
|
114
114
|
it.error = e as Error;
|
|
115
115
|
break;
|
|
@@ -118,7 +118,7 @@ export class PageDescriptorProvider {
|
|
|
118
118
|
try {
|
|
119
119
|
config.params = route.schema?.params
|
|
120
120
|
? this.alepha.parse(route.schema.params, request.params)
|
|
121
|
-
:
|
|
121
|
+
: {};
|
|
122
122
|
} catch (e) {
|
|
123
123
|
it.error = e as Error;
|
|
124
124
|
break;
|
|
@@ -129,11 +129,6 @@ export class PageDescriptorProvider {
|
|
|
129
129
|
...config,
|
|
130
130
|
};
|
|
131
131
|
|
|
132
|
-
// no resolve, render a basic view by default
|
|
133
|
-
if (!route.resolve) {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
132
|
// check if previous layer is the same, reuse if possible
|
|
138
133
|
const previous = request.previous;
|
|
139
134
|
if (previous?.[i] && !forceRefresh && previous[i].name === route.name) {
|
|
@@ -153,16 +148,23 @@ export class PageDescriptorProvider {
|
|
|
153
148
|
// part is the same, reuse previous layer
|
|
154
149
|
it.props = previous[i].props;
|
|
155
150
|
it.error = previous[i].error;
|
|
151
|
+
it.cache = true;
|
|
156
152
|
context = {
|
|
157
153
|
...context,
|
|
158
154
|
...it.props,
|
|
159
155
|
};
|
|
160
156
|
continue;
|
|
161
157
|
}
|
|
158
|
+
|
|
162
159
|
// part is different, force refresh of next layers
|
|
163
160
|
forceRefresh = true;
|
|
164
161
|
}
|
|
165
162
|
|
|
163
|
+
// no resolve, render a basic view by default
|
|
164
|
+
if (!route.resolve) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
|
|
166
168
|
try {
|
|
167
169
|
const props =
|
|
168
170
|
(await route.resolve?.({
|
|
@@ -233,7 +235,7 @@ export class PageDescriptorProvider {
|
|
|
233
235
|
element: this.renderView(i + 1, path, element, it.route),
|
|
234
236
|
index: i + 1,
|
|
235
237
|
path,
|
|
236
|
-
route,
|
|
238
|
+
route: it.route,
|
|
237
239
|
});
|
|
238
240
|
break;
|
|
239
241
|
}
|
|
@@ -253,7 +255,8 @@ export class PageDescriptorProvider {
|
|
|
253
255
|
element: this.renderView(i + 1, path, element, it.route),
|
|
254
256
|
index: i + 1,
|
|
255
257
|
path,
|
|
256
|
-
route,
|
|
258
|
+
route: it.route,
|
|
259
|
+
cache: it.cache,
|
|
257
260
|
});
|
|
258
261
|
}
|
|
259
262
|
|
|
@@ -373,15 +376,15 @@ export class PageDescriptorProvider {
|
|
|
373
376
|
}
|
|
374
377
|
|
|
375
378
|
for (const { value } of pages) {
|
|
379
|
+
if (value[OPTIONS].path === "/*") {
|
|
380
|
+
hasNotFoundHandler = true;
|
|
381
|
+
}
|
|
382
|
+
|
|
376
383
|
// skip children, we only want root pages
|
|
377
384
|
if (hasParent(value)) {
|
|
378
385
|
continue;
|
|
379
386
|
}
|
|
380
387
|
|
|
381
|
-
if (value[OPTIONS].path === "/*") {
|
|
382
|
-
hasNotFoundHandler = true;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
388
|
this.add(this.map(pages, value));
|
|
386
389
|
}
|
|
387
390
|
|
|
@@ -499,6 +502,7 @@ export interface Layer {
|
|
|
499
502
|
index: number;
|
|
500
503
|
path: string;
|
|
501
504
|
route?: PageRoute;
|
|
505
|
+
cache?: boolean;
|
|
502
506
|
}
|
|
503
507
|
|
|
504
508
|
export type PreviousLayerData = Omit<Layer, "element" | "index" | "path">;
|
|
@@ -525,6 +529,7 @@ export interface RouterStackItem {
|
|
|
525
529
|
config?: Record<string, any>;
|
|
526
530
|
props?: Record<string, any>;
|
|
527
531
|
error?: Error;
|
|
532
|
+
cache?: boolean;
|
|
528
533
|
}
|
|
529
534
|
|
|
530
535
|
export interface RouterRenderResult {
|
|
@@ -35,8 +35,35 @@ export class ReactBrowserProvider {
|
|
|
35
35
|
return window.history;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
public get location() {
|
|
39
|
+
return window.location;
|
|
40
|
+
}
|
|
41
|
+
|
|
38
42
|
public get url(): string {
|
|
39
|
-
|
|
43
|
+
let url = this.location.pathname + this.location.search;
|
|
44
|
+
|
|
45
|
+
if (import.meta?.env?.BASE_URL) {
|
|
46
|
+
url = url.replace(import.meta.env?.BASE_URL, "");
|
|
47
|
+
if (!url.startsWith("/")) {
|
|
48
|
+
url = `/${url}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return url;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public pushState(url: string, replace?: boolean) {
|
|
56
|
+
let path = url;
|
|
57
|
+
|
|
58
|
+
if (import.meta?.env?.BASE_URL) {
|
|
59
|
+
path = (import.meta.env?.BASE_URL + path).replaceAll("//", "/");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (replace) {
|
|
63
|
+
this.history.replaceState({}, "", path);
|
|
64
|
+
} else {
|
|
65
|
+
this.history.pushState({}, "", path);
|
|
66
|
+
}
|
|
40
67
|
}
|
|
41
68
|
|
|
42
69
|
public async invalidate(props?: Record<string, any>) {
|
|
@@ -72,16 +99,16 @@ export class ReactBrowserProvider {
|
|
|
72
99
|
// when redirecting in browser
|
|
73
100
|
if (result.context.url.pathname !== url) {
|
|
74
101
|
// TODO: check if losing search params is acceptable?
|
|
75
|
-
this.
|
|
102
|
+
this.pushState(result.context.url.pathname);
|
|
76
103
|
return;
|
|
77
104
|
}
|
|
78
105
|
|
|
79
106
|
if (options.replace) {
|
|
80
|
-
this.
|
|
107
|
+
this.pushState(url);
|
|
81
108
|
return;
|
|
82
109
|
}
|
|
83
110
|
|
|
84
|
-
this.
|
|
111
|
+
this.pushState(url);
|
|
85
112
|
}
|
|
86
113
|
|
|
87
114
|
protected async render(
|
|
@@ -145,6 +172,12 @@ export class ReactBrowserProvider {
|
|
|
145
172
|
});
|
|
146
173
|
|
|
147
174
|
window.addEventListener("popstate", () => {
|
|
175
|
+
// when you update silently queryparams or hash, skip rendering
|
|
176
|
+
// if you want to force a rendering, use #go()
|
|
177
|
+
if (this.state.pathname === location.pathname) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
148
181
|
this.render();
|
|
149
182
|
});
|
|
150
183
|
},
|
|
@@ -17,6 +17,10 @@ declare module "@alepha/core" {
|
|
|
17
17
|
interface Env extends Partial<Static<typeof envSchema>> {}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export interface ReactBrowserRendererOptions {
|
|
21
|
+
scrollRestoration?: "top" | "manual";
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
// TODO: move to ReactBrowserProvider when it will be removed from server-side imports
|
|
21
25
|
export class ReactBrowserRenderer {
|
|
22
26
|
protected readonly browserProvider = $inject(ReactBrowserProvider);
|
|
@@ -26,6 +30,10 @@ export class ReactBrowserRenderer {
|
|
|
26
30
|
|
|
27
31
|
protected root!: Root;
|
|
28
32
|
|
|
33
|
+
public options: ReactBrowserRendererOptions = {
|
|
34
|
+
scrollRestoration: "top",
|
|
35
|
+
};
|
|
36
|
+
|
|
29
37
|
protected getRootElement() {
|
|
30
38
|
const root = this.browserProvider.document.getElementById(
|
|
31
39
|
this.env.REACT_ROOT_ID,
|
|
@@ -57,6 +65,18 @@ export class ReactBrowserRenderer {
|
|
|
57
65
|
}
|
|
58
66
|
},
|
|
59
67
|
});
|
|
68
|
+
|
|
69
|
+
protected readonly onTransitionEnd = $hook({
|
|
70
|
+
on: "react:transition:end",
|
|
71
|
+
handler: () => {
|
|
72
|
+
if (
|
|
73
|
+
this.options.scrollRestoration === "top" &&
|
|
74
|
+
typeof window !== "undefined"
|
|
75
|
+
) {
|
|
76
|
+
window.scrollTo(0, 0);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
});
|
|
60
80
|
}
|
|
61
81
|
|
|
62
82
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
@@ -41,8 +41,8 @@ const envSchema = t.object({
|
|
|
41
41
|
declare module "@alepha/core" {
|
|
42
42
|
interface Env extends Partial<Static<typeof envSchema>> {}
|
|
43
43
|
interface State {
|
|
44
|
-
"
|
|
45
|
-
"
|
|
44
|
+
"react.server.template"?: string;
|
|
45
|
+
"react.server.ssr"?: boolean;
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -67,7 +67,7 @@ export class ReactServerProvider {
|
|
|
67
67
|
const ssrEnabled =
|
|
68
68
|
pages.length > 0 && this.env.REACT_SSR_ENABLED !== false;
|
|
69
69
|
|
|
70
|
-
this.alepha.state("
|
|
70
|
+
this.alepha.state("react.server.ssr", ssrEnabled);
|
|
71
71
|
|
|
72
72
|
for (const { key, instance, value } of pages) {
|
|
73
73
|
const name = value[OPTIONS].name ?? key;
|
|
@@ -127,7 +127,7 @@ export class ReactServerProvider {
|
|
|
127
127
|
|
|
128
128
|
public get template() {
|
|
129
129
|
return (
|
|
130
|
-
this.alepha.state("
|
|
130
|
+
this.alepha.state("react.server.template") ??
|
|
131
131
|
"<!DOCTYPE html><html lang='en'><head></head><body></body></html>"
|
|
132
132
|
);
|
|
133
133
|
}
|