create-stackflow 1.0.5 → 1.0.8

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
@@ -65,7 +65,7 @@ Features:
65
65
  - React Router DOM
66
66
  - Zustand / Redux Toolkit / Context API
67
67
  - React Hook Form
68
- - Zod / Yup validation
68
+ - Zod / Yup validation (Joi is backend-only)
69
69
  - Axios API layer
70
70
  - TanStack Query
71
71
  - Protected routes
@@ -141,14 +141,12 @@ StackFlow automatically generates:
141
141
  my-app/
142
142
 
143
143
  ├── frontend/
144
+ │ └── package.json
144
145
 
145
146
  ├── backend/
147
+ │ └── package.json
146
148
 
147
- ├── package.json
148
-
149
- ├── .gitignore
150
-
151
- └── README.md
149
+ └── node_modules/ (hoisted at project root after install)
152
150
  ```
153
151
 
154
152
  ---
package/cli.js CHANGED
@@ -8,7 +8,7 @@ const program = new Command();
8
8
  program
9
9
  .name("create-stackflow")
10
10
  .description("Generate a production-minded MERN starter with frontend, backend, auth, CRUD, and dashboard UI.")
11
- .version("1.0.5")
11
+ .version("1.0.8")
12
12
  .argument("[project-name]", "project folder name")
13
13
  .option("--skip-install", "generate files without installing dependencies")
14
14
  .option("--yes", "use recommended defaults")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-stackflow",
3
- "version": "1.0.5",
3
+ "version": "1.0.8",
4
4
  "description": "Interactive CLI for generating modern full-stack MERN applications.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,6 +10,7 @@ import { createRootFiles } from "../generators/root.js";
10
10
  import { createBackend } from "../generators/backend.js";
11
11
  import { createFrontend } from "../generators/frontend.js";
12
12
  import { formatName } from "../utils/names.js";
13
+ import { installHoistedWorkspace } from "../utils/install.js";
13
14
 
14
15
  export async function createStackFlow(options) {
15
16
  printBanner();
@@ -46,13 +47,13 @@ export async function createStackFlow(options) {
46
47
 
47
48
  await fs.ensureDir(projectDir);
48
49
 
49
- await step("Creating root workspace", () => createRootFiles(context));
50
50
  await createFrontend(context);
51
51
  await createBackend(context);
52
+ await step("Writing project gitignore files", () => createRootFiles(context));
52
53
 
53
54
  if (!context.skipInstall) {
54
- await step("Installing root workspace dependencies", () =>
55
- execa("npm", ["install"], { cwd: projectDir, stdio: "ignore" }),
55
+ await step("Installing dependencies (root node_modules)", () =>
56
+ installHoistedWorkspace(context),
56
57
  );
57
58
  }
58
59
 
@@ -61,7 +62,7 @@ export async function createStackFlow(options) {
61
62
  if (context.runProject) {
62
63
  console.log(chalk.cyan("\n🚀 Starting the project..."));
63
64
  try {
64
- await execa("npm", ["run", "dev"], { cwd: projectDir, stdio: "inherit" });
65
+ await execa("npm", ["run", "dev"], { cwd: context.frontendDir, stdio: "inherit" });
65
66
  } catch (error) {
66
67
  console.error(chalk.red("\nFailed to start the project."));
67
68
  console.error(chalk.dim(error.message));
@@ -77,6 +78,7 @@ function recommendedDefaults(projectName) {
77
78
  frontend: "react",
78
79
  language: "typescript",
79
80
  backendLanguage: "javascript",
81
+ backendModule: "esm",
80
82
  styling: "tailwind",
81
83
  tailwind: true,
82
84
  uiLibrary: "shadcn",
@@ -103,17 +105,16 @@ function recommendedDefaults(projectName) {
103
105
  orm: "Mongoose",
104
106
  authStrategy: "JWT",
105
107
  passwordHashing: "bcryptjs",
106
- fileUpload: "Multer",
108
+ fileUpload: "None",
107
109
  backendValidation: "None",
108
110
  securityPackages: ["helmet", "cors", "express-rate-limit", "hpp"],
109
- cookieParser: true,
110
111
  logging: "Morgan",
111
- swagger: true,
112
+ swagger: false,
112
113
  socketio: false,
113
114
  winston: false,
114
115
  morgan: true,
115
116
  cloudinary: false,
116
- multer: true,
117
+ multer: false,
117
118
  helmet: true,
118
119
  cors: true,
119
120
  rateLimit: true,
@@ -163,7 +164,7 @@ function printSuccess(context) {
163
164
  console.log(chalk.green.bold("StackFlow app created successfully."));
164
165
  console.log();
165
166
  console.log(chalk.white("Next steps:"));
166
- console.log(chalk.cyan(` cd ${context.projectName}`));
167
+ console.log(chalk.cyan(` cd ${context.projectName}/${frontend}`));
167
168
  console.log(chalk.cyan(" npm run dev"));
168
169
  console.log();
169
170
  console.log(chalk.dim(`Frontend: ${frontend}`));
@@ -1,8 +1,12 @@
1
1
  import path from "node:path";
2
2
  import fs from "fs-extra";
3
3
  import ora from "ora";
4
- import { execa } from "execa";
5
4
  import { ext, writeTemplate } from "../utils/template.js";
5
+ import {
6
+ chatBackendFiles,
7
+ chatServerSocketBlock,
8
+ patchMainRouteForChat,
9
+ } from "./chat.js";
6
10
 
7
11
  export async function createBackend(context) {
8
12
  const spinner = ora("Generating backend API").start();
@@ -15,26 +19,16 @@ export async function createBackend(context) {
15
19
  throw error;
16
20
  }
17
21
 
18
- if (!context.skipInstall) {
19
- const install = ora("Installing backend dependencies").start();
20
- try {
21
- await execa("npm", ["install"], { cwd: context.backendDir, stdio: "ignore" });
22
- install.succeed("Installing backend dependencies");
23
- } catch (error) {
24
- install.fail("Installing backend dependencies");
25
- throw error;
26
- }
27
- }
28
22
  }
29
23
 
30
24
  async function writeBackendFiles(context) {
31
25
  const backendContext = { ...context, language: context.backendLanguage || context.language };
32
26
  const e = ext(backendContext);
33
27
  const isTs = backendContext.language === "typescript";
28
+ const esm = isTs || context.backendModule !== "cjs";
34
29
  const clientUrl = context.frontend === "next" ? "http://localhost:3000" : "http://localhost:5173";
35
30
  const deps = {
36
31
  bcryptjs: "latest",
37
- "cookie-parser": context.cookieParser === false ? undefined : "latest",
38
32
  cors: context.cors === false ? undefined : "latest",
39
33
  dotenv: "latest",
40
34
  express: "latest",
@@ -54,7 +48,6 @@ async function writeBackendFiles(context) {
54
48
  const devDeps = isTs
55
49
  ? {
56
50
  "@types/bcryptjs": "latest",
57
- "@types/cookie-parser": "latest",
58
51
  "@types/cors": "latest",
59
52
  "@types/express": "latest",
60
53
  "@types/jsonwebtoken": "latest",
@@ -70,7 +63,7 @@ async function writeBackendFiles(context) {
70
63
  await writeTemplate(path.join(context.backendDir, "package.json"), JSON.stringify({
71
64
  name: path.basename(context.backendDir),
72
65
  version: "1.0.0",
73
- type: "module",
66
+ ...(esm ? { type: "module" } : {}),
74
67
  private: true,
75
68
  scripts: {
76
69
  dev: isTs ? "tsx watch src/server.ts" : "nodemon src/server.js",
@@ -85,8 +78,8 @@ async function writeBackendFiles(context) {
85
78
  await writeTemplate(path.join(context.backendDir, "tsconfig.json"), `{
86
79
  "compilerOptions": {
87
80
  "target": "ES2022",
88
- "module": "NodeNext",
89
- "moduleResolution": "NodeNext",
81
+ "module": "${context.backendModule === "cjs" ? "CommonJS" : "NodeNext"}",
82
+ "moduleResolution": "${context.backendModule === "cjs" ? "Node" : "NodeNext"}",
90
83
  "strict": false,
91
84
  "noImplicitAny": false,
92
85
  "noCheck": true,
@@ -106,7 +99,6 @@ CLIENT_URL={{clientUrl}}
106
99
  MONGODB_URI=mongodb://127.0.0.1:27017/{{databaseName}}
107
100
  JWT_SECRET=change_this_super_secret_value
108
101
  JWT_EXPIRES_IN=7d
109
- COOKIE_NAME=stackflow_token
110
102
  {{cloudinaryEnv}}
111
103
  `, { ...context, clientUrl, cloudinaryEnv: context.cloudinary ? "CLOUDINARY_CLOUD_NAME=\nCLOUDINARY_API_KEY=\nCLOUDINARY_API_SECRET=" : "" });
112
104
 
@@ -116,19 +108,16 @@ COOKIE_NAME=stackflow_token
116
108
  "src/middleware",
117
109
  "src/models",
118
110
  "src/routes",
119
- "src/services",
120
111
  "src/utils",
121
- "src/validations"
122
112
  ];
123
113
  if (context.multer) dirs.push("uploads");
124
114
  await Promise.all(dirs.map((dir) => fs.ensureDir(path.join(context.backendDir, dir))));
125
115
  if (context.multer) await fs.outputFile(path.join(context.backendDir, "uploads", ".gitkeep"), "");
126
116
 
127
- const files = backendTemplates({ ...context, language: backendContext.language, clientUrl }, e);
128
-
129
- // Filter out unused configuration files
117
+ const files = backendTemplates({ ...context, language: backendContext.language, clientUrl, esm }, e);
118
+
130
119
  if (!context.cloudinary) delete files[`src/config/cloudinary.${e}`];
131
- if (!context.multer) delete files[`src/services/uploadService.${e}`];
120
+ if (!context.multer) delete files[`src/middleware/uploadMiddleware.${e}`];
132
121
  if (!context.winston) delete files[`src/utils/logger.${e}`];
133
122
  if (!context.swagger) delete files[`src/config/swagger.${e}`];
134
123
 
@@ -143,58 +132,62 @@ function compact(object) {
143
132
 
144
133
  function backendTemplates(context, e) {
145
134
  const isTs = context.language === "typescript";
135
+ const esm = context.esm !== false;
136
+ const rel = (p) => (esm ? `${p}.js` : p);
137
+ const im = (binding, from, relative = false) => {
138
+ const src = relative ? rel(from) : from;
139
+ return esm
140
+ ? `import ${binding} from "${src}";`
141
+ : `const ${binding} = require("${src}");`;
142
+ };
143
+ const imDefault = (binding, from, relative = false) => im(binding, from, relative);
144
+ const imn = (names, from, relative = false) => {
145
+ const src = relative ? rel(from) : from;
146
+ return esm
147
+ ? `import { ${names} } from "${src}";`
148
+ : `const { ${names} } = require("${src}");`;
149
+ };
146
150
  const type = (name) => isTs ? `: ${name}` : "";
147
151
  const reqRes = isTs ? "import type { Request, Response, NextFunction } from \"express\";\n" : "";
148
152
  const imports = {
149
- logger: context.winston ? "import { logger } from \"./utils/logger.js\";" : "",
150
- morgan: context.morgan ? "import morgan from \"morgan\";" : "",
151
- swagger: context.swagger ? "import { setupSwagger } from \"./config/swagger.js\";" : "",
152
- socket: context.socketio ? "import { Server } from \"socket.io\";" : ""
153
+ logger: context.winston ? (esm ? imn("logger", "./utils/logger", true) : imn("logger", "./utils/logger", true)) : "",
154
+ morgan: context.morgan ? im("morgan", "morgan") : "",
155
+ swagger: context.swagger ? (esm ? imn("setupSwagger", "./config/swagger", true) : imn("setupSwagger", "./config/swagger", true)) : "",
156
+ socket: context.socketio ? imn("Server", "socket.io") : "",
153
157
  };
154
158
 
155
- return {
156
- [`src/server.${e}`]: `import http from "node:http";
157
- import app from "./app.js";
158
- import { connectDB } from "./config/db.js";
159
+ const templates = {
160
+ [`src/server.${e}`]: `${im("http", "node:http")}
161
+ ${imDefault("app", "./app", true)}
162
+ ${imn("connectDB", "./config/db", true)}
159
163
  ${imports.socket}
160
164
  ${imports.logger}
161
165
 
162
166
  const PORT = Number(process.env.PORT) || 5000;
163
167
 
164
- async function bootstrap() {
168
+ async function startServer() {
165
169
  await connectDB();
166
170
  const server = http.createServer(app);
167
- ${context.socketio ? `
168
- const io = new Server(server, {
169
- cors: {
170
- origin: process.env.CLIENT_URL || "${context.clientUrl}",
171
- credentials: true
172
- }
173
- });
174
-
175
- io.on("connection", (socket) => {
176
- socket.emit("connected", { message: "Socket.IO connected" });
177
- });
178
- ` : ""}
171
+ ${chatServerSocketBlock(context, context.clientUrl, { imn })}
179
172
  server.listen(PORT, () => {
180
173
  ${context.winston ? "logger.info(`API running on http://localhost:${PORT}`);" : "console.log(`API running on http://localhost:${PORT}`);"}
181
174
  });
182
175
  }
