Skip to content

Commit a0104a5

Browse files
feat: refactor CLI tool to orchestrate project setup with modular tasks
1 parent c6a2add commit a0104a5

13 files changed

+226
-173
lines changed

bin/index.js

Lines changed: 2 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -1,175 +1,4 @@
11
#!/usr/bin/env node
2-
import { createInterface } from "readline";
3-
import { execSync } from "child_process";
4-
import fs from "fs";
5-
import path from "path";
6-
import { fileURLToPath } from "url";
2+
import { orchestrateSetup } from "../src/orchestrator.js";
73

8-
const __filename = fileURLToPath(import.meta.url);
9-
const __dirname = path.dirname(__filename);
10-
11-
const rl = createInterface({
12-
input: process.stdin,
13-
output: process.stdout,
14-
});
15-
16-
const ask = (q) => new Promise((res) => rl.question(q, res));
17-
18-
async function main() {
19-
console.log("🚀 Welcome to create-node-backend!");
20-
21-
const projectName = await ask("Project name: ");
22-
const useAuth = await ask("Include auth? (y/n): ");
23-
const useMulter = await ask("Include Multer (file uploads)? (y/n): ");
24-
25-
rl.close();
26-
27-
const projectPath = path.join(process.cwd(), projectName);
28-
if (fs.existsSync(projectPath)) {
29-
console.error(`❌ Project folder already exists. Choose a different name.`);
30-
process.exit(1);
31-
}
32-
fs.mkdirSync(projectPath);
33-
34-
process.chdir(projectPath);
35-
36-
// Step 1: Initialize npm and Install Dependencies
37-
console.log("📦 Initializing npm...");
38-
execSync("npm init -y", { stdio: "inherit" });
39-
40-
// Step 2: Install Dependencies
41-
console.log("📥 Installing required packages...");
42-
execSync("npm install express dotenv", { stdio: "inherit" });
43-
44-
if (useAuth.toLowerCase() === "y") {
45-
console.log("🔐 Adding auth dependencies...");
46-
execSync("npm install argon2 jsonwebtoken", { stdio: "inherit" });
47-
}
48-
49-
if (useMulter.toLowerCase() === "y") {
50-
console.log("📂 Adding Multer...");
51-
execSync("npm install multer", { stdio: "inherit" });
52-
}
53-
54-
console.log("⚙️ Installing dev dependencies...");
55-
execSync("npm install --save-dev nodemon", { stdio: "inherit" });
56-
57-
// Step 3: Modify package.json for ES Modules
58-
const packageJsonPath = path.join(projectPath, "package.json");
59-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
60-
packageJson.type = "module"; // Set the project to use ES Modules
61-
62-
packageJson.scripts = {
63-
...packageJson.scripts,
64-
dev: "nodemon src/index.js", // Add this line for dev script
65-
};
66-
67-
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
68-
69-
// Step 4: Setup Folder Structure
70-
console.log("📁 Creating project structure...");
71-
72-
// Create necessary directories
73-
fs.mkdirSync(path.join(projectPath, "src"));
74-
fs.mkdirSync(path.join(projectPath, "src", "controllers"));
75-
fs.mkdirSync(path.join(projectPath, "src", "models"));
76-
fs.mkdirSync(path.join(projectPath, "src", "routes"));
77-
fs.mkdirSync(path.join(projectPath, "src", "middlewares"));
78-
fs.mkdirSync(path.join(projectPath, "src", "config"));
79-
80-
// Step 5: Write environment configuration
81-
const envContent = `
82-
PORT=3000
83-
JWT_SECRET=your_jwt_secret
84-
`;
85-
86-
fs.writeFileSync(path.join(projectPath, ".env"), envContent.trim());
87-
88-
// Step 6: Write basic project files
89-
90-
// src/index.js (updated to reflect src folder)
91-
const indexContent = `
92-
import express from 'express';
93-
import dotenv from 'dotenv';
94-
import routes from './routes/userRoutes.js';
95-
96-
dotenv.config();
97-
98-
const app = express();
99-
app.use(express.json());
100-
101-
// Basic route for health check
102-
app.get('/', (req, res) => res.send('Hello from ${projectName} backend!'));
103-
104-
// Use Routes
105-
app.use('/api', routes);
106-
107-
const port = process.env.PORT || 3000;
108-
app.listen(port, () => console.log('🚀 Server running on ' + 'http://localhost:' + port));
109-
`;
110-
111-
fs.writeFileSync(
112-
path.join(projectPath, "src", "index.js"),
113-
indexContent.trim()
114-
);
115-
116-
// Create controllers, routes, and models for an example user API
117-
118-
const userController = `
119-
export const getUsers = (req, res) => {
120-
res.json({ message: "List of users" });
121-
};
122-
`;
123-
124-
fs.writeFileSync(
125-
path.join(projectPath, "src", "controllers", "userController.js"),
126-
userController.trim()
127-
);
128-
129-
const userRoutes = `
130-
import express from 'express';
131-
import { getUsers } from '../controllers/userController.js';
132-
133-
const router = express.Router();
134-
135-
router.get('/users', getUsers);
136-
137-
export default router;
138-
`;
139-
140-
fs.writeFileSync(
141-
path.join(projectPath, "src", "routes", "userRoutes.js"),
142-
userRoutes.trim()
143-
);
144-
145-
// Step 7: Optionally Add Auth (JWT)
146-
if (useAuth.toLowerCase() === "yes") {
147-
const authMiddleware = `
148-
import jwt from 'jsonwebtoken';
149-
150-
export const authenticate = (req, res, next) => {
151-
const token = req.header('x-auth-token');
152-
if (!token) {
153-
return res.status(401).json({ msg: 'No token, authorization denied' });
154-
}
155-
try {
156-
const decoded = jwt.verify(token, process.env.JWT_SECRET);
157-
req.user = decoded.user;
158-
next();
159-
} catch (err) {
160-
res.status(401).json({ msg: 'Token is not valid' });
161-
}
162-
};
163-
`;
164-
165-
fs.writeFileSync(
166-
path.join(projectPath, "src", "middlewares", "authMiddleware.js"),
167-
authMiddleware.trim()
168-
);
169-
}
170-
171-
console.log(`👉 Project "${projectName}" created!`);
172-
console.log(`👉 Run: cd ${projectName} && npm run dev`);
173-
}
174-
175-
main();
4+
orchestrateSetup();

