@nestjs-ssr/react 0.1.12 → 0.2.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 +53 -85
- package/dist/cli/init.js +88 -11
- package/dist/cli/init.mjs +88 -11
- package/dist/{index-BiaVDe9J.d.mts → index-C5Knql-9.d.mts} +124 -8
- package/dist/{index-BiaVDe9J.d.ts → index-C5Knql-9.d.ts} +124 -8
- package/dist/index.d.mts +377 -69
- package/dist/index.d.ts +377 -69
- package/dist/index.js +460 -89
- package/dist/index.mjs +459 -85
- package/dist/render/index.d.mts +1 -1
- package/dist/render/index.d.ts +1 -1
- package/dist/render/index.js +253 -64
- package/dist/render/index.mjs +253 -63
- package/dist/templates/entry-client.tsx +80 -13
- package/dist/templates/entry-server.tsx +33 -2
- package/dist/templates/index.html +0 -3
- package/etc/react.api.md +262 -0
- package/package.json +28 -7
- package/src/global.d.ts +1 -1
- package/src/templates/entry-client.tsx +80 -13
- package/src/templates/entry-server.tsx +33 -2
- package/src/templates/index.html +0 -3
package/README.md
CHANGED
|
@@ -1,122 +1,90 @@
|
|
|
1
|
-
#
|
|
1
|
+
# NestJS SSR
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@nestjs-ssr/react)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
2
5
|
|
|
3
6
|
> **⚠️ Preview Release**
|
|
4
7
|
> This package is currently in active development. The API may change between minor versions. Production use is not recommended yet.
|
|
5
8
|
|
|
6
|
-
Server-
|
|
7
|
-
|
|
8
|
-
## Features
|
|
9
|
+
Server-rendered React for NestJS. Controllers return data, components render it.
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
- **Zero Config** - Works out of the box with sensible defaults
|
|
12
|
-
- **Streaming SSR** - Modern renderToPipeableStream support
|
|
13
|
-
- **HMR in Development** - Powered by Vite for instant feedback
|
|
14
|
-
- **Production Ready** - Code splitting, caching, and optimizations built-in
|
|
11
|
+
Clean Architecture: layers separated, dependencies inward, business logic framework-agnostic.
|
|
15
12
|
|
|
16
|
-
##
|
|
13
|
+
## When To Use
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
npm install @nestjs-ssr/react
|
|
20
|
-
npx nestjs-ssr # Set up your project automatically
|
|
21
|
-
```
|
|
15
|
+
**Use this when:**
|
|
22
16
|
|
|
23
|
-
|
|
17
|
+
- You have NestJS and want React instead of Handlebars/EJS
|
|
18
|
+
- Testable layers matter more than file-based routing
|
|
19
|
+
- You want feature modules (controller + service + view together)
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
**Use Next.js when:**
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
- You're starting fresh without NestJS
|
|
24
|
+
- You want the React ecosystem's defaults
|
|
25
|
+
- File-based routing fits your mental model
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
// app.module.ts
|
|
31
|
-
import { RenderModule } from '@nestjs-ssr/react';
|
|
27
|
+
## Quick Start
|
|
32
28
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
})
|
|
36
|
-
export class AppModule {}
|
|
29
|
+
```bash
|
|
30
|
+
npx nestjs-ssr init
|
|
37
31
|
```
|
|
38
32
|
|
|
39
|
-
**2. Create a view component**
|
|
40
|
-
|
|
41
33
|
```typescript
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
message: string;
|
|
34
|
+
@Get(':id')
|
|
35
|
+
@Render(ProductDetail)
|
|
36
|
+
async getProduct(@Param('id') id: string) {
|
|
37
|
+
return { product: await this.productService.findById(id) };
|
|
47
38
|
}
|
|
39
|
+
```
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
```tsx
|
|
42
|
+
export default function ProductDetail({
|
|
43
|
+
data,
|
|
44
|
+
}: PageProps<{ product: Product }>) {
|
|
45
|
+
return <h1>{data.product.name}</h1>;
|
|
51
46
|
}
|
|
52
47
|
```
|
|
53
48
|
|
|
54
|
-
|
|
49
|
+
Type mismatch = build fails.
|
|
50
|
+
|
|
51
|
+
## Test in Isolation
|
|
55
52
|
|
|
56
53
|
```typescript
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
@Controller()
|
|
63
|
-
export class AppController {
|
|
64
|
-
@Get()
|
|
65
|
-
@Render(Home)
|
|
66
|
-
getHome() {
|
|
67
|
-
return { message: 'Hello World' }; // TypeScript validates this!
|
|
68
|
-
}
|
|
69
|
-
}
|
|
54
|
+
// Controller: no React
|
|
55
|
+
expect(await controller.getProduct('123')).toEqual({ product: { id: '123' } });
|
|
56
|
+
|
|
57
|
+
// Component: no NestJS
|
|
58
|
+
render(<ProductDetail data={{ product: mockProduct }} />);
|
|
70
59
|
```
|
|
71
60
|
|
|
72
|
-
|
|
61
|
+
## Features
|
|
73
62
|
|
|
74
|
-
|
|
63
|
+
**Rendering:**
|
|
75
64
|
|
|
76
|
-
|
|
65
|
+
- Type-safe data flow from controller to component
|
|
66
|
+
- Hierarchical layouts (module → controller → method)
|
|
67
|
+
- Head tags via decorators (title, meta, OG, JSON-LD)
|
|
77
68
|
|
|
78
|
-
|
|
79
|
-
import { usePageContext, useParams, useQuery } from '@nestjs-ssr/react';
|
|
69
|
+
**Request Context:**
|
|
80
70
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const params = useParams(); // Route params
|
|
84
|
-
const query = useQuery(); // Query string
|
|
85
|
-
return <div>User ID: {params.id}</div>;
|
|
86
|
-
}
|
|
87
|
-
```
|
|
71
|
+
- Hooks: params, query, headers, session, user agent
|
|
72
|
+
- Whitelist what reaches the client
|
|
88
73
|
|
|
89
|
-
|
|
74
|
+
**Development:**
|
|
90
75
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
export default function MyPage(props: PageProps<MyProps>) {
|
|
95
|
-
return (
|
|
96
|
-
<>
|
|
97
|
-
<Head>
|
|
98
|
-
<title>My Page</title>
|
|
99
|
-
<meta name="description" content="Page description" />
|
|
100
|
-
</Head>
|
|
101
|
-
<div>{props.content}</div>
|
|
102
|
-
</>
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
```
|
|
76
|
+
- Integrated mode: one process, full refresh
|
|
77
|
+
- Proxy mode: separate Vite, true HMR
|
|
106
78
|
|
|
107
|
-
##
|
|
79
|
+
## Docs
|
|
108
80
|
|
|
109
|
-
|
|
110
|
-
- [Examples](https://github.com/georgialexandrov/nestjs-ssr/tree/main/examples)
|
|
111
|
-
- [GitHub](https://github.com/georgialexandrov/nestjs-ssr)
|
|
81
|
+
[Full documentation →](https://georgialexandrov.github.io/nest-ssr/)
|
|
112
82
|
|
|
113
|
-
##
|
|
83
|
+
## Examples
|
|
114
84
|
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
- React 19+
|
|
118
|
-
- TypeScript 5+
|
|
85
|
+
**[Minimal](./examples/minimal/)** - Simplest setup with integrated Vite mode
|
|
86
|
+
**[Minimal HMR](./examples/minimal-hmr/)** - Dual-server architecture for full HMR
|
|
119
87
|
|
|
120
88
|
## License
|
|
121
89
|
|
|
122
|
-
MIT
|
|
90
|
+
MIT
|
package/dist/cli/init.js
CHANGED
|
@@ -32,6 +32,10 @@ var main = citty.defineCommand({
|
|
|
32
32
|
type: "boolean",
|
|
33
33
|
description: "Skip automatic dependency installation",
|
|
34
34
|
default: false
|
|
35
|
+
},
|
|
36
|
+
integration: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: 'Integration type: "separate" (Vite as separate server) or "integrated" (Vite bundled with NestJS)'
|
|
35
39
|
}
|
|
36
40
|
},
|
|
37
41
|
async run({ args }) {
|
|
@@ -39,6 +43,32 @@ var main = citty.defineCommand({
|
|
|
39
43
|
const viewsDir = args.views;
|
|
40
44
|
consola.consola.box("@nestjs-ssr/react initialization");
|
|
41
45
|
consola.consola.start("Setting up your NestJS SSR React project...\n");
|
|
46
|
+
let integrationType = args.integration;
|
|
47
|
+
if (!integrationType) {
|
|
48
|
+
const response = await consola.consola.prompt("How do you want to run Vite during development?", {
|
|
49
|
+
type: "select",
|
|
50
|
+
options: [
|
|
51
|
+
{
|
|
52
|
+
label: "Separate server (Vite runs on its own port, e.g., 5173)",
|
|
53
|
+
value: "separate"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
label: "Integrated with NestJS (Vite middleware runs within NestJS)",
|
|
57
|
+
value: "integrated"
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
});
|
|
61
|
+
integrationType = response;
|
|
62
|
+
}
|
|
63
|
+
if (![
|
|
64
|
+
"separate",
|
|
65
|
+
"integrated"
|
|
66
|
+
].includes(integrationType)) {
|
|
67
|
+
consola.consola.error(`Invalid integration type: "${integrationType}". Must be "separate" or "integrated"`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
consola.consola.info(`Using ${integrationType === "separate" ? "separate server" : "integrated"} mode
|
|
71
|
+
`);
|
|
42
72
|
const templateLocations = [
|
|
43
73
|
path.resolve(__dirname$1, "../../src/templates"),
|
|
44
74
|
path.resolve(__dirname$1, "../templates")
|
|
@@ -76,6 +106,15 @@ var main = citty.defineCommand({
|
|
|
76
106
|
fs.copyFileSync(entryServerSrc, entryServerDest);
|
|
77
107
|
consola.consola.success(`Created ${viewsDir}/entry-server.tsx`);
|
|
78
108
|
}
|
|
109
|
+
consola.consola.start("Creating index.html...");
|
|
110
|
+
const indexHtmlSrc = path.join(templateDir, "index.html");
|
|
111
|
+
const indexHtmlDest = path.join(cwd, viewsDir, "index.html");
|
|
112
|
+
if (fs.existsSync(indexHtmlDest) && !args.force) {
|
|
113
|
+
consola.consola.warn(`${viewsDir}/index.html already exists (use --force to overwrite)`);
|
|
114
|
+
} else {
|
|
115
|
+
fs.copyFileSync(indexHtmlSrc, indexHtmlDest);
|
|
116
|
+
consola.consola.success(`Created ${viewsDir}/index.html`);
|
|
117
|
+
}
|
|
79
118
|
consola.consola.start("Configuring vite.config.js...");
|
|
80
119
|
const viteConfigPath = path.join(cwd, "vite.config.js");
|
|
81
120
|
const viteConfigTs = path.join(cwd, "vite.config.ts");
|
|
@@ -85,12 +124,28 @@ var main = citty.defineCommand({
|
|
|
85
124
|
consola.consola.warn(`${useTypeScript ? "vite.config.ts" : "vite.config.js"} already exists`);
|
|
86
125
|
consola.consola.info("Please manually add to your Vite config:");
|
|
87
126
|
consola.consola.log(" import { resolve } from 'path';");
|
|
127
|
+
if (integrationType === "separate") {
|
|
128
|
+
consola.consola.log(" server: {");
|
|
129
|
+
consola.consola.log(" port: 5173,");
|
|
130
|
+
consola.consola.log(" strictPort: true,");
|
|
131
|
+
consola.consola.log(" hmr: { port: 5173 },");
|
|
132
|
+
consola.consola.log(" },");
|
|
133
|
+
}
|
|
88
134
|
consola.consola.log(" build: {");
|
|
89
135
|
consola.consola.log(" rollupOptions: {");
|
|
90
136
|
consola.consola.log(` input: { client: resolve(__dirname, '${viewsDir}/entry-client.tsx') }`);
|
|
91
137
|
consola.consola.log(" }");
|
|
92
138
|
consola.consola.log(" }");
|
|
93
139
|
} else {
|
|
140
|
+
const serverConfig = integrationType === "separate" ? ` server: {
|
|
141
|
+
port: 5173,
|
|
142
|
+
strictPort: true,
|
|
143
|
+
hmr: { port: 5173 },
|
|
144
|
+
},
|
|
145
|
+
` : ` server: {
|
|
146
|
+
middlewareMode: true,
|
|
147
|
+
},
|
|
148
|
+
`;
|
|
94
149
|
const viteConfig = `import { defineConfig } from 'vite';
|
|
95
150
|
import react from '@vitejs/plugin-react';
|
|
96
151
|
import { resolve } from 'path';
|
|
@@ -102,12 +157,7 @@ export default defineConfig({
|
|
|
102
157
|
'@': resolve(__dirname, 'src'),
|
|
103
158
|
},
|
|
104
159
|
},
|
|
105
|
-
|
|
106
|
-
port: 5173,
|
|
107
|
-
strictPort: true,
|
|
108
|
-
hmr: { port: 5173 },
|
|
109
|
-
},
|
|
110
|
-
build: {
|
|
160
|
+
${serverConfig}build: {
|
|
111
161
|
outDir: 'dist/client',
|
|
112
162
|
manifest: true,
|
|
113
163
|
rollupOptions: {
|
|
@@ -249,6 +299,16 @@ export default defineConfig({
|
|
|
249
299
|
packageJson.scripts["build:server"] = `vite build --ssr ${viewsDir}/entry-server.tsx --outDir dist/server`;
|
|
250
300
|
shouldUpdate = true;
|
|
251
301
|
}
|
|
302
|
+
if (integrationType === "separate") {
|
|
303
|
+
if (!packageJson.scripts["dev:client"]) {
|
|
304
|
+
packageJson.scripts["dev:client"] = "vite";
|
|
305
|
+
shouldUpdate = true;
|
|
306
|
+
}
|
|
307
|
+
if (!packageJson.scripts["dev:server"]) {
|
|
308
|
+
packageJson.scripts["dev:server"] = "nest start --watch";
|
|
309
|
+
shouldUpdate = true;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
252
312
|
const existingBuild = packageJson.scripts.build;
|
|
253
313
|
const recommendedBuild = "pnpm build:client && pnpm build:server && nest build";
|
|
254
314
|
if (!existingBuild) {
|
|
@@ -269,9 +329,9 @@ export default defineConfig({
|
|
|
269
329
|
if (!args["skip-install"]) {
|
|
270
330
|
consola.consola.start("Checking dependencies...");
|
|
271
331
|
const requiredDeps = {
|
|
272
|
-
|
|
332
|
+
react: "^19.0.0",
|
|
273
333
|
"react-dom": "^19.0.0",
|
|
274
|
-
|
|
334
|
+
vite: "^7.0.0",
|
|
275
335
|
"@vitejs/plugin-react": "^4.0.0"
|
|
276
336
|
};
|
|
277
337
|
const missingDeps = [];
|
|
@@ -311,9 +371,26 @@ export default defineConfig({
|
|
|
311
371
|
}
|
|
312
372
|
consola.consola.success("\nInitialization complete!");
|
|
313
373
|
consola.consola.box("Next steps");
|
|
314
|
-
consola.consola.info(
|
|
315
|
-
consola.consola.
|
|
316
|
-
consola.consola.
|
|
374
|
+
consola.consola.info("1. Register RenderModule in your app.module.ts:");
|
|
375
|
+
consola.consola.log(' import { RenderModule } from "@nestjs-ssr/react";');
|
|
376
|
+
consola.consola.log(" @Module({");
|
|
377
|
+
consola.consola.log(" imports: [RenderModule.forRoot()],");
|
|
378
|
+
consola.consola.log(" })");
|
|
379
|
+
consola.consola.info(`
|
|
380
|
+
2. Create your first view component in ${viewsDir}/`);
|
|
381
|
+
consola.consola.info("3. Add a controller method with the @Render decorator:");
|
|
382
|
+
consola.consola.log(' import { Render } from "@nestjs-ssr/react";');
|
|
383
|
+
consola.consola.log(" @Get()");
|
|
384
|
+
consola.consola.log(' @Render("YourComponent")');
|
|
385
|
+
consola.consola.log(' home() { return { props: { message: "Hello" } }; }');
|
|
386
|
+
if (integrationType === "separate") {
|
|
387
|
+
consola.consola.info("\n4. Start both servers:");
|
|
388
|
+
consola.consola.log(" Terminal 1: pnpm dev:client (Vite on port 5173)");
|
|
389
|
+
consola.consola.log(" Terminal 2: pnpm dev:server (NestJS)");
|
|
390
|
+
} else {
|
|
391
|
+
consola.consola.info("\n4. Start the dev server: pnpm start:dev");
|
|
392
|
+
consola.consola.info(" (Vite middleware will be integrated into NestJS)");
|
|
393
|
+
}
|
|
317
394
|
}
|
|
318
395
|
});
|
|
319
396
|
citty.runMain(main);
|
package/dist/cli/init.mjs
CHANGED
|
@@ -29,6 +29,10 @@ var main = defineCommand({
|
|
|
29
29
|
type: "boolean",
|
|
30
30
|
description: "Skip automatic dependency installation",
|
|
31
31
|
default: false
|
|
32
|
+
},
|
|
33
|
+
integration: {
|
|
34
|
+
type: "string",
|
|
35
|
+
description: 'Integration type: "separate" (Vite as separate server) or "integrated" (Vite bundled with NestJS)'
|
|
32
36
|
}
|
|
33
37
|
},
|
|
34
38
|
async run({ args }) {
|
|
@@ -36,6 +40,32 @@ var main = defineCommand({
|
|
|
36
40
|
const viewsDir = args.views;
|
|
37
41
|
consola.box("@nestjs-ssr/react initialization");
|
|
38
42
|
consola.start("Setting up your NestJS SSR React project...\n");
|
|
43
|
+
let integrationType = args.integration;
|
|
44
|
+
if (!integrationType) {
|
|
45
|
+
const response = await consola.prompt("How do you want to run Vite during development?", {
|
|
46
|
+
type: "select",
|
|
47
|
+
options: [
|
|
48
|
+
{
|
|
49
|
+
label: "Separate server (Vite runs on its own port, e.g., 5173)",
|
|
50
|
+
value: "separate"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
label: "Integrated with NestJS (Vite middleware runs within NestJS)",
|
|
54
|
+
value: "integrated"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
});
|
|
58
|
+
integrationType = response;
|
|
59
|
+
}
|
|
60
|
+
if (![
|
|
61
|
+
"separate",
|
|
62
|
+
"integrated"
|
|
63
|
+
].includes(integrationType)) {
|
|
64
|
+
consola.error(`Invalid integration type: "${integrationType}". Must be "separate" or "integrated"`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
consola.info(`Using ${integrationType === "separate" ? "separate server" : "integrated"} mode
|
|
68
|
+
`);
|
|
39
69
|
const templateLocations = [
|
|
40
70
|
resolve(__dirname$1, "../../src/templates"),
|
|
41
71
|
resolve(__dirname$1, "../templates")
|
|
@@ -73,6 +103,15 @@ var main = defineCommand({
|
|
|
73
103
|
copyFileSync(entryServerSrc, entryServerDest);
|
|
74
104
|
consola.success(`Created ${viewsDir}/entry-server.tsx`);
|
|
75
105
|
}
|
|
106
|
+
consola.start("Creating index.html...");
|
|
107
|
+
const indexHtmlSrc = join(templateDir, "index.html");
|
|
108
|
+
const indexHtmlDest = join(cwd, viewsDir, "index.html");
|
|
109
|
+
if (existsSync(indexHtmlDest) && !args.force) {
|
|
110
|
+
consola.warn(`${viewsDir}/index.html already exists (use --force to overwrite)`);
|
|
111
|
+
} else {
|
|
112
|
+
copyFileSync(indexHtmlSrc, indexHtmlDest);
|
|
113
|
+
consola.success(`Created ${viewsDir}/index.html`);
|
|
114
|
+
}
|
|
76
115
|
consola.start("Configuring vite.config.js...");
|
|
77
116
|
const viteConfigPath = join(cwd, "vite.config.js");
|
|
78
117
|
const viteConfigTs = join(cwd, "vite.config.ts");
|
|
@@ -82,12 +121,28 @@ var main = defineCommand({
|
|
|
82
121
|
consola.warn(`${useTypeScript ? "vite.config.ts" : "vite.config.js"} already exists`);
|
|
83
122
|
consola.info("Please manually add to your Vite config:");
|
|
84
123
|
consola.log(" import { resolve } from 'path';");
|
|
124
|
+
if (integrationType === "separate") {
|
|
125
|
+
consola.log(" server: {");
|
|
126
|
+
consola.log(" port: 5173,");
|
|
127
|
+
consola.log(" strictPort: true,");
|
|
128
|
+
consola.log(" hmr: { port: 5173 },");
|
|
129
|
+
consola.log(" },");
|
|
130
|
+
}
|
|
85
131
|
consola.log(" build: {");
|
|
86
132
|
consola.log(" rollupOptions: {");
|
|
87
133
|
consola.log(` input: { client: resolve(__dirname, '${viewsDir}/entry-client.tsx') }`);
|
|
88
134
|
consola.log(" }");
|
|
89
135
|
consola.log(" }");
|
|
90
136
|
} else {
|
|
137
|
+
const serverConfig = integrationType === "separate" ? ` server: {
|
|
138
|
+
port: 5173,
|
|
139
|
+
strictPort: true,
|
|
140
|
+
hmr: { port: 5173 },
|
|
141
|
+
},
|
|
142
|
+
` : ` server: {
|
|
143
|
+
middlewareMode: true,
|
|
144
|
+
},
|
|
145
|
+
`;
|
|
91
146
|
const viteConfig = `import { defineConfig } from 'vite';
|
|
92
147
|
import react from '@vitejs/plugin-react';
|
|
93
148
|
import { resolve } from 'path';
|
|
@@ -99,12 +154,7 @@ export default defineConfig({
|
|
|
99
154
|
'@': resolve(__dirname, 'src'),
|
|
100
155
|
},
|
|
101
156
|
},
|
|
102
|
-
|
|
103
|
-
port: 5173,
|
|
104
|
-
strictPort: true,
|
|
105
|
-
hmr: { port: 5173 },
|
|
106
|
-
},
|
|
107
|
-
build: {
|
|
157
|
+
${serverConfig}build: {
|
|
108
158
|
outDir: 'dist/client',
|
|
109
159
|
manifest: true,
|
|
110
160
|
rollupOptions: {
|
|
@@ -246,6 +296,16 @@ export default defineConfig({
|
|
|
246
296
|
packageJson.scripts["build:server"] = `vite build --ssr ${viewsDir}/entry-server.tsx --outDir dist/server`;
|
|
247
297
|
shouldUpdate = true;
|
|
248
298
|
}
|
|
299
|
+
if (integrationType === "separate") {
|
|
300
|
+
if (!packageJson.scripts["dev:client"]) {
|
|
301
|
+
packageJson.scripts["dev:client"] = "vite";
|
|
302
|
+
shouldUpdate = true;
|
|
303
|
+
}
|
|
304
|
+
if (!packageJson.scripts["dev:server"]) {
|
|
305
|
+
packageJson.scripts["dev:server"] = "nest start --watch";
|
|
306
|
+
shouldUpdate = true;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
249
309
|
const existingBuild = packageJson.scripts.build;
|
|
250
310
|
const recommendedBuild = "pnpm build:client && pnpm build:server && nest build";
|
|
251
311
|
if (!existingBuild) {
|
|
@@ -266,9 +326,9 @@ export default defineConfig({
|
|
|
266
326
|
if (!args["skip-install"]) {
|
|
267
327
|
consola.start("Checking dependencies...");
|
|
268
328
|
const requiredDeps = {
|
|
269
|
-
|
|
329
|
+
react: "^19.0.0",
|
|
270
330
|
"react-dom": "^19.0.0",
|
|
271
|
-
|
|
331
|
+
vite: "^7.0.0",
|
|
272
332
|
"@vitejs/plugin-react": "^4.0.0"
|
|
273
333
|
};
|
|
274
334
|
const missingDeps = [];
|
|
@@ -308,9 +368,26 @@ export default defineConfig({
|
|
|
308
368
|
}
|
|
309
369
|
consola.success("\nInitialization complete!");
|
|
310
370
|
consola.box("Next steps");
|
|
311
|
-
consola.info(
|
|
312
|
-
consola.
|
|
313
|
-
consola.
|
|
371
|
+
consola.info("1. Register RenderModule in your app.module.ts:");
|
|
372
|
+
consola.log(' import { RenderModule } from "@nestjs-ssr/react";');
|
|
373
|
+
consola.log(" @Module({");
|
|
374
|
+
consola.log(" imports: [RenderModule.forRoot()],");
|
|
375
|
+
consola.log(" })");
|
|
376
|
+
consola.info(`
|
|
377
|
+
2. Create your first view component in ${viewsDir}/`);
|
|
378
|
+
consola.info("3. Add a controller method with the @Render decorator:");
|
|
379
|
+
consola.log(' import { Render } from "@nestjs-ssr/react";');
|
|
380
|
+
consola.log(" @Get()");
|
|
381
|
+
consola.log(' @Render("YourComponent")');
|
|
382
|
+
consola.log(' home() { return { props: { message: "Hello" } }; }');
|
|
383
|
+
if (integrationType === "separate") {
|
|
384
|
+
consola.info("\n4. Start both servers:");
|
|
385
|
+
consola.log(" Terminal 1: pnpm dev:client (Vite on port 5173)");
|
|
386
|
+
consola.log(" Terminal 2: pnpm dev:server (NestJS)");
|
|
387
|
+
} else {
|
|
388
|
+
consola.info("\n4. Start the dev server: pnpm start:dev");
|
|
389
|
+
consola.info(" (Vite middleware will be integrated into NestJS)");
|
|
390
|
+
}
|
|
314
391
|
}
|
|
315
392
|
});
|
|
316
393
|
runMain(main);
|