183
176
 
184
- bootstrap().catch((error) => {
177
+ startServer().catch((error) => {
185
178
  ${context.winston ? "logger.error(error);" : "console.error(error);"}
186
179
  process.exit(1);
187
180
  });
188
181
  `,
189
182
  [`src/app.${e}`]: `import "dotenv/config";
190
183
  import express from "express";
191
- ${context.cookieParser === false ? "" : "import cookieParser from \"cookie-parser\";"}
192
184
  ${context.cors === false ? "" : "import cors from \"cors\";"}
193
185
  ${context.helmet === false ? "" : "import helmet from \"helmet\";"}
194
186
  ${context.rateLimit === false ? "" : "import rateLimit from \"express-rate-limit\";"}
195
187
  ${context.hpp ? "import hpp from \"hpp\";" : ""}
196
188
  ${imports.morgan}
197
189
  ${imports.swagger}
190
+ ${context.multer ? "import { uploadsStatic } from \"./middleware/uploadMiddleware.js\";" : ""}
198
191
  import apiRoutes from "./routes/mainRoute.js";
199
192
  import { errorHandler } from "./middleware/errorMiddleware.js";
200
193
  import { notFound } from "./middleware/notFoundMiddleware.js";
@@ -208,17 +201,12 @@ ${context.cors === false ? "" : `app.use(cors({
208
201
  credentials: true
209
202
  }));`}
210
203
  ${context.rateLimit === false ? "" : "app.use(rateLimit({ windowMs: 15 * 60 * 1000, limit: 300 }));"}
211
- app.use("/uploads", express.static("uploads"));
204
+ ${context.multer ? 'app.use("/uploads", uploadsStatic);' : ""}
212
205
  app.use(express.json({ limit: "1mb" }));
213
206
  app.use(express.urlencoded({ extended: true }));
214
- ${context.cookieParser === false ? "" : "app.use(cookieParser());"}
215
207
  ${context.morgan ? "app.use(morgan(\"dev\"));" : ""}
216
208
  ${context.swagger ? "setupSwagger(app);" : ""}
217
209
 
218
- app.get("/api/health", (_req, res) => {
219
- res.json({ status: "ok", service: "stackflow-api" });
220
- });
221
-
222
210
  app.use("/api", apiRoutes);
223
211
  app.use(notFound);
224
212
  app.use(errorHandler);
@@ -274,37 +262,30 @@ export function signToken(userId) {
274
262
  expiresIn: process.env.JWT_EXPIRES_IN || "7d"
275
263
  });
276
264
  }
277
-
278
- export function setAuthCookie(res, token) {
279
- res.cookie(process.env.COOKIE_NAME || "stackflow_token", token, {
280
- httpOnly: true,
281
- sameSite: "lax",
282
- secure: process.env.NODE_ENV === "production",
283
- maxAge: 7 * 24 * 60 * 60 * 1000
284
- });
285
- }
286
265
  `,
287
266
  [`src/middleware/authMiddleware.${e}`]: `${reqRes}import jwt from "jsonwebtoken";
288
267
  import { User } from "../models/userModel.js";
289
268
 
290
269
  export async function protect(req${type("Request & { user?: unknown }")}, res${type("Response")}, next${type("NextFunction")}) {
291
270
  try {
292
- const cookieToken = req.cookies?.[process.env.COOKIE_NAME || "stackflow_token"];
293
- const headerToken = req.headers.authorization?.startsWith("Bearer ")
271
+ const token = req.headers.authorization?.startsWith("Bearer ")
294
272
  ? req.headers.authorization.split(" ")[1]
295
273
  : undefined;
296
- const token = cookieToken || headerToken;
297
- if (!token) return res.status(401).json({ message: "Authentication required" });
274
+ if (!token) {
275
+ return res.status(401).json({ success: false, message: "Authentication required" });
276
+ }
298
277
 
299
278
  const decoded = jwt.verify(token, process.env.JWT_SECRET || "dev_secret");
300
279
  const userId = typeof decoded === "object" && "userId" in decoded ? decoded.userId : null;
301
280
  const user = await User.findById(userId).select("-password");
302
- if (!user) return res.status(401).json({ message: "Invalid authentication" });
281
+ if (!user) {
282
+ return res.status(401).json({ success: false, message: "Invalid authentication" });
283
+ }
303
284
 
304
285
  req.user = user;
305
286
  next();
306
287
  } catch {
307
- res.status(401).json({ message: "Invalid or expired token" });
288
+ res.status(401).json({ success: false, message: "Invalid or expired token" });
308
289
  }
309
290
  }
