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