create-wabck 1.0.0 → 1.1.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 +5 -1
- package/package.json +2 -6
- package/src/generator.js +8 -1
- package/src/index.js +4 -3
- package/src/prompts.js +10 -2
- package/src/templates/base.js +403 -387
- package/src/templates/config.js +1 -55
- package/src/templates/index.js +6 -0
- package/src/templates/routes.js +178 -12
- package/src/templates/swagger.js +601 -0
- package/src/utils/pkg.js +10 -2
package/src/templates/base.js
CHANGED
|
@@ -1,387 +1,403 @@
|
|
|
1
|
-
import { randomBytes } from 'crypto';
|
|
2
|
-
|
|
3
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
-
// Base Templates — app.js/ts, server.js/ts, and all root config files
|
|
5
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
-
|
|
7
|
-
const ext = (isTS) => (isTS ? 'js' : 'js'); // ESM always uses .js imports
|
|
8
|
-
|
|
9
|
-
/** Generate src/app.js or src/app.ts */
|
|
10
|
-
export function app(options) {
|
|
11
|
-
const { apiStyle, language } = options;
|
|
12
|
-
const isTS = language === 'typescript';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}))
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
import
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
import
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
#
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
.
|
|
241
|
-
|
|
242
|
-
.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
*.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
${
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
${validation !== 'none' ?
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
|
377
|
-
|
|
378
|
-
|
|
|
379
|
-
${
|
|
380
|
-
|
|
381
|
-
${
|
|
382
|
-
${
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
// Base Templates — app.js/ts, server.js/ts, and all root config files
|
|
5
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
const ext = (isTS) => (isTS ? 'js' : 'js'); // ESM always uses .js imports
|
|
8
|
+
|
|
9
|
+
/** Generate src/app.js or src/app.ts */
|
|
10
|
+
export function app(options) {
|
|
11
|
+
const { apiStyle, language, swagger } = options;
|
|
12
|
+
const isTS = language === 'typescript';
|
|
13
|
+
const hasSwagger = swagger === true;
|
|
14
|
+
|
|
15
|
+
const typeImport = isTS ? `import { Application } from 'express';\n` : '';
|
|
16
|
+
const appType = isTS ? ': Application' : '';
|
|
17
|
+
|
|
18
|
+
const graphqlBlock =
|
|
19
|
+
apiStyle === 'graphql'
|
|
20
|
+
? `
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
// GraphQL — Apollo Server 4 mounted on /graphql
|
|
23
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
import { expressMiddleware } from '@as-integrations/express4';
|
|
25
|
+
import { createApolloServer } from './graphql/server.js';
|
|
26
|
+
|
|
27
|
+
const apolloServer = createApolloServer();
|
|
28
|
+
await apolloServer.start();
|
|
29
|
+
app.use('/graphql', expressMiddleware(apolloServer, {
|
|
30
|
+
context: async ({ req }) => ({ req }),
|
|
31
|
+
}));
|
|
32
|
+
`
|
|
33
|
+
: '';
|
|
34
|
+
|
|
35
|
+
const swaggerImport = hasSwagger
|
|
36
|
+
? `import { setupSwagger } from './config/swagger.js';\n`
|
|
37
|
+
: '';
|
|
38
|
+
|
|
39
|
+
const swaggerMount = hasSwagger
|
|
40
|
+
? `\n// ─────────────────────────────────────────────────────────────────────────────\n// Swagger UI — available at /api-docs (development)\n// ─────────────────────────────────────────────────────────────────────────────\nif (process.env.NODE_ENV !== 'production') {\n setupSwagger(app);\n}\n`
|
|
41
|
+
: '';
|
|
42
|
+
|
|
43
|
+
return `${typeImport}${swaggerImport}import express${isTS ? ', { Application }' : ''} from 'express';
|
|
44
|
+
import cors from 'cors';
|
|
45
|
+
import helmet from 'helmet';
|
|
46
|
+
import compression from 'compression';
|
|
47
|
+
import morgan from 'morgan';
|
|
48
|
+
import cookieParser from 'cookie-parser';
|
|
49
|
+
import { rateLimiter } from './middlewares/rateLimiter.middleware.js';
|
|
50
|
+
import { errorMiddleware } from './middlewares/error.middleware.js';
|
|
51
|
+
import { notFoundMiddleware } from './middlewares/notFound.middleware.js';
|
|
52
|
+
import routes from './routes/index.js';
|
|
53
|
+
|
|
54
|
+
const app${appType} = express();
|
|
55
|
+
|
|
56
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
57
|
+
// Security Middleware
|
|
58
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
59
|
+
app.use(helmet());
|
|
60
|
+
app.use(
|
|
61
|
+
cors({
|
|
62
|
+
origin: process.env.CORS_ORIGIN || '*',
|
|
63
|
+
credentials: true,
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
app.use(rateLimiter);
|
|
67
|
+
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
// Body Parsing
|
|
70
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
71
|
+
app.use(express.json({ limit: '16kb' }));
|
|
72
|
+
app.use(express.urlencoded({ extended: true, limit: '16kb' }));
|
|
73
|
+
app.use(cookieParser());
|
|
74
|
+
|
|
75
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
76
|
+
// Logging & Compression
|
|
77
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
78
|
+
app.use(compression());
|
|
79
|
+
app.use(morgan(process.env.NODE_ENV === 'production' ? 'combined' : 'dev'));
|
|
80
|
+
${graphqlBlock}
|
|
81
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
82
|
+
// API Routes — versioned under /api/v1
|
|
83
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
84
|
+
app.use('/api/v1', routes);
|
|
85
|
+
${swaggerMount}
|
|
86
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
87
|
+
// Error Handling — Order matters: 404 must come before the error handler
|
|
88
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
89
|
+
app.use(notFoundMiddleware);
|
|
90
|
+
app.use(errorMiddleware);
|
|
91
|
+
|
|
92
|
+
export default app;
|
|
93
|
+
`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Generate src/server.js or src/server.ts */
|
|
97
|
+
export function server(options) {
|
|
98
|
+
const { database, language, projectName, swagger } = options;
|
|
99
|
+
const isTS = language === 'typescript';
|
|
100
|
+
const hasDB = database !== 'none';
|
|
101
|
+
const hasSwagger = swagger === true;
|
|
102
|
+
|
|
103
|
+
const dbImport = hasDB ? `import connectDB from './config/db.js';\n` : '';
|
|
104
|
+
const dbConnect = hasDB ? ` await connectDB();\n` : '';
|
|
105
|
+
const swaggerLog = hasSwagger
|
|
106
|
+
? ` console.log(\` ➜ Docs: http://localhost:\${PORT}/api-docs\`);\n console.log(\` ➜ OpenAPI: http://localhost:\${PORT}/api-docs.yaml (or openapi.yaml)\`);\n`
|
|
107
|
+
: '';
|
|
108
|
+
|
|
109
|
+
return `import 'dotenv/config';
|
|
110
|
+
import app from './app.js';
|
|
111
|
+
${dbImport}
|
|
112
|
+
const PORT = process.env.PORT || 5000;
|
|
113
|
+
const HOST = process.env.HOST || '0.0.0.0';
|
|
114
|
+
|
|
115
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
116
|
+
// Bootstrap — Connect to DB then start HTTP server
|
|
117
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118
|
+
const startServer = async ()${isTS ? ': Promise<void>' : ''} => {
|
|
119
|
+
try {
|
|
120
|
+
${dbConnect} const server = app.listen(PORT, HOST, () => {
|
|
121
|
+
console.log(\`\`);
|
|
122
|
+
console.log(\` 🚀 ${projectName} is running!\`);
|
|
123
|
+
console.log(\` ➜ Local: http://localhost:\${PORT}\`);
|
|
124
|
+
console.log(\` ➜ Health: http://localhost:\${PORT}/api/v1/health\`);
|
|
125
|
+
${swaggerLog} console.log(\`\`);
|
|
126
|
+
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// ── Graceful Shutdown ──────────────────────────────────────────────────
|
|
130
|
+
const shutdown = (signal${isTS ? ': string' : ''}) => {
|
|
131
|
+
console.log(\`\\n Received \${signal}. Gracefully shutting down…\`);
|
|
132
|
+
server.close(() => {
|
|
133
|
+
console.log(' HTTP server closed.');
|
|
134
|
+
process.exit(0);
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
139
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
140
|
+
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error('Failed to start server:', error);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
startServer();
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Generate .env */
|
|
152
|
+
export function envFile(options) {
|
|
153
|
+
const { database, auth, projectName } = options;
|
|
154
|
+
|
|
155
|
+
// Auto-generate a cryptographically secure JWT secret at scaffold time
|
|
156
|
+
const jwtSecret = randomBytes(32).toString('hex');
|
|
157
|
+
|
|
158
|
+
const dbVars = {
|
|
159
|
+
mongodb: 'MONGODB_URI=mongodb://localhost:27017/${projectName}',
|
|
160
|
+
postgresql: 'DATABASE_URL=postgresql://postgres:password@localhost:5432/${projectName}',
|
|
161
|
+
mysql: `DB_HOST=localhost\nDB_PORT=3306\nDB_USER=root\nDB_PASSWORD=password\nDB_NAME=${projectName}`,
|
|
162
|
+
sqlite: `SQLITE_PATH=./database.sqlite`,
|
|
163
|
+
none: '',
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const authVars = {
|
|
167
|
+
jwt: `\nJWT_SECRET=${jwtSecret}\nJWT_EXPIRES_IN=7d\nJWT_REFRESH_EXPIRES_IN=30d`,
|
|
168
|
+
clerk: `\nCLERK_SECRET_KEY=sk_test_your-clerk-secret-key`,
|
|
169
|
+
firebase: `\nFIREBASE_PROJECT_ID=your-firebase-project-id\nFIREBASE_PRIVATE_KEY=your-firebase-private-key\nFIREBASE_CLIENT_EMAIL=your-firebase-client-email`,
|
|
170
|
+
none: '',
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
return `# ─────────────────────────────────────────────
|
|
174
|
+
# ${projectName} — Environment Variables
|
|
175
|
+
# ─────────────────────────────────────────────
|
|
176
|
+
# Copy this file to .env and fill in the values.
|
|
177
|
+
# JWT_SECRET below was auto-generated at scaffold time — keep it secret!
|
|
178
|
+
|
|
179
|
+
NODE_ENV=development
|
|
180
|
+
PORT=5000
|
|
181
|
+
HOST=0.0.0.0
|
|
182
|
+
CORS_ORIGIN=*
|
|
183
|
+
|
|
184
|
+
# Database
|
|
185
|
+
${(dbVars[database] || '').replace('${projectName}', projectName)}
|
|
186
|
+
${authVars[auth] || ''}
|
|
187
|
+
`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Generate .env.example — safe to commit; uses a placeholder instead of the real secret */
|
|
191
|
+
export function envExample(options) {
|
|
192
|
+
const { database, auth, projectName } = options;
|
|
193
|
+
|
|
194
|
+
const dbVars = {
|
|
195
|
+
mongodb: `MONGODB_URI=mongodb://localhost:27017/${projectName}`,
|
|
196
|
+
postgresql: `DATABASE_URL=postgresql://postgres:password@localhost:5432/${projectName}`,
|
|
197
|
+
mysql: `DB_HOST=localhost\nDB_PORT=3306\nDB_USER=root\nDB_PASSWORD=password\nDB_NAME=${projectName}`,
|
|
198
|
+
sqlite: `SQLITE_PATH=./database.sqlite`,
|
|
199
|
+
none: '',
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const authVars = {
|
|
203
|
+
jwt: `\nJWT_SECRET=change-me-generate-with-openssl-rand-hex-32\nJWT_EXPIRES_IN=7d\nJWT_REFRESH_EXPIRES_IN=30d`,
|
|
204
|
+
clerk: `\nCLERK_SECRET_KEY=sk_test_your-clerk-secret-key`,
|
|
205
|
+
firebase: `\nFIREBASE_PROJECT_ID=your-firebase-project-id\nFIREBASE_PRIVATE_KEY=your-firebase-private-key\nFIREBASE_CLIENT_EMAIL=your-firebase-client-email`,
|
|
206
|
+
none: '',
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
return `# ─────────────────────────────────────────────
|
|
210
|
+
# ${projectName} — Environment Variables (EXAMPLE)
|
|
211
|
+
# ─────────────────────────────────────────────
|
|
212
|
+
# This file is safe to commit. Copy to .env and replace placeholder values.
|
|
213
|
+
# Your actual .env already has a generated JWT_SECRET.
|
|
214
|
+
|
|
215
|
+
NODE_ENV=development
|
|
216
|
+
PORT=5000
|
|
217
|
+
HOST=0.0.0.0
|
|
218
|
+
CORS_ORIGIN=*
|
|
219
|
+
|
|
220
|
+
# Database
|
|
221
|
+
${(dbVars[database] || '').replace('${projectName}', projectName)}
|
|
222
|
+
${authVars[auth] || ''}
|
|
223
|
+
`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Generate .gitignore for the generated project */
|
|
227
|
+
export function gitignore() {
|
|
228
|
+
return `# ─── Dependencies ──────────────────────────────
|
|
229
|
+
node_modules/
|
|
230
|
+
.pnp
|
|
231
|
+
.pnp.js
|
|
232
|
+
|
|
233
|
+
# ─── Build Outputs ──────────────────────────────
|
|
234
|
+
dist/
|
|
235
|
+
build/
|
|
236
|
+
*.tsbuildinfo
|
|
237
|
+
|
|
238
|
+
# ─── Environment Variables ──────────────────────
|
|
239
|
+
.env
|
|
240
|
+
.env.local
|
|
241
|
+
.env.*.local
|
|
242
|
+
# .env.example is safe to commit
|
|
243
|
+
|
|
244
|
+
# ─── Logs ───────────────────────────────────────
|
|
245
|
+
logs/
|
|
246
|
+
*.log
|
|
247
|
+
npm-debug.log*
|
|
248
|
+
yarn-debug.log*
|
|
249
|
+
yarn-error.log*
|
|
250
|
+
pnpm-debug.log*
|
|
251
|
+
|
|
252
|
+
# ─── OS / Editor ────────────────────────────────
|
|
253
|
+
.DS_Store
|
|
254
|
+
Thumbs.db
|
|
255
|
+
.idea/
|
|
256
|
+
.vscode/
|
|
257
|
+
|
|
258
|
+
# ─── Database ───────────────────────────────────
|
|
259
|
+
*.sqlite
|
|
260
|
+
*.sqlite3
|
|
261
|
+
database.sqlite
|
|
262
|
+
|
|
263
|
+
# ─── Testing ────────────────────────────────────
|
|
264
|
+
coverage/
|
|
265
|
+
`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Generate .prettierrc */
|
|
269
|
+
export function prettierrc() {
|
|
270
|
+
return JSON.stringify(
|
|
271
|
+
{
|
|
272
|
+
semi: true,
|
|
273
|
+
singleQuote: true,
|
|
274
|
+
trailingComma: 'es5',
|
|
275
|
+
printWidth: 100,
|
|
276
|
+
tabWidth: 2,
|
|
277
|
+
useTabs: false,
|
|
278
|
+
bracketSpacing: true,
|
|
279
|
+
arrowParens: 'always',
|
|
280
|
+
},
|
|
281
|
+
null,
|
|
282
|
+
2
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Generate eslint.config.js */
|
|
287
|
+
export function eslintConfig(options) {
|
|
288
|
+
const isTS = options.language === 'typescript';
|
|
289
|
+
return `import js from '@eslint/js';
|
|
290
|
+
import prettier from 'eslint-plugin-prettier';
|
|
291
|
+
import eslintConfigPrettier from 'eslint-config-prettier';
|
|
292
|
+
|
|
293
|
+
export default [
|
|
294
|
+
js.configs.recommended,
|
|
295
|
+
eslintConfigPrettier,
|
|
296
|
+
{
|
|
297
|
+
plugins: { prettier },
|
|
298
|
+
rules: {
|
|
299
|
+
'prettier/prettier': 'error',
|
|
300
|
+
'no-console': 'warn',
|
|
301
|
+
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
|
302
|
+
'no-duplicate-imports': 'error',
|
|
303
|
+
'prefer-const': 'error',
|
|
304
|
+
'no-var': 'error',
|
|
305
|
+
},
|
|
306
|
+
languageOptions: {
|
|
307
|
+
ecmaVersion: 2022,
|
|
308
|
+
sourceType: 'module',
|
|
309
|
+
globals: {
|
|
310
|
+
process: 'readonly',
|
|
311
|
+
console: 'readonly',
|
|
312
|
+
Buffer: 'readonly',
|
|
313
|
+
URL: 'readonly',
|
|
314
|
+
setTimeout: 'readonly',
|
|
315
|
+
clearTimeout: 'readonly',
|
|
316
|
+
setInterval: 'readonly',
|
|
317
|
+
clearInterval: 'readonly',
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
];
|
|
322
|
+
`;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** Generate README.md */
|
|
326
|
+
export function readme(options) {
|
|
327
|
+
const { projectName, language, database, auth, validation, apiStyle, docker, swagger, packageManager } = options;
|
|
328
|
+
const isTS = language === 'typescript';
|
|
329
|
+
const pm = packageManager;
|
|
330
|
+
|
|
331
|
+
return `# ${projectName}
|
|
332
|
+
|
|
333
|
+
> Production-ready Express.js API — scaffolded by [wabck](https://github.com/wabck/wabck)
|
|
334
|
+
|
|
335
|
+
## 🚀 Quick Start
|
|
336
|
+
|
|
337
|
+
\`\`\`bash
|
|
338
|
+
# Install dependencies
|
|
339
|
+
${pm} ${pm === 'yarn' ? '' : 'install'}
|
|
340
|
+
|
|
341
|
+
# Copy environment variables
|
|
342
|
+
cp .env.example .env
|
|
343
|
+
|
|
344
|
+
# Start development server
|
|
345
|
+
${pm} run dev
|
|
346
|
+
\`\`\`
|
|
347
|
+
|
|
348
|
+
The API will be available at **http://localhost:5000**
|
|
349
|
+
${swagger ? `\n📖 **API Documentation:**
|
|
350
|
+
- **Swagger UI:** http://localhost:5000/api-docs
|
|
351
|
+
- **OpenAPI 3.0 YAML:** \`openapi.yaml\` or http://localhost:5000/api-docs.yaml
|
|
352
|
+
- **OpenAPI 3.0 JSON:** http://localhost:5000/api-docs.json\n` : ''}
|
|
353
|
+
## 📁 Project Structure
|
|
354
|
+
|
|
355
|
+
\`\`\`
|
|
356
|
+
${projectName}/
|
|
357
|
+
├── src/
|
|
358
|
+
│ ├── config/ # DB connection, env validation${swagger ? ', swagger' : ''}
|
|
359
|
+
│ ├── controllers/ # Request handlers
|
|
360
|
+
│ ├── middlewares/ # Error, auth, rate-limiter
|
|
361
|
+
│ ├── models/ # Data models
|
|
362
|
+
│ ├── routes/ # Route definitions
|
|
363
|
+
│ ├── services/ # Business logic
|
|
364
|
+
│ ├── utils/ # asyncHandler, ApiError, ApiResponse
|
|
365
|
+
${validation !== 'none' ? `│ ├── validators/ # Input validation\n` : ''}│ ├── helpers/ # Token, etc.
|
|
366
|
+
│ ├── constants/ # App-wide constants
|
|
367
|
+
│ ├── app.${isTS ? 'ts' : 'js'} # Express app
|
|
368
|
+
│ └── server.${isTS ? 'ts' : 'js'} # Entry point
|
|
369
|
+
${swagger ? '├── openapi.yaml # OpenAPI 3.0 specification (YAML)\n' : ''}├── .env.example
|
|
370
|
+
├── .prettierrc
|
|
371
|
+
└── eslint.config.js
|
|
372
|
+
\`\`\`
|
|
373
|
+
|
|
374
|
+
## 🛠 Stack
|
|
375
|
+
|
|
376
|
+
| Feature | Choice |
|
|
377
|
+
|---------------|---------------------------|
|
|
378
|
+
| Language | ${isTS ? 'TypeScript' : 'JavaScript (ES Modules)'} |
|
|
379
|
+
| Database | ${database === 'none' ? '—' : database} |
|
|
380
|
+
| Auth | ${auth === 'none' ? '—' : auth} |
|
|
381
|
+
${validation !== 'none' ? `| Validation | ${validation} |\n` : ''}| API Style | ${apiStyle === 'graphql' ? 'GraphQL (Apollo Server 4)' : 'REST'} |
|
|
382
|
+
${swagger ? '| API Docs | ✔ Swagger / OpenAPI 3.0 (/api-docs + openapi.yaml) |\n' : ''}| Docker | ${docker ? '✔ Dockerfile + docker-compose' : '—'} |
|
|
383
|
+
|
|
384
|
+
## 🔗 Endpoints
|
|
385
|
+
|
|
386
|
+
| Method | Path | Description |
|
|
387
|
+
|--------|--------------------|--------------------|
|
|
388
|
+
| GET | \`/\` | Root health ping |
|
|
389
|
+
| GET | \`/api/v1/health\` | Detailed health |
|
|
390
|
+
| GET | \`/api/v1/users\` | List users |
|
|
391
|
+
| POST | \`/api/v1/users\` | Create user |
|
|
392
|
+
| GET | \`/api/v1/users/:id\` | Get user by ID |
|
|
393
|
+
| PUT | \`/api/v1/users/:id\` | Update user |
|
|
394
|
+
| DELETE | \`/api/v1/users/:id\` | Delete user |
|
|
395
|
+
${auth === 'jwt' ? `| POST | \`/api/v1/auth/register\` | Register |\n| POST | \`/api/v1/auth/login\` | Login |` : ''}
|
|
396
|
+
${swagger ? `| GET | \`/api-docs\` | Swagger UI (interactive) |\n| GET | \`/api-docs.yaml\` | OpenAPI 3.0 YAML spec |\n| GET | \`/api-docs.json\` | OpenAPI 3.0 JSON spec |\n` : ''}
|
|
397
|
+
${docker ? `## 🐳 Docker\n\n\`\`\`bash\ndocker compose up --build\n\`\`\`\n` : ''}
|
|
398
|
+
${isTS ? `## 🏗 Build\n\n\`\`\`bash\n${pm} run build\n${pm} start\n\`\`\`\n` : ''}
|
|
399
|
+
## 📜 License
|
|
400
|
+
|
|
401
|
+
MIT
|
|
402
|
+
`;
|
|
403
|
+
}
|