310
291
  `,
@@ -321,49 +302,75 @@ export async function protect(req${type("Request & { user?: unknown }")}, res${t
321
302
  }
322
303
  `,
323
304
  [`src/controllers/authController.${e}`]: `import { User } from "../models/userModel.js";
324
- import { signToken, setAuthCookie } from "../utils/jwt.js";
305
+ import { signToken } from "../utils/jwt.js";
325
306
 
326
307
  function publicUser(user) {
327
308
  return { id: user._id, name: user.name, email: user.email };
328
309
  }
329
310
 
330
- export async function register(req, res, next) {
331
- try {
332
- const { name, email, password } = req.body;
333
- const existing = await User.findOne({ email });
334
- if (existing) return res.status(409).json({ message: "Email already registered" });
335
- const user = await User.create({ name, email, password });
336
- const token = signToken(user._id);
337
- setAuthCookie(res, token);
338
- res.status(201).json({ user: publicUser(user), token });
339
- } catch (error) {
340
- next(error);
341
- }
342
- }
311
+ const authController = {
312
+ // Register
313
+ register: async (req, res) => {
314
+ try {
315
+ const { name, email, password } = req.body;
316
+ const existing = await User.findOne({ email });
317
+ if (existing) {
318
+ return res.status(409).json({ success: false, message: "Email already registered" });
319
+ }
320
+ const user = await User.create({ name, email, password });
321
+ const token = signToken(user._id);
322
+ res.status(201).json({
323
+ success: true,
324
+ message: "Account created successfully",
325
+ data: { user: publicUser(user), token }
326
+ });
327
+ } catch (error) {
328
+ res.status(500).json({ success: false, message: error.message });
329
+ }
330
+ },
343
331
 
344
- export async function login(req, res, next) {
345
- try {
346
- const { email, password } = req.body;
347
- const user = await User.findOne({ email });
348
- if (!user || !(await user.comparePassword(password))) {
349
- return res.status(401).json({ message: "Invalid email or password" });
332
+ // Login
333
+ login: async (req, res) => {
334
+ try {
335
+ const { email, password } = req.body;
336
+ const user = await User.findOne({ email });
337
+ if (!user || !(await user.comparePassword(password))) {
338
+ return res.status(401).json({ success: false, message: "Invalid email or password" });
339
+ }
340
+ const token = signToken(user._id);
341
+ res.status(200).json({
342
+ success: true,
343
+ message: "Logged in successfully",
344
+ data: { user: publicUser(user), token }
345
+ });
346
+ } catch (error) {
347
+ res.status(500).json({ success: false, message: error.message });
350
348
  }
351
- const token = signToken(user._id);
352
- setAuthCookie(res, token);
353
- res.json({ user: publicUser(user), token });
354
- } catch (error) {
355
- next(error);
356
- }
357
- }
349
+ },
358
350
 
359
- export async function me(req, res) {
360
- res.json({ user: publicUser(req.user) });
361
- }
351
+ // Current user
352
+ me: async (req, res) => {
353
+ try {
354
+ res.status(200).json({
355
+ success: true,
356
+ message: "Profile fetched successfully",
357
+ data: { user: publicUser(req.user) }
358
+ });
359
+ } catch (error) {
360
+ res.status(500).json({ success: false, message: error.message });
361
+ }
362
+ },
362
363
 
363
- export async function logout(_req, res) {
364
- res.clearCookie(process.env.COOKIE_NAME || "stackflow_token");
365
- res.json({ message: "Logged out" });
366
- }
364
+ // Logout (client clears token)
365
+ logout: async (_req, res) => {
366
+ res.status(200).json({
367
+ success: true,
368
+ message: "Logged out successfully"
369
+ });
370
+ }
371
+ };
372
+
373
+ export default authController;
367
374
  `,
