create-wabck 1.0.0 → 1.1.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/README.md +2 -0
- package/package.json +2 -6
- package/src/generator.js +5 -1
- package/src/index.js +4 -3
- package/src/prompts.js +10 -2
- package/src/templates/base.js +400 -387
- package/src/templates/config.js +75 -24
- package/src/templates/index.js +2 -0
- package/src/templates/routes.js +178 -12
- package/src/utils/pkg.js +10 -2
package/src/templates/config.js
CHANGED
|
@@ -215,19 +215,25 @@ ${database === 'mongodb' ? " mongoUri: process.env.MONGODB_URI || '',\n" : '
|
|
|
215
215
|
`;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
/** Generate src/config/swagger.js */
|
|
219
|
-
export function
|
|
218
|
+
/** Generate src/config/swagger.js or swagger.ts */
|
|
219
|
+
export function swaggerConfig(options) {
|
|
220
220
|
const { projectName, language } = options;
|
|
221
221
|
const isTS = language === 'typescript';
|
|
222
|
+
const ext = isTS ? 'ts' : 'js';
|
|
222
223
|
|
|
223
224
|
return `import swaggerJsdoc from 'swagger-jsdoc';
|
|
225
|
+
import swaggerUi from 'swagger-ui-express';
|
|
224
226
|
import { readFileSync } from 'fs';
|
|
225
227
|
import { fileURLToPath } from 'url';
|
|
226
228
|
import path from 'path';
|
|
227
|
-
|
|
229
|
+
${isTS ? `import type { Express } from 'express';\n` : ''}
|
|
228
230
|
const __filename = fileURLToPath(import.meta.url);
|
|
229
231
|
const __dirname = path.dirname(__filename);
|
|
230
232
|
|
|
233
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
234
|
+
// Swagger / OpenAPI 3.0 Configuration
|
|
235
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
236
|
+
|
|
231
237
|
// Read version from package.json
|
|
232
238
|
let version = '1.0.0';
|
|
233
239
|
try {
|
|
@@ -235,37 +241,82 @@ try {
|
|
|
235
241
|
version = pkg.version;
|
|
236
242
|
} catch { /* use default */ }
|
|
237
243
|
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
244
|
+
const swaggerDefinition = {
|
|
245
|
+
openapi: '3.0.0',
|
|
246
|
+
info: {
|
|
247
|
+
title: '${projectName} API',
|
|
248
|
+
version,
|
|
249
|
+
description: 'REST API documentation for ${projectName}',
|
|
250
|
+
license: { name: 'MIT' },
|
|
251
|
+
contact: { name: '${projectName} Team' },
|
|
252
|
+
},
|
|
253
|
+
servers: [
|
|
254
|
+
{ url: \`http://localhost:\${process.env.PORT || 5000}/api/v1\`, description: 'Development' },
|
|
255
|
+
{ url: '/api/v1', description: 'Relative (proxy-friendly)' },
|
|
256
|
+
],
|
|
257
|
+
components: {
|
|
258
|
+
securitySchemes: {
|
|
259
|
+
bearerAuth: {
|
|
260
|
+
type: 'http',
|
|
261
|
+
scheme: 'bearer',
|
|
262
|
+
bearerFormat: 'JWT',
|
|
263
|
+
description: 'Enter your JWT token. Example: Bearer eyJhbGci...',
|
|
264
|
+
},
|
|
247
265
|
},
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
type:
|
|
255
|
-
|
|
256
|
-
|
|
266
|
+
schemas: {
|
|
267
|
+
ApiResponse: {
|
|
268
|
+
type: 'object',
|
|
269
|
+
properties: {
|
|
270
|
+
success: { type: 'boolean' },
|
|
271
|
+
statusCode: { type: 'integer' },
|
|
272
|
+
message: { type: 'string' },
|
|
273
|
+
data: { },
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
ApiError: {
|
|
277
|
+
type: 'object',
|
|
278
|
+
properties: {
|
|
279
|
+
success: { type: 'boolean', example: false },
|
|
280
|
+
statusCode: { type: 'integer' },
|
|
281
|
+
message: { type: 'string' },
|
|
282
|
+
errors: { type: 'array', items: { type: 'string' } },
|
|
257
283
|
},
|
|
258
284
|
},
|
|
259
285
|
},
|
|
260
|
-
security: [{ bearerAuth: [] }],
|
|
261
286
|
},
|
|
262
|
-
|
|
287
|
+
security: [{ bearerAuth: [] }],
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const swaggerOptions = {
|
|
291
|
+
definition: swaggerDefinition,
|
|
292
|
+
// Scans all route files for @swagger JSDoc annotations
|
|
263
293
|
apis: [
|
|
264
294
|
path.join(__dirname, '../routes/*.js'),
|
|
265
295
|
path.join(__dirname, '../routes/*.ts'),
|
|
266
296
|
],
|
|
267
297
|
};
|
|
268
298
|
|
|
269
|
-
export const swaggerSpec = swaggerJsdoc(
|
|
299
|
+
export const swaggerSpec = swaggerJsdoc(swaggerOptions);
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Mounts the Swagger UI at /api-docs
|
|
303
|
+
* @param {${isTS ? 'Express' : 'import("express").Express'}} app
|
|
304
|
+
*/
|
|
305
|
+
export function setupSwagger(app${isTS ? ': Express' : ''}) {
|
|
306
|
+
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
|
|
307
|
+
customSiteTitle: '${projectName} API Docs',
|
|
308
|
+
swaggerOptions: {
|
|
309
|
+
persistAuthorization: true,
|
|
310
|
+
docExpansion: 'list',
|
|
311
|
+
filter: true,
|
|
312
|
+
},
|
|
313
|
+
}));
|
|
314
|
+
|
|
315
|
+
// Raw JSON spec endpoint (useful for Postman / code-gen)
|
|
316
|
+
app.get('/api-docs.json', (_req, res) => {
|
|
317
|
+
res.setHeader('Content-Type', 'application/json');
|
|
318
|
+
res.send(swaggerSpec);
|
|
319
|
+
});
|
|
320
|
+
}
|
|
270
321
|
`;
|
|
271
322
|
}
|
package/src/templates/index.js
CHANGED
package/src/templates/routes.js
CHANGED
|
@@ -27,11 +27,41 @@ export default router;
|
|
|
27
27
|
|
|
28
28
|
/** Generate src/routes/health.route.js */
|
|
29
29
|
export function healthRoute(options) {
|
|
30
|
-
const { language, database, projectName } = options;
|
|
30
|
+
const { language, database, projectName, swagger } = options;
|
|
31
31
|
const isTS = language === 'typescript';
|
|
32
32
|
|
|
33
33
|
const typeImports = isTS ? `import { Request, Response } from 'express';\n` : '';
|
|
34
34
|
|
|
35
|
+
const swaggerDoc = swagger
|
|
36
|
+
? `/**
|
|
37
|
+
* @swagger
|
|
38
|
+
* /health:
|
|
39
|
+
* get:
|
|
40
|
+
* summary: Health check & uptime
|
|
41
|
+
* tags: [Health]
|
|
42
|
+
* responses:
|
|
43
|
+
* 200:
|
|
44
|
+
* description: Service metrics, uptime, version, and memory usage
|
|
45
|
+
* content:
|
|
46
|
+
* application/json:
|
|
47
|
+
* schema:
|
|
48
|
+
* type: object
|
|
49
|
+
* properties:
|
|
50
|
+
* status: { type: string, example: ok }
|
|
51
|
+
* uptime: { type: integer, example: 120 }
|
|
52
|
+
* version: { type: string, example: 1.0.0 }
|
|
53
|
+
* env: { type: string, example: development }
|
|
54
|
+
* timestamp: { type: string, example: "2026-01-01T00:00:00.000Z" }
|
|
55
|
+
*/
|
|
56
|
+
`
|
|
57
|
+
: `/**
|
|
58
|
+
* GET /api/v1/health
|
|
59
|
+
*
|
|
60
|
+
* Returns the current health status of the API including uptime,
|
|
61
|
+
* version, environment, and timestamp.
|
|
62
|
+
*/
|
|
63
|
+
`;
|
|
64
|
+
|
|
35
65
|
return `${typeImports}import { Router } from 'express';
|
|
36
66
|
import { readFileSync } from 'fs';
|
|
37
67
|
import { fileURLToPath } from 'url';
|
|
@@ -48,13 +78,7 @@ try {
|
|
|
48
78
|
|
|
49
79
|
const router = Router();
|
|
50
80
|
|
|
51
|
-
|
|
52
|
-
* GET /api/v1/health
|
|
53
|
-
*
|
|
54
|
-
* Returns the current health status of the API including uptime,
|
|
55
|
-
* version, environment, and timestamp.
|
|
56
|
-
*/
|
|
57
|
-
router.get('/', (_req${isTS ? ': Request' : ''}, res${isTS ? ': Response' : ''}) => {
|
|
81
|
+
${swaggerDoc}router.get('/', (_req${isTS ? ': Request' : ''}, res${isTS ? ': Response' : ''}) => {
|
|
58
82
|
res.status(200).json({
|
|
59
83
|
status: 'ok',
|
|
60
84
|
uptime: Math.floor(process.uptime()),
|
|
@@ -74,7 +98,7 @@ export default router;
|
|
|
74
98
|
|
|
75
99
|
/** Generate src/routes/user.route.js */
|
|
76
100
|
export function userRoute(options) {
|
|
77
|
-
const { auth, language, validation } = options;
|
|
101
|
+
const { auth, language, validation, swagger } = options;
|
|
78
102
|
const isTS = language === 'typescript';
|
|
79
103
|
const hasJwt = auth === 'jwt';
|
|
80
104
|
const hasValidation = validation && validation !== 'none';
|
|
@@ -106,6 +130,85 @@ export function userRoute(options) {
|
|
|
106
130
|
}
|
|
107
131
|
}
|
|
108
132
|
|
|
133
|
+
const swaggerDoc = swagger
|
|
134
|
+
? `/**
|
|
135
|
+
* @swagger
|
|
136
|
+
* /users:
|
|
137
|
+
* get:
|
|
138
|
+
* summary: Retrieve all users
|
|
139
|
+
* tags: [Users]
|
|
140
|
+
* responses:
|
|
141
|
+
* 200:
|
|
142
|
+
* description: List of users retrieved successfully
|
|
143
|
+
* post:
|
|
144
|
+
* summary: Create a new user
|
|
145
|
+
* tags: [Users]
|
|
146
|
+
* requestBody:
|
|
147
|
+
* required: true
|
|
148
|
+
* content:
|
|
149
|
+
* application/json:
|
|
150
|
+
* schema:
|
|
151
|
+
* type: object
|
|
152
|
+
* required: [name, email]
|
|
153
|
+
* properties:
|
|
154
|
+
* name: { type: string, example: "Alex Smith" }
|
|
155
|
+
* email: { type: string, example: "alex@example.com" }
|
|
156
|
+
* password: { type: string, example: "SecurePass123!" }
|
|
157
|
+
* responses:
|
|
158
|
+
* 201:
|
|
159
|
+
* description: User created
|
|
160
|
+
*
|
|
161
|
+
* /users/{id}:
|
|
162
|
+
* get:
|
|
163
|
+
* summary: Get user by ID
|
|
164
|
+
* tags: [Users]
|
|
165
|
+
* parameters:
|
|
166
|
+
* - in: path
|
|
167
|
+
* name: id
|
|
168
|
+
* required: true
|
|
169
|
+
* schema: { type: string }
|
|
170
|
+
* responses:
|
|
171
|
+
* 200:
|
|
172
|
+
* description: User found
|
|
173
|
+
* 404:
|
|
174
|
+
* description: User not found
|
|
175
|
+
* put:
|
|
176
|
+
* summary: Update user by ID
|
|
177
|
+
* tags: [Users]
|
|
178
|
+
* parameters:
|
|
179
|
+
* - in: path
|
|
180
|
+
* name: id
|
|
181
|
+
* required: true
|
|
182
|
+
* schema: { type: string }
|
|
183
|
+
* requestBody:
|
|
184
|
+
* required: true
|
|
185
|
+
* content:
|
|
186
|
+
* application/json:
|
|
187
|
+
* schema:
|
|
188
|
+
* type: object
|
|
189
|
+
* properties:
|
|
190
|
+
* name: { type: string }
|
|
191
|
+
* email: { type: string }
|
|
192
|
+
* responses:
|
|
193
|
+
* 200:
|
|
194
|
+
* description: User updated
|
|
195
|
+
* delete:
|
|
196
|
+
* summary: Delete user by ID
|
|
197
|
+
* tags: [Users]
|
|
198
|
+
* security:
|
|
199
|
+
* - bearerAuth: []
|
|
200
|
+
* parameters:
|
|
201
|
+
* - in: path
|
|
202
|
+
* name: id
|
|
203
|
+
* required: true
|
|
204
|
+
* schema: { type: string }
|
|
205
|
+
* responses:
|
|
206
|
+
* 200:
|
|
207
|
+
* description: User deleted
|
|
208
|
+
*/
|
|
209
|
+
`
|
|
210
|
+
: '';
|
|
211
|
+
|
|
109
212
|
return `import { Router } from 'express';
|
|
110
213
|
import {
|
|
111
214
|
getAllUsers,
|
|
@@ -120,7 +223,7 @@ const router = Router();
|
|
|
120
223
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
121
224
|
// User Routes — /api/v1/users
|
|
122
225
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
123
|
-
|
|
226
|
+
${swaggerDoc}
|
|
124
227
|
router.get('/', ${guardStart}getAllUsers);
|
|
125
228
|
router.post('/', ${guardStart}${validateCreate}createUser);
|
|
126
229
|
router.get('/:id', ${guardStart}getUserById);
|
|
@@ -133,7 +236,7 @@ export default router;
|
|
|
133
236
|
|
|
134
237
|
/** Generate src/routes/auth.route.js (JWT only) */
|
|
135
238
|
export function authRoute(options) {
|
|
136
|
-
const { language, validation } = options;
|
|
239
|
+
const { language, validation, swagger } = options;
|
|
137
240
|
const isTS = language === 'typescript';
|
|
138
241
|
const hasValidation = validation && validation !== 'none';
|
|
139
242
|
|
|
@@ -153,6 +256,69 @@ export function authRoute(options) {
|
|
|
153
256
|
}
|
|
154
257
|
}
|
|
155
258
|
|
|
259
|
+
const swaggerDoc = swagger
|
|
260
|
+
? `/**
|
|
261
|
+
* @swagger
|
|
262
|
+
* /auth/register:
|
|
263
|
+
* post:
|
|
264
|
+
* summary: Register a new user account
|
|
265
|
+
* tags: [Auth]
|
|
266
|
+
* requestBody:
|
|
267
|
+
* required: true
|
|
268
|
+
* content:
|
|
269
|
+
* application/json:
|
|
270
|
+
* schema:
|
|
271
|
+
* type: object
|
|
272
|
+
* required: [name, email, password]
|
|
273
|
+
* properties:
|
|
274
|
+
* name: { type: string, example: "Jane Doe" }
|
|
275
|
+
* email: { type: string, example: "jane@example.com" }
|
|
276
|
+
* password: { type: string, example: "Secret123!" }
|
|
277
|
+
* responses:
|
|
278
|
+
* 201:
|
|
279
|
+
* description: Registered successfully
|
|
280
|
+
*
|
|
281
|
+
* /auth/login:
|
|
282
|
+
* post:
|
|
283
|
+
* summary: Authenticate user & get JWT tokens
|
|
284
|
+
* tags: [Auth]
|
|
285
|
+
* requestBody:
|
|
286
|
+
* required: true
|
|
287
|
+
* content:
|
|
288
|
+
* application/json:
|
|
289
|
+
* schema:
|
|
290
|
+
* type: object
|
|
291
|
+
* required: [email, password]
|
|
292
|
+
* properties:
|
|
293
|
+
* email: { type: string, example: "jane@example.com" }
|
|
294
|
+
* password: { type: string, example: "Secret123!" }
|
|
295
|
+
* responses:
|
|
296
|
+
* 200:
|
|
297
|
+
* description: Login successful
|
|
298
|
+
*
|
|
299
|
+
* /auth/logout:
|
|
300
|
+
* post:
|
|
301
|
+
* summary: Invalidate user session
|
|
302
|
+
* tags: [Auth]
|
|
303
|
+
* security:
|
|
304
|
+
* - bearerAuth: []
|
|
305
|
+
* responses:
|
|
306
|
+
* 200:
|
|
307
|
+
* description: Logged out
|
|
308
|
+
*
|
|
309
|
+
* /auth/me:
|
|
310
|
+
* get:
|
|
311
|
+
* summary: Get profile of logged-in user
|
|
312
|
+
* tags: [Auth]
|
|
313
|
+
* security:
|
|
314
|
+
* - bearerAuth: []
|
|
315
|
+
* responses:
|
|
316
|
+
* 200:
|
|
317
|
+
* description: Current user profile
|
|
318
|
+
*/
|
|
319
|
+
`
|
|
320
|
+
: '';
|
|
321
|
+
|
|
156
322
|
return `import { Router } from 'express';
|
|
157
323
|
import {
|
|
158
324
|
register,
|
|
@@ -168,7 +334,7 @@ const router = Router();
|
|
|
168
334
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
169
335
|
// Auth Routes — /api/v1/auth
|
|
170
336
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
171
|
-
|
|
337
|
+
${swaggerDoc}
|
|
172
338
|
router.post('/register', authRateLimiter, ${validateRegister}register);
|
|
173
339
|
router.post('/login', authRateLimiter, ${validateLogin}login);
|
|
174
340
|
router.post('/logout', authenticate, logout);
|
package/src/utils/pkg.js
CHANGED
|
@@ -117,11 +117,19 @@ export function buildPackageJson(options) {
|
|
|
117
117
|
|
|
118
118
|
// ── API Style ──────────────────────────────────────────────────────────────
|
|
119
119
|
if (apiStyle === 'graphql') {
|
|
120
|
-
dependencies['@apollo/server']
|
|
121
|
-
dependencies['graphql']
|
|
120
|
+
dependencies['@apollo/server'] = '^4.10.5';
|
|
121
|
+
dependencies['graphql'] = '^16.9.0';
|
|
122
122
|
dependencies['@as-integrations/express4'] = '^1.0.0';
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
// ── Swagger / OpenAPI ──────────────────────────────────────────────────────
|
|
126
|
+
if (options.swagger) {
|
|
127
|
+
dependencies['swagger-jsdoc'] = '^6.2.8';
|
|
128
|
+
dependencies['swagger-ui-express'] = '^5.0.1';
|
|
129
|
+
if (isTS) devDependencies['@types/swagger-ui-express'] = '^4.1.6';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
125
133
|
return {
|
|
126
134
|
name: projectName,
|
|
127
135
|
version: '1.0.0',
|