4bnode 3.5.2 → 4.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 (43) hide show
  1. package/README.md +139 -114
  2. package/add-api.js +288 -156
  3. package/add-crud.js +226 -0
  4. package/add-login.js +152 -319
  5. package/add-mongo-delete.js +67 -138
  6. package/add-mongo-insert.js +87 -204
  7. package/add-mongo-read.js +100 -216
  8. package/add-mongo-schema.js +431 -199
  9. package/add-mongo-update.js +89 -202
  10. package/add-mongo.js +125 -218
  11. package/add-serialport.js +53 -98
  12. package/add-socket.js +43 -66
  13. package/add-websocket.js +68 -140
  14. package/index.js +288 -155
  15. package/lib/env.js +18 -0
  16. package/lib/indexFile.js +87 -0
  17. package/lib/names.js +21 -0
  18. package/lib/project.js +122 -0
  19. package/lib/routeFile.js +56 -0
  20. package/lib/ui.js +161 -0
  21. package/package.json +9 -16
  22. package/skeleton/dev-api.js +2785 -0
  23. package/skeleton/index.js +10 -3
  24. package/skeleton/package.json +9 -5
  25. package/skeleton/public/fonts/NotoSans-Bold.woff2 +0 -0
  26. package/skeleton/public/fonts/NotoSans-Light.woff2 +0 -0
  27. package/skeleton/public/fonts/NotoSans-Medium.woff2 +0 -0
  28. package/skeleton/public/fonts/NotoSans-Regular.woff2 +0 -0
  29. package/skeleton/public/fonts/NotoSans-SemiBold.woff2 +0 -0
  30. package/skeleton/public/images/mongo.png +0 -0
  31. package/skeleton/public/images/mysql.png +0 -0
  32. package/skeleton/public/images/postgresql.png +0 -0
  33. package/skeleton/public/images/sqlite.png +0 -0
  34. package/skeleton/public/index.html +7562 -64
  35. package/README_4bnode.md +0 -197
  36. package/src/auth/passport.js +0 -21
  37. package/src/db.js +0 -13
  38. package/test-server/.env.development +0 -1
  39. package/test-server/.env.production +0 -1
  40. package/test-server/index.js +0 -82
  41. package/test-server/package-lock.json +0 -1267
  42. package/test-server/package.json +0 -20
  43. package/test-server/public/index.html +0 -90