368
375
  [`src/controllers/todoController.${e}`]: `import { Todo } from "../models/todoModel.js";
369
376
 
@@ -450,15 +457,15 @@ const todoController = {
450
457
  export default todoController;
451
458
  `,
452
459
  [`src/routes/authRoute.${e}`]: `import { Router } from "express";
453
- import { login, logout, me, register } from "../controllers/authController.js";
460
+ import authController from "../controllers/authController.js";
454
461
  import { protect } from "../middleware/authMiddleware.js";
455
462
 
456
463
  const router = Router();
457
464
 
458
- router.post("/register", register);
459
- router.post("/login", login);
460
- router.get("/me", protect, me);
461
- router.post("/logout", logout);
465
+ router.post("/register", authController.register);
466
+ router.post("/login", authController.login);
467
+ router.get("/me", protect, authController.me);
468
+ router.post("/logout", protect, authController.logout);
462
469
 
463
470
  export default router;
464
471
  `,
@@ -476,7 +483,7 @@ export default router;
476
483
  [`src/routes/todoRoute.${e}`]: `import { Router } from "express";
477
484
  import todoController from "../controllers/todoController.js";
478
485
  import { protect } from "../middleware/authMiddleware.js";
479
- ${context.multer ? "import { upload } from \"../services/uploadService.js\";" : ""}
486
+ ${context.multer ? "import { upload } from \"../middleware/uploadMiddleware.js\";" : ""}
480
487
 
481
488
  const router = Router();
482
489
 
@@ -489,34 +496,35 @@ router.delete("/:id", todoController.deleteTodo);
489
496
 
490
497
  export default router;
491
498
  `,
