backend-scaffold-cli 1.1.0 → 1.3.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/bin/cli.js +258 -153
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
 
4
4
  const { program } = require('commander');
@@ -8,133 +8,156 @@ 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
- .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: 'useLogging',
31
- message: 'Include logging middleware?',
32
- default: true
33
- },
34
- {
35
- type: 'confirm',
36
- name: 'useValidation',
37
- message: 'Include input validation?',
38
- default: true
39
- },
40
- {
41
- type: 'confirm',
42
- name: 'installDeps',
43
- message: 'Install dependencies now?',
44
- default: true
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
+ if (answers.useAuth) {
69
+ createUserModel(projectPath);
70
+ }
71
+
72
+ console.log(chalk.cyan(' Initializing git repository...'));
73
+ execSync('git init', { cwd: projectPath });
74
+
75
+ if (answers.installDeps) {
76
+ console.log(chalk.cyan(' Installing dependencies...\n'));
77
+ execSync('npm install', { cwd: projectPath, stdio: 'inherit' });
78
+ }
79
+
80
+ console.log(chalk.green.bold('\n Project created successfully!\n'));
81
+ console.log(chalk.cyan('Next steps:'));
82
+ console.log(chalk.white(` cd ${projectName}`));
83
+ console.log(chalk.white(' cp .env.example .env'));
84
+ console.log(chalk.white(' npm start\n'));
85
+ } catch (error) {
86
+ console.error(chalk.red('\n Error creating project:'), error.message);
87
+ process.exit(1);
88
+ }
89
+ });
81
90
 
82
91
  program.parse();
83
92
 
84
- const createFolderStructure=(projectPath) =>{
85
- const folders=[
86
- 'src/config',
87
- 'src/middleware',
88
- 'src/routes',
89
- 'src/controllers',
90
- 'src/models',
91
- 'src/utils',
92
- 'src/validation'
93
- ];
94
-
95
- folders.forEach(folder => {
96
- fs.ensureDirSync(path.join(projectPath, folder));
97
- });
98
-
99
- console.log(chalk.green(' Folder structure created'));
93
+
94
+
95
+
96
+
97
+ const createFolderStructure = (projectPath) => {
98
+ const folders = [
99
+ 'src/config',
100
+ 'src/middleware',
101
+ 'src/routes',
102
+ 'src/controllers',
103
+ 'src/models',
104
+ 'src/utils',
105
+ 'src/validation'
106
+ ];
107
+
108
+ folders.forEach(folder => {
109
+ fs.ensureDirSync(path.join(projectPath, folder));
110
+ });
111
+
112
+ console.log(chalk.green(' Folder structure created'));
100
113
 
101
114
  }
102
115
 
103
116
 
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
- 'express-rate-limit': '^6.7.0',
123
- ...(answers.useAuth && { jsonwebtoken: '^9.0.0' }),
124
- ...(answers.useValidation && { joi: '^17.9.0' })
125
- },
126
- devDependencies: {
127
- nodemon: '^2.0.22',
128
- }
129
117
 
130
- };
131
118
 
132
- fs.writeFileSync(
133
- path.join(projectPath, 'package.json'),
134
- JSON.stringify(packageJson, null, 2)
135
- );
136
119
 
137
- const env = `# Server Configuration
120
+
121
+ const createConfigFiles = (projectPath, answers) => {
122
+ const packageJson = {
123
+ name: path.basename(projectPath),
124
+ version: '1.0.0',
125
+ main: 'src/server.js',
126
+ scripts: {
127
+ start: 'node src/server.js',
128
+ dev: 'nodemon src/server.js',
129
+ test: 'jest'
130
+ },
131
+ keywords: ['express', 'nodejs', 'backend'],
132
+ author: '',
133
+ license: 'MIT',
134
+ dependencies: {
135
+ express: '^4.18.2',
136
+ mongoose: '^7.0.0',
137
+ dotenv: '^16.0.3',
138
+ cors: '^2.8.5',
139
+ ...(answers.useAuth && { jsonwebtoken: '^9.0.0' , bcryptjs: '^2.4.3'}),
140
+ ...(answers.useValidation && { joi: '^17.9.0' }),
141
+ ...(answers.useRateLimit && { 'express-rate-limit': '^6.7.0' }),
142
+
143
+ },
144
+ devDependencies: {
145
+ nodemon: '^2.0.22',
146
+ }
147
+
148
+ };
149
+
150
+ fs.writeFileSync(
151
+ path.join(projectPath, 'package.json'),
152
+ JSON.stringify(packageJson, null, 2)
153
+ );
154
+
155
+
156
+
157
+
158
+
159
+
160
+ const env = `# Server Configuration
138
161
  PORT=5000
139
162
  NODE_ENV=development