package/lib/project.js ADDED
@@ -0,0 +1,122 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export function getProjectRoot() {
5
+ return process.cwd();
6
+ }
7
+
8
+ export function listRoutes() {
9
+ const routesDir = path.join(getProjectRoot(), "src", "routes");
10
+ if (!fs.existsSync(routesDir)) {
11
+ return [];
12
+ }
13
+ return fs.readdirSync(routesDir).filter((f) => f.endsWith(".js"));
14
+ }
15
+
16
+ export function listModels() {
17
+ const modelsDir = path.join(getProjectRoot(), "src", "models");
18
+ if (!fs.existsSync(modelsDir)) {
19
+ return [];
20
+ }
21
+ return fs.readdirSync(modelsDir).filter((f) => f.endsWith(".js"));
22
+ }
23
+
24
+ function extractSchemaBlock(content) {
25
+ const marker = "new mongoose.Schema(";
26
+ const idx = content.indexOf(marker);
27
+ if (idx === -1) return null;
28
+ // Find the opening { after the marker
29
+ const braceStart = content.indexOf("{", idx + marker.length);
30
+ if (braceStart === -1) return null;
31
+ // Match balanced braces to find the outer schema object
32
+ let depth = 0;
33
+ for (let i = braceStart; i < content.length; i++) {
34
+ if (content[i] === "{") depth++;
35
+ else if (content[i] === "}") {
36
+ depth--;
37
+ if (depth === 0) return content.slice(braceStart + 1, i);
38
+ }
39
+ }
40
+ return null;
41
+ }
42
+
43
+ export function getModelSchema(modelName, { includeId = false } = {}) {
44
+ const modelFilePath = path.join(
45
+ getProjectRoot(),
46
+ "src",
47
+ "models",
48
+ modelName + ".js"
49
+ );
50
+ if (!fs.existsSync(modelFilePath)) {
51
+ return [];
52
+ }
53
+
54
+ const content = fs.readFileSync(modelFilePath, "utf8");
55
+ const schemaContent = extractSchemaBlock(content);
56
+
57
+ if (!schemaContent) {
58
+ return [];
59
+ }
60
+
61
+ const fieldRegex = /(\w+)\s*:\s*\{/g;
62
+ const fields = includeId ? ["_id"] : [];
63
+ let fieldMatch;
64
+ while ((fieldMatch = fieldRegex.exec(schemaContent)) !== null) {
65
+ fields.push(fieldMatch[1]);
66
+ }
67
+ return fields;
68
+ }
69
+
70
+ export function getModelSchemaDetailed(modelName) {
71
+ const modelFilePath = path.join(
72
+ getProjectRoot(),
73
+ "src",
74
+ "models",
75
+ modelName + ".js"
76
+ );
77
+ if (!fs.existsSync(modelFilePath)) {
78
+ return [];
79
+ }
80
+
81
+ const content = fs.readFileSync(modelFilePath, "utf8");
82
+ const schemaContent = extractSchemaBlock(content);
83
+ if (!schemaContent) return [];
84
+ const fields = [];
85
+ // Match each field block: fieldName: { type: ..., required: ..., unique: ..., default: ... }
86
+ const fieldRegex = /(\w+)\s*:\s*\{([^}]*)\}/g;
87
+ let fm;
88
+ while ((fm = fieldRegex.exec(schemaContent)) !== null) {
89
+ const name = fm[1];
90
+ const props = fm[2];
91
+ const typeMatch = props.match(/type:\s*([\w.]+)/);
92
+ const type = typeMatch
93
+ ? typeMatch[1].replace("mongoose.Schema.Types.", "")
94
+ : "String";
95
+ const required = /required:\s*true/.test(props);
96
+ const unique = /unique:\s*true/.test(props);
97
+ const defaultMatch = props.match(/default:\s*(.+?)(?:,\s*\w+:|$)/);
98
+ const field = { name, type, required, unique };
99
+ if (defaultMatch) {
100
+ field.default = defaultMatch[1].trim();
101
+ }
102
+ fields.push(field);
103
+ }
104
+ return fields;
105
+ }
106
+
107
+ export function checkMongoConfig() {
108
+ const envPath = path.join(getProjectRoot(), ".env.development");
109
+ return (
110
+ fs.existsSync(envPath) &&
111
+ fs.readFileSync(envPath, "utf8").includes("MONGO_URI")
112
+ );
113
+ }
114
+
115
+ export function checkRoutesDir() {
116
+ return fs.existsSync(path.join(getProjectRoot(), "src", "routes"));
117
+ }
118
+
119
+ export function checkModelsExist() {
120
+ const modelsDir = path.join(getProjectRoot(), "src", "models");
121
+ return fs.existsSync(modelsDir) && fs.readdirSync(modelsDir).length > 0;
122
+ }
@@ -0,0 +1,56 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { getProjectRoot } from "./project.js";
4
+
5
+ export function getRouteFilePath(routeFile) {
6
+ return path.join(getProjectRoot(), "src", "routes", routeFile);
7
+ }
8
+
9
+ export function addImportToRoute(routeFilePath, importStatement) {
10
+ let content = fs.readFileSync(routeFilePath, "utf8");
11
+ if (!content.includes(importStatement.trim())) {
12
+ content = importStatement.trim() + "\n" + content;
13
+ fs.writeFileSync(routeFilePath, content, "utf8");
14
+ }
15
+ return content;
16
+ }
17
+
18
+ export function insertCodeIntoRoute(routeFilePath, codeBlock) {
19
+ let content = fs.readFileSync(routeFilePath, "utf8");
20
+
21
+ // Prefer inserting before "export default router;" as it's more reliable
22
+ const exportIndex = content.lastIndexOf("export default router;");
23
+ if (exportIndex !== -1) {
24
+ content =
25
+ content.slice(0, exportIndex) +
26
+ codeBlock.trim() +
27
+ "\n\n" +
28
+ content.slice(exportIndex);
29
+ } else {
30
+ // Fallback to inserting before last "});"
31
+ const lastClose = content.lastIndexOf("});");
32
+ if (lastClose !== -1) {
33
+ content =
34
+ content.slice(0, lastClose) +
35
+ codeBlock.trim() +
36
+ "\n" +
37
+ content.slice(lastClose);
38
+ } else {
39
+ content += "\n" + codeBlock.trim() + "\n";
40
+ }
41
+ }
42
+
43
+ fs.writeFileSync(routeFilePath, content, "utf8");
44
+ }
45
+
46
+ export function detectHttpMethod(routeFilePath) {
47
+ const content = fs.readFileSync(routeFilePath, "utf8");
48
+ const methodRegex =
49
+ /router\.(get|post|put|patch|delete)\((['"`])\/.*?\2,\s*async\s*\(req,\s*res/;
50
+ const match = content.match(methodRegex);
51
+ return match ? match[1].toLowerCase() : "post";
52
+ }
53
+
54
+ export function getRequestDataSource(method) {
55
+ return method === "get" || method === "delete" ? "req.query" : "req.body";
56
+ }
package/lib/ui.js ADDED
@@ -0,0 +1,161 @@
1
+ import chalk from "chalk";
2
+ import gradient from "gradient-string";
3
+ import ora from "ora";
4
+
5
+ // Brand gradients
6
+ const brand = gradient(["#00c6ff", "#0072ff", "#7b2ff7"]);
7
+ const warm = gradient(["#f7971e", "#ffd200"]);
8
+ const cool = gradient(["#00f260", "#0575e6"]);
9
+ const fire = gradient(["#ff416c", "#ff4b2b"]);
10
+
11
+ // Colors
12
+ const dim = chalk.gray;
13
+ const accent = chalk.hex("#00c6ff");
14
+ const highlight = chalk.hex("#ffd200").bold;
15
+ const success = chalk.hex("#00f260").bold;
16
+ const warn = chalk.hex("#ffd200");
17
+ const err = chalk.hex("#ff416c").bold;
18
+ const info = chalk.hex("#0575e6");
19
+ const muted = chalk.hex("#888888");
20
+ const white = chalk.white;
21
+ const bold = chalk.white.bold;
22
+
23
+ const line = dim(" ─────────────────────────────────────────────────────");
24
+
25
+ const banner = `
26
+ ██╗ ██╗██████╗ ███╗ ██╗ ██████╗ ██████╗ ███████╗
27
+ ██║ ██║██╔══██╗████╗ ██║██╔═══██╗██╔══██╗██╔════╝
28
+ ███████║██████╔╝██╔██╗ ██║██║ ██║██║ ██║█████╗
29
+ ╚════██║██╔══██╗██║╚██╗██║██║ ██║██║ ██║██╔══╝
30
+ ██║██████╔╝██║ ╚████║╚██████╔╝██████╔╝███████╗
31
+ ╚═╝╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚══════╝`;
32
+
33
+ // ── Banner ──────────────────────────────────────────
34
+
35
+ export function showBanner() {
36
+ console.log(brand(banner));
37
+ console.log(line);
38
+ console.log(
39
+ dim(" ") + bold("4Brains Technologies") + dim(" | Node.js Project Generator")
40
+ );
41
+ console.log(line + "\n");
42
+ }
43
+
44
+ // ── Section Header ──────────────────────────────────
45
+
46
+ export function showHeader(command, description) {
47
+ console.log();
48
+ console.log(dim(" ┌─────────────────────────────────────────────────┐"));
49
+ console.log(
50
+ dim(" │ ") + accent("◆ ") + highlight(command) + dim(" ") + white(description) + dim(" │")
51
+ );
52
+ console.log(dim(" └─────────────────────────────────────────────────┘"));
53
+ console.log();
54
+ }
55
+
56
+ // ── Spinners ────────────────────────────────────────
57
+
58
+ export function createSpinner(text) {
59
+ return ora({
60
+ text: muted(text),
61
+ spinner: "dots12",
62
+ color: "cyan",
63
+ indent: 2,
64
+ });
65
+ }
66
+
67
+ // ── Step indicators ─────────────────────────────────
68
+
69
+ export function showStep(step, total, text) {
70
+ const progress = accent(`[${step}/${total}]`);
71
+ console.log(` ${progress} ${white(text)}`);
72
+ }
73
+
74
+ // ── Success ─────────────────────────────────────────
75
+
76
+ export function showSuccess(lines) {
77
+ console.log();
78
+ console.log(dim(" ┌─────────────────────────────────────────────────┐"));
79
+ console.log(dim(" │") + success(" ✔ Done!") + dim(new Array(42).join(" ") + "│"));
80
+ console.log(dim(" └─────────────────────────────────────────────────┘"));
81
+ console.log();
82
+ for (const l of lines) {
83
+ console.log(dim(" │ ") + white(l));
84
+ }
85
+ console.log();
86
+ }
87
+
88
+ // ── Task complete (for add-* commands) ──────────────
89
+
90
+ export function showTaskDone(title, details = []) {
91
+ console.log();
92
+ console.log(` ${success("✔")} ${cool(title)}`);
93
+ for (const d of details) {
94
+ console.log(` ${dim("→")} ${muted(d)}`);
95
+ }
96
+ console.log();
97
+ }
98
+
99
+ // ── Warning ─────────────────────────────────────────
100
+
101
+ export function showWarning(message) {
102
+ console.log(`\n ${warn("⚠")} ${warn(message)}\n`);
103
+ }
104
+
105
+ // ── Error ───────────────────────────────────────────
106
+
107
+ export function showError(message) {
108
+ console.log(`\n ${err("✖")} ${err(message)}\n`);
109
+ }
110
+
111
+ // ── Info ────────────────────────────────────────────
112
+
113
+ export function showInfo(message) {
114
+ console.log(` ${info("ℹ")} ${muted(message)}`);
115
+ }
116
+
117
+ // ── Next Steps ──────────────────────────────────────
118
+
119
+ export function showNextSteps(steps) {
120
+ console.log(accent.bold(" Next steps:\n"));
121
+ for (let i = 0; i < steps.length; i++) {
122
+ const num = highlight(` ${i + 1} `);
123
+ console.log(` ${num} ${white(steps[i])}`);
124
+ }
125
+ console.log();
126
+ console.log(line);
127
+ console.log(dim(" Happy coding! ") + brand("★ 4Brains"));
128
+ console.log(line + "\n");
129
+ }
130
+
131
+ // ── Installing deps (with spinner) ──────────────────
132
+
133
+ export async function installDeps(packages, opts = {}) {
134
+ const { execSync } = await import("child_process");
135
+ const names = Array.isArray(packages) ? packages : [packages];
136
+ const label = names.join(", ");
137
+ const spinner = createSpinner(`Installing ${label}...`);
138
+ spinner.start();
139
+ try {
140
+ execSync(`npm install ${names.join(" ")}`, {
141
+ stdio: "pipe",
142
+ ...opts,
143
+ });
144
+ spinner.succeed(success(`Installed ${label}`));
145
+ } catch (e) {
146
+ spinner.fail(err(`Failed to install ${label}`));
147
+ throw e;
148
+ }
149
+ }
150
+
151
+ // ── File action log ─────────────────────────────────
152
+
153
+ export function showFileAction(action, filePath) {
154
+ const icons = {
155
+ created: chalk.hex("#00f260")("+ "),
156
+ updated: chalk.hex("#0575e6")("~ "),
157
+ deleted: chalk.hex("#ff416c")("- "),
158
+ };
159
+ const icon = icons[action] || dim(" ");
160
+ console.log(` ${icon}${muted(filePath)}`);
161
+ }
package/package.json CHANGED
@@ -1,28 +1,18 @@
1
1
  {
2
2
  "name": "4bnode",
3
- "version": "3.5.2",
4
- "description": "A CLI tool to generate a Node.js app by 4Brains Technologies",
3
+ "version": "4.0.0",
4
+ "description": "A professional tool to generate a Node.js app by 4Brains Technologies",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "bin": {
8
- "4bnode": "index.js",
9
- "add-api": "add-api.js",
10
- "add-mongo": "add-mongo.js",
11
- "add-socket": "add-socket.js",
12
- "add-websocket": "add-websocket.js",
13
- "add-mongo-schema": "add-mongo-schema.js",
14
- "add-login": "add-login.js",
15
- "add-mongo-insert": "add-mongo-insert.js",
16
- "add-mongo-update": "add-mongo-update.js",
17
- "add-mongo-read": "add-mongo-read.js",
18
- "add-mongo-delete": "add-mongo-delete.js"
8
+ "4bnode": "index.js"
19
9
  },
20
10
  "scripts": {
21
11
  "test": "echo \"Error: no test specified\" && exit 1"
22
12
  },
23
13
  "repository": {
24
14
  "type": "git",
25
- "url": "https://github.com/pawan-4brains/4brains-node-generator"
15
+ "url": "https://github.com/pawan-4brains/4bnode"
26
16
  },
27
17
  "keywords": [
28
18
  "node",
@@ -33,8 +23,11 @@
33
23
  "Node.js skeleton"
34
24
  ],
35
25
  "author": "4Brains",
36
- "license": "ISC",
26
+ "license": "Proprietary",
37
27
  "dependencies": {
38
- "chalk": "^5.3.0"
28
+ "@inquirer/prompts": "^8.3.0",
29
+ "chalk": "^5.4.1",
30
+ "gradient-string": "^3.0.0",
31
+ "ora": "^9.3.0"
39
32
  }
40
33
  }