492
- [`src/services/uploadService.${e}`]: context.multer ? `import multer from "multer";
493
-
494
- const storage = multer.diskStorage({
499
+ [`src/middleware/uploadMiddleware.${e}`]: context.multer
500
+ ? `${im("multer", "multer")}
501
+ ${im("express", "express")}
502
+ ${esm ? "" : "const { diskStorage } = multer;\n"}
503
+ const storage = ${esm ? "multer.diskStorage" : "diskStorage"}({
495
504
  destination: "uploads/",
496
505
  filename: (_req, file, callback) => {
497
506
  callback(null, \`\${Date.now()}-\${file.originalname}\`);
498
507
  }
499
508
  });
500
509
 
501
- export const upload = multer({ storage });
502
- ` : `export {};
503
- `,
504
- [`src/validations/authValidation.${e}`]: `export const authValidation = {
505
- register: ["name", "email", "password"],
506
- login: ["email", "password"]
507
- };
508
- `,
509
- [`src/utils/logger.${e}`]: context.winston ? `import winston from "winston";
510
+ ${esm ? "export const upload = multer({ storage });" : "const upload = multer({ storage });\n"}
511
+ ${esm ? "export const uploadsStatic = express.static(\"uploads\");" : "const uploadsStatic = express.static(\"uploads\");\n"}
512
+ ${esm ? "" : "module.exports = { upload, uploadsStatic };"}
513
+ `
514
+ : undefined,
515
+ [`src/utils/logger.${e}`]: context.winston
516
+ ? `${im("winston", "winston")}
510
517
 
511
- export const logger = winston.createLogger({
518
+ const logger = winston.createLogger({
512
519
  level: "info",
513
520
  format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
514
521
  transports: [new winston.transports.Console()]
515
522
  });
516
- ` : `export const logger = console;
517
- `,
518
- [`src/config/swagger.${e}`]: context.swagger ? `import swaggerJSDoc from "swagger-jsdoc";
519
- import swaggerUi from "swagger-ui-express";
523
+ ${esm ? "export { logger };" : "module.exports = { logger };"}
524
+ `
525
+ : undefined,
526
+ [`src/config/swagger.${e}`]: context.swagger ? `${im("swaggerJSDoc", "swagger-jsdoc")}
527
+ ${im("swaggerUi", "swagger-ui-express")}
520
528
 
521
529
  export function setupSwagger(app) {
522
530
  const spec = swaggerJSDoc({
@@ -529,9 +537,11 @@ export function setupSwagger(app) {
529
537
 
530
538
  app.use("/api/docs", swaggerUi.serve, swaggerUi.setup(spec));
531
539
  }
532
- ` : `export function setupSwagger() {}
533
- `,
534
- [`src/config/cloudinary.${e}`]: context.cloudinary ? `import { v2 as cloudinary } from "cloudinary";
540
+ ${esm ? "" : "\nmodule.exports = { setupSwagger };"}
541
+ `
542
+ : undefined,
543
+ [`src/config/cloudinary.${e}`]: context.cloudinary
544
+ ? `${esm ? 'import { v2 as cloudinary } from "cloudinary";' : 'const { v2: cloudinary } = require("cloudinary");'}
535
545
 
536
546
  cloudinary.config({
537
547
  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
@@ -539,8 +549,25 @@ cloudinary.config({
539
549
  api_secret: process.env.CLOUDINARY_API_SECRET
540
550
  });
541
551
 
542
- export { cloudinary };
543
- ` : `export {};
552
+ ${esm ? "export { cloudinary };" : "module.exports = { cloudinary };"}
544
553
  `
554
+ : undefined,
555
+ };
556
+
557
+ const merged = {
558
+ ...templates,
559
+ ...chatBackendFiles(context, e, { im, imn, esm }),
545
560
  };
561
+
562
+ if (merged[`src/routes/mainRoute.${e}`]) {
563
+ merged[`src/routes/mainRoute.${e}`] = patchMainRouteForChat(
564
+ merged[`src/routes/mainRoute.${e}`],
565
+ context,
566
+ e,
567
+ );
568
+ }
569
+
570
+ return Object.fromEntries(
571
+ Object.entries(merged).filter(([, value]) => value !== undefined),
572
+ );
546
573
  }