stack-starter-cli 1.0.0 → 1.1.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 (3) hide show
  1. package/README.md +174 -21
  2. package/bin/index.js +220 -63
  3. package/package.json +20 -7
package/README.md CHANGED
@@ -1,33 +1,186 @@
1
- # starter-cli
1
+ # stack-starter-cli
2
2
 
3
- A simple CLI tool to quickly create full stack starter projects.
3
+ A simple CLI tool to quickly create starter projects for frontend, backend, or full stack development.
4
+
5
+ It helps you create project folders, install dependencies, and generate starter files in a few commands.
6
+
7
+ ---
4
8
 
5
9
  ## Features
6
10
 
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
+ * HTML / CSS / JavaScript starter
12
+ * React starter using Vite
13
+ * Node.js backend using Express
14
+ * Custom backend port support
15
+ * Automatic `.env` file creation
16
+ * Package manager choice: npm, yarn, or pnpm
17
+ * Ready-to-use starter folder structure
18
+
19
+ ---
20
+
21
+ ## Install
22
+
23
+ Install globally from npm:
24
+
25
+ ```bash
26
+ npm install -g stack-starter-cli
27
+ ```
28
+
29
+ After installing, use the command:
30
+
31
+ ```bash
32
+ stack-starter
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Basic Usage
38
+
39
+ ### Interactive mode
40
+
41
+ ```bash
42
+ stack-starter
43
+ ```
44
+
45
+ The CLI will ask for project details step by step.
46
+
47
+ ---
48
+
49
+ ### Create a project with only project name
50
+
51
+ ```bash
52
+ stack-starter my-app
53
+ ```
54
+
55
+ The CLI will ask the remaining questions.
56
+
57
+ ---
58
+
59
+ ## Frontend and Backend Commands
60
+
61
+ ### React + Node.js
62
+
63
+ ```bash
64
+ stack-starter my-app --react --node
65
+ ```
66
+
67
+ ### HTML + Node.js
68
+
69
+ ```bash
70
+ stack-starter my-app --html --node
71
+ ```
72
+
73
+ ### React only
74
+
75
+ ```bash
76
+ stack-starter my-app --react
77
+ ```
78
+
79
+ ### HTML only
80
+
81
+ ```bash
82
+ stack-starter my-app --html
83
+ ```
84
+
85
+ ### Node.js only
11
86
 
12
- ## Run locally
87
+ ```bash
88
+ stack-starter my-app --node
89
+ ```
90
+
91
+ ---
92
+
93
+ ## Version 1.1.0 Features
94
+
95
+ ### Custom backend port
96
+
97
+ ```bash
98
+ stack-starter my-app --react --node --port 8000
99
+ ```
100
+
101
+ If no port is given, the default port is `5000`.
102
+
103
+ If the selected port is invalid or already in use, the CLI shows a clear message.
104
+
105
+ ---
106
+
107
+ ### Package manager selection
108
+
109
+ ### npm
110
+
111
+ ```bash
112
+ stack-starter my-app --react --node --pm npm
113
+ ```
114
+
115
+ ### yarn
116
+
117
+ ```bash
118
+ stack-starter my-app --react --node --pm yarn
119
+ ```
120
+
121
+ ### pnpm
122
+
123
+ ```bash
124
+ stack-starter my-app --react --node --pm pnpm
125
+ ```
126
+
127
+ If the selected package manager is not installed, the CLI will stop and show a message.
128
+
129
+ ---
130
+
131
+ ## Generated Project Structure
132
+
133
+ Example for full stack projects:
134
+
135
+ ```text
136
+ my-app/
137
+ client/
138
+ server/
139
+ .gitignore
140
+ README.md
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Help
146
+
147
+ Show help:
13
148
 
14
149
  ```bash
15
- npm install
16
- node bin/index.js
150
+ stack-starter --help
151
+ ```
17
152
 
18
- After that:
153
+ Show version:
19
154
 
20
- git add .
21
- git commit -m "Add README"
155
+ ```bash
156
+ stack-starter --version
22
157
  ```
23
158
 
