create-flex-stack 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # create-flex-stack
2
+
3
+ Production-ready TypeScript project generator for Express APIs and optional React frontends.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npm create flex-stack@latest
9
+ ```
10
+
11
+ Or run the binary directly:
12
+
13
+ ```bash
14
+ npx create-flex-stack@latest
15
+ ```
16
+
17
+ The CLI will ask for the project name and stack options, then generate the project and install dependencies.
18
+
19
+ ## Features
20
+
21
+ - Express + TypeScript backend
22
+ - Optional React + Vite frontend
23
+ - Architecture choices: Layered/MVC, VMC, Modular, Clean Architecture, Hexagonal Architecture
24
+ - Database choices: PostgreSQL, MySQL, SQLite, MongoDB
25
+ - ORM choices: Prisma, TypeORM, Mongoose
26
+ - Optional JWT auth, RBAC, Swagger, rate limiting, file uploads, Docker, Redis and Jest
27
+ - Generated README with the commands needed for the selected stack
28
+
29
+ ## Generated Project
30
+
31
+ After generation, enter the project folder and follow its generated README. For Prisma projects, the most common commands are:
32
+
33
+ ```bash
34
+ npm run db:generate
35
+ npm run db:push
36
+ npm run dev
37
+ ```
38
+
39
+ For Docker deployments with Prisma:
40
+
41
+ ```bash
42
+ npm run db:generate
43
+ npm run db:migrate:deploy
44
+ docker compose up --build
45
+ ```
46
+
47
+ ## Development
48
+
49
+ ```bash
50
+ npm install
51
+ npm run build
52
+ npm start
53
+ ```
54
+
55
+ ## License
56
+
57
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/env node
2
+ import { intro, outro, text, select, confirm, isCancel, cancel, spinner } from '@clack/prompts';
3
+ import pc from 'picocolors';
4
+ import { generateProject } from './utils/generator.js';
5
+ async function main() {
6
+ intro(pc.bold(pc.cyan('CREATE-FLEX-STACK: Ultimate Full-Stack Boilerplate Generator')));
7
+ // 1. Project Name
8
+ const projectName = await text({
9
+ message: 'Enter your project name:',
10
+ placeholder: 'my-ultimate-stack',
11
+ initialValue: 'my-ultimate-stack',
12
+ validate(value) {
13
+ if (value.length === 0)
14
+ return 'Project name is required';
15
+ if (/[<>:"/\\|?*]/.test(value))
16
+ return 'Project name contains invalid characters';
17
+ },
18
+ });
19
+ if (isCancel(projectName)) {
20
+ cancel('Generation cancelled.');
21
+ process.exit(0);
22
+ }
23
+ // 2. Choose Architecture
24
+ const architecture = await select({
25
+ message: 'Choose your backend architecture:',
26
+ options: [
27
+ { value: 'layered', label: 'Traditional Layered (MVC)', hint: 'controllers, services, repositories, models, routes' },
28
+ { value: 'vmc', label: 'VMC (View, Model, Controller)', hint: 'view DTOs, model/business logic, controllers, routes' },
29
+ { value: 'modular', label: 'Modular / Feature-Based', hint: 'organized by feature folder: users, auth, products' },
30
+ { value: 'clean', label: 'Clean Architecture', hint: 'presentation, application, domain, infrastructure, shared' },
31
+ { value: 'hexagonal', label: 'Hexagonal Architecture (Ports & Adapters)', hint: 'domain, application, ports, adapters' },
32
+ ],
33
+ });
34
+ if (isCancel(architecture)) {
35
+ cancel('Generation cancelled.');
36
+ process.exit(0);
37
+ }
38
+ // 3. Backend Framework
39
+ const frameworkChoice = await select({
40
+ message: 'Choose your backend framework:',
41
+ options: [
42
+ { value: 'express', label: 'ExpressJS (TypeScript) [Fully Implemented]' },
43
+ { value: 'nestjs', label: pc.yellow('NestJS (Coming Soon) [Fallback to Express]') },
44
+ { value: 'fastify', label: pc.yellow('Fastify (Coming Soon) [Fallback to Express]') },
45
+ { value: 'hono', label: pc.yellow('Hono (Coming Soon) [Fallback to Express]') },
46
+ { value: 'koa', label: pc.yellow('Koa (Coming Soon) [Fallback to Express]') },
47
+ { value: 'adonis', label: pc.yellow('AdonisJS (Coming Soon) [Fallback to Express]') },
48
+ ],
49
+ });
50
+ if (isCancel(frameworkChoice)) {
51
+ cancel('Generation cancelled.');
52
+ process.exit(0);
53
+ }
54
+ if (frameworkChoice !== 'express') {
55
+ console.log(pc.yellow(`\n⚠ Framework "${frameworkChoice}" is coming soon. Defaulting backend to ExpressJS.`));
56
+ }
57
+ const framework = 'express';
58
+ // 4. Database Selection
59
+ const databaseChoice = await select({
60
+ message: 'Choose your database:',
61
+ options: [
62
+ { value: 'postgres', label: 'PostgreSQL' },
63
+ { value: 'mysql', label: 'MySQL' },
64
+ { value: 'sqlite', label: 'SQLite' },
65
+ { value: 'mongodb', label: 'MongoDB' },
66
+ { value: 'mariadb', label: pc.yellow('MariaDB (Coming Soon) [Fallback to MySQL]') },
67
+ { value: 'firestore', label: pc.yellow('Firestore (Coming Soon) [Fallback to MongoDB]') },
68
+ { value: 'dynamodb', label: pc.yellow('DynamoDB (Coming Soon) [Fallback to MongoDB]') },
69
+ ],
70
+ });
71
+ if (isCancel(databaseChoice)) {
72
+ cancel('Generation cancelled.');
73
+ process.exit(0);
74
+ }
75
+ let database = 'postgres';
76
+ if (databaseChoice === 'postgres' || databaseChoice === 'mysql' || databaseChoice === 'sqlite' || databaseChoice === 'mongodb') {
77
+ database = databaseChoice;
78
+ }
79
+ else if (databaseChoice === 'mariadb') {
80
+ console.log(pc.yellow('\n⚠ MariaDB is coming soon. Defaulting to MySQL configuration.'));
81
+ database = 'mysql';
82
+ }
83
+ else {
84
+ console.log(pc.yellow(`\n⚠ DB "${databaseChoice}" is coming soon. Defaulting to MongoDB configuration.`));
85
+ database = 'mongodb';
86
+ }
87
+ // 5. ORM Selection based on database
88
+ let ormOptions = [];
89
+ if (database === 'postgres' || database === 'mysql') {
90
+ ormOptions = [
91
+ { value: 'prisma', label: 'Prisma ORM [Default]' },
92
+ { value: 'typeorm', label: 'TypeORM' },
93
+ { value: 'sequelize', label: pc.yellow('Sequelize (Coming Soon) [Fallback to Prisma]') },
94
+ { value: 'drizzle', label: pc.yellow('Drizzle ORM (Coming Soon) [Fallback to Prisma]') },
95
+ ];
96
+ }
97
+ else if (database === 'sqlite') {
98
+ ormOptions = [
99
+ { value: 'prisma', label: 'Prisma ORM [Default]' },
100
+ { value: 'typeorm', label: 'TypeORM' },
101
+ ];
102
+ }
103
+ else {
104
+ // mongodb
105
+ ormOptions = [
106
+ { value: 'mongoose', label: 'Mongoose ODM [Default]' },
107
+ { value: 'prisma', label: 'Prisma ORM' },
108
+ ];
109
+ }
110
+ const ormChoice = await select({
111
+ message: 'Choose your ORM/ODM:',
112
+ options: ormOptions,
113
+ });
114
+ if (isCancel(ormChoice)) {
115
+ cancel('Generation cancelled.');
116
+ process.exit(0);
117
+ }
118
+ let orm = 'prisma';
119
+ if (ormChoice === 'prisma' || ormChoice === 'typeorm' || ormChoice === 'mongoose') {
120
+ orm = ormChoice;
121
+ }
122
+ else {
123
+ console.log(pc.yellow(`\n⚠ ORM "${ormChoice}" is coming soon. Defaulting to Prisma ORM.`));
124
+ orm = 'prisma';
125
+ }
126
+ // 6. Frontend Framework Choice
127
+ const frontendChoice = await select({
128
+ message: 'Choose frontend framework:',
129
+ options: [
130
+ { value: 'none', label: 'None (Backend API only)' },
131
+ { value: 'react', label: 'React (Vite + TypeScript) [Fully Implemented]' },
132
+ { value: 'nextjs', label: pc.yellow('Next.js (Coming Soon) [Fallback to React]') },
133
+ { value: 'vue', label: pc.yellow('Vue 3 (Coming Soon) [Fallback to React]') },
134
+ { value: 'angular', label: pc.yellow('Angular (Coming Soon) [Fallback to React]') },
135
+ ],
136
+ });
137
+ if (isCancel(frontendChoice)) {
138
+ cancel('Generation cancelled.');
139
+ process.exit(0);
140
+ }
141
+ let frontend = false;
142
+ if (frontendChoice === 'react') {
143
+ frontend = true;
144
+ }
145
+ else if (frontendChoice !== 'none') {
146
+ console.log(pc.yellow(`\n⚠ Frontend "${frontendChoice}" is coming soon. Defaulting to React client.`));
147
+ frontend = true;
148
+ }
149
+ // If frontend is React, show UI and State prompts
150
+ if (frontend) {
151
+ const uiChoice = await select({
152
+ message: 'Choose UI library:',
153
+ options: [
154
+ { value: 'tailwind', label: 'TailwindCSS + shadcn/ui [Fully Implemented]' },
155
+ { value: 'mui', label: pc.yellow('Material UI (Coming Soon) [Fallback to TailwindCSS]') },
156
+ { value: 'chakra', label: pc.yellow('Chakra UI (Coming Soon) [Fallback to TailwindCSS]') },
157
+ ],
158
+ });
159
+ if (isCancel(uiChoice)) {
160
+ cancel('Generation cancelled.');
161
+ process.exit(0);
162
+ }
163
+ const stateChoice = await select({
164
+ message: 'Choose State management:',
165
+ options: [
166
+ { value: 'zustand', label: 'Zustand [Fully Implemented]' },
167
+ { value: 'redux', label: pc.yellow('Redux Toolkit (Coming Soon) [Fallback to Zustand]') },
168
+ ],
169
+ });
170
+ if (isCancel(stateChoice)) {
171
+ cancel('Generation cancelled.');
172
+ process.exit(0);
173
+ }
174
+ }
175
+ // 7. Validation Library
176
+ const validationChoice = await select({
177
+ message: 'Choose validation library:',
178
+ options: [
179
+ { value: 'zod', label: 'Zod' },
180
+ { value: 'joi', label: 'Joi' },
181
+ { value: 'yup', label: pc.yellow('Yup (Coming Soon) [Fallback to Zod]') },
182
+ ],
183
+ });
184
+ if (isCancel(validationChoice)) {
185
+ cancel('Generation cancelled.');
186
+ process.exit(0);
187
+ }
188
+ let validation = 'zod';
189
+ if (validationChoice === 'zod' || validationChoice === 'joi') {
190
+ validation = validationChoice;
191
+ }
192
+ else {
193
+ console.log(pc.yellow(`\n⚠ Validation library "${validationChoice}" is coming soon. Defaulting to Zod.`));
194
+ validation = 'zod';
195
+ }
196
+ // 8. Auth Module
197
+ const authChoice = await confirm({
198
+ message: 'Generate User & Auth module? (JWT, Login, Signup, Refresh Tokens)',
199
+ initialValue: true,
200
+ });
201
+ if (isCancel(authChoice)) {
202
+ cancel('Generation cancelled.');
203
+ process.exit(0);
204
+ }
205
+ const auth = Boolean(authChoice);
206
+ let rbac = false;
207
+ if (auth) {
208
+ const rbacChoice = await confirm({
209
+ message: 'Include Role-Based Access Control (RBAC) middleware?',
210
+ initialValue: true,
211
+ });
212
+ if (isCancel(rbacChoice)) {
213
+ cancel('Generation cancelled.');
214
+ process.exit(0);
215
+ }
216
+ rbac = Boolean(rbacChoice);
217
+ }
218
+ // 9. File Upload
219
+ const fileUpload = await confirm({
220
+ message: 'Include File Upload module? (Multer setup)',
221
+ initialValue: true,
222
+ });
223
+ if (isCancel(fileUpload)) {
224
+ cancel('Generation cancelled.');
225
+ process.exit(0);
226
+ }
227
+ // 10. Swagger Docs
228
+ const swagger = await confirm({
229
+ message: 'Include Swagger API Documentation UI?',
230
+ initialValue: true,
231
+ });
232
+ if (isCancel(swagger)) {
233
+ cancel('Generation cancelled.');
234
+ process.exit(0);
235
+ }
236
+ // 11. Docker
237
+ const docker = await confirm({
238
+ message: 'Include Docker & docker-compose configurations?',
239
+ initialValue: true,
240
+ });
241
+ if (isCancel(docker)) {
242
+ cancel('Generation cancelled.');
243
+ process.exit(0);
244
+ }
245
+ // 12. Rate Limiting
246
+ const rateLimiting = await confirm({
247
+ message: 'Include API Rate Limiting security middleware?',
248
+ initialValue: true,
249
+ });
250
+ if (isCancel(rateLimiting)) {
251
+ cancel('Generation cancelled.');
252
+ process.exit(0);
253
+ }
254
+ // 13. Caching (Redis)
255
+ const caching = await confirm({
256
+ message: 'Include Caching layer integration (Redis client)?',
257
+ initialValue: false,
258
+ });
259
+ if (isCancel(caching)) {
260
+ cancel('Generation cancelled.');
261
+ process.exit(0);
262
+ }
263
+ // 14. Testing
264
+ const testing = await confirm({
265
+ message: 'Include testing setup? (Jest)',
266
+ initialValue: true,
267
+ });
268
+ if (isCancel(testing)) {
269
+ cancel('Generation cancelled.');
270
+ process.exit(0);
271
+ }
272
+ const options = {
273
+ projectName: projectName,
274
+ architecture: architecture,
275
+ framework,
276
+ database,
277
+ orm,
278
+ frontend,
279
+ auth,
280
+ docker,
281
+ validation,
282
+ rateLimiting,
283
+ swagger,
284
+ caching,
285
+ rbac,
286
+ fileUpload,
287
+ testing,
288
+ };
289
+ const s = spinner();
290
+ let lastMessage = '';
291
+ try {
292
+ await generateProject(options, (msg, status) => {
293
+ if (status === 'start') {
294
+ lastMessage = msg;
295
+ s.start(msg);
296
+ }
297
+ else if (status === 'stop') {
298
+ s.stop(pc.green(`āœ” ${lastMessage || msg}`));
299
+ }
300
+ else if (status === 'error') {
301
+ s.stop(pc.red(`āŒ ${msg}`));
302
+ }
303
+ else {
304
+ s.message(msg);
305
+ }
306
+ });
307
+ outro(pc.bold(pc.green('šŸŽ‰ Project generated successfully!')));
308
+ // Output next steps
309
+ console.log(pc.cyan('\nNext Steps:'));
310
+ console.log(` cd ${projectName}`);
311
+ if (frontend) {
312
+ console.log(' npm install (at root to install workspace dependencies)');
313
+ console.log(' npm run dev (starts both backend and frontend concurrently)');
314
+ }
315
+ else {
316
+ console.log(' npm install');
317
+ console.log(' npm run dev');
318
+ }
319
+ if (orm === 'prisma') {
320
+ console.log(' npm run db:push (or npx prisma db push from backend)');
321
+ }
322
+ console.log('\nEnjoy building your application!\n');
323
+ }
324
+ catch (error) {
325
+ s.stop(pc.red('āŒ Project generation failed.'));
326
+ outro(pc.red(`Error: ${error.message}`));
327
+ process.exit(1);
328
+ }
329
+ }
330
+ main();