@nestjs-ssr/react 0.2.0 → 0.2.2

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 CHANGED
@@ -27,7 +27,7 @@ Clean Architecture: layers separated, dependencies inward, business logic framew
27
27
  ## Quick Start
28
28
 
29
29
  ```bash
30
- npx nestjs-ssr init
30
+ npx @nestjs-ssr/react init
31
31
  ```
32
32
 
33
33
  ```typescript
package/dist/cli/init.js CHANGED
@@ -32,13 +32,80 @@ 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 }) {
38
42
  const cwd = process.cwd();
39
43
  const viewsDir = args.views;
44
+ const packageJsonPath = path.join(cwd, "package.json");
45
+ const tsconfigPath = path.join(cwd, "tsconfig.json");
40
46
  consola.consola.box("@nestjs-ssr/react initialization");
41
47
  consola.consola.start("Setting up your NestJS SSR React project...\n");
48
+ if (!fs.existsSync(packageJsonPath)) {
49
+ consola.consola.error("No package.json found in current directory");
50
+ consola.consola.info("Please run this command from your NestJS project root");
51
+ process.exit(1);
52
+ }
53
+ try {
54
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
55
+ const allDeps = {
56
+ ...packageJson.dependencies,
57
+ ...packageJson.devDependencies
58
+ };
59
+ const requiredNestDeps = [
60
+ "@nestjs/core",
61
+ "@nestjs/common"
62
+ ];
63
+ const missingNestDeps = requiredNestDeps.filter((dep) => !allDeps[dep]);
64
+ if (missingNestDeps.length > 0) {
65
+ consola.consola.error("This does not appear to be a NestJS project. Missing packages:");
66
+ consola.consola.info(` ${missingNestDeps.join(", ")}`);
67
+ consola.consola.info("\nPlease install NestJS first:");
68
+ consola.consola.info(" npm install @nestjs/core @nestjs/common @nestjs/platform-express");
69
+ consola.consola.info("\nOr create a new NestJS project:");
70
+ consola.consola.info(" npm i -g @nestjs/cli");
71
+ consola.consola.info(" nest new my-project");
72
+ process.exit(1);
73
+ }
74
+ const mainTsPath2 = path.join(cwd, "src/main.ts");
75
+ if (!fs.existsSync(mainTsPath2)) {
76
+ consola.consola.warn("No src/main.ts file found");
77
+ consola.consola.info("Make sure your NestJS application has a main entry point");
78
+ }
79
+ } catch (error) {
80
+ consola.consola.error("Failed to validate package.json:", error);
81
+ process.exit(1);
82
+ }
83
+ let integrationType = args.integration;
84
+ if (!integrationType) {
85
+ const response = await consola.consola.prompt("How do you want to run Vite during development?", {
86
+ type: "select",
87
+ options: [
88
+ {
89
+ label: "Separate server (Vite runs on its own port, e.g., 5173)",
90
+ value: "separate"
91
+ },
92
+ {
93
+ label: "Integrated with NestJS (Vite middleware runs within NestJS)",
94
+ value: "integrated"
95
+ }
96
+ ]
97
+ });
98
+ integrationType = response;
99
+ }
100
+ if (![
101
+ "separate",
102
+ "integrated"
103
+ ].includes(integrationType)) {
104
+ consola.consola.error(`Invalid integration type: "${integrationType}". Must be "separate" or "integrated"`);
105
+ process.exit(1);
106
+ }
107
+ consola.consola.info(`Using ${integrationType === "separate" ? "separate server" : "integrated"} mode
108
+ `);
42
109
  const templateLocations = [
43
110
  path.resolve(__dirname$1, "../../src/templates"),
44
111
  path.resolve(__dirname$1, "../templates")
@@ -49,7 +116,6 @@ var main = citty.defineCommand({
49
116
  consola.consola.info("Searched:", templateLocations);
50
117
  process.exit(1);
51
118
  }
52
- const tsconfigPath = path.join(cwd, "tsconfig.json");
53
119
  if (!fs.existsSync(tsconfigPath)) {
54
120
  consola.consola.error("No tsconfig.json found in project root");
55
121
  consola.consola.info("Please create a tsconfig.json file first");
@@ -76,21 +142,45 @@ var main = citty.defineCommand({
76
142
  fs.copyFileSync(entryServerSrc, entryServerDest);
77
143
  consola.consola.success(`Created ${viewsDir}/entry-server.tsx`);
78
144
  }
79
- consola.consola.start("Configuring vite.config.js...");
80
- const viteConfigPath = path.join(cwd, "vite.config.js");
145
+ consola.consola.start("Creating index.html...");
146
+ const indexHtmlSrc = path.join(templateDir, "index.html");
147
+ const indexHtmlDest = path.join(cwd, viewsDir, "index.html");
148
+ if (fs.existsSync(indexHtmlDest) && !args.force) {
149
+ consola.consola.warn(`${viewsDir}/index.html already exists (use --force to overwrite)`);
150
+ } else {
151
+ fs.copyFileSync(indexHtmlSrc, indexHtmlDest);
152
+ consola.consola.success(`Created ${viewsDir}/index.html`);
153
+ }
154
+ consola.consola.start("Configuring vite.config.ts...");
81
155
  const viteConfigTs = path.join(cwd, "vite.config.ts");
82
- const useTypeScript = fs.existsSync(viteConfigTs);
83
- const configPath = useTypeScript ? viteConfigTs : viteConfigPath;
84
- if (fs.existsSync(configPath)) {
85
- consola.consola.warn(`${useTypeScript ? "vite.config.ts" : "vite.config.js"} already exists`);
156
+ const viteConfigJs = path.join(cwd, "vite.config.js");
157
+ const existingConfig = fs.existsSync(viteConfigTs) || fs.existsSync(viteConfigJs);
158
+ if (existingConfig) {
159
+ consola.consola.warn("vite.config already exists");
86
160
  consola.consola.info("Please manually add to your Vite config:");
87
161
  consola.consola.log(" import { resolve } from 'path';");
162
+ if (integrationType === "separate") {
163
+ consola.consola.log(" server: {");
164
+ consola.consola.log(" port: 5173,");
165
+ consola.consola.log(" strictPort: true,");
166
+ consola.consola.log(" hmr: { port: 5173 },");
167
+ consola.consola.log(" },");
168
+ }
88
169
  consola.consola.log(" build: {");
89
170
  consola.consola.log(" rollupOptions: {");
90
- consola.consola.log(` input: { client: resolve(__dirname, '${viewsDir}/entry-client.tsx') }`);
171
+ consola.consola.log(` input: { client: resolve(process.cwd(), '${viewsDir}/entry-client.tsx') }`);
91
172
  consola.consola.log(" }");
92
173
  consola.consola.log(" }");
93
174
  } else {
175
+ const serverConfig = integrationType === "separate" ? ` server: {
176
+ port: 5173,
177
+ strictPort: true,
178
+ hmr: { port: 5173 },
179
+ },
180
+ ` : ` server: {
181
+ middlewareMode: true,
182
+ },
183
+ `;
94
184
  const viteConfig = `import { defineConfig } from 'vite';
95
185
  import react from '@vitejs/plugin-react';
96
186
  import { resolve } from 'path';
@@ -99,27 +189,34 @@ export default defineConfig({
99
189
  plugins: [react()],
100
190
  resolve: {
101
191
  alias: {
102
- '@': resolve(__dirname, 'src'),
192
+ '@': resolve(process.cwd(), 'src'),
103
193
  },
104
194
  },
105
- server: {
106
- port: 5173,
107
- strictPort: true,
108
- hmr: { port: 5173 },
109
- },
110
- build: {
195
+ ${serverConfig}build: {
111
196
  outDir: 'dist/client',
112
197
  manifest: true,
113
198
  rollupOptions: {
114
199
  input: {
115
- client: resolve(__dirname, '${viewsDir}/entry-client.tsx'),
200
+ client: resolve(process.cwd(), '${viewsDir}/entry-client.tsx'),
201
+ },
202
+ // Externalize optional native dependencies that shouldn't be bundled for browser
203
+ external: (id: string) => {
204
+ // Externalize fsevents - an optional macOS dependency
205
+ if (id.includes('/fsevents') || id.endsWith('fsevents')) {
206
+ return true;
207
+ }
208
+ // Externalize .node native addon files
209
+ if (id.endsWith('.node')) {
210
+ return true;
211
+ }
212
+ return false;
116
213
  },
117
214
  },
118
215
  },
119
216
  });
120
217
  `;
121
- fs.writeFileSync(viteConfigPath, viteConfig);
122
- consola.consola.success("Created vite.config.js");
218
+ fs.writeFileSync(viteConfigTs, viteConfig);
219
+ consola.consola.success("Created vite.config.ts");
123
220
  }
124
221
  consola.consola.start("Configuring tsconfig.json...");
125
222
  try {
@@ -141,14 +238,13 @@ export default defineConfig({
141
238
  ];
142
239
  updated = true;
143
240
  }
144
- if (!tsconfig.include) {
145
- tsconfig.include = [];
146
- }
147
- const hasTsx = tsconfig.include.some((pattern) => pattern.includes("**/*.tsx"));
148
- if (!hasTsx) {
149
- if (!tsconfig.include.includes("src/**/*.tsx")) {
150
- tsconfig.include.push("src/**/*.tsx");
151
- updated = true;
241
+ if (tsconfig.include && tsconfig.include.length > 0) {
242
+ const hasTsx = tsconfig.include.some((pattern) => pattern.includes("**/*.tsx"));
243
+ if (!hasTsx) {
244
+ if (!tsconfig.include.includes("src/**/*.tsx")) {
245
+ tsconfig.include.push("src/**/*.tsx");
246
+ updated = true;
247
+ }
152
248
  }
153
249
  }
154
250
  if (!tsconfig.exclude) {
@@ -230,90 +326,242 @@ export default defineConfig({
230
326
  } catch (error) {
231
327
  consola.consola.error("Failed to update nest-cli.json:", error);
232
328
  }
233
- consola.consola.start("Configuring build scripts...");
234
- const packageJsonPath = path.join(cwd, "package.json");
235
- if (!fs.existsSync(packageJsonPath)) {
236
- consola.consola.warn("No package.json found, skipping build script setup");
237
- } else {
238
- try {
239
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
240
- if (!packageJson.scripts) {
241
- packageJson.scripts = {};
329
+ consola.consola.start("Configuring main.ts...");
330
+ const mainTsPath = path.join(cwd, "src/main.ts");
331
+ try {
332
+ if (fs.existsSync(mainTsPath)) {
333
+ let mainTs = fs.readFileSync(mainTsPath, "utf-8");
334
+ if (mainTs.includes("enableShutdownHooks")) {
335
+ consola.consola.info("main.ts already has enableShutdownHooks()");
336
+ } else {
337
+ const createPattern = /(const\s+app\s*=\s*await\s+NestFactory\.create[^;]+;)/;
338
+ const match = mainTs.match(createPattern);
339
+ if (match) {
340
+ const createLine = match[1];
341
+ const replacement = `${createLine}
342
+
343
+ // Enable graceful shutdown for proper Vite cleanup
344
+ app.enableShutdownHooks();`;
345
+ mainTs = mainTs.replace(createLine, replacement);
346
+ fs.writeFileSync(mainTsPath, mainTs);
347
+ consola.consola.success("Added enableShutdownHooks() to main.ts");
348
+ } else {
349
+ consola.consola.warn("Could not find NestFactory.create in main.ts, please add manually:");
350
+ consola.consola.log(" app.enableShutdownHooks();");
351
+ }
352
+ }
353
+ }
354
+ } catch (error) {
355
+ consola.consola.warn("Failed to update main.ts:", error);
356
+ consola.consola.info("Please manually add to your main.ts after NestFactory.create():");
357
+ consola.consola.log(" app.enableShutdownHooks();");
358
+ }
359
+ consola.consola.start("Configuring app.module.ts...");
360
+ const appModulePath = path.join(cwd, "src/app.module.ts");
361
+ try {
362
+ if (fs.existsSync(appModulePath)) {
363
+ let appModule = fs.readFileSync(appModulePath, "utf-8");
364
+ if (appModule.includes("RenderModule")) {
365
+ consola.consola.info("app.module.ts already has RenderModule");
366
+ } else {
367
+ let updated = false;
368
+ const importStatement = "import { RenderModule } from '@nestjs-ssr/react';";
369
+ if (!appModule.includes(importStatement)) {
370
+ const nestImportPattern = /(import\s+.*from\s+['"]@nestjs\/[^'"]+['"];?\n)/g;
371
+ const matches = [
372
+ ...appModule.matchAll(nestImportPattern)
373
+ ];
374
+ if (matches.length > 0) {
375
+ const lastMatch = matches[matches.length - 1];
376
+ const insertPos = lastMatch.index + lastMatch[0].length;
377
+ appModule = appModule.slice(0, insertPos) + importStatement + "\n" + appModule.slice(insertPos);
378
+ updated = true;
379
+ } else {
380
+ const anyImportPattern = /(import\s+.*from\s+['"][^'"]+['"];?\n)/g;
381
+ const anyMatches = [
382
+ ...appModule.matchAll(anyImportPattern)
383
+ ];
384
+ if (anyMatches.length > 0) {
385
+ const lastMatch = anyMatches[anyMatches.length - 1];
386
+ const insertPos = lastMatch.index + lastMatch[0].length;
387
+ appModule = appModule.slice(0, insertPos) + importStatement + "\n" + appModule.slice(insertPos);
388
+ updated = true;
389
+ }
390
+ }
391
+ }
392
+ const importsPattern = /(imports:\s*\[)([^\]]*)/;
393
+ const importsMatch = appModule.match(importsPattern);
394
+ if (importsMatch) {
395
+ const existingImports = importsMatch[2].trim();
396
+ const renderModuleConfig = integrationType === "separate" ? "RenderModule.register({ vite: { mode: 'proxy', port: 5173 } })" : "RenderModule.forRoot()";
397
+ if (existingImports === "") {
398
+ appModule = appModule.replace(importsPattern, `$1${renderModuleConfig}`);
399
+ } else {
400
+ appModule = appModule.replace(importsPattern, `$1$2, ${renderModuleConfig}`);
401
+ }
402
+ updated = true;
403
+ }
404
+ if (updated) {
405
+ fs.writeFileSync(appModulePath, appModule);
406
+ consola.consola.success("Added RenderModule to app.module.ts");
407
+ } else {
408
+ consola.consola.warn("Could not automatically update app.module.ts, please add manually:");
409
+ consola.consola.log(` import { RenderModule } from '@nestjs-ssr/react';`);
410
+ consola.consola.log(" // In @Module imports:");
411
+ consola.consola.log(" RenderModule.forRoot()");
412
+ }
242
413
  }
243
- let shouldUpdate = false;
244
- if (!packageJson.scripts["build:client"]) {
245
- packageJson.scripts["build:client"] = "vite build --ssrManifest --outDir dist/client";
414
+ } else {
415
+ consola.consola.warn("No src/app.module.ts found");
416
+ consola.consola.info("Please manually add RenderModule to your app module");
417
+ }
418
+ } catch (error) {
419
+ consola.consola.warn("Failed to update app.module.ts:", error);
420
+ consola.consola.info("Please manually add to your app.module.ts:");
421
+ consola.consola.log(` import { RenderModule } from '@nestjs-ssr/react';`);
422
+ consola.consola.log(" // In @Module imports:");
423
+ consola.consola.log(" RenderModule.forRoot()");
424
+ }
425
+ consola.consola.start("Configuring build scripts...");
426
+ try {
427
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
428
+ if (!packageJson.scripts) {
429
+ packageJson.scripts = {};
430
+ }
431
+ let shouldUpdate = false;
432
+ if (!packageJson.scripts["build:client"]) {
433
+ packageJson.scripts["build:client"] = `vite build --ssrManifest --outDir dist/client && cp ${viewsDir}/index.html dist/client/index.html`;
434
+ shouldUpdate = true;
435
+ }
436
+ if (!packageJson.scripts["build:server"]) {
437
+ packageJson.scripts["build:server"] = `vite build --ssr ${viewsDir}/entry-server.tsx --outDir dist/server`;
438
+ shouldUpdate = true;
439
+ }
440
+ if (integrationType === "separate") {
441
+ if (!packageJson.scripts["dev:vite"]) {
442
+ packageJson.scripts["dev:vite"] = "vite --port 5173";
246
443
  shouldUpdate = true;
247
444
  }
248
- if (!packageJson.scripts["build:server"]) {
249
- packageJson.scripts["build:server"] = `vite build --ssr ${viewsDir}/entry-server.tsx --outDir dist/server`;
445
+ if (!packageJson.scripts["dev:nest"]) {
446
+ packageJson.scripts["dev:nest"] = "nest start --watch --watchAssets --preserveWatchOutput";
250
447
  shouldUpdate = true;
251
448
  }
252
- const existingBuild = packageJson.scripts.build;
253
- const recommendedBuild = "pnpm build:client && pnpm build:server && nest build";
254
- if (!existingBuild) {
449
+ packageJson.scripts["start:dev"] = 'concurrently --raw -n vite,nest -c cyan,green "pnpm:dev:vite" "pnpm:dev:nest"';
450
+ shouldUpdate = true;
451
+ }
452
+ const existingBuild = packageJson.scripts.build;
453
+ const recommendedBuild = "nest build && pnpm build:client && pnpm build:server";
454
+ if (!existingBuild) {
455
+ packageJson.scripts.build = recommendedBuild;
456
+ shouldUpdate = true;
457
+ consola.consola.success("Created build script");
458
+ } else if (existingBuild !== recommendedBuild) {
459
+ if (!existingBuild.includes("build:client") || !existingBuild.includes("build:server")) {
460
+ consola.consola.warn(`Found existing build script: "${existingBuild}"`);
461
+ consola.consola.info(`Updating to: ${recommendedBuild}`);
255
462
  packageJson.scripts.build = recommendedBuild;
256
463
  shouldUpdate = true;
257
- } else if (!existingBuild.includes("build:client") || !existingBuild.includes("build:server")) {
258
- consola.consola.warn(`Found existing build script: "${existingBuild}"`);
259
- consola.consola.info("SSR requires building client and server bundles");
260
- consola.consola.info(`Recommended: ${recommendedBuild}`);
261
- consola.consola.info("Please manually update your build script in package.json");
262
464
  } else {
263
465
  consola.consola.info("Build scripts already configured");
264
466
  }
265
- if (shouldUpdate) {
266
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
267
- consola.consola.success("Updated build scripts in package.json");
467
+ } else {
468
+ consola.consola.info("Build scripts already configured");
469
+ }
470
+ if (shouldUpdate) {
471
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
472
+ consola.consola.success("Updated build scripts in package.json");
473
+ }
474
+ if (!args["skip-install"]) {
475
+ consola.consola.start("Checking dependencies...");
476
+ const requiredDeps = {
477
+ react: "^19.0.0",
478
+ "react-dom": "^19.0.0",
479
+ vite: "^7.0.0",
480
+ "@vitejs/plugin-react": "^4.0.0"
481
+ };
482
+ const requiredDevDeps = {
483
+ "@types/react": "^19.0.0",
484
+ "@types/react-dom": "^19.0.0"
485
+ };
486
+ if (integrationType === "separate") {
487
+ requiredDeps["http-proxy-middleware"] = "^3.0.0";
488
+ requiredDevDeps["concurrently"] = "^9.0.0";
268
489
  }
269
- if (!args["skip-install"]) {
270
- consola.consola.start("Checking dependencies...");
271
- const requiredDeps = {
272
- react: "^19.0.0",
273
- "react-dom": "^19.0.0",
274
- vite: "^7.0.0",
275
- "@vitejs/plugin-react": "^4.0.0"
276
- };
277
- const missingDeps = [];
278
- const allDeps = {
279
- ...packageJson.dependencies,
280
- ...packageJson.devDependencies
281
- };
282
- for (const [dep, version] of Object.entries(requiredDeps)) {
283
- if (!allDeps[dep]) {
284
- missingDeps.push(`${dep}@${version}`);
285
- }
490
+ const missingDeps = [];
491
+ const missingDevDeps = [];
492
+ const allDeps = {
493
+ ...packageJson.dependencies,
494
+ ...packageJson.devDependencies
495
+ };
496
+ for (const [dep, version] of Object.entries(requiredDeps)) {
497
+ if (!allDeps[dep]) {
498
+ missingDeps.push(`${dep}@${version}`);
286
499
  }
287
- if (missingDeps.length > 0) {
288
- consola.consola.info(`Missing dependencies: ${missingDeps.join(", ")}`);
289
- let packageManager = "npm";
290
- if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) packageManager = "pnpm";
291
- else if (fs.existsSync(path.join(cwd, "yarn.lock"))) packageManager = "yarn";
292
- const installCmd = packageManager === "npm" ? `npm install ${missingDeps.join(" ")}` : `${packageManager} add ${missingDeps.join(" ")}`;
293
- try {
294
- consola.consola.start(`Installing dependencies with ${packageManager}...`);
295
- child_process.execSync(installCmd, {
296
- cwd,
297
- stdio: "inherit"
298
- });
299
- consola.consola.success("Dependencies installed!");
300
- } catch (error) {
301
- consola.consola.error("Failed to install dependencies:", error);
302
- consola.consola.info(`Please manually run: ${installCmd}`);
303
- }
304
- } else {
305
- consola.consola.success("All required dependencies are already installed");
500
+ }
501
+ for (const [dep, version] of Object.entries(requiredDevDeps)) {
502
+ if (!allDeps[dep]) {
503
+ missingDevDeps.push(`${dep}@${version}`);
504
+ }
505
+ }
506
+ let packageManager = "npm";
507
+ if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) packageManager = "pnpm";
508
+ else if (fs.existsSync(path.join(cwd, "yarn.lock"))) packageManager = "yarn";
509
+ if (missingDeps.length > 0) {
510
+ consola.consola.info(`Missing dependencies: ${missingDeps.join(", ")}`);
511
+ const installCmd = packageManager === "npm" ? `npm install ${missingDeps.join(" ")}` : `${packageManager} add ${missingDeps.join(" ")}`;
512
+ try {
513
+ consola.consola.start(`Installing dependencies with ${packageManager}...`);
514
+ child_process.execSync(installCmd, {
515
+ cwd,
516
+ stdio: "inherit"
517
+ });
518
+ consola.consola.success("Dependencies installed!");
519
+ } catch (error) {
520
+ consola.consola.error("Failed to install dependencies:", error);
521
+ consola.consola.info(`Please manually run: ${installCmd}`);
522
+ }
523
+ }
524
+ if (missingDevDeps.length > 0) {
525
+ consola.consola.info(`Missing dev dependencies: ${missingDevDeps.join(", ")}`);
526
+ const installDevCmd = packageManager === "npm" ? `npm install -D ${missingDevDeps.join(" ")}` : `${packageManager} add -D ${missingDevDeps.join(" ")}`;
527
+ try {
528
+ consola.consola.start(`Installing dev dependencies with ${packageManager}...`);
529
+ child_process.execSync(installDevCmd, {
530
+ cwd,
531
+ stdio: "inherit"
532
+ });
533
+ consola.consola.success("Dev dependencies installed!");
534
+ } catch (error) {
535
+ consola.consola.error("Failed to install dev dependencies:", error);
536
+ consola.consola.info(`Please manually run: ${installDevCmd}`);
306
537
  }
307
538
  }
308
- } catch (error) {
309
- consola.consola.error("Failed to update package.json:", error);
539
+ if (missingDeps.length === 0 && missingDevDeps.length === 0) {
540
+ consola.consola.success("All required dependencies are already installed");
541
+ }
310
542
  }
543
+ } catch (error) {
544
+ consola.consola.error("Failed to update package.json:", error);
311
545
  }
312
546
  consola.consola.success("\nInitialization complete!");
313
547
  consola.consola.box("Next steps");
314
548
  consola.consola.info(`1. Create your first view component in ${viewsDir}/`);
315
- consola.consola.info("2. Render it from a NestJS controller using render.render()");
316
- consola.consola.info("3. Run your dev server with: pnpm start:dev");
549
+ consola.consola.info("2. Add a controller method with the @Render decorator:");
550
+ consola.consola.log(' import { Render } from "@nestjs-ssr/react";');
551
+ consola.consola.log(" @Get()");
552
+ consola.consola.log(" @Render(Home)");
553
+ consola.consola.log(' home() { return { message: "Hello" }; }');
554
+ if (integrationType === "separate") {
555
+ consola.consola.info("\n3. Start development with HMR:");
556
+ consola.consola.log(" pnpm start:dev");
557
+ consola.consola.info(" This runs both Vite (port 5173) and NestJS concurrently");
558
+ consola.consola.info("\n Or run them separately:");
559
+ consola.consola.log(" Terminal 1: pnpm dev:vite");
560
+ consola.consola.log(" Terminal 2: pnpm dev:nest");
561
+ } else {
562
+ consola.consola.info("\n3. Start the dev server: pnpm start:dev");
563
+ consola.consola.info(" (Vite middleware will be integrated into NestJS)");
564
+ }
317
565
  }
318
566
  });
319
567
  citty.runMain(main);