create-xpressforge 1.0.0 → 1.0.1
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 +112 -21
- package/bin/cli.js +1 -1
- package/package.json +12 -2
- package/src/generators/appGenerator.js +4 -6
- package/src/generators/projectGenerator.js +44 -8
package/README.md
CHANGED
|
@@ -1,46 +1,137 @@
|
|
|
1
1
|
# create-xpressforge
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Scaffold a production-ready Node.js + Express project in seconds — with your choice of architecture, database, auth, and language.
|
|
4
4
|
|
|
5
5
|
[](https://npmjs.com/package/create-xpressforge)
|
|
6
6
|
[](LICENSE)
|
|
7
|
+
[](#)
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
9
12
|
|
|
10
13
|
```bash
|
|
11
14
|
npx create-xpressforge my-app
|
|
12
15
|
```
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
That's it. Follow the prompts, then:
|
|
15
18
|
|
|
16
19
|
```bash
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
cd my-app
|
|
21
|
+
npm install
|
|
22
|
+
cp .env.example .env # add your DB URI and secrets
|
|
23
|
+
npm run dev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Interactive prompts
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
? Project name: my-app
|
|
32
|
+
? Project structure: MVC / Modular / Layered
|
|
33
|
+
? Database: MongoDB / PostgreSQL / MySQL / None
|
|
34
|
+
? Authentication: JWT / Session / None
|
|
35
|
+
? Extra features: Rate limiting, Helmet, CORS, Morgan, Validation, Multer, Socket.io, Swagger
|
|
36
|
+
? Language: JavaScript / TypeScript
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## What gets generated
|
|
42
|
+
|
|
43
|
+
Running `npx create-xpressforge my-app` with **MVC + MongoDB + JWT** produces:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
my-app/
|
|
47
|
+
├── src/
|
|
48
|
+
│ ├── config/
|
|
49
|
+
│ │ ├── db.js # MongoDB connection with retry logic
|
|
50
|
+
│ │ └── env.js # Centralised env config
|
|
51
|
+
│ ├── controllers/
|
|
52
|
+
│ │ ├── authController.js # register, login, refresh, getMe
|
|
53
|
+
│ │ └── userController.js # full CRUD with pagination
|
|
54
|
+
│ ├── middlewares/
|
|
55
|
+
│ │ ├── authenticate.js # JWT verify + role-based authorize()
|
|
56
|
+
│ │ ├── errorHandler.js # global error handler
|
|
57
|
+
│ │ └── notFound.js
|
|
58
|
+
│ ├── models/
|
|
59
|
+
│ │ └── User.js # Mongoose schema with indexes
|
|
60
|
+
│ ├── routes/
|
|
61
|
+
│ │ ├── index.js
|
|
62
|
+
│ │ ├── authRoutes.js
|
|
63
|
+
│ │ └── userRoutes.js
|
|
64
|
+
│ ├── services/
|
|
65
|
+
│ │ └── userService.js
|
|
66
|
+
│ └── utils/
|
|
67
|
+
│ ├── apiResponse.js # sendSuccess / sendError / sendPaginated
|
|
68
|
+
│ └── logger.js # coloured console logger
|
|
69
|
+
├── .env # pre-filled with your choices
|
|
70
|
+
├── .env.example # safe to commit
|
|
71
|
+
├── .gitignore
|
|
72
|
+
├── package.json
|
|
73
|
+
├── README.md # auto-generated for your stack
|
|
74
|
+
└── server.js
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## API endpoints (out of the box)
|
|
80
|
+
|
|
81
|
+
| Method | Endpoint | Description | Auth |
|
|
82
|
+
| -------- | ----------------------- | -------------------- | -------- |
|
|
83
|
+
| `POST` | `/api/v1/auth/register` | Register a new user | — |
|
|
84
|
+
| `POST` | `/api/v1/auth/login` | Login, get tokens | — |
|
|
85
|
+
| `POST` | `/api/v1/auth/refresh` | Refresh access token | — |
|
|
86
|
+
| `GET` | `/api/v1/auth/me` | Get current user | ✅ |
|
|
87
|
+
| `GET` | `/api/v1/users` | Paginated user list | ✅ |
|
|
88
|
+
| `GET` | `/api/v1/users/:id` | Get user by ID | ✅ |
|
|
89
|
+
| `PUT` | `/api/v1/users/:id` | Update user | ✅ |
|
|
90
|
+
| `DELETE` | `/api/v1/users/:id` | Delete user | ✅ Admin |
|
|
91
|
+
| `GET` | `/health` | Health check | — |
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Consistent API response format
|
|
96
|
+
|
|
97
|
+
Every endpoint returns the same shape — no surprises:
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
// success
|
|
101
|
+
{ "success": true, "message": "Login successful", "data": { ... } }
|
|
102
|
+
|
|
103
|
+
// error
|
|
104
|
+
{ "success": false, "message": "Invalid email or password" }
|
|
105
|
+
|
|
106
|
+
// paginated
|
|
107
|
+
{ "success": true, "data": [...], "pagination": { "total": 100, "page": 2, "limit": 10, "totalPages": 10 } }
|
|
19
108
|
```
|
|
20
109
|
|
|
21
|
-
|
|
110
|
+
---
|
|
22
111
|
|
|
23
|
-
|
|
112
|
+
## Available scripts
|
|
24
113
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
114
|
+
```bash
|
|
115
|
+
npm run dev # nodemon hot-reload (JS) / tsx watch (TS)
|
|
116
|
+
npm start # production
|
|
117
|
+
npm run build # compile TypeScript (TS only)
|
|
118
|
+
npm test # vitest
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Global install
|
|
30
124
|
|
|
31
|
-
|
|
125
|
+
```bash
|
|
126
|
+
npm install -g create-xpressforge
|
|
127
|
+
create-xpressforge my-app
|
|
128
|
+
```
|
|
32
129
|
|
|
33
|
-
|
|
34
|
-
- Consistent `apiResponse` helper (`sendSuccess`, `sendError`, `sendPaginated`)
|
|
35
|
-
- Custom logger utility
|
|
36
|
-
- 404 not-found middleware
|
|
37
|
-
- Working User CRUD example
|
|
38
|
-
- `.env` + `.env.example` with all variables listed
|
|
39
|
-
- Auto-generated README with your stack details
|
|
130
|
+
---
|
|
40
131
|
|
|
41
132
|
## Author
|
|
42
133
|
|
|
43
|
-
Hammad Sadi
|
|
134
|
+
**Hammad Sadi** · [hammad.sadi@yahoo.com](mailto:hammad.sadi@yahoo.com)
|
|
44
135
|
|
|
45
136
|
## License
|
|
46
137
|
|
package/bin/cli.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-xpressforge",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Production-ready Node.js + Express project scaffolder — MVC, Modular, Layered structures with DB, Auth & more",
|
|
5
5
|
"author": "Hammad Sadi <hammad.sadi@yahoo.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"create-xpressforge": "./bin/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"main": "./src/index.js",
|
|
11
|
+
"type": "module",
|
|
11
12
|
"keywords": [
|
|
12
13
|
"express",
|
|
13
14
|
"nodejs",
|
|
@@ -27,7 +28,12 @@
|
|
|
27
28
|
"src"
|
|
28
29
|
],
|
|
29
30
|
"engines": {
|
|
30
|
-
"node": ">=
|
|
31
|
+
"node": ">=18.0.0"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"test:watch": "vitest",
|
|
36
|
+
"test:coverage": "vitest run --coverage"
|
|
31
37
|
},
|
|
32
38
|
"dependencies": {
|
|
33
39
|
"@inquirer/prompts": "^5.0.0",
|
|
@@ -36,6 +42,10 @@
|
|
|
36
42
|
"fs-extra": "^11.2.0",
|
|
37
43
|
"gradient-string": "^2.0.2"
|
|
38
44
|
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"vitest": "^1.6.0",
|
|
47
|
+
"@vitest/coverage-v8": "^1.6.0"
|
|
48
|
+
},
|
|
39
49
|
"repository": {
|
|
40
50
|
"type": "git",
|
|
41
51
|
"url": "https://github.com/hammadsadi/create-xpressforge"
|
|
@@ -3,17 +3,15 @@ export function generateAppJs(answers) {
|
|
|
3
3
|
|
|
4
4
|
const imports = [
|
|
5
5
|
`import express from 'express';`,
|
|
6
|
-
extras.includes('cors') ? `import cors from 'cors';`
|
|
7
|
-
extras.includes('helmet') ? `import helmet from 'helmet';`
|
|
8
|
-
extras.includes('morgan') ? `import morgan from 'morgan';`
|
|
6
|
+
extras.includes('cors') ? `import cors from 'cors';` : '',
|
|
7
|
+
extras.includes('helmet') ? `import helmet from 'helmet';` : '',
|
|
8
|
+
extras.includes('morgan') ? `import morgan from 'morgan';` : '',
|
|
9
9
|
extras.includes('rateLimit') ? `import rateLimit from 'express-rate-limit';` : '',
|
|
10
10
|
extras.includes('swagger') ? `import swaggerUi from 'swagger-ui-express';\nimport { swaggerSpec } from './config/swagger.js';` : '',
|
|
11
11
|
database !== 'none' ? `import { connectDB } from './config/db.js';` : '',
|
|
12
12
|
`import { errorHandler } from './middlewares/errorHandler.js';`,
|
|
13
13
|
`import { notFound } from './middlewares/notFound.js';`,
|
|
14
|
-
|
|
15
|
-
structure === 'modular' ? `import routes from './routes/index.js';` : '',
|
|
16
|
-
structure === 'layered' ? `import routes from './routes/index.js';` : '',
|
|
14
|
+
`import routes from './routes/index.js';`,
|
|
17
15
|
].filter(Boolean).join('\n');
|
|
18
16
|
|
|
19
17
|
const dbInit = database !== 'none' ? `\n// Connect to database\nconnectDB();\n` : '';
|
|
@@ -47,6 +47,14 @@ export async function generateProject(answers, targetDir) {
|
|
|
47
47
|
// 7. server.js entry
|
|
48
48
|
await fs.writeFile(path.join(targetDir, `server.${ext}`), serverContent(answers));
|
|
49
49
|
|
|
50
|
+
// 7b. Swagger config (only if swagger selected)
|
|
51
|
+
if (answers.extras.includes('swagger')) {
|
|
52
|
+
await fs.writeFile(
|
|
53
|
+
path.join(targetDir, 'src', 'config', `swagger.${ext}`),
|
|
54
|
+
swaggerConfigContent(answers)
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
50
58
|
// 8. DB config
|
|
51
59
|
if (answers.database !== 'none') {
|
|
52
60
|
spinner.text = 'Setting up database connection...';
|
|
@@ -127,24 +135,52 @@ coverage/
|
|
|
127
135
|
}
|
|
128
136
|
|
|
129
137
|
function serverContent(answers) {
|
|
130
|
-
const ext = answers.language === 'ts' ?
|
|
131
|
-
return
|
|
132
|
-
import
|
|
138
|
+
const ext = answers.language === 'ts' ? 'ts' : 'js';
|
|
139
|
+
return `import 'dotenv/config';
|
|
140
|
+
import app from './src/app.js';
|
|
133
141
|
|
|
134
|
-
const PORT = env.PORT || 3000;
|
|
142
|
+
const PORT = process.env.PORT || 3000;
|
|
143
|
+
const NODE_ENV = process.env.NODE_ENV || 'development';
|
|
135
144
|
|
|
136
145
|
app.listen(PORT, () => {
|
|
137
|
-
console.log(
|
|
138
|
-
console.log(
|
|
146
|
+
console.log(\`✅ Server running on http://localhost:\${PORT}\`);
|
|
147
|
+
console.log(\`📦 Environment: \${NODE_ENV}\`);
|
|
139
148
|
});
|
|
140
149
|
`;
|
|
141
150
|
}
|
|
142
151
|
|
|
152
|
+
function swaggerConfigContent(answers) {
|
|
153
|
+
return `import swaggerJsdoc from 'swagger-jsdoc';
|
|
154
|
+
|
|
155
|
+
const options = {
|
|
156
|
+
definition: {
|
|
157
|
+
openapi: '3.0.0',
|
|
158
|
+
info: {
|
|
159
|
+
title: '${answers.projectName} API',
|
|
160
|
+
version: '1.0.0',
|
|
161
|
+
description: 'API documentation for ${answers.projectName}',
|
|
162
|
+
},
|
|
163
|
+
servers: [
|
|
164
|
+
{ url: \`http://localhost:\${process.env.PORT || 3000}/api/v1\`, description: 'Development' },
|
|
165
|
+
],
|
|
166
|
+
components: {
|
|
167
|
+
securitySchemes: {
|
|
168
|
+
bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
apis: ['./src/routes/*.js'],
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export const swaggerSpec = swaggerJsdoc(options);
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
|
|
143
179
|
function envConfigContent(answers) {
|
|
144
180
|
return `export const env = {
|
|
145
|
-
PORT:
|
|
181
|
+
PORT: process.env.PORT || 3000,
|
|
146
182
|
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
147
|
-
${answers.database === 'mongodb'
|
|
183
|
+
${answers.database === 'mongodb' ? ` MONGO_URI: process.env.MONGO_URI,\n` : ''}${answers.database === 'postgresql' || answers.database === 'mysql' ? ` DATABASE_URL: process.env.DATABASE_URL,\n` : ''}${answers.auth === 'jwt' ? ` JWT_SECRET: process.env.JWT_SECRET,\n JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '7d',\n JWT_REFRESH_SECRET: process.env.JWT_REFRESH_SECRET,\n JWT_REFRESH_EXPIRES_IN: process.env.JWT_REFRESH_EXPIRES_IN || '30d',\n` : ''}};
|
|
148
184
|
`;
|
|
149
185
|
}
|
|
150
186
|
|