create-autostack 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.
Files changed (96) hide show
  1. package/README.md +237 -0
  2. package/bin/index.js +675 -0
  3. package/lib/PackageManagerClass.js +185 -0
  4. package/package.json +55 -0
  5. package/templates/assets/fastapi.svg +1 -0
  6. package/templates/assets/flask.svg +1 -0
  7. package/templates/assets/mongodb.svg +1 -0
  8. package/templates/assets/mysql.svg +1 -0
  9. package/templates/assets/nodejs.svg +1 -0
  10. package/templates/assets/postgresql.svg +1 -0
  11. package/templates/backend/FASTAPI/MONGODB/.env +1 -0
  12. package/templates/backend/FASTAPI/MONGODB/config.py +26 -0
  13. package/templates/backend/FASTAPI/MONGODB/main.py +32 -0
  14. package/templates/backend/FASTAPI/MONGODB/models.py +56 -0
  15. package/templates/backend/FASTAPI/MONGODB/requirements.txt +6 -0
  16. package/templates/backend/FASTAPI/MONGODB/routes.py +189 -0
  17. package/templates/backend/FASTAPI/MONGODB/schemas.py +31 -0
  18. package/templates/backend/FASTAPI/MYSQL/.env +5 -0
  19. package/templates/backend/FASTAPI/MYSQL/config.py +29 -0
  20. package/templates/backend/FASTAPI/MYSQL/main.py +32 -0
  21. package/templates/backend/FASTAPI/MYSQL/models.py +31 -0
  22. package/templates/backend/FASTAPI/MYSQL/requirements.txt +6 -0
  23. package/templates/backend/FASTAPI/MYSQL/routes.py +180 -0
  24. package/templates/backend/FASTAPI/MYSQL/schemas.py +36 -0
  25. package/templates/backend/FASTAPI/POSTGRESQL/.env +5 -0
  26. package/templates/backend/FASTAPI/POSTGRESQL/config.py +29 -0
  27. package/templates/backend/FASTAPI/POSTGRESQL/main.py +32 -0
  28. package/templates/backend/FASTAPI/POSTGRESQL/models.py +31 -0
  29. package/templates/backend/FASTAPI/POSTGRESQL/requirements.txt +6 -0
  30. package/templates/backend/FASTAPI/POSTGRESQL/routes.py +180 -0
  31. package/templates/backend/FASTAPI/POSTGRESQL/schemas.py +36 -0
  32. package/templates/backend/FLASK/MONGODB/.env +6 -0
  33. package/templates/backend/FLASK/MONGODB/config.py +27 -0
  34. package/templates/backend/FLASK/MONGODB/main.py +5 -0
  35. package/templates/backend/FLASK/MONGODB/models.py +16 -0
  36. package/templates/backend/FLASK/MONGODB/requirements.txt +6 -0
  37. package/templates/backend/FLASK/MONGODB/routes.py +137 -0
  38. package/templates/backend/FLASK/MYSQL/.env +7 -0
  39. package/templates/backend/FLASK/MYSQL/config.py +24 -0
  40. package/templates/backend/FLASK/MYSQL/main.py +8 -0
  41. package/templates/backend/FLASK/MYSQL/models.py +26 -0
  42. package/templates/backend/FLASK/MYSQL/requirements.txt +6 -0
  43. package/templates/backend/FLASK/MYSQL/routes.py +135 -0
  44. package/templates/backend/FLASK/POSTGRESQL/.env +5 -0
  45. package/templates/backend/FLASK/POSTGRESQL/config.py +24 -0
  46. package/templates/backend/FLASK/POSTGRESQL/main.py +7 -0
  47. package/templates/backend/FLASK/POSTGRESQL/models.py +26 -0
  48. package/templates/backend/FLASK/POSTGRESQL/requirements.txt +6 -0
  49. package/templates/backend/FLASK/POSTGRESQL/routes.py +134 -0
  50. package/templates/backend/NODEJS/MONGODB/config/dbConn.js +11 -0
  51. package/templates/backend/NODEJS/MONGODB/controllers/noteController.js +79 -0
  52. package/templates/backend/NODEJS/MONGODB/controllers/userController.js +79 -0
  53. package/templates/backend/NODEJS/MONGODB/index.js +39 -0
  54. package/templates/backend/NODEJS/MONGODB/models/noteModel.js +17 -0
  55. package/templates/backend/NODEJS/MONGODB/models/userModel.js +17 -0
  56. package/templates/backend/NODEJS/MYSQL/config/dbConn.js +20 -0
  57. package/templates/backend/NODEJS/MYSQL/controllers/noteController.js +97 -0
  58. package/templates/backend/NODEJS/MYSQL/controllers/userController.js +98 -0
  59. package/templates/backend/NODEJS/MYSQL/index.js +39 -0
  60. package/templates/backend/NODEJS/MYSQL/models.txt +3 -0
  61. package/templates/backend/NODEJS/POSTGRESQL/config/dbConn.js +28 -0
  62. package/templates/backend/NODEJS/POSTGRESQL/controllers/noteController.js +91 -0
  63. package/templates/backend/NODEJS/POSTGRESQL/controllers/userController.js +92 -0
  64. package/templates/backend/NODEJS/POSTGRESQL/index.js +39 -0
  65. package/templates/backend/NODEJS/POSTGRESQL/models.txt +3 -0
  66. package/templates/backend/NODEJS/common/middleware/upstash.js +12 -0
  67. package/templates/backend/NODEJS/common/routes/noteRoutes.js +22 -0
  68. package/templates/backend/NODEJS/common/routes/userRoutes.js +22 -0
  69. package/templates/frontend/PREACT/src/app.jsx +103 -0
  70. package/templates/frontend/PREACT/vite.config.js +8 -0
  71. package/templates/frontend/PREACT-TS/src/app.tsx +112 -0
  72. package/templates/frontend/PREACT-TS/vite.config.ts +8 -0
  73. package/templates/frontend/REACT/src/App.css +27 -0
  74. package/templates/frontend/REACT/src/App.jsx +105 -0
  75. package/templates/frontend/REACT/vite.config.js +8 -0
  76. package/templates/frontend/REACT-SWC/src/App.css +27 -0
  77. package/templates/frontend/REACT-SWC/src/App.jsx +105 -0
  78. package/templates/frontend/REACT-SWC/vite.config.js +8 -0
  79. package/templates/frontend/REACT-SWC-TS/src/App.css +27 -0
  80. package/templates/frontend/REACT-SWC-TS/src/App.tsx +114 -0
  81. package/templates/frontend/REACT-SWC-TS/vite.config.ts +8 -0
  82. package/templates/frontend/REACT-TS/src/App.css +27 -0
  83. package/templates/frontend/REACT-TS/src/App.tsx +114 -0
  84. package/templates/frontend/REACT-TS/vite.config.ts +8 -0
  85. package/templates/frontend/SOLID/src/App.jsx +104 -0
  86. package/templates/frontend/SOLID/vite.config.js +7 -0
  87. package/templates/frontend/SOLID-TS/src/App.tsx +116 -0
  88. package/templates/frontend/SOLID-TS/vite.config.ts +7 -0
  89. package/templates/frontend/SVELTE/src/App.svelte +104 -0
  90. package/templates/frontend/SVELTE/vite.config.js +8 -0
  91. package/templates/frontend/SVELTE-TS/src/App.svelte +116 -0
  92. package/templates/frontend/SVELTE-TS/vite.config.ts +8 -0
  93. package/templates/frontend/VUE/src/App.vue +96 -0
  94. package/templates/frontend/VUE/vite.config.js +8 -0
  95. package/templates/frontend/VUE-TS/src/App.vue +112 -0
  96. package/templates/frontend/VUE-TS/vite.config.ts +8 -0
