@alepha/react 0.9.4 → 0.9.5
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 +18 -6
- package/dist/index.browser.js +196 -77
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cjs +203 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +240 -195
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +219 -174
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +204 -81
- package/dist/index.js.map +1 -1
- package/package.json +14 -12
- package/src/components/Link.tsx +2 -5
- package/src/components/NestedView.tsx +159 -16
- package/src/descriptors/$page.ts +84 -1
- package/src/hooks/useActive.ts +0 -1
- package/src/hooks/useRouterEvents.ts +27 -19
- package/src/index.browser.ts +3 -0
- package/src/index.ts +6 -1
- package/src/providers/ReactBrowserProvider.ts +19 -14
- package/src/providers/ReactBrowserRendererProvider.ts +22 -0
- package/src/providers/ReactBrowserRouterProvider.ts +8 -3
- package/src/providers/ReactPageProvider.ts +46 -1
- package/src/providers/ReactServerProvider.ts +22 -3
- package/src/services/ReactRouter.ts +5 -8
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
$env,
|
|
3
|
+
$hook,
|
|
4
|
+
$inject,
|
|
5
|
+
Alepha,
|
|
6
|
+
type Static,
|
|
7
|
+
type TSchema,
|
|
8
|
+
TypeGuard,
|
|
9
|
+
t,
|
|
10
|
+
} from "@alepha/core";
|
|
2
11
|
import { $logger } from "@alepha/logger";
|
|
3
12
|
import { createElement, type ReactNode, StrictMode } from "react";
|
|
4
13
|
import ClientOnly from "../components/ClientOnly.tsx";
|
|
@@ -99,6 +108,30 @@ export class ReactPageProvider {
|
|
|
99
108
|
return root;
|
|
100
109
|
}
|
|
101
110
|
|
|
111
|
+
protected convertStringObjectToObject = (
|
|
112
|
+
schema?: TSchema,
|
|
113
|
+
value?: any,
|
|
114
|
+
): any => {
|
|
115
|
+
if (TypeGuard.IsObject(schema) && typeof value === "object") {
|
|
116
|
+
for (const key in schema.properties) {
|
|
117
|
+
if (
|
|
118
|
+
TypeGuard.IsObject(schema.properties[key]) &&
|
|
119
|
+
typeof value[key] === "string"
|
|
120
|
+
) {
|
|
121
|
+
try {
|
|
122
|
+
value[key] = this.alepha.parse(
|
|
123
|
+
schema.properties[key],
|
|
124
|
+
decodeURIComponent(value[key]),
|
|
125
|
+
);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
// ignore
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return value;
|
|
133
|
+
};
|
|
134
|
+
|
|
102
135
|
/**
|
|
103
136
|
* Create a new RouterState based on a given route and request.
|
|
104
137
|
* This method resolves the layers for the route, applying any query and params schemas defined in the route.
|
|
@@ -126,6 +159,7 @@ export class ReactPageProvider {
|
|
|
126
159
|
const config: Record<string, any> = {};
|
|
127
160
|
|
|
128
161
|
try {
|
|
162
|
+
this.convertStringObjectToObject(route.schema?.query, state.query);
|
|
129
163
|
config.query = route.schema?.query
|
|
130
164
|
? this.alepha.parse(route.schema.query, state.query)
|
|
131
165
|
: {};
|
|
@@ -331,6 +365,12 @@ export class ReactPageProvider {
|
|
|
331
365
|
page: PageRoute,
|
|
332
366
|
props: Record<string, any>,
|
|
333
367
|
): Promise<ReactNode> {
|
|
368
|
+
if (page.lazy && page.component) {
|
|
369
|
+
this.log.warn(
|
|
370
|
+
`Page ${page.name} has both lazy and component options, lazy will be used`,
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
334
374
|
if (page.lazy) {
|
|
335
375
|
const component = await page.lazy(); // load component
|
|
336
376
|
return createElement(component.default, props);
|
|
@@ -605,6 +645,11 @@ export interface ReactRouterState {
|
|
|
605
645
|
* Query parameters extracted from the URL for the current page.
|
|
606
646
|
*/
|
|
607
647
|
query: Record<string, string>;
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Optional meta information associated with the current page.
|
|
651
|
+
*/
|
|
652
|
+
meta: Record<string, any>;
|
|
608
653
|
}
|
|
609
654
|
|
|
610
655
|
export interface RouterStackItem {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { $logger } from "@alepha/logger";
|
|
13
13
|
import {
|
|
14
14
|
type ServerHandler,
|
|
15
|
+
ServerProvider,
|
|
15
16
|
ServerRouterProvider,
|
|
16
17
|
ServerTimingProvider,
|
|
17
18
|
} from "@alepha/server";
|
|
@@ -21,6 +22,7 @@ import { renderToString } from "react-dom/server";
|
|
|
21
22
|
import {
|
|
22
23
|
$page,
|
|
23
24
|
type PageDescriptorRenderOptions,
|
|
25
|
+
type PageDescriptorRenderResult,
|
|
24
26
|
} from "../descriptors/$page.ts";
|
|
25
27
|
import { Redirection } from "../errors/Redirection.ts";
|
|
26
28
|
import type { ReactHydrationState } from "./ReactBrowserProvider.ts";
|
|
@@ -53,6 +55,7 @@ export class ReactServerProvider {
|
|
|
53
55
|
protected readonly log = $logger();
|
|
54
56
|
protected readonly alepha = $inject(Alepha);
|
|
55
57
|
protected readonly pageApi = $inject(ReactPageProvider);
|
|
58
|
+
protected readonly serverProvider = $inject(ServerProvider);
|
|
56
59
|
protected readonly serverStaticProvider = $inject(ServerStaticProvider);
|
|
57
60
|
protected readonly serverRouterProvider = $inject(ServerRouterProvider);
|
|
58
61
|
protected readonly serverTimingProvider = $inject(ServerTimingProvider);
|
|
@@ -74,6 +77,19 @@ export class ReactServerProvider {
|
|
|
74
77
|
|
|
75
78
|
for (const page of pages) {
|
|
76
79
|
page.render = this.createRenderFunction(page.name);
|
|
80
|
+
page.fetch = async (options) => {
|
|
81
|
+
const response = await fetch(
|
|
82
|
+
`${this.serverProvider.hostname}/${page.pathname(options)}`,
|
|
83
|
+
);
|
|
84
|
+
const html = await response.text();
|
|
85
|
+
if (options?.html) return { html, response };
|
|
86
|
+
// take only text inside the root div
|
|
87
|
+
const match = html.match(this.ROOT_DIV_REGEX);
|
|
88
|
+
if (match) {
|
|
89
|
+
return { html: match[3], response };
|
|
90
|
+
}
|
|
91
|
+
throw new AlephaError("Invalid HTML response");
|
|
92
|
+
};
|
|
77
93
|
}
|
|
78
94
|
|
|
79
95
|
// development mode
|
|
@@ -194,7 +210,9 @@ export class ReactServerProvider {
|
|
|
194
210
|
* For testing purposes, creates a render function that can be used.
|
|
195
211
|
*/
|
|
196
212
|
protected createRenderFunction(name: string, withIndex = false) {
|
|
197
|
-
return async (
|
|
213
|
+
return async (
|
|
214
|
+
options: PageDescriptorRenderOptions = {},
|
|
215
|
+
): Promise<PageDescriptorRenderResult> => {
|
|
198
216
|
const page = this.pageApi.page(name);
|
|
199
217
|
const url = new URL(this.pageApi.url(name, options));
|
|
200
218
|
|
|
@@ -204,6 +222,7 @@ export class ReactServerProvider {
|
|
|
204
222
|
query: options.query ?? {},
|
|
205
223
|
onError: () => null,
|
|
206
224
|
layers: [],
|
|
225
|
+
meta: {},
|
|
207
226
|
};
|
|
208
227
|
|
|
209
228
|
const state = entry as ReactRouterState;
|
|
@@ -222,7 +241,7 @@ export class ReactServerProvider {
|
|
|
222
241
|
);
|
|
223
242
|
|
|
224
243
|
if (redirect) {
|
|
225
|
-
|
|
244
|
+
return { state, html: "", redirect };
|
|
226
245
|
}
|
|
227
246
|
|
|
228
247
|
if (!withIndex && !options.html) {
|
|
@@ -241,7 +260,7 @@ export class ReactServerProvider {
|
|
|
241
260
|
);
|
|
242
261
|
|
|
243
262
|
if (html instanceof Redirection) {
|
|
244
|
-
|
|
263
|
+
return { state, html: "", redirect };
|
|
245
264
|
}
|
|
246
265
|
|
|
247
266
|
const result = {
|
|
@@ -33,8 +33,8 @@ export class ReactRouter<T extends object> {
|
|
|
33
33
|
public path(
|
|
34
34
|
name: keyof VirtualRouter<T>,
|
|
35
35
|
config: {
|
|
36
|
-
params?: Record<string,
|
|
37
|
-
query?: Record<string,
|
|
36
|
+
params?: Record<string, any>;
|
|
37
|
+
query?: Record<string, any>;
|
|
38
38
|
} = {},
|
|
39
39
|
): string {
|
|
40
40
|
return this.pageApi.pathname(name as string, {
|
|
@@ -116,17 +116,14 @@ export class ReactRouter<T extends object> {
|
|
|
116
116
|
await this.browser?.go(path as string, options);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
public anchor(
|
|
120
|
-
path: string,
|
|
121
|
-
options?: { params?: Record<string, any> },
|
|
122
|
-
): AnchorProps;
|
|
119
|
+
public anchor(path: string, options?: RouterGoOptions): AnchorProps;
|
|
123
120
|
public anchor(
|
|
124
121
|
path: keyof VirtualRouter<T>,
|
|
125
|
-
options?:
|
|
122
|
+
options?: RouterGoOptions,
|
|
126
123
|
): AnchorProps;
|
|
127
124
|
public anchor(
|
|
128
125
|
path: string | keyof VirtualRouter<T>,
|
|
129
|
-
options:
|
|
126
|
+
options: RouterGoOptions = {},
|
|
130
127
|
): AnchorProps {
|
|
131
128
|
let href = path as string;
|
|
132
129
|
|