@alepha/react 0.14.4 → 0.15.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 +10 -0
- package/dist/auth/index.browser.js +603 -242
- package/dist/auth/index.browser.js.map +1 -1
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1317 -952
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.d.ts +17 -17
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +20 -20
- package/dist/core/index.js.map +1 -1
- package/dist/form/index.d.ts +9 -10
- package/dist/form/index.d.ts.map +1 -1
- package/dist/form/index.js +15 -15
- package/dist/form/index.js.map +1 -1
- package/dist/head/index.browser.js +20 -0
- package/dist/head/index.browser.js.map +1 -1
- package/dist/head/index.d.ts +62 -64
- package/dist/head/index.d.ts.map +1 -1
- package/dist/head/index.js +20 -0
- package/dist/head/index.js.map +1 -1
- package/dist/i18n/index.d.ts +9 -9
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js.map +1 -1
- package/dist/router/index.browser.js +605 -244
- package/dist/router/index.browser.js.map +1 -1
- package/dist/router/index.d.ts +100 -111
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +1317 -952
- package/dist/router/index.js.map +1 -1
- package/dist/websocket/index.d.ts +0 -1
- package/dist/websocket/index.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/auth/__tests__/$auth.spec.ts +164 -150
- package/src/auth/index.ts +9 -3
- package/src/auth/services/ReactAuth.ts +15 -5
- package/src/core/hooks/useAction.ts +1 -2
- package/src/core/index.ts +4 -4
- package/src/form/errors/FormValidationError.ts +4 -6
- package/src/form/hooks/useFormState.ts +1 -1
- package/src/form/index.ts +1 -1
- package/src/form/services/FormModel.ts +31 -25
- package/src/head/helpers/SeoExpander.ts +2 -1
- package/src/head/hooks/useHead.spec.tsx +2 -2
- package/src/head/index.browser.ts +2 -2
- package/src/head/index.ts +4 -4
- package/src/head/interfaces/Head.ts +15 -3
- package/src/head/primitives/$head.ts +2 -5
- package/src/head/providers/BrowserHeadProvider.ts +55 -0
- package/src/head/providers/HeadProvider.ts +4 -1
- package/src/i18n/__tests__/integration.spec.tsx +1 -1
- package/src/i18n/components/Localize.spec.tsx +2 -2
- package/src/i18n/hooks/useI18n.browser.spec.tsx +2 -2
- package/src/i18n/index.ts +1 -1
- package/src/i18n/primitives/$dictionary.ts +1 -1
- package/src/i18n/providers/I18nProvider.spec.ts +1 -1
- package/src/i18n/providers/I18nProvider.ts +1 -1
- package/src/router/__tests__/page-head-browser.browser.spec.ts +5 -1
- package/src/router/__tests__/page-head.spec.ts +11 -7
- package/src/router/__tests__/seo-head.spec.ts +7 -3
- package/src/router/atoms/ssrManifestAtom.ts +2 -11
- package/src/router/components/ErrorViewer.tsx +626 -167
- package/src/router/components/Link.tsx +4 -2
- package/src/router/components/NestedView.tsx +7 -9
- package/src/router/components/NotFound.tsx +2 -2
- package/src/router/hooks/useQueryParams.ts +1 -1
- package/src/router/hooks/useRouter.ts +1 -1
- package/src/router/hooks/useRouterState.ts +1 -1
- package/src/router/index.browser.ts +10 -11
- package/src/router/index.shared.ts +7 -7
- package/src/router/index.ts +10 -7
- package/src/router/primitives/$page.browser.spec.tsx +6 -1
- package/src/router/primitives/$page.spec.tsx +7 -1
- package/src/router/primitives/$page.ts +5 -9
- package/src/router/providers/ReactBrowserProvider.ts +17 -6
- package/src/router/providers/ReactBrowserRouterProvider.ts +1 -1
- package/src/router/providers/ReactPageProvider.ts +4 -3
- package/src/router/providers/ReactServerProvider.ts +32 -50
- package/src/router/providers/ReactServerTemplateProvider.ts +336 -155
- package/src/router/providers/SSRManifestProvider.ts +17 -60
- package/src/router/services/ReactPageService.ts +4 -1
- package/src/router/services/ReactRouter.ts +6 -5
|
@@ -14,8 +14,10 @@ const Link = (props: LinkProps) => {
|
|
|
14
14
|
const router = useRouter();
|
|
15
15
|
|
|
16
16
|
return createElement(
|
|
17
|
-
"a",
|
|
18
|
-
|
|
17
|
+
"a",
|
|
18
|
+
{ ...props, ...router.anchor(props.href) },
|
|
19
|
+
props.children,
|
|
20
|
+
);
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
export default Link;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { ErrorBoundary, useAlepha, useEvents } from "@alepha/react";
|
|
1
2
|
import { memo, type ReactNode, use, useRef, useState } from "react";
|
|
2
|
-
import type { ReactRouterState } from "../providers/ReactPageProvider.ts";
|
|
3
3
|
import { RouterLayerContext } from "../contexts/RouterLayerContext.ts";
|
|
4
4
|
import { Redirection } from "../errors/Redirection.ts";
|
|
5
5
|
import { useRouterState } from "../hooks/useRouterState.ts";
|
|
6
6
|
import type { PageAnimation } from "../primitives/$page.ts";
|
|
7
|
+
import type { ReactRouterState } from "../providers/ReactPageProvider.ts";
|
|
7
8
|
import ErrorViewer from "./ErrorViewer.tsx";
|
|
8
|
-
import { ErrorBoundary, useAlepha, useEvents } from "@alepha/react";
|
|
9
9
|
|
|
10
10
|
export interface NestedViewProps {
|
|
11
11
|
children?: ReactNode;
|
|
@@ -152,18 +152,16 @@ const NestedView = (props: NestedViewProps) => {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
const fallback = (error: Error) => {
|
|
155
|
-
const result = onError?.(error, state) ??
|
|
155
|
+
const result = onError?.(error, state) ?? (
|
|
156
|
+
<ErrorViewer error={error} alepha={alepha} />
|
|
157
|
+
);
|
|
156
158
|
if (result instanceof Redirection) {
|
|
157
159
|
return "Redirection inside ErrorBoundary is not allowed.";
|
|
158
160
|
}
|
|
159
161
|
return result as ReactNode;
|
|
160
|
-
}
|
|
162
|
+
};
|
|
161
163
|
|
|
162
|
-
return
|
|
163
|
-
<ErrorBoundary fallback={fallback}>
|
|
164
|
-
{element}
|
|
165
|
-
</ErrorBoundary>
|
|
166
|
-
);
|
|
164
|
+
return <ErrorBoundary fallback={fallback}>{element}</ErrorBoundary>;
|
|
167
165
|
};
|
|
168
166
|
|
|
169
167
|
export default memo(NestedView);
|
|
@@ -3,7 +3,7 @@ import type { CSSProperties } from "react";
|
|
|
3
3
|
/**
|
|
4
4
|
* Default 404 Not Found page component.
|
|
5
5
|
*/
|
|
6
|
-
|
|
6
|
+
const NotFound = (props: { style?: CSSProperties }) => (
|
|
7
7
|
<div
|
|
8
8
|
style={{
|
|
9
9
|
width: "100%",
|
|
@@ -25,6 +25,6 @@ import type { CSSProperties } from "react";
|
|
|
25
25
|
Page not found
|
|
26
26
|
</div>
|
|
27
27
|
</div>
|
|
28
|
-
)
|
|
28
|
+
);
|
|
29
29
|
|
|
30
30
|
export default NotFound;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { useStore } from "@alepha/react";
|
|
1
2
|
import { AlephaError } from "alepha";
|
|
2
3
|
import type { ReactRouterState } from "../providers/ReactPageProvider.ts";
|
|
3
|
-
import { useStore } from "@alepha/react";
|
|
4
4
|
|
|
5
5
|
export const useRouterState = (): ReactRouterState => {
|
|
6
6
|
const [state] = useStore("alepha.react.router.state");
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
+
import { AlephaReact } from "@alepha/react";
|
|
1
2
|
import { $module } from "alepha";
|
|
3
|
+
import { AlephaDateTime } from "alepha/datetime";
|
|
4
|
+
import { AlephaServer } from "alepha/server";
|
|
5
|
+
import { AlephaServerLinks } from "alepha/server/links";
|
|
2
6
|
import { $page } from "./primitives/$page.ts";
|
|
3
|
-
import {
|
|
7
|
+
import { ReactBrowserProvider } from "./providers/ReactBrowserProvider.ts";
|
|
4
8
|
import { ReactBrowserRendererProvider } from "./providers/ReactBrowserRendererProvider.ts";
|
|
5
9
|
import { ReactBrowserRouterProvider } from "./providers/ReactBrowserRouterProvider.ts";
|
|
6
|
-
import { ReactPageService } from "./services/ReactPageService.ts";
|
|
7
10
|
import { ReactPageProvider } from "./providers/ReactPageProvider.ts";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { AlephaServer } from "alepha/server";
|
|
11
|
-
import { AlephaServerLinks } from "alepha/server/links";
|
|
12
|
-
import { AlephaReact } from "@alepha/react";
|
|
11
|
+
import { ReactPageService } from "./services/ReactPageService.ts";
|
|
12
|
+
import { ReactRouter } from "./services/ReactRouter.ts";
|
|
13
13
|
|
|
14
14
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
15
15
|
|
|
16
16
|
export * from "./index.shared.ts";
|
|
17
|
-
export * from "./providers/ReactBrowserProvider.ts"
|
|
18
|
-
export * from "./providers/
|
|
19
|
-
export * from "./providers/
|
|
17
|
+
export * from "./providers/ReactBrowserProvider.ts";
|
|
18
|
+
export * from "./providers/ReactBrowserRendererProvider.ts";
|
|
19
|
+
export * from "./providers/ReactBrowserRouterProvider.ts";
|
|
20
20
|
|
|
21
21
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
22
22
|
|
|
@@ -43,4 +43,3 @@ export const AlephaReactRouter = $module({
|
|
|
43
43
|
.with(ReactBrowserRendererProvider)
|
|
44
44
|
.with(ReactRouter),
|
|
45
45
|
});
|
|
46
|
-
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
export { default as ErrorViewer } from "./components/ErrorViewer.tsx";
|
|
2
1
|
export type * from "./components/ErrorViewer.tsx";
|
|
2
|
+
export { default as ErrorViewer } from "./components/ErrorViewer.tsx";
|
|
3
|
+
export type * from "./components/Link.tsx";
|
|
3
4
|
export { default as Link, type LinkProps } from "./components/Link.tsx";
|
|
4
|
-
export type *
|
|
5
|
+
export type * from "./components/NestedView.tsx";
|
|
5
6
|
export { default as NestedView } from "./components/NestedView.tsx";
|
|
6
|
-
export type * from "./components/NestedView.tsx";
|
|
7
|
-
export { default as NotFound } from "./components/NotFound.tsx";
|
|
8
7
|
export type * from "./components/NotFound.tsx";
|
|
8
|
+
export { default as NotFound } from "./components/NotFound.tsx";
|
|
9
9
|
export * from "./constants/PAGE_PRELOAD_KEY.ts";
|
|
10
10
|
export * from "./contexts/RouterLayerContext.ts";
|
|
11
|
-
export * from "./primitives/$page.ts";
|
|
12
11
|
export * from "./errors/Redirection.ts";
|
|
13
12
|
export * from "./hooks/useActive.ts";
|
|
14
13
|
export * from "./hooks/useQueryParams.ts";
|
|
15
14
|
export * from "./hooks/useRouter.ts";
|
|
16
15
|
export * from "./hooks/useRouterState.ts";
|
|
17
|
-
export * from "./
|
|
18
|
-
export * from "./services/ReactPageService.ts"
|
|
16
|
+
export * from "./primitives/$page.ts";
|
|
19
17
|
export * from "./providers/ReactPageProvider.ts";
|
|
18
|
+
export * from "./services/ReactPageService.ts";
|
|
19
|
+
export * from "./services/ReactRouter.ts";
|
package/src/router/index.ts
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
import { AlephaReact } from "@alepha/react";
|
|
2
2
|
import { $module } from "alepha";
|
|
3
|
-
import {
|
|
4
|
-
import { ReactRouter } from "./services/ReactRouter.ts";
|
|
5
|
-
import { ReactPageProvider, type ReactRouterState } from "./providers/ReactPageProvider.ts";
|
|
3
|
+
import { AlephaDateTime } from "alepha/datetime";
|
|
6
4
|
import { AlephaServer, type ServerRequest } from "alepha/server";
|
|
5
|
+
import { AlephaServerCache } from "alepha/server/cache";
|
|
6
|
+
import { AlephaServerLinks } from "alepha/server/links";
|
|
7
7
|
import type { ReactNode } from "react";
|
|
8
|
+
import { $page, type PageAnimation } from "./primitives/$page.ts";
|
|
8
9
|
import type { ReactHydrationState } from "./providers/ReactBrowserProvider.ts";
|
|
10
|
+
import {
|
|
11
|
+
ReactPageProvider,
|
|
12
|
+
type ReactRouterState,
|
|
13
|
+
} from "./providers/ReactPageProvider.ts";
|
|
9
14
|
import { ReactServerProvider } from "./providers/ReactServerProvider.ts";
|
|
10
15
|
import { ReactServerTemplateProvider } from "./providers/ReactServerTemplateProvider.ts";
|
|
11
16
|
import { SSRManifestProvider } from "./providers/SSRManifestProvider.ts";
|
|
12
17
|
import { ReactPageServerService } from "./services/ReactPageServerService.ts";
|
|
13
|
-
import { AlephaServerCache } from "alepha/server/cache";
|
|
14
|
-
import { AlephaServerLinks } from "alepha/server/links";
|
|
15
18
|
import { ReactPageService } from "./services/ReactPageService.ts";
|
|
16
|
-
import {
|
|
19
|
+
import { ReactRouter } from "./services/ReactRouter.ts";
|
|
17
20
|
|
|
18
21
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
19
22
|
|
|
20
23
|
export * from "./index.shared.ts";
|
|
21
|
-
export * from "./providers/ReactPageProvider.ts";
|
|
22
24
|
export * from "./providers/ReactBrowserProvider.ts";
|
|
25
|
+
export * from "./providers/ReactPageProvider.ts";
|
|
23
26
|
export * from "./providers/ReactServerProvider.ts";
|
|
24
27
|
export * from "./providers/ReactServerTemplateProvider.ts";
|
|
25
28
|
export * from "./providers/SSRManifestProvider.ts";
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { AlephaReact } from "@alepha/react";
|
|
2
|
-
import { $page, NestedView, Redirection, ReactRouter } from "../index.browser.ts";
|
|
3
2
|
import { waitFor } from "@testing-library/dom";
|
|
4
3
|
import { Alepha, t } from "alepha";
|
|
5
4
|
import { act } from "react";
|
|
6
5
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import {
|
|
7
|
+
$page,
|
|
8
|
+
NestedView,
|
|
9
|
+
ReactRouter,
|
|
10
|
+
Redirection,
|
|
11
|
+
} from "../index.browser.ts";
|
|
7
12
|
|
|
8
13
|
describe("$page browser tests", () => {
|
|
9
14
|
let alepha: Alepha;
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import { $page, PagePrimitive, Redirection, NestedView, type ReactRouterState } from "../index.ts";
|
|
2
1
|
import { Alepha, t } from "alepha";
|
|
3
2
|
import type { FC } from "react";
|
|
4
3
|
import { beforeEach, describe, test, vi } from "vitest";
|
|
4
|
+
import {
|
|
5
|
+
$page,
|
|
6
|
+
NestedView,
|
|
7
|
+
PagePrimitive,
|
|
8
|
+
type ReactRouterState,
|
|
9
|
+
Redirection,
|
|
10
|
+
} from "../index.ts";
|
|
5
11
|
|
|
6
12
|
describe("$page primitive tests", () => {
|
|
7
13
|
let alepha: Alepha;
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
+
import type { ClientOnlyProps } from "@alepha/react";
|
|
2
|
+
import type { Head } from "@alepha/react/head";
|
|
1
3
|
import {
|
|
2
4
|
$inject,
|
|
3
5
|
type Async,
|
|
4
6
|
createPrimitive,
|
|
5
|
-
Primitive,
|
|
6
7
|
KIND,
|
|
8
|
+
Primitive,
|
|
7
9
|
type Static,
|
|
8
10
|
type TSchema,
|
|
9
11
|
} from "alepha";
|
|
10
12
|
import type { ServerRequest } from "alepha/server";
|
|
11
13
|
import type { ServerRouteCache } from "alepha/server/cache";
|
|
12
14
|
import type { FC, ReactNode } from "react";
|
|
15
|
+
import { PAGE_PRELOAD_KEY } from "../constants/PAGE_PRELOAD_KEY.ts";
|
|
13
16
|
import type { Redirection } from "../errors/Redirection.ts";
|
|
14
17
|
import type { ReactRouterState } from "../providers/ReactPageProvider.ts";
|
|
15
18
|
import { ReactPageService } from "../services/ReactPageService.ts";
|
|
16
|
-
import type { ClientOnlyProps } from "@alepha/react";
|
|
17
|
-
import type { Head } from "@alepha/react/head";
|
|
18
|
-
import { PAGE_PRELOAD_KEY } from "../constants/PAGE_PRELOAD_KEY.ts";
|
|
19
|
-
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* Main primitive for defining a React route in the application.
|
|
@@ -113,10 +112,7 @@ export const $page = <
|
|
|
113
112
|
>(
|
|
114
113
|
options: PagePrimitiveOptions<TConfig, TProps, TPropsParent>,
|
|
115
114
|
): PagePrimitive<TConfig, TProps, TPropsParent> => {
|
|
116
|
-
return createPrimitive(
|
|
117
|
-
PagePrimitive<TConfig, TProps, TPropsParent>,
|
|
118
|
-
options,
|
|
119
|
-
);
|
|
115
|
+
return createPrimitive(PagePrimitive<TConfig, TProps, TPropsParent>, options);
|
|
120
116
|
};
|
|
121
117
|
|
|
122
118
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
@@ -1,15 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BrowserHeadProvider } from "@alepha/react/head";
|
|
2
|
+
import {
|
|
3
|
+
$atom,
|
|
4
|
+
$hook,
|
|
5
|
+
$inject,
|
|
6
|
+
$use,
|
|
7
|
+
Alepha,
|
|
8
|
+
type State,
|
|
9
|
+
type Static,
|
|
10
|
+
t,
|
|
11
|
+
} from "alepha";
|
|
2
12
|
import { DateTimeProvider } from "alepha/datetime";
|
|
3
13
|
import { $logger } from "alepha/logger";
|
|
4
14
|
import { LinkProvider } from "alepha/server/links";
|
|
5
|
-
import { BrowserHeadProvider } from "@alepha/react/head";
|
|
6
|
-
import { ReactBrowserRouterProvider } from "./ReactBrowserRouterProvider.ts";
|
|
7
|
-
import type { PreviousLayerData, ReactRouterState, } from "./ReactPageProvider.ts";
|
|
8
15
|
import type { RouterGoOptions } from "../services/ReactRouter.ts";
|
|
16
|
+
import { ReactBrowserRouterProvider } from "./ReactBrowserRouterProvider.ts";
|
|
17
|
+
import type {
|
|
18
|
+
PreviousLayerData,
|
|
19
|
+
ReactRouterState,
|
|
20
|
+
} from "./ReactPageProvider.ts";
|
|
9
21
|
|
|
10
22
|
export type { RouterGoOptions } from "../services/ReactRouter.ts";
|
|
11
23
|
|
|
12
|
-
|
|
13
24
|
/**
|
|
14
25
|
* React browser renderer configuration atom
|
|
15
26
|
*/
|
|
@@ -247,7 +258,7 @@ export class ReactBrowserProvider {
|
|
|
247
258
|
// low budget, but works for now
|
|
248
259
|
for (const [key, value] of Object.entries(hydration)) {
|
|
249
260
|
if (key !== "layers") {
|
|
250
|
-
this.alepha.
|
|
261
|
+
this.alepha.set(key as keyof State, value);
|
|
251
262
|
}
|
|
252
263
|
}
|
|
253
264
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { BrowserHeadProvider } from "@alepha/react/head";
|
|
1
2
|
import { $hook, $inject, Alepha } from "alepha";
|
|
2
3
|
import { $logger } from "alepha/logger";
|
|
3
4
|
import { type Route, RouterProvider } from "alepha/router";
|
|
4
5
|
import { createElement, type ReactNode } from "react";
|
|
5
|
-
import { BrowserHeadProvider } from "@alepha/react/head";
|
|
6
6
|
import NotFoundPage from "../components/NotFound.tsx";
|
|
7
7
|
import {
|
|
8
8
|
isPageRoute,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AlephaContext, ClientOnly } from "@alepha/react";
|
|
2
|
+
import type { Head } from "@alepha/react/head";
|
|
1
3
|
import {
|
|
2
4
|
$env,
|
|
3
5
|
$hook,
|
|
@@ -10,7 +12,6 @@ import {
|
|
|
10
12
|
} from "alepha";
|
|
11
13
|
import { $logger } from "alepha/logger";
|
|
12
14
|
import { createElement, type ReactNode, StrictMode } from "react";
|
|
13
|
-
import { AlephaContext, ClientOnly } from "@alepha/react";
|
|
14
15
|
import ErrorViewer from "../components/ErrorViewer.tsx";
|
|
15
16
|
import NestedView from "../components/NestedView.tsx";
|
|
16
17
|
import NotFoundPage from "../components/NotFound.tsx";
|
|
@@ -22,7 +23,6 @@ import {
|
|
|
22
23
|
type PagePrimitive,
|
|
23
24
|
type PagePrimitiveOptions,
|
|
24
25
|
} from "../primitives/$page.ts";
|
|
25
|
-
import type { Head } from "@alepha/react/head";
|
|
26
26
|
|
|
27
27
|
const envSchema = t.object({
|
|
28
28
|
REACT_STRICT_MODE: t.boolean({ default: true }),
|
|
@@ -476,7 +476,8 @@ export class ReactPageProvider {
|
|
|
476
476
|
value: {
|
|
477
477
|
index,
|
|
478
478
|
path,
|
|
479
|
-
onError:
|
|
479
|
+
onError:
|
|
480
|
+
this.getErrorHandler(page) ?? ((error) => this.renderError(error)),
|
|
480
481
|
},
|
|
481
482
|
},
|
|
482
483
|
element,
|
|
@@ -1,15 +1,33 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import { ServerHeadProvider } from "@alepha/react/head";
|
|
3
|
+
import {
|
|
4
|
+
$atom,
|
|
5
|
+
$env,
|
|
6
|
+
$hook,
|
|
7
|
+
$inject,
|
|
8
|
+
$use,
|
|
9
|
+
Alepha,
|
|
10
|
+
AlephaError,
|
|
11
|
+
type Static,
|
|
12
|
+
t,
|
|
13
|
+
} from "alepha";
|
|
3
14
|
import { FileSystemProvider } from "alepha/file";
|
|
4
15
|
import { $logger } from "alepha/logger";
|
|
5
|
-
import { type ServerHandler, ServerRouterProvider
|
|
16
|
+
import { type ServerHandler, ServerRouterProvider } from "alepha/server";
|
|
6
17
|
import { ServerLinksProvider } from "alepha/server/links";
|
|
7
18
|
import { ServerStaticProvider } from "alepha/server/static";
|
|
8
19
|
import { renderToReadableStream } from "react-dom/server";
|
|
9
|
-
import { ServerHeadProvider } from "@alepha/react/head";
|
|
10
20
|
import { Redirection } from "../errors/Redirection.ts";
|
|
11
|
-
import {
|
|
12
|
-
|
|
21
|
+
import {
|
|
22
|
+
$page,
|
|
23
|
+
type PagePrimitiveRenderOptions,
|
|
24
|
+
type PagePrimitiveRenderResult,
|
|
25
|
+
} from "../primitives/$page.ts";
|
|
26
|
+
import {
|
|
27
|
+
type PageRoute,
|
|
28
|
+
ReactPageProvider,
|
|
29
|
+
type ReactRouterState,
|
|
30
|
+
} from "./ReactPageProvider.ts";
|
|
13
31
|
import { ReactServerTemplateProvider } from "./ReactServerTemplateProvider.ts";
|
|
14
32
|
import { SSRManifestProvider } from "./SSRManifestProvider.ts";
|
|
15
33
|
|
|
@@ -44,7 +62,6 @@ export class ReactServerProvider {
|
|
|
44
62
|
protected readonly serverHeadProvider = $inject(ServerHeadProvider);
|
|
45
63
|
protected readonly serverStaticProvider = $inject(ServerStaticProvider);
|
|
46
64
|
protected readonly serverRouterProvider = $inject(ServerRouterProvider);
|
|
47
|
-
protected readonly serverTimingProvider = $inject(ServerTimingProvider);
|
|
48
65
|
protected readonly ssrManifestProvider = $inject(SSRManifestProvider);
|
|
49
66
|
|
|
50
67
|
/**
|
|
@@ -67,21 +84,11 @@ export class ReactServerProvider {
|
|
|
67
84
|
|
|
68
85
|
this.alepha.store.set("alepha.react.server.ssr", ssrEnabled);
|
|
69
86
|
|
|
70
|
-
if (ssrEnabled) {
|
|
71
|
-
this.log.info("SSR streaming enabled");
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// development mode
|
|
75
|
-
if (this.alepha.isViteDev()) {
|
|
76
|
-
await this.configureVite(ssrEnabled);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
87
|
// production mode
|
|
81
88
|
let root = "";
|
|
82
89
|
|
|
83
90
|
// non-serverless mode only -> serve static files
|
|
84
|
-
if (!this.alepha.isServerless()) {
|
|
91
|
+
if (!this.alepha.isServerless() && !this.alepha.isViteDev()) {
|
|
85
92
|
root = await this.getPublicDirectory();
|
|
86
93
|
if (!root) {
|
|
87
94
|
this.log.warn(
|
|
@@ -198,7 +205,7 @@ export class ReactServerProvider {
|
|
|
198
205
|
if (parts.length > 0) {
|
|
199
206
|
// Pass assets so they get stripped from original head content
|
|
200
207
|
this.templateProvider.setEarlyHeadContent(
|
|
201
|
-
parts.join("\n")
|
|
208
|
+
`${parts.join("\n")}\n`,
|
|
202
209
|
assets,
|
|
203
210
|
);
|
|
204
211
|
this.log.debug("Early head content set", {
|
|
@@ -240,26 +247,6 @@ export class ReactServerProvider {
|
|
|
240
247
|
});
|
|
241
248
|
}
|
|
242
249
|
|
|
243
|
-
/**
|
|
244
|
-
* Configure Vite for SSR in development mode.
|
|
245
|
-
*/
|
|
246
|
-
protected async configureVite(ssrEnabled: boolean) {
|
|
247
|
-
if (!ssrEnabled) {
|
|
248
|
-
// do nothing, vite will handle everything for us
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const url = `http://localhost:${this.alepha.env.SERVER_PORT ?? "5173"}`;
|
|
253
|
-
|
|
254
|
-
this.log.info("SSR (dev) OK", { url });
|
|
255
|
-
|
|
256
|
-
await this.registerPages(() =>
|
|
257
|
-
fetch(`${url}/index.html`)
|
|
258
|
-
.then((it) => it.text())
|
|
259
|
-
.catch(() => undefined),
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
250
|
/**
|
|
264
251
|
* Create the request handler for a page route.
|
|
265
252
|
*/
|
|
@@ -338,9 +325,9 @@ export class ReactServerProvider {
|
|
|
338
325
|
const result = await this.renderPage(route, state);
|
|
339
326
|
|
|
340
327
|
if (result.redirect) {
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
return
|
|
328
|
+
// Return redirect URL - template provider will inject meta refresh
|
|
329
|
+
// since HTTP headers have already been sent
|
|
330
|
+
return { redirect: result.redirect };
|
|
344
331
|
}
|
|
345
332
|
|
|
346
333
|
return { state, reactStream: result.reactStream! };
|
|
@@ -354,7 +341,8 @@ export class ReactServerProvider {
|
|
|
354
341
|
});
|
|
355
342
|
// Can't do redirect after streaming started - already handled above
|
|
356
343
|
} else {
|
|
357
|
-
|
|
344
|
+
// disable logging here, it's noisy and duplicate
|
|
345
|
+
// this.log.error("HTML stream error", error);
|
|
358
346
|
}
|
|
359
347
|
},
|
|
360
348
|
},
|
|
@@ -389,10 +377,7 @@ export class ReactServerProvider {
|
|
|
389
377
|
state: ReactRouterState,
|
|
390
378
|
): Promise<{ redirect?: string; reactStream?: ReadableStream<Uint8Array> }> {
|
|
391
379
|
// Resolve page layers (loaders)
|
|
392
|
-
this.serverTimingProvider.beginTiming("createLayers");
|
|
393
380
|
const { redirect } = await this.pageApi.createLayers(route, state);
|
|
394
|
-
this.serverTimingProvider.endTiming("createLayers");
|
|
395
|
-
|
|
396
381
|
if (redirect) {
|
|
397
382
|
this.log.debug("Resolver resulted in redirection", { redirect });
|
|
398
383
|
return { redirect };
|
|
@@ -409,7 +394,6 @@ export class ReactServerProvider {
|
|
|
409
394
|
}
|
|
410
395
|
|
|
411
396
|
// Render React to stream
|
|
412
|
-
this.serverTimingProvider.beginTiming("renderToStream");
|
|
413
397
|
|
|
414
398
|
const element = this.pageApi.root(state);
|
|
415
399
|
this.alepha.store.set("alepha.react.router.state", state);
|
|
@@ -421,13 +405,12 @@ export class ReactServerProvider {
|
|
|
421
405
|
redirect: error.redirect,
|
|
422
406
|
});
|
|
423
407
|
} else {
|
|
424
|
-
|
|
408
|
+
// disable logging here, it's noisy and duplicate
|
|
409
|
+
// this.log.error("Streaming render error", error);
|
|
425
410
|
}
|
|
426
411
|
},
|
|
427
412
|
});
|
|
428
413
|
|
|
429
|
-
this.serverTimingProvider.endTiming("renderToStream");
|
|
430
|
-
|
|
431
414
|
return { reactStream };
|
|
432
415
|
}
|
|
433
416
|
|
|
@@ -526,7 +509,6 @@ export class ReactServerProvider {
|
|
|
526
509
|
|
|
527
510
|
type TemplateLoader = () => Promise<string | undefined>;
|
|
528
511
|
|
|
529
|
-
|
|
530
512
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
531
513
|
|
|
532
514
|
const envSchema = t.object({
|