backend-scaffold-cli 1.0.2 → 1.2.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/bin/cli.js +217 -141
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -8,132 +8,153 @@ const fs = require('fs-extra');
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const { execSync } = require('child_process');
|
|
10
10
|
|
|
11
|
-
const version= '1.0.0';
|
|
11
|
+
const version = '1.0.0';
|
|
12
12
|
|
|
13
13
|
program
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
]);
|
|
47
|
-
|
|
48
|
-
const projectPath = path.resolve(process.cwd(), projectName);
|
|
49
|
-
|
|
50
|
-
if (fs.existsSync(projectPath)) {
|
|
51
|
-
console.log(chalk.red(`Error: Directory ${projectName} already exists.`));
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
console.log(chalk.blue(`Creating project in ${projectPath}\n`));
|
|
56
|
-
fs.ensureDirSync(projectPath);
|
|
57
|
-
|
|
58
|
-
createFolderStructure(projectPath);
|
|
59
|
-
createConfigFiles(projectPath, answers);
|
|
60
|
-
createMiddlewareFiles(projectPath, answers);
|
|
61
|
-
createRouteFiles(projectPath, answers);
|
|
62
|
-
|
|
63
|
-
console.log(chalk.cyan(' Initializing git repository...'));
|
|
64
|
-
execSync('git init', { cwd: projectPath });
|
|
65
|
-
|
|
66
|
-
if (answers.installDeps) {
|
|
67
|
-
console.log(chalk.cyan(' Installing dependencies...\n'));
|
|
68
|
-
execSync('npm install', { cwd: projectPath, stdio: 'inherit' });
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
console.log(chalk.green.bold('\n Project created successfully!\n'));
|
|
72
|
-
console.log(chalk.cyan('Next steps:'));
|
|
73
|
-
console.log(chalk.white(` cd ${projectName}`));
|
|
74
|
-
console.log(chalk.white(' cp .env.example .env'));
|
|
75
|
-
console.log(chalk.white(' npm start\n'));
|
|
76
|
-
}catch (error) {
|
|
77
|
-
console.error(chalk.red('\n Error creating project:'), error.message);
|
|
78
|
-
process.exit(1);
|
|
14
|
+
.version(version)
|
|
15
|
+
.description('Create a new Express.js backend application')
|
|
16
|
+
.argument('<project-name>', 'Name of the project')
|
|
17
|
+
.action(async (projectName) => {
|
|
18
|
+
try {
|
|
19
|
+
console.log(chalk.blue(`Creating a new Express.js backend application: ${projectName}`));
|
|
20
|
+
|
|
21
|
+
const answers = await inquirer.prompt([
|
|
22
|
+
{
|
|
23
|
+
type: 'confirm',
|
|
24
|
+
name: 'useAuth',
|
|
25
|
+
message: 'Include JWT authentication?',
|
|
26
|
+
default: true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'confirm',
|
|
30
|
+
name: 'useValidation',
|
|
31
|
+
message: 'Include input validation?',
|
|
32
|
+
default: true
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
type: 'confirm',
|
|
36
|
+
name: 'useRateLimit',
|
|
37
|
+
message: 'Include rate limiting?',
|
|
38
|
+
default: true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: 'confirm',
|
|
42
|
+
name: 'installDeps',
|
|
43
|
+
message: 'Install dependencies now?',
|
|
44
|
+
default: true
|
|
79
45
|
}
|
|
80
|
-
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
const projectPath = path.resolve(process.cwd(), projectName);
|
|
55
|
+
|
|
56
|
+
if (fs.existsSync(projectPath)) {
|
|
57
|
+
console.log(chalk.red(`Error: Directory ${projectName} already exists.`));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.log(chalk.blue(`Creating project in ${projectPath}\n`));
|
|
62
|
+
fs.ensureDirSync(projectPath);
|
|
63
|
+
|
|
64
|
+
createFolderStructure(projectPath);
|
|
65
|
+
createConfigFiles(projectPath, answers);
|
|
66
|
+
createMiddlewareFiles(projectPath, answers);
|
|
67
|
+
createRouteFiles(projectPath, answers);
|
|
68
|
+
|
|
69
|
+
console.log(chalk.cyan(' Initializing git repository...'));
|
|
70
|
+
execSync('git init', { cwd: projectPath });
|
|
71
|
+
|
|
72
|
+
if (answers.installDeps) {
|
|
73
|
+
console.log(chalk.cyan(' Installing dependencies...\n'));
|
|
74
|
+
execSync('npm install', { cwd: projectPath, stdio: 'inherit' });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(chalk.green.bold('\n Project created successfully!\n'));
|
|
78
|
+
console.log(chalk.cyan('Next steps:'));
|
|
79
|
+
console.log(chalk.white(` cd ${projectName}`));
|
|
80
|
+
console.log(chalk.white(' cp .env.example .env'));
|
|
81
|
+
console.log(chalk.white(' npm start\n'));
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(chalk.red('\n Error creating project:'), error.message);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
81
87
|
|
|
82
88
|
program.parse();
|
|
83
89
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
const createFolderStructure = (projectPath) => {
|
|
95
|
+
const folders = [
|
|
96
|
+
'src/config',
|
|
97
|
+
'src/middleware',
|
|
98
|
+
'src/routes',
|
|
99
|
+
'src/controllers',
|
|
100
|
+
'src/models',
|
|
101
|
+
'src/utils',
|
|
102
|
+
'src/validation'
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
folders.forEach(folder => {
|
|
106
|
+
fs.ensureDirSync(path.join(projectPath, folder));
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
console.log(chalk.green(' Folder structure created'));
|
|
100
110
|
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
|
|
104
|
-
const createConfigFiles= (projectPath,answers) =>{
|
|
105
|
-
const packageJson={
|
|
106
|
-
name: path.basename(projectPath),
|
|
107
|
-
version: '1.0.0',
|
|
108
|
-
main: 'src/server.js',
|
|
109
|
-
scripts: {
|
|
110
|
-
start: 'node src/server.js',
|
|
111
|
-
dev: 'nodemon src/server.js',
|
|
112
|
-
test: 'jest'
|
|
113
|
-
},
|
|
114
|
-
keywords: ['express', 'nodejs', 'backend'],
|
|
115
|
-
author: '',
|
|
116
|
-
license: 'MIT',
|
|
117
|
-
dependencies: {
|
|
118
|
-
express: '^4.18.2',
|
|
119
|
-
mongoose: '^7.0.0',
|
|
120
|
-
dotenv: '^16.0.3',
|
|
121
|
-
cors: '^2.8.5',
|
|
122
|
-
...(answers.useAuth && { jsonwebtoken: '^9.0.0' }),
|
|
123
|
-
...(answers.useValidation && { joi: '^17.9.0' })
|
|
124
|
-
},
|
|
125
|
-
devDependencies: {
|
|
126
|
-
nodemon: '^2.0.22',
|
|
127
|
-
}
|
|
128
114
|
|
|
129
|
-
};
|
|
130
115
|
|
|
131
|
-
fs.writeFileSync(
|
|
132
|
-
path.join(projectPath, 'package.json'),
|
|
133
|
-
JSON.stringify(packageJson, null, 2)
|
|
134
|
-
);
|
|
135
116
|
|
|
136
|
-
|
|
117
|
+
|
|
118
|
+
const createConfigFiles = (projectPath, answers) => {
|
|
119
|
+
const packageJson = {
|
|
120
|
+
name: path.basename(projectPath),
|
|
121
|
+
version: '1.0.0',
|
|
122
|
+
main: 'src/server.js',
|
|
123
|
+
scripts: {
|
|
124
|
+
start: 'node src/server.js',
|
|
125
|
+
dev: 'nodemon src/server.js',
|
|
126
|
+
test: 'jest'
|
|
127
|
+
},
|
|
128
|
+
keywords: ['express', 'nodejs', 'backend'],
|
|
129
|
+
author: '',
|
|
130
|
+
license: 'MIT',
|
|
131
|
+
dependencies: {
|
|
132
|
+
express: '^4.18.2',
|
|
133
|
+
mongoose: '^7.0.0',
|
|
134
|
+
dotenv: '^16.0.3',
|
|
135
|
+
cors: '^2.8.5',
|
|
136
|
+
...(answers.useAuth && { jsonwebtoken: '^9.0.0' }),
|
|
137
|
+
...(answers.useValidation && { joi: '^17.9.0' }),
|
|
138
|
+
...(answers.useRateLimit && { 'express-rate-limit': '^6.7.0' }),
|
|
139
|
+
|
|
140
|
+
},
|
|
141
|
+
devDependencies: {
|
|
142
|
+
nodemon: '^2.0.22',
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
fs.writeFileSync(
|
|
148
|
+
path.join(projectPath, 'package.json'),
|
|
149
|
+
JSON.stringify(packageJson, null, 2)
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
const env = `# Server Configuration
|
|
137
158
|
PORT=5000
|
|
138
159
|
NODE_ENV=development
|
|
139
160
|
|
|
@@ -157,10 +178,12 @@ CORS_ORIGIN=http://localhost:3000
|
|
|
157
178
|
LOG_LEVEL=debug
|
|
158
179
|
`;
|
|
159
180
|
|
|
160
|
-
|
|
181
|
+
fs.writeFileSync(path.join(projectPath, '.env.example'), env);
|
|
182
|
+
|
|
161
183
|
|
|
162
184
|
|
|
163
|
-
|
|
185
|
+
|
|
186
|
+
const gitignore = `node_modules/
|
|
164
187
|
.env
|
|
165
188
|
.env.local
|
|
166
189
|
.env.*.local
|
|
@@ -174,9 +197,9 @@ yarn-debug.log*
|
|
|
174
197
|
.vscode/
|
|
175
198
|
`;
|
|
176
199
|
|
|
177
|
-
|
|
200
|
+
fs.writeFileSync(path.join(projectPath, '.gitignore'), gitignore);
|
|
178
201
|
|
|
179
|
-
|
|
202
|
+
console.log(chalk.green(' Config files created'));
|
|
180
203
|
|
|
181
204
|
|
|
182
205
|
};
|
|
@@ -184,6 +207,8 @@ yarn-debug.log*
|
|
|
184
207
|
|
|
185
208
|
|
|
186
209
|
|
|
210
|
+
|
|
211
|
+
|
|
187
212
|
function createMiddlewareFiles(projectPath, answers) {
|
|
188
213
|
const errorHandler = `// Middleware for handling errors
|
|
189
214
|
class ErrorHandler extends Error {
|
|
@@ -222,13 +247,16 @@ const errorMiddleware = (err, req, res, next) => {
|
|
|
222
247
|
module.exports = { errorMiddleware, ErrorHandler };
|
|
223
248
|
`;
|
|
224
249
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
250
|
+
fs.writeFileSync(
|
|
251
|
+
path.join(projectPath, 'src/middleware/errorHandler.js'),
|
|
252
|
+
errorHandler
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
|
|
229
257
|
|
|
230
258
|
|
|
231
|
-
|
|
259
|
+
const logger = `// Simple logging middleware
|
|
232
260
|
const loggerMiddleware = (req, res, next) => {
|
|
233
261
|
const start = Date.now();
|
|
234
262
|
|
|
@@ -243,14 +271,48 @@ const loggerMiddleware = (req, res, next) => {
|
|
|
243
271
|
};
|
|
244
272
|
|
|
245
273
|
module.exports = loggerMiddleware;
|
|
274
|
+
`;
|
|
275
|
+
|
|
276
|
+
fs.writeFileSync(
|
|
277
|
+
path.join(projectPath, 'src/middleware/logger.js'),
|
|
278
|
+
logger
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
if (answers.useRateLimit) {
|
|
288
|
+
const rateLimiter = `const ratelimit= require('express-rate-limit');
|
|
289
|
+
|
|
290
|
+
const limiter=ratelimit({
|
|
291
|
+
windowMs: 15 * 60 * 1000,
|
|
292
|
+
max: 100,
|
|
293
|
+
message: 'Too many requests from this IP, please try again after 15 minutes',
|
|
294
|
+
standardHeaders: true,
|
|
295
|
+
legacyHeaders: false,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
module.exports=limiter;
|
|
299
|
+
|
|
300
|
+
|
|
246
301
|
`;
|
|
247
302
|
|
|
248
303
|
fs.writeFileSync(
|
|
249
|
-
|
|
250
|
-
|
|
304
|
+
path.join(projectPath, 'src/middleware/rateLimiter.js'),
|
|
305
|
+
rateLimiter
|
|
251
306
|
);
|
|
252
307
|
|
|
253
|
-
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
if (answers.useAuth) {
|
|
254
316
|
const auth = `const jwt = require('jsonwebtoken');
|
|
255
317
|
const { ErrorHandler } = require('./errorHandler');
|
|
256
318
|
|
|
@@ -274,11 +336,14 @@ module.exports = authenticateToken;
|
|
|
274
336
|
`;
|
|
275
337
|
|
|
276
338
|
fs.writeFileSync(
|
|
277
|
-
|
|
278
|
-
|
|
339
|
+
path.join(projectPath, 'src/middleware/auth.js'),
|
|
340
|
+
auth
|
|
279
341
|
);
|
|
280
342
|
}
|
|
281
343
|
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
|
|
282
347
|
if (answers.useValidation) {
|
|
283
348
|
const validation = `const Joi = require('joi');
|
|
284
349
|
|
|
@@ -306,8 +371,8 @@ module.exports = validateRequest;
|
|
|
306
371
|
`;
|
|
307
372
|
|
|
308
373
|
fs.writeFileSync(
|
|
309
|
-
|
|
310
|
-
|
|
374
|
+
path.join(projectPath, 'src/middleware/validation.js'),
|
|
375
|
+
validation
|
|
311
376
|
);
|
|
312
377
|
}
|
|
313
378
|
|
|
@@ -315,8 +380,11 @@ module.exports = validateRequest;
|
|
|
315
380
|
}
|
|
316
381
|
|
|
317
382
|
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
|
|
318
386
|
const createRouteFiles = (projectPath, answers) => {
|
|
319
|
-
|
|
387
|
+
const routes = `const express = require('express');
|
|
320
388
|
const router = express.Router();
|
|
321
389
|
${answers.useAuth ? "const authenticateToken = require('../middleware/auth');" : ''}
|
|
322
390
|
|
|
@@ -334,14 +402,16 @@ router.get('/protected', authenticateToken, (req, res) => {
|
|
|
334
402
|
|
|
335
403
|
module.exports = router;
|
|
336
404
|
`;
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
405
|
+
fs.writeFileSync(
|
|
406
|
+
path.join(projectPath, 'src/routes/index.js'),
|
|
407
|
+
routes
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
|
|
341
411
|
|
|
342
|
-
|
|
343
412
|
|
|
344
|
-
|
|
413
|
+
|
|
414
|
+
const dbConfig = `const mongoose = require('mongoose');
|
|
345
415
|
const dns = require('dns');
|
|
346
416
|
|
|
347
417
|
// Set DNS servers
|
|
@@ -375,12 +445,16 @@ const connectDB = async () => {
|
|
|
375
445
|
module.exports = connectDB;
|
|
376
446
|
`;
|
|
377
447
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
448
|
+
fs.writeFileSync(
|
|
449
|
+
path.join(projectPath, 'src/config/db.js'),
|
|
450
|
+
dbConfig
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
|
|
382
456
|
|
|
383
|
-
|
|
457
|
+
const server = `require('dotenv').config();
|
|
384
458
|
const express = require('express');
|
|
385
459
|
const cors = require('cors');
|
|
386
460
|
const connectDB = require('./config/db');
|
|
@@ -389,6 +463,7 @@ const loggerMiddleware = require('./middleware/logger');
|
|
|
389
463
|
const { errorMiddleware } = require('./middleware/errorHandler');
|
|
390
464
|
|
|
391
465
|
const app = express();
|
|
466
|
+
${answers.useRateLimit ? "const limiter = require('./middleware/rateLimiter');" : ''}
|
|
392
467
|
const PORT = process.env.PORT || 5000;
|
|
393
468
|
|
|
394
469
|
// Connect to MongoDB
|
|
@@ -402,6 +477,7 @@ app.use(cors({
|
|
|
402
477
|
app.use(express.json());
|
|
403
478
|
app.use(express.urlencoded({ extended: true }));
|
|
404
479
|
app.use(loggerMiddleware);
|
|
480
|
+
${answers.useRateLimit ? 'app.use(limiter);' : ''}
|
|
405
481
|
|
|
406
482
|
|
|
407
483
|
// Routes
|