src/orchestrator.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { askQuestion } from "./utils/askQuestion.js";
2+
import { setupNpm } from "./tasks/setupNpm.js";
3+
import { installDependencies } from "./tasks/installDependencies.js";
4+
import { setupFolderStructure } from "./tasks/setupFolderStructure.js";
5+
import { setupEnv } from "./tasks/setupEnv.js";
6+
import { addAuth } from "./tasks/addAuth.js";
7+
import { addMulter } from "./tasks/addMulter.js";
8+
import { configureScripts } from "./tasks/configureScripts.js";
9+
import { configurePackageJson } from './tasks/configurePackageJson.js';
10+
11+
export async function orchestrateSetup() {
12+
console.log("🚀 Welcome to create-node-backend!");
13+
14+
const projectName = await askQuestion("Project name: ");
15+
const useAuth = await askQuestion("Include auth? (y/n): ");
16+
const useMulter = await askQuestion("Include Multer (file uploads)? (y/n): ");
17+
18+
await setupNpm(projectName);
19+
await installDependencies(useAuth, useMulter);
20+
await setupFolderStructure(projectName);
21+
await setupEnv(projectName);
22+
if (useAuth.toLowerCase() === "y") await addAuth(projectName);
23+
if (useMulter.toLowerCase() === "y") await addMulter(projectName);
24+
await configureScripts(projectName);
25+
await configurePackageJson(projectName);
26+
27+
console.log(`👉 Project "${projectName}" created!`);
28+
console.log(`👉 Run: cd ${projectName} && npm run dev`);
29+
}

src/tasks/addAuth.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import fs from "fs";
2+
import path from "path";
3+
4+
export async function addAuth(projectName) {
5+
const authMiddleware = `
6+
import jwt from 'jsonwebtoken';
7+
8+
export const authenticate = (req, res, next) => {
9+
const token = req.header('x-auth-token');
10+
if (!token) {
11+
return res.status(401).json({ msg: 'No token, authorization denied' });
12+
}
13+
try {
14+
const decoded = jwt.verify(token, process.env.JWT_SECRET);
15+
req.user = decoded.user;
16+
next();
17+
} catch (err) {
18+
res.status(401).json({ msg: 'Token is not valid' });
19+
}
20+
};
21+
`;
22+
fs.writeFileSync(
23+
path.join(process.cwd(), "src", "middlewares", "authMiddleware.js"),
24+
authMiddleware.trim()
25+
);
26+
}

src/tasks/addMulter.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
4+
export async function addMulter(projectName) {
5+
const multerSetup = `
6+
// Multer config will go here if needed in future
7+
// Placeholder file to show Multer integration point
8+
`;
9+
fs.writeFileSync(
10+
path.join(process.cwd(), 'src', 'middlewares', 'multerSetup.js'),
11+
multerSetup.trim()
12+
);
13+
}

