@nestjs-ssr/react 0.3.2 → 0.3.4
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 +2 -2
- package/dist/cli/init.js +5 -5
- package/dist/cli/init.mjs +5 -5
- package/dist/index.js +54 -46
- package/dist/index.mjs +24 -16
- package/dist/render/index.js +41 -34
- package/dist/render/index.mjs +23 -15
- package/dist/templates/entry-client.tsx +48 -25
- package/dist/templates/entry-server.tsx +18 -0
- package/etc/react.api.md +250 -262
- package/package.json +1 -1
- package/src/templates/entry-client.tsx +48 -25
- package/src/templates/entry-server.tsx +18 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="@nestjs-ssr/react/global" />
|
|
2
|
+
|
|
2
3
|
import React, { StrictMode } from 'react';
|
|
3
4
|
import { hydrateRoot } from 'react-dom/client';
|
|
4
5
|
import {
|
|
@@ -10,6 +11,15 @@ const componentName = window.__COMPONENT_NAME__;
|
|
|
10
11
|
const initialProps = window.__INITIAL_STATE__ || {};
|
|
11
12
|
const renderContext = window.__CONTEXT__ || {};
|
|
12
13
|
|
|
14
|
+
// Auto-discover root layout using Vite's glob import (must match server-side discovery)
|
|
15
|
+
// @ts-ignore - Vite-specific API
|
|
16
|
+
const layoutModules = import.meta.glob('@/views/layout.tsx', {
|
|
17
|
+
eager: true,
|
|
18
|
+
}) as Record<string, { default: React.ComponentType<any> }>;
|
|
19
|
+
|
|
20
|
+
const layoutPath = Object.keys(layoutModules)[0];
|
|
21
|
+
const RootLayout = layoutPath ? layoutModules[layoutPath].default : null;
|
|
22
|
+
|
|
13
23
|
// Auto-import all view components using Vite's glob feature
|
|
14
24
|
// Exclude entry-client.tsx and entry-server.tsx from the glob
|
|
15
25
|
// @ts-ignore - Vite-specific API
|
|
@@ -111,40 +121,53 @@ function hasLayout(
|
|
|
111
121
|
function composeWithLayout(
|
|
112
122
|
ViewComponent: React.ComponentType<any>,
|
|
113
123
|
props: any,
|
|
124
|
+
context?: any,
|
|
125
|
+
layouts: Array<{ layout: React.ComponentType<any>; props?: any }> = [],
|
|
114
126
|
): React.ReactElement {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
while (hasLayout(currentComponent)) {
|
|
130
|
-
layoutChain.push({
|
|
131
|
-
Layout: currentComponent.layout,
|
|
132
|
-
layoutProps: currentComponent.layoutProps || {},
|
|
133
|
-
});
|
|
134
|
-
currentComponent = currentComponent.layout;
|
|
127
|
+
// Start with the page component
|
|
128
|
+
let result = <ViewComponent {...props} />;
|
|
129
|
+
|
|
130
|
+
// If no layouts passed, check if component has its own layout chain
|
|
131
|
+
if (layouts.length === 0 && hasLayout(ViewComponent)) {
|
|
132
|
+
let currentComponent: any = ViewComponent;
|
|
133
|
+
while (hasLayout(currentComponent)) {
|
|
134
|
+
layouts.push({
|
|
135
|
+
layout: currentComponent.layout,
|
|
136
|
+
props: currentComponent.layoutProps || {},
|
|
137
|
+
});
|
|
138
|
+
currentComponent = currentComponent.layout;
|
|
139
|
+
}
|
|
135
140
|
}
|
|
136
141
|
|
|
137
|
-
// Wrap
|
|
138
|
-
|
|
139
|
-
for (const { Layout, layoutProps } of
|
|
140
|
-
|
|
142
|
+
// Wrap with each layout in the chain
|
|
143
|
+
// Must match server-side wrapping with data-layout and data-outlet attributes
|
|
144
|
+
for (const { layout: Layout, props: layoutProps } of layouts) {
|
|
145
|
+
const layoutName = Layout.displayName || Layout.name || 'Layout';
|
|
146
|
+
result = (
|
|
147
|
+
<div data-layout={layoutName}>
|
|
148
|
+
<Layout context={context} layoutProps={layoutProps}>
|
|
149
|
+
<div data-outlet={layoutName}>{result}</div>
|
|
150
|
+
</Layout>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
141
153
|
}
|
|
142
154
|
|
|
143
155
|
return result;
|
|
144
156
|
}
|
|
145
157
|
|
|
158
|
+
// Build layouts array - use RootLayout if it exists (matching server behavior)
|
|
159
|
+
const layouts: Array<{ layout: React.ComponentType<any>; props?: any }> = [];
|
|
160
|
+
if (RootLayout) {
|
|
161
|
+
layouts.push({ layout: RootLayout, props: {} });
|
|
162
|
+
}
|
|
163
|
+
|
|
146
164
|
// Compose the component with its layout (if any)
|
|
147
|
-
const composedElement = composeWithLayout(
|
|
165
|
+
const composedElement = composeWithLayout(
|
|
166
|
+
ViewComponent,
|
|
167
|
+
initialProps,
|
|
168
|
+
renderContext,
|
|
169
|
+
layouts,
|
|
170
|
+
);
|
|
148
171
|
|
|
149
172
|
// Wrap with providers to make context and navigation state available via hooks
|
|
150
173
|
const wrappedElement = (
|
|
@@ -2,6 +2,24 @@ import React from 'react';
|
|
|
2
2
|
import { renderToString, renderToPipeableStream } from 'react-dom/server';
|
|
3
3
|
import { PageContextProvider } from '@nestjs-ssr/react/client';
|
|
4
4
|
|
|
5
|
+
// Auto-discover root layout using Vite's glob import
|
|
6
|
+
// This eagerly loads layout if it exists, null otherwise
|
|
7
|
+
// @ts-ignore - Vite-specific API
|
|
8
|
+
const layoutModules = import.meta.glob('@/views/layout.tsx', {
|
|
9
|
+
eager: true,
|
|
10
|
+
}) as Record<string, { default: React.ComponentType<any> }>;
|
|
11
|
+
|
|
12
|
+
const layoutPath = Object.keys(layoutModules)[0];
|
|
13
|
+
const RootLayout = layoutPath ? layoutModules[layoutPath].default : null;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get the root layout component.
|
|
17
|
+
* Used by RenderService in production when dynamic import isn't available.
|
|
18
|
+
*/
|
|
19
|
+
export function getRootLayout(): React.ComponentType<any> | null {
|
|
20
|
+
return RootLayout;
|
|
21
|
+
}
|
|
22
|
+
|
|
5
23
|
/**
|
|
6
24
|
* Compose a component with its layouts from the interceptor.
|
|
7
25
|
* Layouts are passed from the RenderInterceptor based on decorators.
|