create-fullstack-boilerplate 2.1.9 ā 2.1.11
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 +6 -6
- package/index.js +1 -141
- package/lib/addDB.js +0 -2
- package/lib/addRoute.js +4 -6
- package/lib/copyProject.js +29 -10
- package/lib/prompts.js +10 -10
- package/lib/setupExtraDB.js +87 -89
- package/lib/testDBConnection.js +1 -35
- package/lib/utils.js +1 -130
- package/package.json +46 -46
- package/lib/assertDependency.js +0 -14
- package/lib/ensureDir.js +0 -7
- package/lib/installDeps.js +0 -11
- package/lib/runInstall.js +0 -10
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# create-fullstack-
|
|
1
|
+
# create-fullstack-boilerplate
|
|
2
2
|
|
|
3
3
|
A powerful CLI tool to quickly scaffold a complete fullstack application with React, Node.js, Express, and Sequelize with multi-database support. Get started building real features instead of spending time on project setup!
|
|
4
4
|
|
|
@@ -25,14 +25,14 @@ A powerful CLI tool to quickly scaffold a complete fullstack application with Re
|
|
|
25
25
|
### Create a New Project
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
npx create-fullstack-
|
|
28
|
+
npx create-fullstack-boilerplate
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
Or install globally:
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
npm install -g create-fullstack-
|
|
35
|
-
create-fullstack-
|
|
34
|
+
npm install -g create-fullstack-boilerplate
|
|
35
|
+
create-fullstack-boilerplate
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
Follow the interactive prompts:
|
|
@@ -73,7 +73,7 @@ npm run dev
|
|
|
73
73
|
Navigate to your project root and run:
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
|
-
npx create-fullstack-
|
|
76
|
+
npx create-fullstack-boilerplate add-db
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
The CLI will prompt you for:
|
|
@@ -125,7 +125,7 @@ The CLI provides appropriate data types based on your selected database dialect:
|
|
|
125
125
|
Navigate to your project root and run:
|
|
126
126
|
|
|
127
127
|
```bash
|
|
128
|
-
npx create-fullstack-
|
|
128
|
+
npx create-fullstack-boilerplate add-route
|
|
129
129
|
```
|
|
130
130
|
|
|
131
131
|
The CLI will prompt you for:
|
package/index.js
CHANGED
|
@@ -133,144 +133,4 @@
|
|
|
133
133
|
}
|
|
134
134
|
process.exit(1);
|
|
135
135
|
}
|
|
136
|
-
})();
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// #!/usr/bin/env node
|
|
141
|
-
|
|
142
|
-
// (async () => {
|
|
143
|
-
// const argv = process.argv.slice(2);
|
|
144
|
-
|
|
145
|
-
// if (argv[0] === "add-db") {
|
|
146
|
-
// await require("./lib/addDB")();
|
|
147
|
-
// return;
|
|
148
|
-
// }
|
|
149
|
-
|
|
150
|
-
// if (argv[0] === "add-route") {
|
|
151
|
-
// await require("./lib/addRoute")();
|
|
152
|
-
// return;
|
|
153
|
-
// }
|
|
154
|
-
|
|
155
|
-
// // ---- Your existing create-project code ----
|
|
156
|
-
// const path = require("path");
|
|
157
|
-
// // const { ensure } = require("./lib/utils");
|
|
158
|
-
// const copyProject = require('./lib/copyProject')
|
|
159
|
-
// const { runInstall, installDBDriver } = require("./lib/utils");
|
|
160
|
-
// const setupMainDB = require("./lib/setupMainDB");
|
|
161
|
-
// const setupExtraDB = require("./lib/setupExtraDB");
|
|
162
|
-
// const testDBConnection = require("./lib/testDBConnection");
|
|
163
|
-
// const { mainPrompts, extraDBPrompts } = require("./lib/prompts");
|
|
164
|
-
|
|
165
|
-
// const fs = require("fs-extra");
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// // Resolve template folder dynamically
|
|
169
|
-
// let templateDir;
|
|
170
|
-
|
|
171
|
-
// // Try __dirname first (works for local/dev)
|
|
172
|
-
// const localTemplate = path.resolve(__dirname, "template");
|
|
173
|
-
// console.log("š Checking local template:", localTemplate);
|
|
174
|
-
|
|
175
|
-
// if (fs.existsSync(localTemplate)) {
|
|
176
|
-
// templateDir = localTemplate;
|
|
177
|
-
// console.log("ā
Found local template");
|
|
178
|
-
// } else {
|
|
179
|
-
// // Fallback for npx (temp folder)
|
|
180
|
-
// try {
|
|
181
|
-
// const pkg = require.resolve("create-fullstack-boilerplate/package.json");
|
|
182
|
-
// templateDir = path.resolve(path.dirname(pkg), "template");
|
|
183
|
-
// console.log("ā
Found npm template:", templateDir);
|
|
184
|
-
// } catch (err) {
|
|
185
|
-
// console.error("ā Could not resolve package:", err.message);
|
|
186
|
-
// }
|
|
187
|
-
// }
|
|
188
|
-
|
|
189
|
-
// if (!templateDir || !fs.existsSync(templateDir)) {
|
|
190
|
-
// throw new Error(
|
|
191
|
-
// `Template folder not found!\nSearched: ${localTemplate}\nMake sure 'template' is included in package.json 'files' field.`
|
|
192
|
-
// );
|
|
193
|
-
// }
|
|
194
|
-
|
|
195
|
-
// console.log("ā
Using template folder:", templateDir);
|
|
196
|
-
|
|
197
|
-
// console.log("=== DEBUG INFO ===");
|
|
198
|
-
// console.log("Current directory:", process.cwd());
|
|
199
|
-
// console.log("Template directory:", templateDir);
|
|
200
|
-
// console.log("Template exists:", fs.existsSync(templateDir));
|
|
201
|
-
// if (fs.existsSync(templateDir)) {
|
|
202
|
-
// console.log("Template contents:", fs.readdirSync(templateDir));
|
|
203
|
-
// }
|
|
204
|
-
// console.log("==================");
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
// try {
|
|
208
|
-
// console.log("\nšļø Setting Up Full Stack Project...\n");
|
|
209
|
-
|
|
210
|
-
// const answers = await mainPrompts();
|
|
211
|
-
// const targetDir = path.join(process.cwd(), answers.projectName);
|
|
212
|
-
|
|
213
|
-
// console.log("Target directory:", targetDir);
|
|
214
|
-
|
|
215
|
-
// // Remove the IIFE wrapper and just await directly
|
|
216
|
-
// try {
|
|
217
|
-
// console.log("š Copying project files...");
|
|
218
|
-
// await copyProject(templateDir, targetDir);
|
|
219
|
-
// console.log("ā
Project copied successfully");
|
|
220
|
-
// } catch (err) {
|
|
221
|
-
// console.error("ā Failed to copy project files:", err.message);
|
|
222
|
-
// throw err; // Re-throw to stop execution
|
|
223
|
-
// }
|
|
224
|
-
|
|
225
|
-
// await setupMainDB(targetDir, answers.dbDialect);
|
|
226
|
-
// console.log("ā”ļø Installing backend dependencies...");
|
|
227
|
-
// await runInstall(path.join(targetDir, "Backend"));
|
|
228
|
-
|
|
229
|
-
// console.log("ā”ļø Installing frontend dependencies...");
|
|
230
|
-
// await runInstall(path.join(targetDir, "Frontend"));
|
|
231
|
-
|
|
232
|
-
// console.log("ā”ļø Installing Your Db Dialect: ", answers.dbDialect)
|
|
233
|
-
// await installDBDriver(path.join(targetDir, "Backend"), answers.dbDialect);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
// if (answers.addExtraDB) {
|
|
237
|
-
// const extraDB = await extraDBPrompts();
|
|
238
|
-
// console.log(`\nš Testing connection for ${extraDB.dbKey}...`);
|
|
239
|
-
// await installDBDriver(path.join(targetDir, "Backend"), extraDB.dialect);
|
|
240
|
-
// const ok = await testDBConnection(targetDir, extraDB);
|
|
241
|
-
|
|
242
|
-
// if (ok) {
|
|
243
|
-
// await setupExtraDB(targetDir, extraDB);
|
|
244
|
-
// console.log(`ā
Extra DB '${extraDB.dbKey}' successfully integrated.\n`);
|
|
245
|
-
// } else {
|
|
246
|
-
// console.log(`ā ļø Connection failed. Skipping extra DB setup.\n`);
|
|
247
|
-
// }
|
|
248
|
-
// }
|
|
249
|
-
|
|
250
|
-
// if (answers.initGit) {
|
|
251
|
-
// try {
|
|
252
|
-
// const execa = require("execa").execa || require("execa");
|
|
253
|
-
// await execa("git", ["init"], { cwd: targetDir });
|
|
254
|
-
// if (answers.remoteRepo) {
|
|
255
|
-
// await execa("git", ["remote", "add", "origin", answers.remoteRepo], { cwd: targetDir });
|
|
256
|
-
// }
|
|
257
|
-
// console.log("ā
Git initialized.\n");
|
|
258
|
-
// } catch (gitErr) {
|
|
259
|
-
// console.log(`ā ļø Git initialization failed: ${gitErr.message}`);
|
|
260
|
-
// console.log(` You can initialize git manually later.\n`);
|
|
261
|
-
// }
|
|
262
|
-
// }
|
|
263
|
-
|
|
264
|
-
// console.log(`\nš Project Ready!`);
|
|
265
|
-
// console.log(`\nā”ļø Next Steps:`);
|
|
266
|
-
// console.log(` cd ${answers.projectName}`);
|
|
267
|
-
// console.log(` cd backend && npm run dev`);
|
|
268
|
-
// console.log(` cd ../frontend && npm run dev\n`);
|
|
269
|
-
// } catch (err) {
|
|
270
|
-
// console.error(`\nā Error creating project: ${err.message}\n`);
|
|
271
|
-
// if (err.stack && process.env.DEBUG) {
|
|
272
|
-
// console.error(err.stack);
|
|
273
|
-
// }
|
|
274
|
-
// process.exit(1);
|
|
275
|
-
// }
|
|
276
|
-
// })();
|
|
136
|
+
})();
|
package/lib/addDB.js
CHANGED
|
@@ -2,8 +2,6 @@ const path = require("path");
|
|
|
2
2
|
const { extraDBPrompts } = require("./prompts");
|
|
3
3
|
const testDBConnection = require("./testDBConnection");
|
|
4
4
|
const setupExtraDB = require("./setupExtraDB");
|
|
5
|
-
// const { ensure } = require("./utils");
|
|
6
|
-
// const fs = ensure("fs-extra");
|
|
7
5
|
const fs = require("fs-extra");
|
|
8
6
|
|
|
9
7
|
|
package/lib/addRoute.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
|
-
// const { ensure } = require("./utils");
|
|
3
2
|
const { routePrompts } = require("./prompts");
|
|
4
|
-
// const fs = ensure("fs-extra");
|
|
5
3
|
const fs = require("fs-extra");
|
|
6
4
|
|
|
7
5
|
|
|
@@ -28,7 +26,7 @@ module.exports = async function addRoute() {
|
|
|
28
26
|
try {
|
|
29
27
|
// Get route info from user
|
|
30
28
|
const routeInfo = await routePrompts();
|
|
31
|
-
|
|
29
|
+
|
|
32
30
|
const routeFileName = `${routeInfo.routeName}Routes.js`;
|
|
33
31
|
const serviceFileName = `${routeInfo.routeName}Service.js`;
|
|
34
32
|
const routeFilePath = path.join(routesDir, routeFileName);
|
|
@@ -71,10 +69,10 @@ module.exports = async function addRoute() {
|
|
|
71
69
|
};
|
|
72
70
|
|
|
73
71
|
function generateRouteFile(routeInfo) {
|
|
74
|
-
const requireAuth = routeInfo.needsAuth
|
|
75
|
-
? `const { authenticateToken } = require('../middleware/authMiddleware');\n`
|
|
72
|
+
const requireAuth = routeInfo.needsAuth
|
|
73
|
+
? `const { authenticateToken } = require('../middleware/authMiddleware');\n`
|
|
76
74
|
: '';
|
|
77
|
-
|
|
75
|
+
|
|
78
76
|
const authMiddleware = routeInfo.needsAuth ? 'authenticateToken, ' : '';
|
|
79
77
|
|
|
80
78
|
return `const express = require('express');
|
package/lib/copyProject.js
CHANGED
|
@@ -1,39 +1,58 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
const fs = require("fs-extra");
|
|
3
|
-
const { normalizePath } = require("./utils"); // IMPORT THIS
|
|
4
3
|
|
|
5
4
|
module.exports = async function copyProject(src, dest) {
|
|
6
5
|
try {
|
|
7
|
-
// Use normalizePath for
|
|
8
|
-
const resolvedSrc =
|
|
9
|
-
const resolvedDest =
|
|
6
|
+
// Use path.resolve without normalizePath for fs operations
|
|
7
|
+
const resolvedSrc = path.resolve(src);
|
|
8
|
+
const resolvedDest = path.resolve(dest);
|
|
10
9
|
|
|
10
|
+
console.log(`š Copying from: ${resolvedSrc}`);
|
|
11
|
+
console.log(`š Copying to: ${resolvedDest}`);
|
|
12
|
+
|
|
13
|
+
// Check source exists
|
|
11
14
|
if (!await fs.pathExists(resolvedSrc)) {
|
|
12
15
|
throw new Error(`Source template directory not found: ${resolvedSrc}`);
|
|
13
16
|
}
|
|
14
17
|
|
|
18
|
+
// Check if destination already exists
|
|
15
19
|
if (await fs.pathExists(resolvedDest)) {
|
|
16
20
|
throw new Error(`Destination directory already exists: ${resolvedDest}`);
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
console.log(`š Copying to: ${resolvedDest}`);
|
|
21
|
-
|
|
23
|
+
// Perform the copy
|
|
22
24
|
await fs.copy(resolvedSrc, resolvedDest, {
|
|
23
25
|
overwrite: false,
|
|
24
26
|
errorOnExist: false,
|
|
25
27
|
filter: (srcPath) => {
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
+
// Don't skip node_modules using includes, use a more reliable check
|
|
29
|
+
const relativePath = path.relative(resolvedSrc, srcPath);
|
|
30
|
+
const shouldCopy = !relativePath.split(path.sep).includes("node_modules");
|
|
28
31
|
return shouldCopy;
|
|
29
32
|
}
|
|
30
33
|
});
|
|
31
34
|
|
|
35
|
+
console.log("š Copy operation completed, verifying...");
|
|
36
|
+
|
|
37
|
+
// Add a small delay to ensure filesystem sync (especially on Windows)
|
|
38
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
39
|
+
|
|
32
40
|
// Verify the copy was successful
|
|
33
|
-
|
|
41
|
+
const destExists = await fs.pathExists(resolvedDest);
|
|
42
|
+
console.log(`š Destination exists: ${destExists}`);
|
|
43
|
+
|
|
44
|
+
if (!destExists) {
|
|
34
45
|
throw new Error("Copy operation completed but destination directory not found");
|
|
35
46
|
}
|
|
36
47
|
|
|
48
|
+
// Verify contents were copied
|
|
49
|
+
const destContents = await fs.readdir(resolvedDest);
|
|
50
|
+
console.log(`š Destination contents: ${destContents.join(", ")}`);
|
|
51
|
+
|
|
52
|
+
if (destContents.length === 0) {
|
|
53
|
+
throw new Error("Destination directory is empty after copy");
|
|
54
|
+
}
|
|
55
|
+
|
|
37
56
|
console.log("ā
Files copied successfully.");
|
|
38
57
|
} catch (err) {
|
|
39
58
|
console.error("ā Copy operation failed:", err.message);
|
package/lib/prompts.js
CHANGED
|
@@ -46,8 +46,8 @@ module.exports.mainPrompts = () => {
|
|
|
46
46
|
|
|
47
47
|
module.exports.extraDBPrompts = async () => {
|
|
48
48
|
const basicInfo = await inquirer.prompt([
|
|
49
|
-
{
|
|
50
|
-
name: 'dbKey',
|
|
49
|
+
{
|
|
50
|
+
name: 'dbKey',
|
|
51
51
|
message: 'DB Identifier (e.g., REPORTING_DB):',
|
|
52
52
|
validate: (input) => {
|
|
53
53
|
if (!input || input.trim().length === 0) {
|
|
@@ -63,9 +63,9 @@ module.exports.extraDBPrompts = async () => {
|
|
|
63
63
|
{ name: 'username', message: 'DB Username:', validate: (input) => input.trim().length > 0 ? true : 'Cannot be empty' },
|
|
64
64
|
{ name: 'password', message: 'DB Password:' },
|
|
65
65
|
{ name: 'host', message: 'DB Host:', default: 'localhost' },
|
|
66
|
-
{
|
|
67
|
-
name: 'port',
|
|
68
|
-
message: 'DB Port:',
|
|
66
|
+
{
|
|
67
|
+
name: 'port',
|
|
68
|
+
message: 'DB Port:',
|
|
69
69
|
default: '3306',
|
|
70
70
|
validate: (input) => {
|
|
71
71
|
const port = parseInt(input);
|
|
@@ -81,9 +81,9 @@ module.exports.extraDBPrompts = async () => {
|
|
|
81
81
|
message: 'DB Dialect:',
|
|
82
82
|
choices: ['mysql', 'mariadb', 'postgres']
|
|
83
83
|
},
|
|
84
|
-
{
|
|
85
|
-
name: 'tableName',
|
|
86
|
-
message: 'Model / Table Name:',
|
|
84
|
+
{
|
|
85
|
+
name: 'tableName',
|
|
86
|
+
message: 'Model / Table Name:',
|
|
87
87
|
default: 'sample_table',
|
|
88
88
|
validate: (input) => {
|
|
89
89
|
if (!input || input.trim().length === 0) {
|
|
@@ -113,9 +113,9 @@ module.exports.extraDBPrompts = async () => {
|
|
|
113
113
|
|
|
114
114
|
async function collectTableAttributes(dialect) {
|
|
115
115
|
const attributes = [];
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
console.log('\nš Define table attributes. Start with the primary key.\n');
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
// Primary key first
|
|
120
120
|
const pkAnswers = await inquirer.prompt([
|
|
121
121
|
{
|
package/lib/setupExtraDB.js
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
|
-
|
|
3
|
-
// const fs = ensure("fs-extra");
|
|
4
|
-
const fs = require("fs-extra"); // no assert needed
|
|
2
|
+
const fs = require("fs-extra");
|
|
5
3
|
|
|
6
4
|
|
|
7
5
|
module.exports = async function setupExtraDB(targetDir, dbConfig) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
6
|
+
const backendDir = path.join(targetDir, "Backend");
|
|
7
|
+
const modelsDir = path.join(backendDir, "Models");
|
|
8
|
+
const dbDir = path.join(backendDir, "DB");
|
|
9
|
+
|
|
10
|
+
// 1. Create folder for this DB's models
|
|
11
|
+
const dbFolderName = dbConfig.dbKey;
|
|
12
|
+
const dbModelsFolder = path.join(modelsDir, dbFolderName);
|
|
13
|
+
await fs.ensureDir(dbModelsFolder);
|
|
14
|
+
|
|
15
|
+
// 2. Create config file for this DB
|
|
16
|
+
const configFileName = `${dbFolderName}.config.js`;
|
|
17
|
+
const configPath = path.join(dbDir, configFileName);
|
|
18
|
+
|
|
19
|
+
const configContent = `module.exports = {
|
|
22
20
|
development: {
|
|
23
21
|
username: process.env.${dbConfig.dbKey}_USER || "${dbConfig.username}",
|
|
24
22
|
password: process.env.${dbConfig.dbKey}_PASSWORD || "${dbConfig.password}",
|
|
@@ -46,91 +44,91 @@ module.exports = async function setupExtraDB(targetDir, dbConfig) {
|
|
|
46
44
|
};
|
|
47
45
|
`;
|
|
48
46
|
|
|
49
|
-
|
|
47
|
+
await fs.writeFile(configPath, configContent);
|
|
48
|
+
|
|
49
|
+
// 3. Create a sample model file
|
|
50
|
+
const modelContent = generateModelContent(dbConfig);
|
|
51
|
+
const modelFileName = `${dbConfig.tableName}.js`;
|
|
52
|
+
const modelPath = path.join(dbModelsFolder, modelFileName);
|
|
53
|
+
await fs.writeFile(modelPath, modelContent);
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const modelPath = path.join(dbModelsFolder, modelFileName);
|
|
55
|
-
await fs.writeFile(modelPath, modelContent);
|
|
55
|
+
// 4. Update dbConfigs.js to register this DB
|
|
56
|
+
const dbConfigsPath = path.join(dbDir, "dbConfigs.js");
|
|
57
|
+
let dbConfigsContent = await fs.readFile(dbConfigsPath, "utf8");
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
const dbConfigsPath = path.join(dbDir, "dbConfigs.js");
|
|
59
|
-
let dbConfigsContent = await fs.readFile(dbConfigsPath, "utf8");
|
|
60
|
-
|
|
61
|
-
const newEntry = ` {
|
|
59
|
+
const newEntry = ` {
|
|
62
60
|
key: "${dbConfig.dbKey}",
|
|
63
61
|
folder: "${dbConfig.dbKey}",
|
|
64
62
|
configPath: "/../DB/${configFileName}"
|
|
65
63
|
}`;
|
|
66
64
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
// Insert before the closing bracket
|
|
66
|
+
dbConfigsContent = dbConfigsContent.replace(
|
|
67
|
+
/const dbConfigs = \[\s*\];/,
|
|
68
|
+
`const dbConfigs = [
|
|
71
69
|
${newEntry}
|
|
72
70
|
];`
|
|
73
|
-
|
|
71
|
+
);
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
// If there are already entries, append
|
|
74
|
+
if (!dbConfigsContent.includes(newEntry) && dbConfigsContent.includes("const dbConfigs = [")) {
|
|
75
|
+
dbConfigsContent = dbConfigsContent.replace(
|
|
76
|
+
/(const dbConfigs = \[[\s\S]*?)(\];)/,
|
|
77
|
+
`$1,
|
|
80
78
|
${newEntry}
|
|
81
79
|
$2`
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
);
|
|
81
|
+
}
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (!envContent.includes(`${dbConfig.dbKey}_USER`)) {
|
|
94
|
-
envContent += envVars;
|
|
95
|
-
await fs.writeFile(envPath, envContent);
|
|
96
|
-
}
|
|
83
|
+
await fs.writeFile(dbConfigsPath, dbConfigsContent);
|
|
84
|
+
|
|
85
|
+
// 5. Update .env file with DB credentials
|
|
86
|
+
const envPath = path.join(backendDir, ".env");
|
|
87
|
+
let envContent = await fs.readFile(envPath, "utf8");
|
|
88
|
+
|
|
89
|
+
const envVars = `\n# ${dbConfig.dbKey} Database Configuration\n${dbConfig.dbKey}_USER=${dbConfig.username}\n${dbConfig.dbKey}_PASSWORD=${dbConfig.password}\n${dbConfig.dbKey}_NAME=${dbConfig.database}\n${dbConfig.dbKey}_HOST=${dbConfig.host}\n${dbConfig.dbKey}_PORT=${dbConfig.port}\n`;
|
|
97
90
|
|
|
98
|
-
|
|
91
|
+
if (!envContent.includes(`${dbConfig.dbKey}_USER`)) {
|
|
92
|
+
envContent += envVars;
|
|
93
|
+
await fs.writeFile(envPath, envContent);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(`ā
Database '${dbConfig.dbKey}' configured successfully.`);
|
|
99
97
|
};
|
|
100
98
|
|
|
101
99
|
function generateModelContent(dbConfig) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
100
|
+
const modelName = dbConfig.tableName.charAt(0).toUpperCase() + dbConfig.tableName.slice(1);
|
|
101
|
+
|
|
102
|
+
// Generate model content based on whether attributes were provided
|
|
103
|
+
if (dbConfig.attributes && dbConfig.attributes.length > 0) {
|
|
104
|
+
const attributesStr = dbConfig.attributes.map(attr => {
|
|
105
|
+
let attrDef = ` ${attr.name}: {\n type: DataTypes.${attr.type}`;
|
|
106
|
+
|
|
107
|
+
if (attr.length) {
|
|
108
|
+
attrDef += `(${attr.length})`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (attr.primaryKey) {
|
|
112
|
+
attrDef += `,\n primaryKey: true,\n autoIncrement: true`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (attr.allowNull === false) {
|
|
116
|
+
attrDef += `,\n allowNull: false`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (attr.unique) {
|
|
120
|
+
attrDef += `,\n unique: true`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (attr.defaultValue !== undefined) {
|
|
124
|
+
attrDef += `,\n defaultValue: ${JSON.stringify(attr.defaultValue)}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
attrDef += `\n }`;
|
|
128
|
+
return attrDef;
|
|
129
|
+
}).join(',\n');
|
|
130
|
+
|
|
131
|
+
return `'use strict';
|
|
134
132
|
module.exports = (sequelize, DataTypes) => {
|
|
135
133
|
const ${modelName} = sequelize.define('${modelName}', {
|
|
136
134
|
${attributesStr}
|
|
@@ -147,9 +145,9 @@ ${attributesStr}
|
|
|
147
145
|
return ${modelName};
|
|
148
146
|
};
|
|
149
147
|
`;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
} else {
|
|
149
|
+
// Simple model with just ID
|
|
150
|
+
return `'use strict';
|
|
153
151
|
module.exports = (sequelize, DataTypes) => {
|
|
154
152
|
const ${modelName} = sequelize.define('${modelName}', {
|
|
155
153
|
id: {
|
|
@@ -170,5 +168,5 @@ module.exports = (sequelize, DataTypes) => {
|
|
|
170
168
|
return ${modelName};
|
|
171
169
|
};
|
|
172
170
|
`;
|
|
173
|
-
|
|
171
|
+
}
|
|
174
172
|
}
|
package/lib/testDBConnection.js
CHANGED
|
@@ -1,39 +1,5 @@
|
|
|
1
|
-
// const path = require("path");
|
|
2
|
-
|
|
3
|
-
// async function testDBConnection(targetDir, dbConfig) {
|
|
4
|
-
// const sequelizePath = path.join(targetDir, "backend", "node_modules", "sequelize");
|
|
5
|
-
// const { Sequelize } = require(sequelizePath);
|
|
6
|
-
|
|
7
|
-
// const sequelize = new Sequelize(
|
|
8
|
-
// dbConfig.database,
|
|
9
|
-
// dbConfig.username,
|
|
10
|
-
// dbConfig.password,
|
|
11
|
-
// {
|
|
12
|
-
// host: dbConfig.host,
|
|
13
|
-
// port: dbConfig.port,
|
|
14
|
-
// dialect: dbConfig.dialect,
|
|
15
|
-
// logging: false
|
|
16
|
-
// }
|
|
17
|
-
// );
|
|
18
|
-
|
|
19
|
-
// try {
|
|
20
|
-
// await sequelize.authenticate();
|
|
21
|
-
// console.log(`ā
Connection successful to ${dbConfig.dbKey}`);
|
|
22
|
-
// await sequelize.close();
|
|
23
|
-
// return true;
|
|
24
|
-
// } catch (err) {
|
|
25
|
-
// console.log(`ā Failed to connect to ${dbConfig.dbKey}`);
|
|
26
|
-
// console.log("Error:", err.message);
|
|
27
|
-
// return false;
|
|
28
|
-
// }
|
|
29
|
-
// }
|
|
30
|
-
|
|
31
|
-
// module.exports = testDBConnection;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
1
|
const path = require("path");
|
|
36
|
-
const { assertDependency } = require("./utils");
|
|
2
|
+
const { assertDependency } = require("./utils");
|
|
37
3
|
|
|
38
4
|
async function testDBConnection(targetDir, dbConfig) {
|
|
39
5
|
const backendDir = path.join(targetDir, "Backend");
|
package/lib/utils.js
CHANGED
|
@@ -1,132 +1,3 @@
|
|
|
1
|
-
// const fs = require("fs-extra");
|
|
2
|
-
// const path = require("path");
|
|
3
|
-
// const execa = require("execa");
|
|
4
|
-
|
|
5
|
-
// // ------------------ Logging ------------------
|
|
6
|
-
// function log(step) {
|
|
7
|
-
// console.log(`\nā”ļø ${step}`);
|
|
8
|
-
// }
|
|
9
|
-
|
|
10
|
-
// // ------------------ Replace in Files ------------------
|
|
11
|
-
// async function replaceInFiles(base, replacements) {
|
|
12
|
-
// const exts = [".js", ".ts", ".json", ".md", ".env", ".yml"];
|
|
13
|
-
|
|
14
|
-
// async function walk(dir) {
|
|
15
|
-
// // Check if directory exists
|
|
16
|
-
// if (!await fs.pathExists(dir)) {
|
|
17
|
-
// console.warn(`Warning: Directory ${dir} does not exist, skipping...`);
|
|
18
|
-
// return;
|
|
19
|
-
// }
|
|
20
|
-
|
|
21
|
-
// let items;
|
|
22
|
-
// try {
|
|
23
|
-
// items = await fs.readdir(dir);
|
|
24
|
-
// } catch (err) {
|
|
25
|
-
// console.warn(`Warning: Could not read directory ${dir}: ${err.message}`);
|
|
26
|
-
// return;
|
|
27
|
-
// }
|
|
28
|
-
|
|
29
|
-
// for (const item of items) {
|
|
30
|
-
// const fullPath = path.join(dir, item);
|
|
31
|
-
// try {
|
|
32
|
-
// const stat = await fs.stat(fullPath);
|
|
33
|
-
// if (stat.isDirectory()) {
|
|
34
|
-
// await walk(fullPath);
|
|
35
|
-
// } else if (exts.includes(path.extname(fullPath))) {
|
|
36
|
-
// let data = await fs.readFile(fullPath, "utf8");
|
|
37
|
-
// for (const [k, v] of Object.entries(replacements)) {
|
|
38
|
-
// data = data.replaceAll(k, v);
|
|
39
|
-
// }
|
|
40
|
-
// await fs.writeFile(fullPath, data);
|
|
41
|
-
// }
|
|
42
|
-
// } catch (err) {
|
|
43
|
-
// console.warn(`Warning: Error processing ${fullPath}: ${err.message}`);
|
|
44
|
-
// }
|
|
45
|
-
// }
|
|
46
|
-
// }
|
|
47
|
-
|
|
48
|
-
// await walk(base);
|
|
49
|
-
// }
|
|
50
|
-
|
|
51
|
-
// // ------------------ Ensure Directory ------------------
|
|
52
|
-
// async function ensureDir(dir) {
|
|
53
|
-
// await fs.ensureDir(dir);
|
|
54
|
-
// }
|
|
55
|
-
|
|
56
|
-
// // ------------------ Assert Dependency ------------------
|
|
57
|
-
// function assertDependency(depName, cwd) {
|
|
58
|
-
// try {
|
|
59
|
-
// return require(path.join(cwd, "node_modules", depName));
|
|
60
|
-
// } catch (err) {
|
|
61
|
-
// throw new Error(
|
|
62
|
-
// `Dependency '${depName}' is not installed in ${cwd}.\n` +
|
|
63
|
-
// `Run: cd ${cwd} && npm install ${depName}`
|
|
64
|
-
// );
|
|
65
|
-
// }
|
|
66
|
-
// }
|
|
67
|
-
|
|
68
|
-
// // ------------------ Run npm install ------------------
|
|
69
|
-
// async function runInstall(cwd) {
|
|
70
|
-
// // Normalize path for Windows
|
|
71
|
-
// const normalizedCwd = path.normalize(cwd);
|
|
72
|
-
|
|
73
|
-
// // Verify directory exists
|
|
74
|
-
// if (!await fs.pathExists(normalizedCwd)) {
|
|
75
|
-
// throw new Error(`Directory does not exist: ${normalizedCwd}`);
|
|
76
|
-
// }
|
|
77
|
-
|
|
78
|
-
// try {
|
|
79
|
-
// await execa("npm", ["install"], {
|
|
80
|
-
// cwd: normalizedCwd,
|
|
81
|
-
// stdio: "inherit",
|
|
82
|
-
// shell: true // Use shell for Windows compatibility
|
|
83
|
-
// });
|
|
84
|
-
// } catch (err) {
|
|
85
|
-
// throw new Error(`npm install failed in ${normalizedCwd}: ${err.message}`);
|
|
86
|
-
// }
|
|
87
|
-
// }
|
|
88
|
-
|
|
89
|
-
// // ------------------ Installing Dynamic DB Drivers ----------------
|
|
90
|
-
|
|
91
|
-
// async function installDBDriver(targetDir, dialect) {
|
|
92
|
-
// let pkg;
|
|
93
|
-
// if (dialect === "mariadb") pkg = "mariadb";
|
|
94
|
-
// if (dialect === "mysql") pkg = "mysql2";
|
|
95
|
-
// if (dialect === "postgres") pkg = "pg";
|
|
96
|
-
// if (!pkg) return;
|
|
97
|
-
|
|
98
|
-
// // Normalize path for Windows
|
|
99
|
-
// const normalizedDir = path.normalize(targetDir);
|
|
100
|
-
|
|
101
|
-
// console.log(`ā”ļø Installing ${pkg}...`);
|
|
102
|
-
// try {
|
|
103
|
-
// await execa("npm", ["install", pkg], {
|
|
104
|
-
// cwd: normalizedDir,
|
|
105
|
-
// stdio: "inherit",
|
|
106
|
-
// shell: true // Use shell for Windows compatibility
|
|
107
|
-
// });
|
|
108
|
-
// } catch (err) {
|
|
109
|
-
// console.warn(`Warning: Failed to install ${pkg}: ${err.message}`);
|
|
110
|
-
// }
|
|
111
|
-
// }
|
|
112
|
-
|
|
113
|
-
// // Add this function to utils.js
|
|
114
|
-
// function normalizePath(p) {
|
|
115
|
-
// return path.normalize(p).replace(/\\/g, '/');
|
|
116
|
-
// }
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// module.exports = {
|
|
120
|
-
// log,
|
|
121
|
-
// replaceInFiles,
|
|
122
|
-
// ensureDir,
|
|
123
|
-
// assertDependency,
|
|
124
|
-
// runInstall,
|
|
125
|
-
// installDBDriver,
|
|
126
|
-
// normalizePath
|
|
127
|
-
// };
|
|
128
|
-
|
|
129
|
-
|
|
130
1
|
const fs = require("fs-extra");
|
|
131
2
|
const path = require("path");
|
|
132
3
|
const execa = require("execa");
|
|
@@ -250,5 +121,5 @@ module.exports = {
|
|
|
250
121
|
assertDependency,
|
|
251
122
|
runInstall,
|
|
252
123
|
installDBDriver,
|
|
253
|
-
normalizePath
|
|
124
|
+
normalizePath
|
|
254
125
|
};
|
package/package.json
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "create-fullstack-boilerplate",
|
|
3
|
-
"version": "2.1.
|
|
4
|
-
"description": "A Full Stack Application Comprised of React for Frontend, Express & Sequelize with Node js for Backend to help developers get started on real implementation instead of spending time setting up projects.",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"create-fullstack-boilerplate": "index.js"
|
|
8
|
-
},
|
|
9
|
-
"preferGlobal": false,
|
|
10
|
-
"engines": {
|
|
11
|
-
"node": ">=20"
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"index.js",
|
|
15
|
-
"lib",
|
|
16
|
-
"template"
|
|
17
|
-
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
20
|
-
},
|
|
21
|
-
"repository": {
|
|
22
|
-
"type": "git",
|
|
23
|
-
"url": "git+https://github.com/mhuzaifi0604/react-boilerplate.git"
|
|
24
|
-
},
|
|
25
|
-
"keywords": [
|
|
26
|
-
"fullstack",
|
|
27
|
-
"boilerplate",
|
|
28
|
-
"react",
|
|
29
|
-
"node",
|
|
30
|
-
"js",
|
|
31
|
-
"fullstack",
|
|
32
|
-
"app"
|
|
33
|
-
],
|
|
34
|
-
"dependencies": {
|
|
35
|
-
"cpy": "^12.1.0",
|
|
36
|
-
"execa": "^5.1.1",
|
|
37
|
-
"fs-extra": "^11.3.2",
|
|
38
|
-
"inquirer": "^8.2.7"
|
|
39
|
-
},
|
|
40
|
-
"author": "Muhammad Huzaifa",
|
|
41
|
-
"license": "ISC",
|
|
42
|
-
"bugs": {
|
|
43
|
-
"url": "https://github.com/mhuzaifi0604/react-boilerplate/issues"
|
|
44
|
-
},
|
|
45
|
-
"homepage": "https://github.com/mhuzaifi0604/react-boilerplate#readme"
|
|
46
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "create-fullstack-boilerplate",
|
|
3
|
+
"version": "2.1.11",
|
|
4
|
+
"description": "A Full Stack Application Comprised of React for Frontend, Express & Sequelize with Node js for Backend to help developers get started on real implementation instead of spending time setting up projects.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-fullstack-boilerplate": "index.js"
|
|
8
|
+
},
|
|
9
|
+
"preferGlobal": false,
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=20"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"index.js",
|
|
15
|
+
"lib",
|
|
16
|
+
"template"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/mhuzaifi0604/react-boilerplate.git"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"fullstack",
|
|
27
|
+
"boilerplate",
|
|
28
|
+
"react",
|
|
29
|
+
"node",
|
|
30
|
+
"js",
|
|
31
|
+
"fullstack",
|
|
32
|
+
"app"
|
|
33
|
+
],
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"cpy": "^12.1.0",
|
|
36
|
+
"execa": "^5.1.1",
|
|
37
|
+
"fs-extra": "^11.3.2",
|
|
38
|
+
"inquirer": "^8.2.7"
|
|
39
|
+
},
|
|
40
|
+
"author": "Muhammad Huzaifa",
|
|
41
|
+
"license": "ISC",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/mhuzaifi0604/react-boilerplate/issues"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/mhuzaifi0604/react-boilerplate#readme"
|
|
46
|
+
}
|
package/lib/assertDependency.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// const path = require("path");
|
|
2
|
-
|
|
3
|
-
// function assertDependency(depName, cwd) {
|
|
4
|
-
// try {
|
|
5
|
-
// return require(path.join(cwd, "node_modules", depName));
|
|
6
|
-
// } catch (err) {
|
|
7
|
-
// throw new Error(
|
|
8
|
-
// `Dependency '${depName}' is not installed in ${cwd}.\n` +
|
|
9
|
-
// `Run: cd ${cwd} && npm install ${depName}`
|
|
10
|
-
// );
|
|
11
|
-
// }
|
|
12
|
-
// }
|
|
13
|
-
|
|
14
|
-
// module.exports = assertDependency;
|
package/lib/ensureDir.js
DELETED
package/lib/installDeps.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// const execa = require("execa");
|
|
2
|
-
// const path = require("path");
|
|
3
|
-
// const { log } = require("./utils");
|
|
4
|
-
|
|
5
|
-
// module.exports = async function installDeps(targetDir) {
|
|
6
|
-
// log("Installing backend dependencies...");
|
|
7
|
-
// await execa("npm", ["install"], { cwd: path.join(targetDir, "backend"), stdio: "inherit" });
|
|
8
|
-
|
|
9
|
-
// log("Installing frontend dependencies...");
|
|
10
|
-
// await execa("npm", ["install"], { cwd: path.join(targetDir, "frontend"), stdio: "inherit" });
|
|
11
|
-
// };
|