159
+ ---
160
+
161
+ ## Example Commands
162
+
24
163
  ```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
164
+ stack-starter
165
+ stack-starter my-app
166
+ stack-starter my-app --react
167
+ stack-starter my-app --html
168
+ stack-starter my-app --react --node
169
+ stack-starter my-app --html --node
170
+ stack-starter my-app --react --node --port 8000
171
+ stack-starter my-app --react --node --pm npm
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Notes
177
+
178
+ * The project folder name must be unique.
179
+ * If the folder already exists, the CLI will stop before asking setup questions.
180
+ * For backend projects, a `.env` file is created automatically.
181
+
182
+ ---
183
+
184
+ ## License
185
+
186
+ MIT
package/bin/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  // -----------------------------
4
4
  // Required packages
@@ -6,22 +6,25 @@
6
6
  const inquirer = require("inquirer").default;
7
7
  const fs = require("fs");
8
8
  const path = require("path");
9
+ const net = require("net");
9
10
  const { execSync } = require("child_process");
10
11
 
11
12
  // -----------------------------
12
- // starter --help and --version
13
+ // Help / Version
13
14
  // -----------------------------
14
15
  const packageJson = require("../package.json");
15
16
 
16
17
  if (process.argv.includes("--help") || process.argv.includes("-h")) {
17
18
  console.log(`
18
- starter - project scaffolding CLI
19
+ stack-starter - project scaffolding CLI
19
20
 
20
21
  Usage:
21
- starter
22
- starter my-app
23
- starter my-app --react --node
24
- starter my-app --html
22
+ stack-starter
23
+ stack-starter my-app
24
+ stack-starter my-app --react --node
25
+ stack-starter my-app --html
26
+ stack-starter my-app --react --node --port 8000
27
+ stack-starter my-app --react --node --pm pnpm
25
28
 
26
29
  Options:
27
30
  --help, -h Show help
@@ -29,11 +32,8 @@ Options:
29
32
  --react Use React.js (Vite)
30
33
  --html Use HTML / CSS / JavaScript
31
34
  --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