package/bin/index.js ADDED
@@ -0,0 +1,675 @@
1
+ #!/usr/bin/env node
2
+
3
+ import * as fs from 'node:fs/promises'
4
+ import { createSpinner } from 'nanospinner';
5
+ import { exec, execSync } from 'node:child_process';
6
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
7
+ import { existsSync, readdirSync } from 'node:fs';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { dirname, join } from 'node:path';
10
+ import colors from 'picocolors';
11
+ import * as prompts from '@clack/prompts';
12
+ import mri from 'mri';
13
+ import figlet from 'figlet'
14
+ import { promisify } from 'node:util';
15
+ import { platform } from 'os';
16
+ import PackageManagerCommands from '../lib/PackageManagerClass.js';
17
+
18
+ const {
19
+ yellow,
20
+ blue,
21
+ red,
22
+ green,
23
+ white,
24
+ magenta,
25
+ cyan,
26
+ whiteBright,
27
+ greenBright,
28
+ blueBright,
29
+ dim,
30
+ italic
31
+ } = colors
32
+
33
+ const BKEND_FRAMEWORKS = {
34
+ "NODEJS": {
35
+ "COLOR": green
36
+ },
37
+ "FLASK": {
38
+ "COLOR": red
39
+ },
40
+ "FASTAPI": {
41
+ "COLOR": blue
42
+ }
43
+ }
44
+
45
+ const FRONTEND_FRAMEWORKS = {
46
+ // "VANILLA": {
47
+ // "COLOR": yellow,
48
+ // "CSS_FILE": "style.css", // No default CSS file created
49
+ // },
50
+ "VUE": {
51
+ "COLOR": green,
52
+ "CSS_FILE": "style.css", // Typically in src/assets/ or src/
53
+ },
54
+ "REACT": {
55
+ "COLOR": cyan,
56
+ "CSS_FILE": "index.css", // In src/ directory
57
+ },
58
+ "REACT-SWC": {
59
+ "COLOR": cyan,
60
+ "CSS_FILE": "index.css", // Same as React
61
+ },
62
+ "PREACT": {
63
+ "COLOR": magenta,
64
+ "CSS_FILE": "style.css", // In src/ directory
65
+ },
66
+ // "LIT": {
67
+ // "COLOR": redBright,
68
+ // "CSS_FILE": null, // No external CSS - uses CSS-in-JS
69
+ // },
70
+ "SVELTE": {
71
+ "COLOR": red,
72
+ "CSS_FILE": "app.css", // In src/ directory
73
+ },
74
+ "SOLID": {
75
+ "COLOR": blue,
76
+ "CSS_FILE": "App.css", // In src/ directory
77
+ },
78
+ // "QWIK": {
79
+ // "COLOR": blueBright,
80
+ // "CSS_FILE": "global.css", // In src/ directory
81
+ //}
82
+ }
83
+ const DATABASES = {
84
+ "MONGODB" : {
85
+ "NODEJS_LIB": "mongoose",
86
+ "FLASK_LIB": "",
87
+ "FASTAPI_LIB": "",
88
+ "COLOR": green
89
+ },
90
+ "MYSQL": {
91
+ "NODEJS_LIB": 'mysql2',
92
+ "FLASK_LIB": "",
93
+ "FASTAPI_LIB": "",
94
+ "COLOR": yellow
95
+ },
96
+ "POSTGRESQL" : {
97
+ "NODEJS_LIB": 'pg',
98
+ "FLASK_LIB": "",
99
+ "FASTAPI_LIB": "",
100
+ "COLOR": blue
101
+ }
102
+ }
103
+
104
+ const PACKAGE_MANAGERS = {
105
+ "npm": {
106
+ "COLOR": red
107
+ },
108
+ "yarn": {
109
+ "COLOR": blue
110
+ },
111
+ "pnpm": {
112
+ "COLOR": yellow
113
+ },
114
+ "bun": {
115
+ "COLOR": white
116
+ },
117
+ "deno": {
118
+ "COLOR": green
119
+ },
120
+ "pip" : {
121
+ "COLOR": magenta
122
+ }
123
+ }
124
+
125
+ const __filename = fileURLToPath(import.meta.url);
126
+ const __dirname = dirname(__filename);
127
+ const TEMPLATES_DIR = join(__dirname, '../templates');
128
+ const CWD = process.cwd();
129
+
130
+ let PROJECT_NAME;
131
+ let PROJECT_PATH;
132
+ let FRONTEND;
133
+ let DATABASE;
134
+ let BACKEND;
135
+ let PKG_MGR_BK;
136
+ let PKG_MGR_FD;
137
+ let ENV_CHOICE;
138
+ let isTerminating;
139
+
140
+ const terminate = async () => { // To be checked
141
+ if (isTerminating) return;
142
+ isTerminating = true;
143
+
144
+ if (PROJECT_PATH === undefined) return;
145
+
146
+ console.log(dim('\nRemoving partial installation...'));
147
+ if (PROJECT_NAME && existsSync(join(CWD, PROJECT_PATH))) {
148
+ if (PROJECT_PATH === '') { // Empty strings
149
+ await fs.rm(join(CWD, 'frontend'), { recursive: true, force: true });
150
+ await fs.rm(join(CWD, 'backend'), { recursive: true, force: true });
151
+ } else {
152
+ await fs.rm(join(CWD, PROJECT_NAME), { recursive: true, force: true });
153
+ }
154
+ console.log(green('Cleaned up partial installation'));
155
+ }
156
+
157
+ console.log(whiteBright('\nGoodbye!\n'));
158
+ process.exit(0);
159
+ }
160
+
161
+ process.on('SIGINT', terminate); // CTRL + C
162
+
163
+ process.on('SIGTERM', terminate); // Other termination signals
164
+
165
+ process.on('uncaughtException', (error) => {
166
+ console.error('\nUnexpected error:', error.message);
167
+ terminate();
168
+ });
169
+
170
+ const isValidProjectName = (name) => {
171
+ // Allows: letters, numbers, hyphens, underscores, dots
172
+ // No spaces, no special chars, no starting with dot (except '.')
173
+ const regex = /^[a-zA-Z0-9]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$|^\.$/;
174
+ return regex.test(name);
175
+ };
176
+
177
+ const checkEmptyDir = (path) => {
178
+ try {
179
+ const files = readdirSync(path);
180
+ const visibleFiles = files.filter(f => !f.startsWith('.'));
181
+ return visibleFiles.length === 0;
182
+ } catch {
183
+ return true; // Directory doesn't exist = "empty"
184
+ }
185
+ }
186
+
187
+ const createFile = async (destPath, content, format = 'utf8') => {
188
+ try {
189
+ await writeFile(destPath, content, format)
190
+ } catch (error) {
191
+ console.error('Error creating file: ', error);
192
+ terminate();
193
+ }
194
+ }
195
+
196
+ const readAFile = async (name) => {
197
+ try {
198
+ const data = await readFile(name);
199
+ return data;
200
+ } catch(error) {
201
+ console.log("Unable to read file. Installation Stopped");
202
+ terminate()
203
+ }
204
+ }
205
+
206
+ const copyFile = async (srcPath, destPath, modify=false, modificationFn=null) => {
207
+ const data = await readAFile(srcPath);
208
+ if (modify) {
209
+ const modifiedData = modificationFn(data);
210
+ await writeFile(destPath, modifiedData);
211
+ return;
212
+ }
213
+ await writeFile(destPath, data);
214
+ }
215
+
216
+ const prependToFile = async (destPath, content) => {
217
+ try {
218
+ const existing = await readFile(destPath, 'utf8');
219
+ await writeFile(destPath, content + '\n' + existing, 'utf8');
220
+ } catch (error) {
221
+ console.error('Error prepending to file:', error);
222
+ terminate();
223
+ }
224
+ };
225
+
226
+ const copyDirectory = async (src, dest) => {
227
+ await fs.cp(src, dest, { recursive: true, force: true});
228
+ };
229
+
230
+ const runWithSpinner = async (description, asyncFn) => {
231
+ const spinner = createSpinner(description).start();
232
+ try {
233
+ await asyncFn();
234
+ spinner.success({ text: `${description}` });
235
+ } catch (error) {
236
+ spinner.error({ text: `${description}` });
237
+ throw error;
238
+ }
239
+ };
240
+
241
+ const dbConfigurations = async () => {
242
+
243
+ if (ENV_CHOICE === 'Yes') {
244
+ switch (DATABASE) {
245
+ case 'MONGODB':
246
+ const connStr = await prompts.password({
247
+ message: 'Please enter your MongoDB connection string: '
248
+ })
249
+ createFile(`.${PROJECT_PATH}/backend/.env`, `PORT=5000\nMONGO_URI=${connStr}`);
250
+ break;
251
+
252
+ case 'MYSQL':
253
+ const mysqlUser = await prompts.password({
254
+ message: 'Please enter your MySQL username: '
255
+ })
256
+ const mysqlPass = await prompts.password({
257
+ message: 'Please enter your MySQL password: '
258
+ })
259
+ const mysqlHost = await prompts.password({
260
+ message: 'Please enter your MySQL host: '
261
+ })
262
+ createFile(`.${PROJECT_PATH}/backend/.env`, `PORT=5000\nUSER=${mysqlUser}\nPASSWORD=${mysqlPass}\nHOST=${mysqlHost}`)
263
+ break;
264
+
265
+ case 'POSTGRESQL':
266
+ const postgresUser = await prompts.password({
267
+ message: 'Please enter your PostgreSQL username: '
268
+ })
269
+ const postgresPass = await prompts.password({
270
+ message: 'Please enter your PostgreSQL password: '
271
+ })
272
+ const postgresHost = await prompts.password({
273
+ message: 'Please enter your PostgreSQL host: '
274
+ })
275
+ createFile(`.${PROJECT_PATH}/backend/.env`, `PORT=5000\nUSER=${postgresUser}\nPASSWORD=${postgresPass}\nHOST=${postgresHost}`);
276
+ break;
277
+ }
278
+ return DATABASES[DATABASE][`${BACKEND}_LIB`];
279
+
280
+ } else {
281
+ console.log("Okay, example environment variables have not been set. The file will be created and you can edit accordingly..");
282
+ switch (DATABASE) {
283
+ case 'MONGODB':
284
+ createFile(`.${PROJECT_PATH}/backend/.env`, `PORT=5000`);
285
+
286
+ case 'MYSQL':
287
+ createFile(`.${PROJECT_PATH}/backend/.env`, `PORT=5000`);
288
+
289
+ case 'POSTGRESQL':
290
+ createFile(`.${PROJECT_PATH}/backend/.env`, `PORT=5000`);
291
+ }
292
+ return DATABASES[DATABASE][`${BACKEND}_LIB`];
293
+ }
294
+ }
295
+
296
+ const cancel = () => {
297
+ prompts.cancel(yellow("Installation Interrupted"))
298
+ terminate();
299
+ }
300
+
301
+ // Backend Creation
302
+ const createBackend = async () => {
303
+ try {
304
+ switch (BACKEND) {
305
+ case 'NODEJS':
306
+ // 4. Create Main Folder
307
+ await mkdir(`.${PROJECT_PATH}/backend/src`, { recursive: true });
308
+
309
+ // 5. Initialize Backend
310
+ console.log(whiteBright("\nINITIALIZING BACKEND...\n"))
311
+ execSync(PKG_MGR_BK.init(), { cwd: `.${PROJECT_PATH}/backend`});
312
+
313
+ // 6. Ask user for environment variables selection
314
+ ENV_CHOICE = await prompts.select({
315
+ message: `Do you want to configure your environment variables right now? (recommended):
316
+ Or do it later?`,
317
+ options: [
318
+ {label: "Yes (Do it now)", value: "Yes"},
319
+ {label: "No (I'll do it later)", value: "No"}
320
+ ],
321
+ })
322
+ if (prompts.isCancel(ENV_CHOICE)) return cancel()
323
+
324
+ const dbPkg = await dbConfigurations() ?? "";
325
+
326
+ // 7. Copy Template Directory and Other Files
327
+ const backendPath_js = `.${PROJECT_PATH}/backend/src`;
328
+ console.log('\n')
329
+ await runWithSpinner(
330
+ whiteBright("COPYING TEMPLATE DIRECTORY"),
331
+ async () => {
332
+ await copyDirectory(join(TEMPLATES_DIR, 'backend', BACKEND, DATABASE), backendPath_js);
333
+ await copyDirectory(join(TEMPLATES_DIR, 'backend', BACKEND, 'common'), backendPath_js);
334
+ }
335
+ );
336
+
337
+ copyFile( // For package.json
338
+ `.${PROJECT_PATH}/backend/package.json`,
339
+ `.${PROJECT_PATH}/backend/package.json`,
340
+ true,
341
+ (data) => { // The modificationFn
342
+ const jsonObject = JSON.parse(data);
343
+ jsonObject['main'] = 'src/index.js'
344
+ jsonObject['type'] = 'module';
345
+ jsonObject['scripts'] = {
346
+ 'dev': 'nodemon src/index.js',
347
+ 'start': 'node src/index.js'
348
+ }
349
+ return JSON.stringify(jsonObject, null, 4); // 4 is for indentation whereas null is for the replacer fn which we don't require
350
+ });
351
+
352
+ await runWithSpinner('Installing backend packages...', async () => {
353
+ const execPromise = promisify(exec);
354
+ const packages = ['express', dbPkg, 'dotenv', 'nodemon', 'redis', 'cors'].filter(Boolean);
355
+ const command = PKG_MGR_BK.install(packages);
356
+ await execPromise(command, { cwd: `.${PROJECT_PATH}/backend` });
357
+ });
358
+ console.log(greenBright("\nBACKEND CREATED!"));
359
+ break;
360
+
361
+ case 'FLASK':
362
+ // 4. Create Main Folder
363
+ await mkdir(`.${PROJECT_PATH}/backend`, { recursive: true });
364
+
365
+ // 5. Create virtual environment
366
+ await runWithSpinner(whiteBright("CREATING VIRTUAL ENVIRONMENT"), async () => {
367
+ const createVenvCmd = PKG_MGR_BK.createVirtualEnv('.venv');
368
+ const execPromise = promisify(exec);
369
+ await execPromise(createVenvCmd, { cwd: `.${PROJECT_PATH}/backend`, shell: true });
370
+ });
371
+
372
+ // 6. Ask if user wants to configure .env file. If yes, configure. Else, create with default values.
373
+ ENV_CHOICE = await prompts.select({
374
+ message: `Do you want to configure your environment variables right now? (recommended):
375
+ Or do it later?`,
376
+ options: [
377
+ {label: "Yes (Do it now)", value: "Yes"},
378
+ {label: "No (I'll do it later)", value: "No"}
379
+ ],
380
+ })
381
+ if (prompts.isCancel(ENV_CHOICE)) return cancel()
382
+
383
+ const flask_pkg = await dbConfigurations();
384
+ const backendPath_fl = `.${PROJECT_PATH}/backend`;
385
+
386
+ // 7. Copy template directory with it's contents based on selected database
387
+ await runWithSpinner(
388
+ whiteBright("COPYING TEMPLATE DIRECTORY"),
389
+ async () => {
390
+ await copyDirectory(join(TEMPLATES_DIR, 'backend', BACKEND, DATABASE), backendPath_fl);
391
+ }
392
+ );
393
+
394
+ // 8. Copy requirements.txt file
395
+ await runWithSpinner(
396
+ whiteBright("COPYING REQUIREMENTS.TXT"),
397
+ async () => {
398
+ await copyFile(join(TEMPLATES_DIR, 'backend', BACKEND, DATABASE, 'requirements.txt'), `.${PROJECT_PATH}/backend/requirements.txt`);
399
+ }
400
+ );
401
+
402
+ // 9. Install relevant packages using pip install -r requirements.txt
403
+ await runWithSpinner('Installing backend packages...', async () => {
404
+ const execPromise = promisify(exec);
405
+ const command = PKG_MGR_BK.install();
406
+ await execPromise(command, { cwd: `.${PROJECT_PATH}/backend`, shell: platform() === 'win32' ? process.env.ComSpec : '/bin/sh' });
407
+ });
408
+ console.log(greenBright("\nBACKEND CREATED!"));
409
+
410
+ break;
411
+ case 'FASTAPI':
412
+ // 4. Create Main Folder
413
+ await mkdir(`.${PROJECT_PATH}/backend`, { recursive: true });
414
+
415
+ // 5. Create virtual environment
416
+ await runWithSpinner(whiteBright("CREATING VIRTUAL ENVIRONMENT"), async () => {
417
+ const createVenvCmd = PKG_MGR_BK.createVirtualEnv('.venv');
418
+ const execPromise = promisify(exec);
419
+ await execPromise(createVenvCmd, { cwd: `.${PROJECT_PATH}/backend`, shell: platform() === 'win32' ? process.env.ComSpec : '/bin/sh' });
420
+ });
421
+
422
+ // 6. Ask if user wants to configure .env file. If yes, configure. Else, create with default values.
423
+ ENV_CHOICE = await prompts.select({
424
+ message: `Do you want to configure your environment variables right now? (recommended):
425
+ Or do it later?`,
426
+ options: [
427
+ {label: "Yes (Do it now)", value: "Yes"},
428
+ {label: "No (I'll do it later)", value: "No"}
429
+ ],
430
+ })
431
+ if (prompts.isCancel(ENV_CHOICE)) return cancel()
432
+
433
+ const fastapi_pkg = await dbConfigurations();
434
+ const backendPath_fa = `.${PROJECT_PATH}/backend`;
435
+
436
+ // 7. Copy template directory with it's contents based on selected database
437
+ await runWithSpinner(
438
+ whiteBright("COPYING TEMPLATE DIRECTORY"),
439
+ async () => {
440
+ await copyDirectory(join(TEMPLATES_DIR, 'backend', BACKEND, DATABASE), backendPath_fa);
441
+ }
442
+ );
443
+
444
+ // 8. Copy requirements.txt file
445
+ await runWithSpinner(
446
+ whiteBright("COPYING REQUIREMENTS.TXT"),
447
+ async () => {
448
+ await copyFile(join(TEMPLATES_DIR, 'backend', BACKEND, DATABASE, 'requirements.txt'), `.${PROJECT_PATH}/backend/requirements.txt`);
449
+ }
450
+ );
451
+
452
+ // 9. Install relevant packages using pip install -r requirements.txt
453
+ await runWithSpinner('Installing backend packages...', async () => {
454
+ const execPromise = promisify(exec);
455
+ const command = PKG_MGR_BK.install();
456
+ await execPromise(command, { cwd: `.${PROJECT_PATH}/backend`, shell: platform() === 'win32' ? process.env.ComSpec : '/bin/sh' });
457
+ });
458
+ console.log(greenBright("\nBACKEND CREATED!"));
459
+
460
+ break;
461
+ }
462
+ } catch(err) {
463
+ console.error("Unable to create backend folder", err);
464
+ terminate();
465
+ }
466
+ }
467
+
468
+ // Frontend Creation
469
+ const createFrontend = async () => {
470
+ if (isTerminating) return;
471
+ console.log(whiteBright("\nCREATING FRONTEND\n"))
472
+
473
+ const baseTech = await prompts.select({
474
+ message: 'Please pick a frontend framework:',
475
+ options: Object.keys(FRONTEND_FRAMEWORKS).map((name) => {
476
+ const color = FRONTEND_FRAMEWORKS[`${name}`]['COLOR'];
477
+ return {
478
+ label: color(name), value: name,
479
+ }
480
+ }),
481
+ })
482
+ if (prompts.isCancel(baseTech)) return cancel();
483
+
484
+ const tsOption = await prompts.select({
485
+ message: `Use TypeScript?`,
486
+ options: [
487
+ {label: 'Yes', value: 'Yes'},
488
+ {label: 'No', value: 'No'}
489
+ ]
490
+ })
491
+ if (prompts.success) return;
492
+ FRONTEND = tsOption === 'Yes' ? (baseTech.toLowerCase() + '-ts') : baseTech.toLowerCase();
493
+
494
+ const cmd = PKG_MGR_FD.create(`frontend ${PKG_MGR_FD.manager === 'npm' ? '--' : ''} --template ${FRONTEND}`);
495
+ console.log("\n");
496
+ await runWithSpinner(whiteBright('CREATING FRONTEND FOLDER WITH VITE'),
497
+ async () => {
498
+ const execPromise = promisify(exec);
499
+ await execPromise(
500
+ cmd,
501
+ {
502
+ cwd: `.${PROJECT_PATH}`,
503
+ }
504
+ );
505
+ }
506
+ );
507
+
508
+ // Installing Tailwind
509
+ await runWithSpinner(
510
+ whiteBright("INSTALLING TAILWINDCSS"),
511
+ async () => {
512
+ const execPromise = promisify(exec);
513
+ await execPromise(
514
+ PKG_MGR_FD.install(['tailwindcss', '@tailwindcss/vite']),
515
+ {
516
+ cwd: `.${PROJECT_PATH}/frontend`,
517
+ }
518
+ );
519
+ }
520
+ );
521
+
522
+
523
+ const fileName = FRONTEND_FRAMEWORKS[baseTech]['CSS_FILE'];
524
+ if (!fileName) {
525
+ // handle Lit projects - TO-DO LATER
526
+ }
527
+ createFile(`.${PROJECT_PATH}/frontend/src/${fileName}`, '@import "tailwindcss";');
528
+
529
+ await copyFile( // For backend svg
530
+ join(TEMPLATES_DIR, 'assets', `${BACKEND.toLowerCase()}.svg`),
531
+ `.${PROJECT_PATH}/frontend/src/assets/backend.svg`
532
+ );
533
+
534
+ await copyFile( // For database svg
535
+ join(TEMPLATES_DIR, 'assets', `${DATABASE.toLowerCase()}.svg`),
536
+ `.${PROJECT_PATH}/frontend/src/assets/database.svg`
537
+ );
538
+
539
+ await runWithSpinner(
540
+ whiteBright("CREATING API CONNECTIONS..."),
541
+ async () => {
542
+ await copyDirectory(join(TEMPLATES_DIR, 'frontend', FRONTEND.toUpperCase()), `.${PROJECT_PATH}/frontend`);
543
+ }
544
+ );
545
+
546
+ await runWithSpinner(
547
+ whiteBright("FINAL INSTALLATIONS..."),
548
+ async () => {
549
+ const execPromise = promisify(exec);
550
+ await execPromise(PKG_MGR_FD.install(), { cwd: `.${PROJECT_PATH}/frontend`});
551
+ }
552
+ );
553
+
554
+ console.log(greenBright("\nFRONTEND CREATED"));
555
+
556
+ console.log(dim(`For starting your project:
557
+ 1) cd frontend && npm run dev
558
+ 2) ${BACKEND === 'NODEJS' ? 'cd backend && npm run dev' : 'After activating virtual environment,cd backend && run python main.py'}
559
+ `))
560
+
561
+ console.log(whiteBright("Happy Coding!"))
562
+
563
+ }
564
+
565
+ const start = async () => { // Main function
566
+ console.log(
567
+ (greenBright(figlet.textSync('AUTOSTACK', { horizontalLayout: 'full' }))
568
+ ));
569
+ console.log(whiteBright(italic('--- An NPM package designed to automate the setup of your full-stack projects ---')));
570
+ console.log(`
571
+ ${blueBright('---------------------------- Supported Technologies ---------------------------\n')}
572
+ ╔═════════════════════════╦═════════════════════════╦═════════════════════════╗
573
+ ║ FRONTEND ║ BACKEND ║ DATABASE ║
574
+ ╠═════════════════════════╬═════════════════════════╬═════════════════════════╣
575
+ ║ Vanilla (JS/TS) ║ Node.js ║ MongoDB ║
576
+ ║ Vue (JS/TS) ║ Flask ║ PostgreSQL ║
577
+ ║ React (JS/TS) ║ FastAPI ║ MySQL ║
578
+ ║ React-SWC (JS/TS) ║ ║ ║
579
+ ║ Preact (JS/TS) ║ ║ ║
580
+ ║ Svelte (JS/TS) ║ ║ ║
581
+ ║ Solid (JS/TS) ║ ║ ║
582
+ ╚═════════════════════════╩═════════════════════════╩═════════════════════════╝
583
+ `);
584
+
585
+ // 1. Ask Project Name
586
+ PROJECT_NAME = await prompts.text({
587
+ message: 'Please enter project name:',
588
+ placeholder: '.',
589
+ defaultValue: '.',
590
+ validate: (value) => {
591
+ if (!value) return 'Project name is required';
592
+ if (value === '.') {
593
+ const currentDirPath = CWD;
594
+ if (!checkEmptyDir(currentDirPath)) {
595
+ return 'Current directory is not empty. Please use an empty directory.'
596
+ }
597
+ return;
598
+ }
599
+ if (!isValidProjectName(value)) {
600
+ return 'Invalid name. Use letters, numbers, hyphens, underscores only';
601
+ }
602
+ if (existsSync(join(CWD, value))) { // Relative to user dir
603
+ return "A directory with this name already exists. Please choose a different name."
604
+ }
605
+ }
606
+ });
607
+ if (prompts.isCancel(PROJECT_NAME)) return cancel()
608
+
609
+ if (PROJECT_NAME !== '.') {
610
+ await mkdir(`./${PROJECT_NAME}`);
611
+ PROJECT_PATH = '/' + PROJECT_NAME;
612
+ } else { // Means project_name is '.' i.e. need to create in cwd
613
+ PROJECT_PATH = '';
614
+ }
615
+
616
+ // 2. Ask For Backend Technology
617
+ BACKEND = await prompts.select({
618
+ message: 'Please pick a backend framework:',
619
+ options: Object.keys(BKEND_FRAMEWORKS).map((name) => {
620
+ const color = BKEND_FRAMEWORKS[`${name}`]['COLOR'];
621
+ return {
622
+ label: color(name), value: name,
623
+ }
624
+ }),
625
+ })
626
+ if (prompts.isCancel(BACKEND)) return cancel();
627
+
628
+ // 3. Ask For Database
629
+ DATABASE = await prompts.select({
630
+ message: 'Please select a database:',
631
+ options: Object.keys(DATABASES).map((db) => {
632
+ const color = DATABASES[`${db}`]['COLOR'];
633
+ return {
634
+ label: color(db), value: db,
635
+ }
636
+ })
637
+ })
638
+ if (prompts.isCancel(DATABASE)) return cancel();
639
+
640
+ // 4. Ask For Package Manager
641
+ const pkg_mgr_bk = await prompts.select({
642
+ message: 'Please select a package manager for handling your backend:',
643
+ options: Object.keys(PACKAGE_MANAGERS).map((pkg) => {
644
+ const color = PACKAGE_MANAGERS[`${pkg}`]['COLOR'];
645
+ return {
646
+ label: color(pkg), value: pkg,
647
+ }}),
648
+ validate: (value) => {
649
+ if (value === "pip" && BACKEND === "NODEJS") {
650
+ return 'pip cannot be used for installation of NodeJS. Please select a different package manager.';
651
+ } else if (value !== "pip" && ['FLASK', 'FASTAPI'].includes(BACKEND)) {
652
+ return `${value} cannot be used for installation of ${BACKEND}. Please select a different package manager.`;
653
+ }
654
+ }
655
+ })
656
+ if (prompts.isCancel(pkg_mgr_bk)) return cancel();
657
+
658
+ const pkg_mgr_fd = await prompts.select({
659
+ message: 'Please select a package manager for handling your frontend:',
660
+ options: Object.keys(PACKAGE_MANAGERS).filter((pkg) => pkg !== 'pip').map((pkg) => {
661
+ const color = PACKAGE_MANAGERS[`${pkg}`]['COLOR'];
662
+ return {
663
+ label: color(pkg), value: pkg,
664
+ }})
665
+ })
666
+ if (prompts.isCancel(pkg_mgr_fd)) return cancel();
667
+
668
+ PKG_MGR_BK = new PackageManagerCommands(pkg_mgr_bk);
669
+ PKG_MGR_FD = new PackageManagerCommands(pkg_mgr_fd);
670
+
671
+ await createBackend();
672
+ await createFrontend();
673
+ }
674
+
675
+ start();