neatnode 1.0.1 → 3.0.1

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 (62) hide show
  1. package/README.md +107 -0
  2. package/bin/index.js +1 -1
  3. package/package.json +9 -1
  4. package/src/actions/createProject.js +26 -9
  5. package/src/actions/removeCRUD.js +49 -0
  6. package/src/cli.js +48 -8
  7. package/src/utils/copyTemplate.js +19 -10
  8. package/templates/express-basic/.env.example +3 -0
  9. package/templates/express-basic/package-lock.json +1505 -0
  10. package/templates/express-basic/package.json +21 -0
  11. package/templates/express-basic/server.js +11 -0
  12. package/templates/express-basic/src/app.js +45 -0
  13. package/templates/express-basic/src/config/db.config.js +12 -0
  14. package/templates/express-basic/src/config/env.config.js +11 -0
  15. package/templates/express-basic/src/controllers/todo.controller.js +90 -0
  16. package/templates/express-basic/src/middleware/notFound.middleware.js +5 -0
  17. package/templates/express-basic/src/models/todo.model.js +21 -0
  18. package/templates/express-basic/src/routes/index.route.js +10 -0
  19. package/templates/express-basic/src/routes/todo.route.js +15 -0
  20. package/templates/express-basic/src/utils/responseHandler.js +7 -0
  21. package/templates/express-rest-api/.env.example +7 -0
  22. package/templates/express-rest-api/package-lock.json +1977 -0
  23. package/templates/express-rest-api/package.json +30 -0
  24. package/templates/express-rest-api/server.js +14 -0
  25. package/templates/express-rest-api/src/app.js +55 -0
  26. package/templates/express-rest-api/src/config/db.config.js +14 -0
  27. package/templates/express-rest-api/src/config/env.config.js +13 -0
  28. package/templates/express-rest-api/src/config/logger.config.js +20 -0
  29. package/templates/express-rest-api/src/controllers/user.controller.js +35 -0
  30. package/templates/express-rest-api/src/middleware/auth.middleware.js +36 -0
  31. package/templates/express-rest-api/src/middleware/error.middleware.js +32 -0
  32. package/templates/express-rest-api/src/middleware/rateLimiter.js +13 -0
  33. package/templates/express-rest-api/src/middleware/validateRequest.middleware.js +15 -0
  34. package/templates/express-rest-api/src/models/user.model.js +31 -0
  35. package/templates/express-rest-api/src/routes/user.route.js +14 -0
  36. package/templates/express-rest-api/src/schemas/user.schema.js +43 -0
  37. package/templates/express-rest-api/src/services/user.service.js +54 -0
  38. package/templates/express-rest-api/src/utils/ApiError.js +17 -0
  39. package/templates/express-rest-api/src/utils/ApiResponse.js +9 -0
  40. package/templates/express-rest-api/src/utils/CatchAsync.js +5 -0
  41. package/templates/express-rest-api/src/utils/Token.js +16 -0
  42. package/templates/express-socket/.env.example +5 -0
  43. package/templates/express-socket/package-lock.json +2262 -0
  44. package/templates/express-socket/package.json +31 -0
  45. package/templates/express-socket/server.js +19 -0
  46. package/templates/express-socket/src/app.js +33 -0
  47. package/templates/express-socket/src/config/db.config.js +14 -0
  48. package/templates/express-socket/src/config/env.config.js +10 -0
  49. package/templates/express-socket/src/config/logger.js +20 -0
  50. package/templates/express-socket/src/config/socket.js +23 -0
  51. package/templates/express-socket/src/middleware/auth.middleware.js +36 -0
  52. package/templates/express-socket/src/middleware/error.middleware.js +31 -0
  53. package/templates/express-socket/src/middleware/rateLimiter.js +14 -0
  54. package/templates/express-socket/src/middleware/validateRequest.middleware.js +15 -0
  55. package/templates/express-socket/src/utils/ApiError.js +17 -0
  56. package/templates/express-socket/src/utils/ApiResponse.js +9 -0
  57. package/templates/express-socket/src/utils/CatchAsync.js +5 -0
  58. package/templates/express-socket/src/utils/Token.js +16 -0
  59. package/templates/express-basic/.env +0 -0
  60. package/templates/express-basic/app.js +0 -5
  61. package/templates/express-rest-api/index.js +0 -5
  62. package/templates/express-socket/index.js +0 -5
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # NeatNode
2
+
3
+ **NeatNode** is a plug-and-play CLI that scaffolds clean, production-ready Node.js backends in seconds.
4
+ It comes with pre-built templates, optional CRUD modules, and a modern developer workflow.
5
+
6
+ ---
7
+
8
+ ## šŸš€ Features
9
+
10
+ * Multiple templates: **Basic API**, **REST API**, and **Socket API**
11
+ * Clean MVC folder structure
12
+ * Optional CRUD scaffolding (User or Todo)
13
+ * Integrated logging (Winston + Morgan)
14
+ * Security middleware (Helmet, Rate Limiter)
15
+ * Dynamic CLI with file removal & template customization
16
+ * Ready for ESM, Redis, and future TypeScript support
17
+
18
+ ---
19
+
20
+ ## 🧭 Installation
21
+
22
+ ```bash
23
+ npm install -g neatnode
24
+ ```
25
+
26
+ or run directly:
27
+
28
+ ```bash
29
+ npx neatnode
30
+ ```
31
+
32
+ ---
33
+
34
+ ## āš™ļø Usage
35
+
36
+ ```bash
37
+ npx neatnode
38
+ ```
39
+
40
+ * Enter your **project name**
41
+ * Choose a **template** (Basic / REST / Socket)
42
+ * Select whether to include **CRUD** examples
43
+ * Install dependencies and start your project:
44
+
45
+ ```bash
46
+ cd my-app
47
+ npm install
48
+ npm run dev
49
+ ```
50
+
51
+ ---
52
+
53
+ ## šŸ“ Templates
54
+
55
+ ### **Basic API**
56
+
57
+ Minimal Express setup with optional Todo CRUD.
58
+ Perfect for small projects or quick prototypes.
59
+
60
+ ### **REST API**
61
+
62
+ Full architecture with controllers, services, models, routes, error handling, validation, and logging.
63
+ Ideal for scalable, production-grade APIs.
64
+
65
+ ### **Socket API**
66
+
67
+ Express + Socket.io integration with logging and CORS setup.
68
+ Best for chat apps or real-time systems.
69
+
70
+ ---
71
+
72
+ ## 🧩 Example Structure
73
+
74
+ ```
75
+ src/
76
+ ā”œā”€ā”€ controllers/
77
+ ā”œā”€ā”€ models/
78
+ ā”œā”€ā”€ routes/
79
+ ā”œā”€ā”€ services/
80
+ ā”œā”€ā”€ utils/
81
+ └── app.js
82
+ ```
83
+
84
+ ---
85
+
86
+ ## šŸ› ļø Built With
87
+
88
+ * Node.js & Express
89
+ * Inquirer (CLI)
90
+ * fs-extra & path (File system automation)
91
+ * Morgan & Winston (Logging)
92
+ * Joi (Validation)
93
+ * Helmet, Rate Limiter (Security)
94
+
95
+ ---
96
+
97
+ ## šŸ§‘ā€šŸ’» Author
98
+
99
+ **Aakash Gupta**
100
+ [GitHub](https://github.com/aakash-gupta02) Ā· [Email](mailto:aakashgupta052004@gmail.com)
101
+
102
+ ---
103
+
104
+ ## 🪪 License
105
+
106
+ MIT Ā© Aakash Gupta
107
+
package/bin/index.js CHANGED
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  import("../src/cli.js");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neatnode",
3
- "version": "1.0.1",
3
+ "version": "3.0.1",
4
4
  "description": "Plug & Play Node.js backend starter templates — build REST APIs, socket servers, and more in seconds.",
5
5
  "bin": {
6
6
  "neatnode": "./bin/index.js"
@@ -15,6 +15,14 @@
15
15
  "template",
16
16
  "neatnode"
17
17
  ],
18
+ "files": [
19
+ "bin/",
20
+ "src/",
21
+ "templates/",
22
+ "utils/",
23
+ "package.json",
24
+ "README.md"
25
+ ],
18
26
  "author": {
19
27
  "name": "Aakash Gupta",
20
28
  "email": "aakashgupta052004@gmail.com",
@@ -1,32 +1,49 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
+ import os from "os";
3
4
  import { fileURLToPath } from "url";
4
5
  import { copyTemplate } from "../utils/copyTemplate.js";
6
+ import { removeCrud, removeCrudReferences } from "./removeCRUD.js";
5
7
 
6
8
  const __filename = fileURLToPath(import.meta.url);
7
9
  const __dirname = path.dirname(__filename);
8
10
 
9
- export async function createProject({ projectName, templatePath }) {
11
+ export async function createProject({ projectName, templatePath, includeCrud, crudName }) {
10
12
  try {
11
- const targetPath = path.join(process.cwd(), projectName);
13
+ // If user wants to create in current dir (.)
14
+ const targetPath = projectName === "." ? process.cwd() : path.join(process.cwd(), projectName);
12
15
 
13
- // Prevent overwriting existing folder
14
- if (fs.existsSync(targetPath)) {
16
+ if (fs.existsSync(targetPath) && projectName !== ".") {
15
17
  console.error(`āŒ Folder "${projectName}" already exists.`);
16
18
  process.exit(1);
17
19
  }
18
20
 
19
- // Copy template
20
- console.log("šŸ“ Creating project folder...");
21
- fs.mkdirSync(targetPath);
22
- await copyTemplate(templatePath, targetPath);
21
+ if (projectName !== ".") {
22
+ console.log("Creating project folder...");
23
+ fs.mkdirSync(targetPath);
24
+ }
25
+
26
+ await copyTemplate(templatePath, targetPath, {
27
+ "project-name": projectName === "." ? path.basename(process.cwd()) : projectName,
28
+ "author": os.userInfo().username || "author",
29
+ });
30
+
31
+ if (!includeCrud) {
32
+ console.log("šŸ—‘ļø Removing CRUD files...");
33
+ removeCrud(targetPath, crudName);
34
+ removeCrudReferences(path.join(targetPath, "src", "app.js"));
35
+ } else {
36
+ console.log("āœ… Including CRUD functionality...");
37
+ }
23
38
 
24
39
  console.log(`āœ… Project "${projectName}" created successfully!`);
25
- console.log(`\ncd ${projectName}`);
40
+ console.log(`\ncd ${projectName === "." ? "" : projectName}`);
26
41
  console.log("npm install");
27
42
  console.log("npm run dev (or npm start)\n");
43
+
28
44
  } catch (err) {
29
45
  console.error("Failed to create project:", err);
30
46
  process.exit(1);
31
47
  }
32
48
  }
49
+
@@ -0,0 +1,49 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+
5
+ export function removeCrud(targetPath, name) {
6
+ try {
7
+ const crudPaths = [
8
+ `src/models/${name}.model.js`,
9
+ `src/controllers/${name}.controller.js`,
10
+ `src/routes/${name}.route.js`,
11
+ `src/services/${name}.service.js`,
12
+ `src/validations/${name}.validation.js`,
13
+ "src/middlewares/auth.middleware.js",
14
+ `src/schemas/${name}.schema.js`
15
+ ];
16
+
17
+ crudPaths.forEach(relPath => {
18
+ const absPath = path.join(targetPath, relPath);
19
+
20
+ if (fs.existsSync(absPath)) {
21
+ fs.rmSync(absPath, { force: true });
22
+ console.log(`āœ” Removed: ${relPath}`);
23
+ } // else {
24
+ // console.log(`Skipped (not found): ${relPath}`);
25
+ // }
26
+ });
27
+ } catch (err) {
28
+ console.error("āŒ Error while removing CRUD files:", err.message);
29
+ }
30
+ }
31
+
32
+
33
+ export function removeCrudReferences(appJsPath) {
34
+ let content = fs.readFileSync(appJsPath, "utf8");
35
+
36
+ // Remove imports block
37
+ content = content.replace(
38
+ /\/\/ ROUTE_IMPORTS_START[\s\S]*?\/\/ ROUTE_IMPORTS_END/,
39
+ ""
40
+ );
41
+
42
+ // Remove route usage block
43
+ content = content.replace(
44
+ /\/\/ ROUTE_USES_START[\s\S]*?\/\/ ROUTE_USES_END/,
45
+ ""
46
+ );
47
+
48
+ fs.writeFileSync(appJsPath, content, "utf8");
49
+ }
package/src/cli.js CHANGED
@@ -1,34 +1,74 @@
1
+ #!/usr/bin/env node
1
2
  import inquirer from "inquirer";
2
3
  import { createProject } from "./actions/createProject.js";
3
4
  import templates from "./config/templates.js";
4
5
 
5
6
  async function main() {
6
- console.log("Welcome to NodeNeat CLI!");
7
+ console.log("\nšŸš€ Welcome to NodeNeat CLI!\n");
7
8
 
8
- const { projectName } = await inquirer.prompt([
9
+ // Step 1: Ask for project name
10
+ const { projectName } = await inquirer.prompt([
9
11
  {
10
12
  name: "projectName",
11
13
  type: "input",
12
14
  message: "Enter your project folder name:",
13
- default: "my-app"
15
+ default: "my-app",
16
+ validate: (input) => input.trim() !== "" || "Project name cannot be empty."
14
17
  }
15
18
  ]);
16
19
 
17
-
20
+ // Step 2: Choose template
18
21
  const { template } = await inquirer.prompt([
19
22
  {
20
23
  name: "template",
21
24
  type: "list",
22
25
  message: "Choose a template:",
23
- choices: templates.map(t => t.name)
26
+ choices: templates.map((t) => t.name)
24
27
  }
25
28
  ]);
26
29
 
27
- const chosen = templates.find(t => t.name === template);
30
+ const chosen = templates.find((t) => t.name === template);
31
+
32
+ // Step 3: Optional prompt if REST API
33
+ let includeCrud = false;
34
+ let crudName = "";
35
+ if (chosen.name === "Basic Express") {
36
+ const answer = await inquirer.prompt([
37
+ {
38
+ name: "includeCrud",
39
+ type: "confirm",
40
+ message: "Include example Todo CRUD setup?",
41
+ default: true
42
+ }
43
+ ]);
44
+ includeCrud = answer.includeCrud;
45
+ crudName = "todo";
46
+ }
47
+
48
+ if (chosen.name === "REST API") {
49
+ const answer = await inquirer.prompt([
50
+ {
51
+ name: "includeCrud",
52
+ type: "confirm",
53
+ message: "Include example User CRUD setup?",
54
+ default: true
55
+ }
56
+ ]);
57
+ includeCrud = answer.includeCrud;
58
+ crudName = "user";
59
+ }
60
+
61
+ // Step 4: Create the project
28
62
  await createProject({
29
63
  projectName,
30
- templatePath: chosen.path
64
+ templatePath: chosen.path,
65
+ includeCrud,
66
+ crudName
31
67
  });
68
+
69
+ console.log(`\nāœ… Project "${projectName}" created successfully using "${chosen.name}" template.\n`);
32
70
  }
33
71
 
34
- main();
72
+ main().catch((err) => {
73
+ console.error("āŒ Error:", err.message);
74
+ });
@@ -1,31 +1,40 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
3
 
4
- export async function copyTemplate(srcDir, destDir) {
5
-
6
- const ignoreList = ["node_modules", ".git", ".env"];
4
+ export async function copyTemplate(srcDir, destDir, replacements = {}, ) {
5
+ const ignoreList = ["node_modules", ".git", ".env", "package-lock.json"];
7
6
 
8
- // make sure destination exists
9
7
  if (!fs.existsSync(destDir)) {
10
8
  fs.mkdirSync(destDir, { recursive: true });
11
9
  }
12
10
 
13
- // read all files/folders in template
14
11
  const items = fs.readdirSync(srcDir, { withFileTypes: true });
15
12
 
16
13
  for (const item of items) {
17
- // skip ignored ones
18
14
  if (ignoreList.includes(item.name)) continue;
19
15
 
20
16
  const srcPath = path.join(srcDir, item.name);
21
17
  const destPath = path.join(destDir, item.name);
22
18
 
23
19
  if (item.isDirectory()) {
24
- // recurse for folders
25
- await copyTemplate(srcPath, destPath);
20
+ // recursively copy folders
21
+ await copyTemplate(srcPath, destPath, replacements);
26
22
  } else {
27
- // copy individual files
28
- fs.copyFileSync(srcPath, destPath);
23
+ // for certain files, replace placeholders
24
+ if (["package.json"].includes(item.name)) {
25
+ let content = fs.readFileSync(srcPath, "utf-8");
26
+
27
+ // replace all {{key}} with provided replacements
28
+ for (const [key, value] of Object.entries(replacements)) {
29
+ const regex = new RegExp(`project-name`, "g");
30
+ content = content.replace(regex, value);
31
+ }
32
+
33
+ fs.writeFileSync(destPath, content, "utf-8");
34
+ } else {
35
+ // just copy normal files
36
+ fs.copyFileSync(srcPath, destPath);
37
+ }
29
38
  }
30
39
  }
31
40
  }
@@ -0,0 +1,3 @@
1
+ PORT=3000
2
+ MONGODB_URI=mongodb://localhost:27017/mydatabase
3
+ CLIENT_URL=http://localhost:5137 # Frontend URL