35
+ --port Custom backend port
36
+ --pm Package manager (npm, yarn, pnpm)
37
37
  `);
38
38
  process.exit(0);
39
39
  }
@@ -55,8 +55,14 @@ const useReact = process.argv.includes("--react");
55
55
  const useHtml = process.argv.includes("--html");
56
56
  const useNode = process.argv.includes("--node");
57
57
 
58
+ const portIndex = process.argv.indexOf("--port");
59
+ const cliPort = portIndex !== -1 ? process.argv[portIndex + 1] : null;
60
+
61
+ const pmIndex = process.argv.indexOf("--pm");
62
+ const cliPackageManager = pmIndex !== -1 ? process.argv[pmIndex + 1] : null;
63
+
58
64
  // -----------------------------
59
- // Resolve CLI-selected frontend/backend
65
+ // Resolve frontend/backend
60
66
  // -----------------------------
61
67
  let cliFrontend = null;
62
68
  let cliBackend = null;
@@ -65,8 +71,30 @@ if (useReact) cliFrontend = "React.js (Vite)";
65
71
  if (useHtml) cliFrontend = "HTML / CSS / JavaScript";
66
72
  if (useNode) cliBackend = "Node.js (Express)";
67
73
 
74
+ // if user explicitly chooses only frontend
75
+ if ((useReact || useHtml) && !useNode) {
76
+ cliBackend = "None";
77
+ }
78
+
79
+ // if user explicitly chooses only backend
80
+ if (useNode && !useReact && !useHtml) {
81
+ cliFrontend = "None";
82
+ }
83
+
84
+ // -----------------------------
85
+ // Early duplicate folder check
86
+ // -----------------------------
87
+ if (cliProjectName) {
88
+ const earlyProjectPath = path.join(process.cwd(), cliProjectName);
89
+
90
+ if (fs.existsSync(earlyProjectPath)) {
91
+ console.log("\nA project with this name already exists.");
92
+ process.exit(0);
93
+ }
94
+ }
95
+
68
96
  // -----------------------------
69
- // Helper: copy template files
97
+ // Helper: copy template
70
98
  // -----------------------------
71
99
  function copyTemplate(source, destination) {
72
100
  const content = fs.readFileSync(source, "utf-8");
@@ -74,7 +102,54 @@ function copyTemplate(source, destination) {
74
102
  }
75
103
 
76
104
  // -----------------------------
77
- // Ask user for project details
105
+ // Helper: package manager installed?
106
+ // -----------------------------
107
+ function isPackageManagerInstalled(pm) {
108
+ try {
109
+ execSync(`${pm} --version`, {
110
+ stdio: "ignore",
111
+ shell: process.env.ComSpec,
112
+ });
113
+ return true;
114
+ } catch {
115
+ return false;
116
+ }
117
+ }
118
+
119
+ // -----------------------------
120
+ // Early package manager check
121
+ // -----------------------------
122
+ if (cliPackageManager) {
123
+ if (!["npm", "yarn", "pnpm"].includes(cliPackageManager)) {
124
+ console.log("\nInvalid package manager. Use npm, yarn, or pnpm.");
125
+ process.exit(0);
126
+ }
127
+
128
+ if (!isPackageManagerInstalled(cliPackageManager)) {
129
+ console.log(`\n${cliPackageManager} is not installed on this machine.`);
130
+ process.exit(0);
131
+ }
132
+ }
133
+
134
+ // -----------------------------
135
+ // Helper: check free port
136
+ // -----------------------------
137
+ function isPortFree(port) {
138
+ return new Promise((resolve) => {
139
+ const server = net.createServer();
140
+
141
+ server.once("error", () => resolve(false));
142
+
143
+ server.once("listening", () => {
144
+ server.close(() => resolve(true));
145
+ });
146
+
147
+ server.listen(port);
148
+ });
149
+ }
150
+
151
+ // -----------------------------
152
+ // Prompt
78
153
  // -----------------------------
79
154
  inquirer
80
155
  .prompt([
@@ -113,46 +188,87 @@ inquirer
113
188
  default: 1,
114
189
  when: !cliBackend,
115
190
  },
116
- ])
117
- .then((answers) => {
118
- if (cliProjectName) {
119
- answers.projectName = cliProjectName;
120
- }
191
+ {
192
+ name: "port",
193
+ message: "Enter backend port:",
194
+ default: cliPort || "5000",
195
+ when(answers) {
196
+ return (
197
+ !cliPort &&
198
+ (cliBackend === "Node.js (Express)" ||
199
+ answers.backend === "Node.js (Express)")
200
+ );
201
+ },
202
+ validate(input) {
203
+ if (!/^[0-9]+$/.test(input)) {
204
+ return "Entered wrong port number format. Example: 8000";
205
+ }
121
206
 
122
- if (cliFrontend) {
123
- answers.frontend = cliFrontend;
124
- }
207
+ const port = Number(input);
125
208
 
126
- if (cliBackend) {
127
- answers.backend = cliBackend;
128
- }
209
+ if (port < 1 || port > 65535) {
210
+ return "Port must be between 1 and 65535.";
211
+ }
129
212
 
130
- if (!answers.frontend) {
131
- answers.frontend = "None";
132
- }
213
+ return true;
214
+ },
215
+ },
216
+ {
217
+ name: "packageManager",
218
+ message: "Choose package manager:",
219
+ type: "rawlist",
220
+ choices: ["npm", "yarn", "pnpm"],
221
+ default: 0,
222
+ when: !cliPackageManager,
223
+ },
224
+ ])
225
+ .then(async (answers) => {
226
+ if (cliProjectName) answers.projectName = cliProjectName;
227
+ if (cliFrontend) answers.frontend = cliFrontend;
228
+ if (cliBackend) answers.backend = cliBackend;
133
229
 
134
- if (!answers.backend) {
135
- answers.backend = "None";
136
- }
230
+ if (!answers.frontend) answers.frontend = "None";
231
+ if (!answers.backend) answers.backend = "None";
137
232
 
138
- const projectPath = path.join(process.cwd(), answers.projectName);
233
+ answers.packageManager =
234
+ cliPackageManager || answers.packageManager || "npm";
139
235
 
140
236
  // -----------------------------
141
- // Prevent duplicate folder names
237
+ // Late duplicate check (manual prompt project name)
142
238
  // -----------------------------
239
+ const projectPath = path.join(process.cwd(), answers.projectName);
240
+
143
241
  if (fs.existsSync(projectPath)) {
144
242
  console.log("\nA project with this name already exists.");
145
243
  return;
146
244
  }
147
245
 
148
246
  // -----------------------------
149
- // Create root project folder
247
+ // Port validation
150
248
  // -----------------------------
151
- fs.mkdirSync(projectPath);
249
+ if (answers.backend === "Node.js (Express)") {
250
+ answers.port = Number(cliPort || answers.port || 5000);
251
+
252
+ if (!/^[0-9]+$/.test(String(answers.port))) {
253
+ console.log("\nEntered wrong port number format. Example: 8000");
254
+ return;
255
+ }
256
+
257
+ const free = await isPortFree(answers.port);
258
+
259
+ if (!free) {
260
+ console.log(
261
+ `\nSomething is already running on port ${answers.port}. Choose another port.`
262
+ );
263
+ return;
264
+ }
265
+ }
152
266
 
153
267
  // -----------------------------
154
- // Base project files
268
+ // Create project root
155
269
  // -----------------------------
270
+ fs.mkdirSync(projectPath);
271
+
156
272
  fs.writeFileSync(
157
273
  path.join(projectPath, ".gitignore"),
158
274
  "node_modules\n.env\ndist\n.vite\n.DS_Store\ncoverage"
@@ -164,7 +280,7 @@ inquirer
164
280
  );
165
281
 
166
282
  // =====================================================
167
- // BACKEND SETUP
283
+ // BACKEND
168
284
  // =====================================================
169
285
  if (answers.backend === "Node.js (Express)") {
170
286
  const serverPath = path.join(projectPath, "server");
@@ -174,6 +290,8 @@ inquirer
174
290
  console.log("\nSetting up backend...");
175
291
 
176
292
  try {
293
+ const pm = answers.packageManager;
294
+
177
295
  execSync("npm init -y", {
178
296
  cwd: serverPath,
179
297
  stdio: "inherit",
@@ -191,35 +309,68 @@ inquirer
191
309
  };
192
310
 
193
311
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
312
+ }
194
313
 
195
- console.log("✅ Scripts added to package.json");
314
+ if (pm === "npm") {
315
+ execSync("npm install express cors dotenv", {
316
+ cwd: serverPath,
317
+ stdio: "inherit",
318
+ shell: process.env.ComSpec,
319
+ });
320
+
321
+ execSync("npm install -D nodemon", {
322
+ cwd: serverPath,
323
+ stdio: "inherit",
324
+ shell: process.env.ComSpec,
325
+ });
196
326
  }
197
327
 
198
- execSync("npm install express cors dotenv", {
199
- cwd: serverPath,
200
- stdio: "inherit",
201
- shell: process.env.ComSpec,
202
- });
328
+ if (pm === "yarn") {
329
+ execSync("yarn add express cors dotenv", {
330
+ cwd: serverPath,
331
+ stdio: "inherit",
332
+ shell: process.env.ComSpec,
333
+ });
334
+
335
+ execSync("yarn add -D nodemon", {
336
+ cwd: serverPath,
337
+ stdio: "inherit",
338
+ shell: process.env.ComSpec,
339
+ });
340
+ }
203
341
 
204
- execSync("npm install -D nodemon", {
205
- cwd: serverPath,
206
- stdio: "inherit",
207
- shell: process.env.ComSpec,
208
- });
342
+ if (pm === "pnpm") {
343
+ execSync("pnpm add express cors dotenv", {
344
+ cwd: serverPath,
345
+ stdio: "inherit",
346
+ shell: process.env.ComSpec,
347
+ });
348
+
349
+ execSync("pnpm add -D nodemon", {
350
+ cwd: serverPath,
351
+ stdio: "inherit",
352
+ shell: process.env.ComSpec,
353
+ });
354
+ }
209
355
 
210
356
  copyTemplate(
211
357
  path.join(__dirname, "..", "templates", "server", "index.js"),
212
358
  path.join(serverPath, "index.js")
213
359
  );
360
+
361
+ fs.writeFileSync(
362
+ path.join(serverPath, ".env"),
363
+ `PORT=${answers.port}`
364
+ );
214
365
  } catch (error) {
215
366
  console.log("\n❌ Backend setup failed.");
216
- console.log("Check npm installation or internet connection.");
367
+ console.log("Check package manager installation or internet connection.");
217
368
  return;
218
369
  }
219
370
  }
220
371
 
221
372
  // =====================================================
222
- // FRONTEND SETUP - HTML / CSS / JS
373
+ // HTML FRONTEND
223
374
  // =====================================================
224
375
  if (answers.frontend === "HTML / CSS / JavaScript") {
225
376
  const clientPath = path.join(projectPath, "client");
@@ -267,10 +418,14 @@ inquirer
267
418
  );
268
419
 
269
420
  if (answers.backend === "Node.js (Express)") {
270
- copyTemplate(
421
+ let script = fs.readFileSync(
271
422
  path.join(__dirname, "..", "templates", "html", "script.js"),
272
- path.join(clientPath, "script.js")
423
+ "utf8"
273
424
  );
425
+
426
+ script = script.replace(/5000/g, answers.port);
427
+
428
+ fs.writeFileSync(path.join(clientPath, "script.js"), script);
274
429
  } else {
275
430
  fs.writeFileSync(
276
431
  path.join(clientPath, "script.js"),
@@ -286,7 +441,7 @@ messageEl.textContent = "No backend selected. Frontend is ready.";`
286
441
  }
