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