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.
- package/README.md +237 -0
- package/bin/index.js +675 -0
- package/lib/PackageManagerClass.js +185 -0
- package/package.json +55 -0
- package/templates/assets/fastapi.svg +1 -0
- package/templates/assets/flask.svg +1 -0
- package/templates/assets/mongodb.svg +1 -0
- package/templates/assets/mysql.svg +1 -0
- package/templates/assets/nodejs.svg +1 -0
- package/templates/assets/postgresql.svg +1 -0
- package/templates/backend/FASTAPI/MONGODB/.env +1 -0
- package/templates/backend/FASTAPI/MONGODB/config.py +26 -0
- package/templates/backend/FASTAPI/MONGODB/main.py +32 -0
- package/templates/backend/FASTAPI/MONGODB/models.py +56 -0
- package/templates/backend/FASTAPI/MONGODB/requirements.txt +6 -0
- package/templates/backend/FASTAPI/MONGODB/routes.py +189 -0
- package/templates/backend/FASTAPI/MONGODB/schemas.py +31 -0
- package/templates/backend/FASTAPI/MYSQL/.env +5 -0
- package/templates/backend/FASTAPI/MYSQL/config.py +29 -0
- package/templates/backend/FASTAPI/MYSQL/main.py +32 -0
- package/templates/backend/FASTAPI/MYSQL/models.py +31 -0
- package/templates/backend/FASTAPI/MYSQL/requirements.txt +6 -0
- package/templates/backend/FASTAPI/MYSQL/routes.py +180 -0
- package/templates/backend/FASTAPI/MYSQL/schemas.py +36 -0
- package/templates/backend/FASTAPI/POSTGRESQL/.env +5 -0
- package/templates/backend/FASTAPI/POSTGRESQL/config.py +29 -0
- package/templates/backend/FASTAPI/POSTGRESQL/main.py +32 -0
- package/templates/backend/FASTAPI/POSTGRESQL/models.py +31 -0
- package/templates/backend/FASTAPI/POSTGRESQL/requirements.txt +6 -0
- package/templates/backend/FASTAPI/POSTGRESQL/routes.py +180 -0
- package/templates/backend/FASTAPI/POSTGRESQL/schemas.py +36 -0
- package/templates/backend/FLASK/MONGODB/.env +6 -0
- package/templates/backend/FLASK/MONGODB/config.py +27 -0
- package/templates/backend/FLASK/MONGODB/main.py +5 -0
- package/templates/backend/FLASK/MONGODB/models.py +16 -0
- package/templates/backend/FLASK/MONGODB/requirements.txt +6 -0
- package/templates/backend/FLASK/MONGODB/routes.py +137 -0
- package/templates/backend/FLASK/MYSQL/.env +7 -0
- package/templates/backend/FLASK/MYSQL/config.py +24 -0
- package/templates/backend/FLASK/MYSQL/main.py +8 -0
- package/templates/backend/FLASK/MYSQL/models.py +26 -0
- package/templates/backend/FLASK/MYSQL/requirements.txt +6 -0
- package/templates/backend/FLASK/MYSQL/routes.py +135 -0
- package/templates/backend/FLASK/POSTGRESQL/.env +5 -0
- package/templates/backend/FLASK/POSTGRESQL/config.py +24 -0
- package/templates/backend/FLASK/POSTGRESQL/main.py +7 -0
- package/templates/backend/FLASK/POSTGRESQL/models.py +26 -0
- package/templates/backend/FLASK/POSTGRESQL/requirements.txt +6 -0
- package/templates/backend/FLASK/POSTGRESQL/routes.py +134 -0
- package/templates/backend/NODEJS/MONGODB/config/dbConn.js +11 -0
- package/templates/backend/NODEJS/MONGODB/controllers/noteController.js +79 -0
- package/templates/backend/NODEJS/MONGODB/controllers/userController.js +79 -0
- package/templates/backend/NODEJS/MONGODB/index.js +39 -0
- package/templates/backend/NODEJS/MONGODB/models/noteModel.js +17 -0
- package/templates/backend/NODEJS/MONGODB/models/userModel.js +17 -0
- package/templates/backend/NODEJS/MYSQL/config/dbConn.js +20 -0
- package/templates/backend/NODEJS/MYSQL/controllers/noteController.js +97 -0
- package/templates/backend/NODEJS/MYSQL/controllers/userController.js +98 -0
- package/templates/backend/NODEJS/MYSQL/index.js +39 -0
- package/templates/backend/NODEJS/MYSQL/models.txt +3 -0
- package/templates/backend/NODEJS/POSTGRESQL/config/dbConn.js +28 -0
- package/templates/backend/NODEJS/POSTGRESQL/controllers/noteController.js +91 -0
- package/templates/backend/NODEJS/POSTGRESQL/controllers/userController.js +92 -0
- package/templates/backend/NODEJS/POSTGRESQL/index.js +39 -0
- package/templates/backend/NODEJS/POSTGRESQL/models.txt +3 -0
- package/templates/backend/NODEJS/common/middleware/upstash.js +12 -0
- package/templates/backend/NODEJS/common/routes/noteRoutes.js +22 -0
- package/templates/backend/NODEJS/common/routes/userRoutes.js +22 -0
- package/templates/frontend/PREACT/src/app.jsx +103 -0
- package/templates/frontend/PREACT/vite.config.js +8 -0
- package/templates/frontend/PREACT-TS/src/app.tsx +112 -0
- package/templates/frontend/PREACT-TS/vite.config.ts +8 -0
- package/templates/frontend/REACT/src/App.css +27 -0
- package/templates/frontend/REACT/src/App.jsx +105 -0
- package/templates/frontend/REACT/vite.config.js +8 -0
- package/templates/frontend/REACT-SWC/src/App.css +27 -0
- package/templates/frontend/REACT-SWC/src/App.jsx +105 -0
- package/templates/frontend/REACT-SWC/vite.config.js +8 -0
- package/templates/frontend/REACT-SWC-TS/src/App.css +27 -0
- package/templates/frontend/REACT-SWC-TS/src/App.tsx +114 -0
- package/templates/frontend/REACT-SWC-TS/vite.config.ts +8 -0
- package/templates/frontend/REACT-TS/src/App.css +27 -0
- package/templates/frontend/REACT-TS/src/App.tsx +114 -0
- package/templates/frontend/REACT-TS/vite.config.ts +8 -0
- package/templates/frontend/SOLID/src/App.jsx +104 -0
- package/templates/frontend/SOLID/vite.config.js +7 -0
- package/templates/frontend/SOLID-TS/src/App.tsx +116 -0
- package/templates/frontend/SOLID-TS/vite.config.ts +7 -0
- package/templates/frontend/SVELTE/src/App.svelte +104 -0
- package/templates/frontend/SVELTE/vite.config.js +8 -0
- package/templates/frontend/SVELTE-TS/src/App.svelte +116 -0
- package/templates/frontend/SVELTE-TS/vite.config.ts +8 -0
- package/templates/frontend/VUE/src/App.vue +96 -0
- package/templates/frontend/VUE/vite.config.js +8 -0
- package/templates/frontend/VUE-TS/src/App.vue +112 -0
- 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();
|