stack-starter-cli 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 +33 -0
- package/bin/index.js +364 -0
- package/package.json +30 -0
- package/templates/html/index.html +16 -0
- package/templates/html/script.js +13 -0
- package/templates/html/style.css +11 -0
- package/templates/react/App.jsx +21 -0
- package/templates/server/index.js +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# starter-cli
|
|
2
|
+
|
|
3
|
+
A simple CLI tool to quickly create full stack starter projects.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- HTML / CSS / JavaScript starter
|
|
8
|
+
- React.js starter using Vite
|
|
9
|
+
- Node.js backend using Express
|
|
10
|
+
- Automatic starter files and folder structure
|
|
11
|
+
|
|
12
|
+
## Run locally
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install
|
|
16
|
+
node bin/index.js
|
|
17
|
+
|
|
18
|
+
After that:
|
|
19
|
+
|
|
20
|
+
git add .
|
|
21
|
+
git commit -m "Add README"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
shortcuts:
|
|
26
|
+
- stack-starter
|
|
27
|
+
- stack-starter my-app
|
|
28
|
+
- stack-starter my-app --react
|
|
29
|
+
- stack-starter my-app --html
|
|
30
|
+
- stack-starter my-app --react --node
|
|
31
|
+
- stack-starter my-app --html --node
|
|
32
|
+
- stack-starter --help
|
|
33
|
+
- stack-starter --version
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// -----------------------------
|
|
4
|
+
// Required packages
|
|
5
|
+
// -----------------------------
|
|
6
|
+
const inquirer = require("inquirer").default;
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const { execSync } = require("child_process");
|
|
10
|
+
|
|
11
|
+
// -----------------------------
|
|
12
|
+
// starter --help and --version
|
|
13
|
+
// -----------------------------
|
|
14
|
+
const packageJson = require("../package.json");
|
|
15
|
+
|
|
16
|
+
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
17
|
+
console.log(`
|
|
18
|
+
starter - project scaffolding CLI
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
starter
|
|
22
|
+
starter my-app
|
|
23
|
+
starter my-app --react --node
|
|
24
|
+
starter my-app --html
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
--help, -h Show help
|
|
28
|
+
--version, -v Show version
|
|
29
|
+
--react Use React.js (Vite)
|
|
30
|
+
--html Use HTML / CSS / JavaScript
|
|
31
|
+
--node Use Node.js (Express)
|
|
32
|
+
|
|
33
|
+
What it can create:
|
|
34
|
+
• HTML / CSS / JavaScript starter
|
|
35
|
+
• React.js (Vite) starter
|
|
36
|
+
• Node.js (Express) backend
|
|
37
|
+
`);
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
42
|
+
console.log(packageJson.version);
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// -----------------------------
|
|
47
|
+
// CLI flags
|
|
48
|
+
// -----------------------------
|
|
49
|
+
const cliProjectName =
|
|
50
|
+
process.argv[2] && !process.argv[2].startsWith("-")
|
|
51
|
+
? process.argv[2]
|
|
52
|
+
: null;
|
|
53
|
+
|
|
54
|
+
const useReact = process.argv.includes("--react");
|
|
55
|
+
const useHtml = process.argv.includes("--html");
|
|
56
|
+
const useNode = process.argv.includes("--node");
|
|
57
|
+
|
|
58
|
+
// -----------------------------
|
|
59
|
+
// Resolve CLI-selected frontend/backend
|
|
60
|
+
// -----------------------------
|
|
61
|
+
let cliFrontend = null;
|
|
62
|
+
let cliBackend = null;
|
|
63
|
+
|
|
64
|
+
if (useReact) cliFrontend = "React.js (Vite)";
|
|
65
|
+
if (useHtml) cliFrontend = "HTML / CSS / JavaScript";
|
|
66
|
+
if (useNode) cliBackend = "Node.js (Express)";
|
|
67
|
+
|
|
68
|
+
// -----------------------------
|
|
69
|
+
// Helper: copy template files
|
|
70
|
+
// -----------------------------
|
|
71
|
+
function copyTemplate(source, destination) {
|
|
72
|
+
const content = fs.readFileSync(source, "utf-8");
|
|
73
|
+
fs.writeFileSync(destination, content);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// -----------------------------
|
|
77
|
+
// Ask user for project details
|
|
78
|
+
// -----------------------------
|
|
79
|
+
inquirer
|
|
80
|
+
.prompt([
|
|
81
|
+
{
|
|
82
|
+
name: "projectName",
|
|
83
|
+
message: "Enter your project name:",
|
|
84
|
+
default: cliProjectName || undefined,
|
|
85
|
+
when: !cliProjectName,
|
|
86
|
+
validate(input) {
|
|
87
|
+
const projectName = cliProjectName || input;
|
|
88
|
+
|
|
89
|
+
if (!projectName.trim()) {
|
|
90
|
+
return "Project name cannot be empty.";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(projectName)) {
|
|
94
|
+
return "Use only letters, numbers, hyphen (-), or underscore (_).";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return true;
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "frontend",
|
|
102
|
+
message: "Choose frontend:",
|
|
103
|
+
type: "rawlist",
|
|
104
|
+
choices: ["HTML / CSS / JavaScript", "React.js (Vite)", "None"],
|
|
105
|
+
default: 2,
|
|
106
|
+
when: !cliFrontend,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "backend",
|
|
110
|
+
message: "Choose backend:",
|
|
111
|
+
type: "rawlist",
|
|
112
|
+
choices: ["Node.js (Express)", "None"],
|
|
113
|
+
default: 1,
|
|
114
|
+
when: !cliBackend,
|
|
115
|
+
},
|
|
116
|
+
])
|
|
117
|
+
.then((answers) => {
|
|
118
|
+
if (cliProjectName) {
|
|
119
|
+
answers.projectName = cliProjectName;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (cliFrontend) {
|
|
123
|
+
answers.frontend = cliFrontend;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (cliBackend) {
|
|
127
|
+
answers.backend = cliBackend;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!answers.frontend) {
|
|
131
|
+
answers.frontend = "None";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!answers.backend) {
|
|
135
|
+
answers.backend = "None";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const projectPath = path.join(process.cwd(), answers.projectName);
|
|
139
|
+
|
|
140
|
+
// -----------------------------
|
|
141
|
+
// Prevent duplicate folder names
|
|
142
|
+
// -----------------------------
|
|
143
|
+
if (fs.existsSync(projectPath)) {
|
|
144
|
+
console.log("\nA project with this name already exists.");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// -----------------------------
|
|
149
|
+
// Create root project folder
|
|
150
|
+
// -----------------------------
|
|
151
|
+
fs.mkdirSync(projectPath);
|
|
152
|
+
|
|
153
|
+
// -----------------------------
|
|
154
|
+
// Base project files
|
|
155
|
+
// -----------------------------
|
|
156
|
+
fs.writeFileSync(
|
|
157
|
+
path.join(projectPath, ".gitignore"),
|
|
158
|
+
"node_modules\n.env\ndist\n.vite\n.DS_Store\ncoverage"
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
fs.writeFileSync(
|
|
162
|
+
path.join(projectPath, "README.md"),
|
|
163
|
+
`# ${answers.projectName}`
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// =====================================================
|
|
167
|
+
// BACKEND SETUP
|
|
168
|
+
// =====================================================
|
|
169
|
+
if (answers.backend === "Node.js (Express)") {
|
|
170
|
+
const serverPath = path.join(projectPath, "server");
|
|
171
|
+
|
|
172
|
+
fs.mkdirSync(serverPath);
|
|
173
|
+
|
|
174
|
+
console.log("\nSetting up backend...");
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
execSync("npm init -y", {
|
|
178
|
+
cwd: serverPath,
|
|
179
|
+
stdio: "inherit",
|
|
180
|
+
shell: process.env.ComSpec,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const pkgPath = path.join(serverPath, "package.json");
|
|
184
|
+
|
|
185
|
+
if (fs.existsSync(pkgPath)) {
|
|
186
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
187
|
+
|
|
188
|
+
pkg.scripts = {
|
|
189
|
+
start: "node index.js",
|
|
190
|
+
dev: "nodemon index.js",
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
194
|
+
|
|
195
|
+
console.log("✅ Scripts added to package.json");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
execSync("npm install express cors dotenv", {
|
|
199
|
+
cwd: serverPath,
|
|
200
|
+
stdio: "inherit",
|
|
201
|
+
shell: process.env.ComSpec,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
execSync("npm install -D nodemon", {
|
|
205
|
+
cwd: serverPath,
|
|
206
|
+
stdio: "inherit",
|
|
207
|
+
shell: process.env.ComSpec,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
copyTemplate(
|
|
211
|
+
path.join(__dirname, "..", "templates", "server", "index.js"),
|
|
212
|
+
path.join(serverPath, "index.js")
|
|
213
|
+
);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
console.log("\n❌ Backend setup failed.");
|
|
216
|
+
console.log("Check npm installation or internet connection.");
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// =====================================================
|
|
222
|
+
// FRONTEND SETUP - HTML / CSS / JS
|
|
223
|
+
// =====================================================
|
|
224
|
+
if (answers.frontend === "HTML / CSS / JavaScript") {
|
|
225
|
+
const clientPath = path.join(projectPath, "client");
|
|
226
|
+
|
|
227
|
+
fs.mkdirSync(clientPath);
|
|
228
|
+
|
|
229
|
+
console.log("\nSetting up HTML / CSS / JavaScript frontend...");
|
|
230
|
+
|
|
231
|
+
fs.writeFileSync(
|
|
232
|
+
path.join(clientPath, "index.html"),
|
|
233
|
+
`<!DOCTYPE html>
|
|
234
|
+
<html lang="en">
|
|
235
|
+
<head>
|
|
236
|
+
<meta charset="UTF-8">
|
|
237
|
+
<title>${answers.projectName}</title>
|
|
238
|
+
<link rel="stylesheet" href="style.css">
|
|
239
|
+
</head>
|
|
240
|
+
<body>
|
|
241
|
+
<h1>Frontend Status: <span id="status">Loading...</span></h1>
|
|
242
|
+
|
|
243
|
+
<div id="response-box" style="padding: 20px; border: 1px solid #ccc; margin-top: 20px;">
|
|
244
|
+
Backend Message: <strong id="message">Please wait...</strong>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<script src="script.js"></script>
|
|
248
|
+
</body>
|
|
249
|
+
</html>`
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
fs.writeFileSync(
|
|
253
|
+
path.join(clientPath, "style.css"),
|
|
254
|
+
`body {
|
|
255
|
+
font-family: Arial, sans-serif;
|
|
256
|
+
padding: 40px;
|
|
257
|
+
text-align: center;
|
|
258
|
+
background: #f5f5f5;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
#response-box {
|
|
262
|
+
background: white;
|
|
263
|
+
display: inline-block;
|
|
264
|
+
padding: 20px;
|
|
265
|
+
border-radius: 8px;
|
|
266
|
+
}`
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
if (answers.backend === "Node.js (Express)") {
|
|
270
|
+
copyTemplate(
|
|
271
|
+
path.join(__dirname, "..", "templates", "html", "script.js"),
|
|
272
|
+
path.join(clientPath, "script.js")
|
|
273
|
+
);
|
|
274
|
+
} else {
|
|
275
|
+
fs.writeFileSync(
|
|
276
|
+
path.join(clientPath, "script.js"),
|
|
277
|
+
`const statusEl = document.getElementById("status");
|
|
278
|
+
const messageEl = document.getElementById("message");
|
|
279
|
+
|
|
280
|
+
statusEl.textContent = "Standalone";
|
|
281
|
+
statusEl.style.color = "green";
|
|
282
|
+
|
|
283
|
+
messageEl.textContent = "No backend selected. Frontend is ready.";`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// =====================================================
|
|
289
|
+
// FRONTEND SETUP - REACT
|
|
290
|
+
// =====================================================
|
|
291
|
+
else if (answers.frontend === "React.js (Vite)") {
|
|
292
|
+
const clientPath = path.join(projectPath, "client");
|
|
293
|
+
|
|
294
|
+
console.log("\nScaffolding React project...");
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
execSync("npm create vite@latest client --yes -- --template react", {
|
|
298
|
+
cwd: projectPath,
|
|
299
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
300
|
+
shell: process.env.ComSpec,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
console.log("Installing React dependencies...");
|
|
304
|
+
|
|
305
|
+
execSync("npm install", {
|
|
306
|
+
cwd: clientPath,
|
|
307
|
+
stdio: "inherit",
|
|
308
|
+
shell: process.env.ComSpec,
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
console.log("✅ React installation complete!");
|
|
312
|
+
|
|
313
|
+
if (answers.backend === "Node.js (Express)") {
|
|
314
|
+
copyTemplate(
|
|
315
|
+
path.join(__dirname, "..", "templates", "react", "App.jsx"),
|
|
316
|
+
path.join(clientPath, "src", "App.jsx")
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
} catch (error) {
|
|
320
|
+
console.log("\n❌ React setup failed.");
|
|
321
|
+
console.log("Check npm installation or internet connection.");
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// =====================================================
|
|
327
|
+
// FINAL INSTRUCTIONS
|
|
328
|
+
// =====================================================
|
|
329
|
+
console.log(`\n✅ Project "${answers.projectName}" created successfully!`);
|
|
330
|
+
|
|
331
|
+
if (
|
|
332
|
+
answers.frontend === "HTML / CSS / JavaScript" &&
|
|
333
|
+
answers.backend === "Node.js (Express)"
|
|
334
|
+
) {
|
|
335
|
+
console.log("\nRun backend:");
|
|
336
|
+
console.log(`cd ${answers.projectName}/server`);
|
|
337
|
+
console.log("npm run dev");
|
|
338
|
+
|
|
339
|
+
console.log("\nThen open frontend:");
|
|
340
|
+
console.log(`${answers.projectName}/client/index.html`);
|
|
341
|
+
} else if (
|
|
342
|
+
answers.frontend === "React.js (Vite)" &&
|
|
343
|
+
answers.backend === "Node.js (Express)"
|
|
344
|
+
) {
|
|
345
|
+
console.log("\nRun backend:");
|
|
346
|
+
console.log(`cd ${answers.projectName}/server`);
|
|
347
|
+
console.log("npm run dev");
|
|
348
|
+
|
|
349
|
+
console.log("\nOpen another terminal and run frontend:");
|
|
350
|
+
console.log(`cd ${answers.projectName}/client`);
|
|
351
|
+
console.log("npm run dev");
|
|
352
|
+
} else if (answers.frontend === "HTML / CSS / JavaScript") {
|
|
353
|
+
console.log("\nOpen frontend:");
|
|
354
|
+
console.log(`${answers.projectName}/client/index.html`);
|
|
355
|
+
} else if (answers.frontend === "React.js (Vite)") {
|
|
356
|
+
console.log("\nRun frontend:");
|
|
357
|
+
console.log(`cd ${answers.projectName}/client`);
|
|
358
|
+
console.log("npm run dev");
|
|
359
|
+
} else if (answers.backend === "Node.js (Express)") {
|
|
360
|
+
console.log("\nRun backend:");
|
|
361
|
+
console.log(`cd ${answers.projectName}/server`);
|
|
362
|
+
console.log("npm run dev");
|
|
363
|
+
}
|
|
364
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stack-starter-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI to generate frontend and backend starter projects",
|
|
5
|
+
"main": "bin/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"stack-starter": "./bin/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"templates"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "node bin/index.js"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"cli",
|
|
18
|
+
"starter",
|
|
19
|
+
"scaffold",
|
|
20
|
+
"node",
|
|
21
|
+
"react",
|
|
22
|
+
"express"
|
|
23
|
+
],
|
|
24
|
+
"author": "SACHITH KASA",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"type": "commonjs",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"inquirer": "^13.4.2"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Starter Project</title>
|
|
6
|
+
<link rel="stylesheet" href="style.css">
|
|
7
|
+
<script src="script.js" defer></script>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<h1>Frontend Status: <span id="status">Connecting...</span></h1>
|
|
11
|
+
<div id="response-box">
|
|
12
|
+
Backend Message: <strong id="message">Waiting for server...</strong>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const messageEl = document.getElementById("message");
|
|
2
|
+
const statusEl = document.getElementById("status");
|
|
3
|
+
|
|
4
|
+
fetch("http://localhost:5000/")
|
|
5
|
+
.then((res) => res.text())
|
|
6
|
+
.then((data) => {
|
|
7
|
+
statusEl.textContent = "Online";
|
|
8
|
+
messageEl.textContent = data;
|
|
9
|
+
})
|
|
10
|
+
.catch(() => {
|
|
11
|
+
statusEl.textContent = "Offline";
|
|
12
|
+
messageEl.textContent = "Could not connect to backend";
|
|
13
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
function App() {
|
|
4
|
+
const [message, setMessage] = useState("Connecting to backend...");
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
fetch("http://localhost:5000/")
|
|
8
|
+
.then((res) => res.text())
|
|
9
|
+
.then((data) => setMessage(data))
|
|
10
|
+
.catch(() => setMessage("Could not connect to backend"));
|
|
11
|
+
}, []);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div style={{ padding: "40px", fontFamily: "Arial" }}>
|
|
15
|
+
<h1>React Frontend Connected</h1>
|
|
16
|
+
<p>{message}</p>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default App;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const cors = require("cors");
|
|
3
|
+
require("dotenv").config();
|
|
4
|
+
|
|
5
|
+
const app = express();
|
|
6
|
+
|
|
7
|
+
app.use(cors());
|
|
8
|
+
app.use(express.json());
|
|
9
|
+
|
|
10
|
+
app.get("/", (req, res) => {
|
|
11
|
+
res.send("Server is running and connected to Frontend!");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const PORT = process.env.PORT || 5000;
|
|
15
|
+
|
|
16
|
+
app.listen(PORT, () => {
|
|
17
|
+
console.log(`Server running on port ${PORT}`);
|
|
18
|
+
});
|