140
163
 
@@ -158,10 +181,12 @@ CORS_ORIGIN=http://localhost:3000
158
181
  LOG_LEVEL=debug
159
182
  `;
160
183
 
161
- fs.writeFileSync(path.join(projectPath, '.env.example'), env);
184
+ fs.writeFileSync(path.join(projectPath, '.env.example'), env);
185
+
162
186
 
163
187
 
164
- const gitignore = `node_modules/
188
+
189
+ const gitignore = `node_modules/
165
190
  .env
166
191
  .env.local
167
192
  .env.*.local
@@ -175,9 +200,9 @@ yarn-debug.log*
175
200
  .vscode/
176
201
  `;
177
202
 
178
- fs.writeFileSync(path.join(projectPath, '.gitignore'), gitignore);
203
+ fs.writeFileSync(path.join(projectPath, '.gitignore'), gitignore);
179
204
 
180
- console.log(chalk.green(' Config files created'));
205
+ console.log(chalk.green(' Config files created'));
181
206
 
182
207
 
183
208
  };
@@ -185,6 +210,8 @@ yarn-debug.log*
185
210
 
186
211
 
187
212
 
213
+
214
+
188
215
  function createMiddlewareFiles(projectPath, answers) {
189
216
  const errorHandler = `// Middleware for handling errors
190
217
  class ErrorHandler extends Error {
@@ -223,15 +250,16 @@ const errorMiddleware = (err, req, res, next) => {
223
250
  module.exports = { errorMiddleware, ErrorHandler };
224
251
  `;
225
252
 
226
- fs.writeFileSync(
227
- path.join(projectPath, 'src/middleware/errorHandler.js'),
228
- errorHandler
229
- );
253
+ fs.writeFileSync(
254
+ path.join(projectPath, 'src/middleware/errorHandler.js'),
255
+ errorHandler
256
+ );
230
257
 
231
258
 
232
259
 
233
260
 
234
- const logger = `// Simple logging middleware
261
+
262
+ const logger = `// Simple logging middleware
235
263
  const loggerMiddleware = (req, res, next) => {
236
264
  const start = Date.now();
237
265
 
@@ -248,19 +276,24 @@ const loggerMiddleware = (req, res, next) => {
248
276
  module.exports = loggerMiddleware;
249
277
  `;
250
278
 
251
- fs.writeFileSync(
252
- path.join(projectPath, 'src/middleware/logger.js'),
253
- logger
254
- );
279
+ fs.writeFileSync(
280
+ path.join(projectPath, 'src/middleware/logger.js'),
281
+ logger
282
+ );
283
+
284
+
285
+
286
+
255
287
 
256
288
 
257
289
 
258
- const rateLimiter = `const ratelimit= require('express-rate-limit');
290
+ if (answers.useRateLimit) {
291
+ const rateLimiter = `const ratelimit= require('express-rate-limit');
259
292
 
260
293
  const limiter=ratelimit({
261
294
  windowMs: 15 * 60 * 1000,
262
295
  max: 100,
263
- message: 'Too many requests from this IP, please try again after 15 minutes'
296
+ message: 'Too many requests from this IP, please try again after 15 minutes',
264
297
  standardHeaders: true,
265
298
  legacyHeaders: false,
266
299
  });
@@ -271,13 +304,19 @@ module.exports=limiter;
271
304
  `;
272
305
 
273
306
  fs.writeFileSync(
274
- path.join(projectPath, 'src/middleware/rateLimiter.js'),
275
- rateLimiter
307
+ path.join(projectPath, 'src/middleware/rateLimiter.js'),
308
+ rateLimiter
276
309
  );
277
310
 
311
+ }
312
+
313
+
314
+
278
315
 
279
- if (answers.useAuth) {
280
- const auth = `const jwt = require('jsonwebtoken');
316
+
317
+
318
+ if (answers.useAuth) {
319
+ const auth = `const jwt = require('jsonwebtoken');
281
320
  const { ErrorHandler } = require('./errorHandler');
282
321
 
283
322
  const authenticateToken = (req, res, next) => {
@@ -300,15 +339,16 @@ module.exports = authenticateToken;
300
339
  `;
301
340
 
302
341
  fs.writeFileSync(
303
- path.join(projectPath, 'src/middleware/auth.js'),
304
- auth
342
+ path.join(projectPath, 'src/middleware/auth.js'),
343
+ auth
305
344
  );
306
345
  }
307
346
 
308
347
 
309
348
 
310
- if (answers.useValidation) {
311
- const validation = `const Joi = require('joi');
349
+
350
+ if (answers.useValidation) {
351
+ const validation = `const Joi = require('joi');
312
352
 
313
353
  const validateRequest = (schema) => {
314
354
  return (req, res, next) => {
@@ -334,8 +374,8 @@ module.exports = validateRequest;
334
374
  `;
335
375
 
336
376
  fs.writeFileSync(
337
- path.join(projectPath, 'src/middleware/validation.js'),
338
- validation
377
+ path.join(projectPath, 'src/middleware/validation.js'),
378
+ validation
339
379
  );
340
380
  }
341
381
 
@@ -365,14 +405,16 @@ router.get('/protected', authenticateToken, (req, res) => {
365
405
 
366
406
  module.exports = router;
367
407
  `;
368
- fs.writeFileSync(
369
- path.join(projectPath, 'src/routes/index.js'),
370
- routes
371
- );
408
+ fs.writeFileSync(
409
+ path.join(projectPath, 'src/routes/index.js'),
410
+ routes
411
+ );
412
+
413
+
372
414
 
373
-
374
415
 
375
- const dbConfig = `const mongoose = require('mongoose');
416
+
417
+ const dbConfig = `const mongoose = require('mongoose');
376
418
  const dns = require('dns');
377
419
 
378
420
  // Set DNS servers
@@ -406,15 +448,16 @@ const connectDB = async () => {
406
448
  module.exports = connectDB;
407
449
  `;
408
450
 
409
- fs.writeFileSync(
410
- path.join(projectPath, 'src/config/db.js'),
411
- dbConfig
412
- );
451
+ fs.writeFileSync(
452
+ path.join(projectPath, 'src/config/db.js'),
453
+ dbConfig
454
+ );
455
+
413
456
 
414
457
 
415
458
 
416
459
 
417
- const server = `require('dotenv').config();
460
+ const server = `require('dotenv').config();
418
461
  const express = require('express');
419
462
  const cors = require('cors');
420
463
  const connectDB = require('./config/db');
@@ -423,7 +466,7 @@ const loggerMiddleware = require('./middleware/logger');
423
466
  const { errorMiddleware } = require('./middleware/errorHandler');
424
467
 
425
468
  const app = express();
426
- const limiter = require('./middleware/rateLimiter');
469
+ ${answers.useRateLimit ? "const limiter = require('./middleware/rateLimiter');" : ''}
427
470
  const PORT = process.env.PORT || 5000;
428
471
 
429
472
  // Connect to MongoDB
@@ -437,7 +480,7 @@ app.use(cors({
437
480
  app.use(express.json());
438
481
  app.use(express.urlencoded({ extended: true }));
439
482
  app.use(loggerMiddleware);
440
- app.use(limiter);
483
+ ${answers.useRateLimit ? 'app.use(limiter);' : ''}
441
484
 
442
485
 
443
486
  // Routes
@@ -473,3 +516,65 @@ process.on('unhandledRejection', (err) => {
473
516
 
474
517
  console.log(chalk.green('Route and config files created'));
475
518
  }
519
+
520
+
521
+
522
+
523
+
524
+ function createUserModel(projectPath) {
525
+ const userModel = `const mongoose = require('mongoose');
526
+ const bcrypt = require('bcryptjs');
527
+
528
+ const userSchema = new mongoose.Schema(
529
+ {
530
+ name:{
531
+ type: String,
532
+ required: [true, 'Name is required'],
533
+ trim: true,
534
+ },
535
+ email: {
536
+ type: String,
537
+ required: [true, 'Email is required'],
538
+ unique: true,
539
+ lowercase: true,
540
+ trim: true,
541
+ match: [/.+@.+\\..+/, 'Please fill a valid email address'],
542
+ },
543
+ password: {
544
+ type: String,
545
+ required: [true, 'Password is required'],
546
+ minlength: [6, 'Password must be at least 6 characters long'],
547
+ select: false
548
+ },
549
+ },
550
+ { timestamps: true }
551
+
552
+ );
553
+
554
+
555
+ //hash password before saving
556
+ userSchema.pre('save', async function (next) {
557
+ if (!this.isModified('password')) {
558
+ return next();
559
+ }
560
+ this.password = await bcrypt.hash(this.password,10);
561
+ next();
562
+ });
563
+
564
+
565
+ //compare password method
566
+ userSchema.methods.comparePassword = async function(candidatePassword) {
567
+ return await bcrypt.compare(candidatePassword, this.password);
568
+ };
569
+
570
+ module.exports = mongoose.model('User', userSchema);
571
+
572
+ `;
573
+
574
+ fs.writeFileSync(
575
+ path.join(projectPath, 'src/models/User.js'),
576
+ userModel
577
+ )
578
+
579
+ console.log(chalk.green('User model created'));
580
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-scaffold-cli",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "CLI tool to scaffold Express.js backend projects with MongoDB, middleware, and common setup",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {