create-backend-apps-express 1.0.0

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.
Files changed (2) hide show
  1. package/index.js +357 -0
  2. package/package.json +24 -0
package/index.js ADDED
@@ -0,0 +1,357 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import { execSync } from "child_process";
6
+ import inquirer from "inquirer";
7
+ import cliProgress from "cli-progress";
8
+
9
+ function write(file, content) {
10
+ fs.writeFileSync(file, content);
11
+ }
12
+
13
+ function updatePackageJson(basePath, isTS, packageManager) {
14
+ const pkgPath = path.join(basePath, "package.json");
15
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
16
+
17
+ pkg.type = "module";
18
+
19
+ if (packageManager === "npm" || packageManager === "pnpm") {
20
+ pkg.scripts = {
21
+ dev: isTS
22
+ ? "nodemon --watch src --exec tsx src/index.ts"
23
+ : "nodemon src/index.js",
24
+ start: isTS ? "node dist/index.js" : "node src/index.js",
25
+ };
26
+ } else if (packageManager === "yarn") {
27
+ pkg.scripts = {
28
+ dev: isTS
29
+ ? "yarn nodemon --watch src --exec tsx src/index.ts"
30
+ : "yarn nodemon src/index.js",
31
+ start: isTS ? "node dist/index.js" : "node src/index.js",
32
+ };
33
+ } else if (packageManager === "bun") {
34
+ pkg.scripts = {
35
+ dev: isTS ? "bun run tsx src/index.ts" : "bun run src/index.js",
36
+ start: isTS ? "bun run src/index.ts" : "bun run src/index.js",
37
+ };
38
+ }
39
+
40
+ if (isTS) pkg.main = "dist/index.js";
41
+
42
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
43
+ }
44
+
45
+ function createStructure(
46
+ base,
47
+ isTS,
48
+ useCluster,
49
+ usePrisma,
50
+ useCors,
51
+ envConfig,
52
+ ) {
53
+ const folders = [
54
+ "src",
55
+ "src/controllers",
56
+ "src/routes",
57
+ "src/utils",
58
+ "src/models",
59
+ "src/schema",
60
+ "src/middlewares",
61
+ "src/config",
62
+ ];
63
+
64
+ folders.forEach((f) => fs.mkdirSync(path.join(base, f), { recursive: true }));
65
+
66
+ const indexContent = isTS
67
+ ? `import express from "express";
68
+ import dotenv from "dotenv";
69
+ ${useCluster ? 'import cluster from "cluster";\nimport os from "os";' : ""}
70
+ ${useCors ? 'import cors from "cors";' : ""}
71
+
72
+ dotenv.config();
73
+
74
+ const app = express();
75
+ ${useCors ? "app.use(cors());" : ""}
76
+ app.use(express.json());
77
+
78
+ app.get("/", (req, res) => {
79
+ res.send("API Running...");
80
+ });
81
+
82
+ const PORT = process.env.PORT || ${envConfig.PORT};
83
+
84
+ ${
85
+ useCluster
86
+ ? `if (cluster.isPrimary) {
87
+ const totalCPUs = os.cpus().length;
88
+ for (let i = 0; i < totalCPUs; i++) cluster.fork();
89
+ cluster.on("exit", () => cluster.fork());
90
+ } else {
91
+ app.listen(PORT, () => {
92
+ console.log("Server running on port " + PORT);
93
+ });
94
+ }`
95
+ : `app.listen(PORT, () => {
96
+ console.log("Server running on port " + PORT);
97
+ });`
98
+ }
99
+ `
100
+ : `import express from "express";
101
+ import dotenv from "dotenv";
102
+ ${useCors ? 'import cors from "cors";' : ""}
103
+
104
+ dotenv.config();
105
+
106
+ const app = express();
107
+ ${useCors ? "app.use(cors());" : ""}
108
+ app.use(express.json());
109
+
110
+ app.get("/", (req, res) => {
111
+ res.send("API Running...");
112
+ });
113
+
114
+ const PORT = process.env.PORT || ${envConfig.PORT};
115
+
116
+ app.listen(PORT, () => {
117
+ console.log("Server running on port " + PORT);
118
+ });
119
+ `;
120
+
121
+ write(path.join(base, isTS ? "src/index.ts" : "src/index.js"), indexContent);
122
+
123
+ write(
124
+ path.join(base, ".env"),
125
+ `PORT=${envConfig.PORT}
126
+ DATABASE_URL=${envConfig.DATABASE_URL || ""}
127
+ JWT_SECRET=${envConfig.JWT_SECRET}`,
128
+ );
129
+
130
+ if (usePrisma && isTS) {
131
+ write(
132
+ path.join(base, "prisma.config.ts"),
133
+ `import "dotenv/config";
134
+ import { defineConfig, env } from "prisma/config";
135
+
136
+ export default defineConfig({
137
+ schema: "prisma/schema.prisma",
138
+ migrations: {
139
+ path: "prisma/migrations",
140
+ },
141
+ datasource: {
142
+ url: env("DATABASE_URL"),
143
+ },
144
+ });`,
145
+ );
146
+ }
147
+ }
148
+
149
+ // ⚡ FIXED: installDeps now takes cwd
150
+ function installDeps(packageManager, deps, dev = false, cwd) {
151
+ let cmd = "";
152
+ if (packageManager === "npm") {
153
+ cmd = `npm install ${dev ? "-D " : ""}${deps.join(" ")}`;
154
+ } else if (packageManager === "yarn") {
155
+ cmd = `yarn add ${dev ? "-D " : ""}${deps.join(" ")}`;
156
+ } else if (packageManager === "pnpm") {
157
+ cmd = `pnpm add ${dev ? "-D " : ""}${deps.join(" ")}`;
158
+ } else if (packageManager === "bun") {
159
+ cmd = `bun add ${dev ? "-d " : ""}${deps.join(" ")}`;
160
+ }
161
+
162
+ execSync(cmd, { stdio: "ignore", cwd }); // ⚡ FIXED: ensure deps installed in project folder
163
+ }
164
+
165
+ // ⚡ FIXED: runPackageCommand also ignores cwd and stdout
166
+ function runPackageCommand(packageManager, cmd, cwd) {
167
+ let finalCmd = cmd;
168
+ if (packageManager === "yarn") finalCmd = cmd.replace(/^npx /, "yarn ");
169
+ if (packageManager === "pnpm") finalCmd = cmd; // pnpm uses npx normally
170
+ if (packageManager === "bun") finalCmd = cmd.replace(/^npx /, "bun ");
171
+ execSync(finalCmd, { stdio: "ignore", cwd });
172
+ }
173
+
174
+ async function main() {
175
+ console.log("Backend CLI Started...");
176
+ const args = process.argv.slice(2);
177
+ let projectName = args[0];
178
+ if (!projectName) {
179
+ const ans = await inquirer.prompt([
180
+ { type: "input", name: "projectName", message: "Project name:" },
181
+ ]);
182
+ projectName = ans.projectName;
183
+ }
184
+ const targetPath = path.join(process.cwd(), projectName);
185
+ console.log(targetPath);
186
+
187
+ if (!fs.existsSync(targetPath)) fs.mkdirSync(targetPath);
188
+
189
+ const { packageManager } = await inquirer.prompt([
190
+ {
191
+ type: "list",
192
+ name: "packageManager",
193
+ message: "Package manager:",
194
+ choices: ["npm", "yarn", "pnpm", "bun"],
195
+ },
196
+ ]);
197
+
198
+ const { typescript } = await inquirer.prompt([
199
+ {
200
+ type: "list",
201
+ name: "typescript",
202
+ message: "Use TypeScript?",
203
+ choices: ["yes", "no"],
204
+ },
205
+ ]);
206
+ const isTS = typescript === "yes";
207
+
208
+ const answers = await inquirer.prompt([
209
+ {
210
+ type: "list",
211
+ name: "cors",
212
+ message: "Install cors?",
213
+ choices: ["yes", "no"],
214
+ },
215
+ {
216
+ type: "list",
217
+ name: "jwt",
218
+ message: "Install jwt?",
219
+ choices: ["yes", "no"],
220
+ },
221
+ {
222
+ type: "list",
223
+ name: "bcrypt",
224
+ message: "Install bcrypt?",
225
+ choices: ["yes", "no"],
226
+ },
227
+ {
228
+ type: "list",
229
+ name: "postgres",
230
+ message: "Use postgres?",
231
+ choices: ["yes", "no"],
232
+ when: () => isTS,
233
+ },
234
+ {
235
+ type: "list",
236
+ name: "prisma",
237
+ message: "Install prisma?",
238
+ choices: ["yes", "no"],
239
+ when: () => isTS,
240
+ },
241
+ {
242
+ type: "list",
243
+ name: "cluster",
244
+ message: "Use cluster mode?",
245
+ choices: ["yes", "no"],
246
+ },
247
+ ]);
248
+
249
+ const envAnswers = await inquirer.prompt([
250
+ { type: "input", name: "PORT", message: "Enter PORT:", default: "5000" },
251
+ {
252
+ type: "input",
253
+ name: "DATABASE_URL",
254
+ message: "Enter DATABASE_URL:",
255
+ when: () => isTS && answers.postgres === "yes",
256
+ },
257
+ {
258
+ type: "input",
259
+ name: "JWT_SECRET",
260
+ message: "Enter JWT_SECRET:",
261
+ default: "secret",
262
+ },
263
+ ]);
264
+
265
+ const config = {
266
+ typescript: isTS,
267
+ cors: answers.cors === "yes",
268
+ jwt: answers.jwt === "yes",
269
+ bcrypt: answers.bcrypt === "yes",
270
+ postgres: answers.postgres === "yes",
271
+ prisma: answers.prisma === "yes",
272
+ cluster: answers.cluster === "yes",
273
+ };
274
+
275
+ const bar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
276
+ bar.start(100, 0);
277
+
278
+ // Initialize project
279
+ if (packageManager === "npm" || packageManager === "pnpm") {
280
+ execSync(`${packageManager} init -y`, {
281
+ cwd: targetPath,
282
+ stdio: "ignore",
283
+ });
284
+ } else if (packageManager === "yarn") {
285
+ execSync("yarn init -y", { cwd: targetPath, stdio: "ignore" }); // ⚡ FIXED
286
+ } else if (packageManager === "bun") {
287
+ execSync("bun init -y", { cwd: targetPath, stdio: "ignore" }); // ⚡ FIXED
288
+ }
289
+
290
+ updatePackageJson(targetPath, config.typescript, packageManager);
291
+ bar.update(10);
292
+
293
+ const deps = ["express", "dotenv"];
294
+ if (config.cors) deps.push("cors");
295
+ if (config.jwt) deps.push("jsonwebtoken");
296
+ if (config.bcrypt) deps.push("bcrypt");
297
+ if (config.postgres) deps.push("pg");
298
+
299
+ // ⚡ FIXED: add cwd
300
+ installDeps(packageManager, deps, false, targetPath);
301
+ installDeps(packageManager, ["nodemon", "tsx"], true, targetPath);
302
+ bar.update(40);
303
+
304
+ if (config.typescript) {
305
+ installDeps(
306
+ packageManager,
307
+ ["typescript", "ts-node", "@types/node", "@types/express"],
308
+ true,
309
+ targetPath, // ⚡ FIXED
310
+ );
311
+
312
+ runPackageCommand(packageManager, "npx tsc --init", targetPath);
313
+
314
+ const tsconfigPath = path.join(targetPath, "tsconfig.json");
315
+ const tsconfigContent = {
316
+ compilerOptions: {
317
+ module: "ESNext",
318
+ moduleResolution: "bundler",
319
+ target: "ES2023",
320
+ strict: true,
321
+ esModuleInterop: true,
322
+ rootDir: "src",
323
+ outDir: "dist",
324
+ },
325
+ };
326
+ fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfigContent, null, 2));
327
+ }
328
+ bar.update(70);
329
+
330
+ if (config.prisma) {
331
+ const prismaDeps = [
332
+ "@prisma/client",
333
+ "@prisma/adapter-pg",
334
+ "pg",
335
+ "prisma-client",
336
+ ];
337
+ const prismaDevDeps = ["prisma", "@types/pg"];
338
+ installDeps(packageManager, prismaDeps, false, targetPath); // ⚡ FIXED
339
+ installDeps(packageManager, prismaDevDeps, true, targetPath); // ⚡ FIXED
340
+
341
+ runPackageCommand(packageManager, "npx prisma init", targetPath);
342
+ }
343
+
344
+ createStructure(
345
+ targetPath,
346
+ isTS,
347
+ config.cluster,
348
+ config.prisma,
349
+ config.cors,
350
+ envAnswers,
351
+ );
352
+ console.log("Project Ready........");
353
+ bar.update(100);
354
+ bar.stop();
355
+ }
356
+
357
+ main();
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "create-backend-apps-express",
3
+ "version": "1.0.0",
4
+ "description": "CLI to create Node.js backend projects",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "create-backend-apps-express": "index.js"
8
+ },
9
+ "keywords": [
10
+ "cli",
11
+ "backend",
12
+ "generator"
13
+ ],
14
+ "author": "S M MAJIDUL ISLAM",
15
+ "license": "MIT",
16
+ "type": "module",
17
+ "dependencies": {
18
+ "cli-progress": "^3.12.0",
19
+ "inquirer": "^8.2.7"
20
+ },
21
+ "engines": {
22
+ "node": ">=18"
23
+ }
24
+ }