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.
- package/index.js +357 -0
- 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
|
+
}
|