287
442
 
288
443
  // =====================================================
289
- // FRONTEND SETUP - REACT
444
+ // REACT FRONTEND
290
445
  // =====================================================
291
446
  else if (answers.frontend === "React.js (Vite)") {
292
447
  const clientPath = path.join(projectPath, "client");
@@ -308,13 +463,15 @@ messageEl.textContent = "No backend selected. Frontend is ready.";`
308
463
  shell: process.env.ComSpec,
309
464
  });
310
465
 
311
- console.log("✅ React installation complete!");
312
-
313
466
  if (answers.backend === "Node.js (Express)") {
314
- copyTemplate(
467
+ let appCode = fs.readFileSync(
315
468
  path.join(__dirname, "..", "templates", "react", "App.jsx"),
316
- path.join(clientPath, "src", "App.jsx")
469
+ "utf8"
317
470
  );
471
+
472
+ appCode = appCode.replace(/5000/g, answers.port);
473
+
474
+ fs.writeFileSync(path.join(clientPath, "src", "App.jsx"), appCode);
318
475
  }
319
476
  } catch (error) {
320
477
  console.log("\n❌ React setup failed.");
@@ -324,7 +481,7 @@ messageEl.textContent = "No backend selected. Frontend is ready.";`
324
481
  }
325
482
 
