@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 +1 -1
- package/dist/cli/init.js +339 -91
- package/dist/cli/init.mjs +341 -93
- package/dist/client.d.mts +319 -0
- package/dist/client.d.ts +319 -0
- package/dist/client.js +207 -0
- package/dist/client.mjs +200 -0
- package/dist/{index-C5Knql-9.d.mts → index-CaGD266H.d.ts} +2 -104
- package/dist/{index-C5Knql-9.d.ts → index-Dq1yt0sX.d.mts} +2 -104
- package/dist/index.d.mts +9 -321
- package/dist/index.d.ts +9 -321
- package/dist/index.js +144 -91
- package/dist/index.mjs +115 -63
- package/dist/render/index.d.mts +2 -1
- package/dist/render/index.d.ts +2 -1
- package/dist/render/index.js +134 -81
- package/dist/render/index.mjs +115 -63
- package/dist/render-response.interface-Dc-Kwb09.d.mts +104 -0
- package/dist/render-response.interface-Dc-Kwb09.d.ts +104 -0
- package/dist/templates/entry-client.tsx +6 -3
- package/dist/templates/entry-server.tsx +33 -2
- package/dist/templates/index.html +15 -12
- package/package.json +13 -1
- package/src/templates/entry-client.tsx +6 -3
- package/src/templates/entry-server.tsx +33 -2
- package/src/templates/index.html +15 -12
package/README.md
CHANGED
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("
|
|
80
|
-
const
|
|
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
|
|
83
|
-
const
|
|
84
|
-
if (
|
|
85
|
-
consola.consola.warn(
|
|
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(
|
|
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(
|
|
192
|
+
'@': resolve(process.cwd(), 'src'),
|
|
103
193
|
},
|
|
104
194
|
},
|
|
105
|
-
|
|
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(
|
|
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(
|
|
122
|
-
consola.consola.success("Created vite.config.
|
|
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 (
|
|
145
|
-
tsconfig.include
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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["
|
|
249
|
-
packageJson.scripts["
|
|
445
|
+
if (!packageJson.scripts["dev:nest"]) {
|
|
446
|
+
packageJson.scripts["dev:nest"] = "nest start --watch --watchAssets --preserveWatchOutput";
|
|
250
447
|
shouldUpdate = true;
|
|
251
448
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
consola.consola.success("
|
|
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
|
-
|
|
309
|
-
|
|
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.
|
|
316
|
-
consola.consola.
|
|
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);
|