src/tasks/configurePackageJson.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import fs from "fs";
2+
import path from "path";
3+
4+
export async function configurePackageJson(projectName) {
5+
const packageJsonPath = path.join(process.cwd(), "package.json");
6+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
7+
8+
// Ensure ESM
9+
packageJson.type = "module";
10+
11+
// Add dev script
12+
packageJson.scripts = {
13+
...packageJson.scripts,
14+
dev: "nodemon src/index.js",
15+
};
16+
17+
// Write back to package.json
18+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
19+
console.log('✅ package.json updated: added "type": "module" and dev script');
20+
}

src/tasks/configureScripts.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import fs from "fs";
2+
import path from "path";
3+
4+
export async function configureScripts(projectName) {
5+
const indexContent = `
6+
import express from 'express';
7+
import dotenv from 'dotenv';
8+
import routes from './routes/userRoutes.js';
9+
10+
dotenv.config();
11+
12+
const app = express();
13+
app.use(express.json());
14+
15+
app.get('/', (req, res) => res.send('Hello from ${projectName} backend!'));
16+
17+
app.use('/api', routes);
18+
19+
const port = process.env.PORT || 3000;
20+
app.listen(port, () => console.log('🚀 Server running on http://localhost:' + port));
21+
`;
22+
23+
fs.writeFileSync(
24+
path.join(process.cwd(), "src", "index.js"),
25+
indexContent.trim()
26+
);
27+
28+
const userController = `
29+
export const getUsers = (req, res) => {
30+
res.json({ message: "List of users" });
31+
};
32+
`;
33+
fs.writeFileSync(
34+
path.join(process.cwd(), "src", "controllers", "userController.js"),
35+
userController.trim()
36+
);
37+
38+
const userRoutes = `
39+
import express from 'express';
40+
import { getUsers } from '../controllers/userController.js';
41+
42+
const router = express.Router();
43+
44+
router.get('/users', getUsers);
45+
46+
export default router;
47+
`;
48+
fs.writeFileSync(
49+
path.join(process.cwd(), "src", "routes", "userRoutes.js"),
50+
userRoutes.trim()
51+
);
52+
}

src/tasks/installDependencies.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { execSync } from "child_process";
2+
3+
export async function installDependencies(useAuth, useMulter) {
4+
console.log("📥 Installing core dependencies...");
5+
execSync("npm install express dotenv", { stdio: "inherit" });
6+
7+
if (useAuth.toLowerCase() === "y") {
8+
console.log("🔐 Installing auth dependencies...");
9+
execSync("npm install argon2 jsonwebtoken", { stdio: "inherit" });
10+
}
11+
12+
if (useMulter.toLowerCase() === "y") {
13+
console.log("📂 Installing Multer...");
14+
execSync("npm install multer", { stdio: "inherit" });
15+
}
16+
17+
console.log("⚙️ Installing dev dependencies...");
18+
execSync("npm install --save-dev nodemon", { stdio: "inherit" });
19+
}

src/tasks/setupEnv.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import fs from "fs";
2+
import path from "path";
3+
4+
export async function setupEnv(projectName) {
5+
const envContent = `
6+
PORT=3000
7+
JWT_SECRET=your_jwt_secret
8+
`;
9+
fs.writeFileSync(path.join(process.cwd(), ".env"), envContent.trim());
10+
}

src/tasks/setupFolderStructure.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import fs from "fs";
2+
import path from "path";
3+
4+
export async function setupFolderStructure(projectName) {
5+
console.log("📁 Creating project folders...");
6+
const base = path.join(process.cwd(), "src");
7+
fs.mkdirSync(base);
8+
fs.mkdirSync(path.join(base, "controllers"));
9+
fs.mkdirSync(path.join(base, "models"));
10+
fs.mkdirSync(path.join(base, "routes"));
11+
fs.mkdirSync(path.join(base, "middlewares"));
12+
fs.mkdirSync(path.join(base, "config"));
13+
}

src/tasks/setupNpm.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { execSync } from "child_process";
2+
import fs from "fs";
3+
import path from "path";
4+
5+
export async function setupNpm(projectName) {
6+
const projectPath = path.join(process.cwd(), projectName);
7+
8+
if (fs.existsSync(projectPath)) {
9+
console.error(`❌ Project folder already exists. Choose a different name.`);
10+
process.exit(1);
11+
}
12+
13+
fs.mkdirSync(projectPath);
14+
process.chdir(projectPath);
15+
16+
console.log("📦 Initializing npm...");
17+
execSync("npm init -y", { stdio: "inherit" });
18+
}

0 commit comments

Comments
 (0)