326
483
  // =====================================================
327
- // FINAL INSTRUCTIONS
484
+ // FINAL
328
485
  // =====================================================
329
486
  console.log(`\n✅ Project "${answers.projectName}" created successfully!`);
330
487
 
@@ -334,7 +491,7 @@ messageEl.textContent = "No backend selected. Frontend is ready.";`
334
491
  ) {
335
492
  console.log("\nRun backend:");
336
493
  console.log(`cd ${answers.projectName}/server`);
337
- console.log("npm run dev");
494
+ console.log(`${answers.packageManager} run dev`);
338
495
 
339
496
  console.log("\nThen open frontend:");
340
497
  console.log(`${answers.projectName}/client/index.html`);
@@ -344,7 +501,7 @@ messageEl.textContent = "No backend selected. Frontend is ready.";`
344
501
  ) {
345
502
  console.log("\nRun backend:");
346
503
  console.log(`cd ${answers.projectName}/server`);
347
- console.log("npm run dev");
504
+ console.log(`${answers.packageManager} run dev`);
348
505
 
349
506
  console.log("\nOpen another terminal and run frontend:");
350
507
  console.log(`cd ${answers.projectName}/client`);
@@ -359,6 +516,6 @@ messageEl.textContent = "No backend selected. Frontend is ready.";`
359
516
  } else if (answers.backend === "Node.js (Express)") {
360
517
  console.log("\nRun backend:");
361
518
  console.log(`cd ${answers.projectName}/server`);
362
- console.log("npm run dev");
519
+ console.log(`${answers.packageManager} run dev`);
363
520
  }
364
- });
521
+ });
package/package.json CHANGED
@@ -1,30 +1,43 @@
1
1
  {
2
2
  "name": "stack-starter-cli",
3
- "version": "1.0.0",
4
- "description": "CLI to generate frontend and backend starter projects",
3
+ "version": "1.1.0",
4
+ "description": "CLI tool to quickly scaffold HTML, React, and Node.js starter projects.",
5
5
  "main": "bin/index.js",
6
6
  "bin": {
7
7
  "stack-starter": "./bin/index.js"
8
8
  },
9
9
  "files": [
10
10
  "bin",
11
- "templates"
11
+ "templates",
12
+ "README.md"
12
13
  ],
13
14
  "scripts": {
14
15
  "start": "node bin/index.js"
15
16
  },
16
17
  "keywords": [
17
18
  "cli",
18
- "starter",
19
19
  "scaffold",
20
- "node",
20
+ "starter",
21
+ "boilerplate",
22
+ "fullstack",
23
+ "frontend",
24
+ "backend",
21
25
  "react",
22
- "express"
26
+ "vite",
27
+ "express",
28
+ "node"
23
29
  ],
24
30
  "author": "SACHITH KASA",
25
31
  "license": "MIT",
26
32
  "type": "commonjs",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/SACHITH31/starter_cli"
36
+ },
37
+ "engines": {
38
+ "node": ">=18"
39
+ },
27
40
  "dependencies": {
28
41
  "inquirer": "^13.4.2"
29
42
  }
30
- }
43
+ }