create-stardrive 1.0.0 → 1.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 (2) hide show
  1. package/bin/index.js +132 -21
  2. package/package.json +1 -1
package/bin/index.js CHANGED
@@ -3,12 +3,50 @@
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import os from "node:os";
6
+ import readline from "node:readline/promises";
7
+ import { stdin as input, stdout as output } from "node:process";
6
8
  import { execSync, spawnSync } from "node:child_process";
7
9
 
8
10
  const args = process.argv.slice(2);
9
11
 
10
12
  const skipInstall = args.includes("--no-install");
11
13
 
14
+ //
15
+ // Pretty logging helpers
16
+ //
17
+
18
+ const supportsColor =
19
+ output.isTTY && process.env.TERM !== "dumb" && !process.env.NO_COLOR;
20
+
21
+ const paint = (code, text) =>
22
+ supportsColor ? `\x1b[${code}m${text}\x1b[0m` : text;
23
+
24
+ const c = {
25
+ dim: (t) => paint("2", t),
26
+ bold: (t) => paint("1", t),
27
+ cyan: (t) => paint("36", t),
28
+ magenta: (t) => paint("35", t),
29
+ green: (t) => paint("32", t),
30
+ yellow: (t) => paint("33", t),
31
+ red: (t) => paint("31", t),
32
+ };
33
+
34
+ const banner = `
35
+ ${c.magenta("★")} ${c.bold(c.cyan("Stardrive Launchpad"))} ${c.magenta("★")}
36
+ ${c.dim("Fueling your next Astro project...")}
37
+ `;
38
+
39
+ let stepNum = 0;
40
+ const step = (msg) =>
41
+ console.log(`\n${c.cyan(`[${++stepNum}]`)} ${c.bold(msg)}`);
42
+ const info = (msg) => console.log(` ${c.dim(msg)}`);
43
+ const ok = (msg) => console.log(` ${c.green("✓")} ${msg}`);
44
+ const fail = (msg) => console.error(`\n${c.red("✗")} ${msg}\n`);
45
+
46
+ //
47
+ // Argument parsing
48
+ //
49
+
12
50
  function parseFlag(name) {
13
51
  const eqIndex = args.findIndex((a) => a.startsWith(`${name}=`));
14
52
 
@@ -34,9 +72,6 @@ const positional = args.filter((a, i) => {
34
72
  return true;
35
73
  });
36
74
 
37
- const projectName = positional[0] || "my-stardrive";
38
- const targetDir = path.resolve(projectName);
39
-
40
75
  function detectPackageManager() {
41
76
  const ua = process.env.npm_config_user_agent || "";
42
77
 
@@ -56,17 +91,72 @@ function commandExists(cmd) {
56
91
  const pm = detectPackageManager();
57
92
 
58
93
  if (!commandExists(pm)) {
59
- console.error(`${pm} is not installed`);
94
+ fail(`${pm} is not installed`);
60
95
  process.exit(1);
61
96
  }
62
97
 
63
- if (fs.existsSync(targetDir)) {
64
- console.error(`Directory "${projectName}" already exists`);
65
- process.exit(1);
98
+ //
99
+ // Project name (interactive if not provided)
100
+ //
101
+
102
+ function isValidProjectName(name) {
103
+ return /^[a-z0-9._-]+$/i.test(name) && !/^[._]/.test(name);
104
+ }
105
+
106
+ async function askProjectName(initial) {
107
+ if (initial && isValidProjectName(initial)) {
108
+ const targetDir = path.resolve(initial);
109
+ if (!fs.existsSync(targetDir)) return initial;
110
+ fail(`Directory "${initial}" already exists.`);
111
+ }
112
+
113
+ if (!input.isTTY) {
114
+ fail(
115
+ `Project name "${initial || ""}" is missing or invalid and stdin is not interactive.`
116
+ );
117
+ process.exit(1);
118
+ }
119
+
120
+ const rl = readline.createInterface({ input, output });
121
+
122
+ try {
123
+ while (true) {
124
+ const answer = (
125
+ await rl.question(
126
+ `${c.cyan("?")} ${c.bold("Project name:")} ${c.dim("(my-stardrive) ")}`
127
+ )
128
+ ).trim();
129
+
130
+ const name = answer || "my-stardrive";
131
+
132
+ if (!isValidProjectName(name)) {
133
+ console.log(
134
+ ` ${c.yellow("!")} Use letters, digits, dots, dashes or underscores.`
135
+ );
136
+ continue;
137
+ }
138
+
139
+ const targetDir = path.resolve(name);
140
+
141
+ if (fs.existsSync(targetDir)) {
142
+ console.log(` ${c.yellow("!")} "${name}" already exists. Try another.`);
143
+ continue;
144
+ }
145
+
146
+ return name;
147
+ }
148
+ } finally {
149
+ rl.close();
150
+ }
66
151
  }
67
152
 
153
+ console.log(banner);
154
+
155
+ const projectName = await askProjectName(positional[0]);
156
+ const targetDir = path.resolve(projectName);
157
+
68
158
  //
69
- // Download template repo
159
+ // Resolve which tag to clone
70
160
  //
71
161
 
72
162
  const repo = "https://github.com/Peltmonger/stardrive.git";
@@ -102,7 +192,7 @@ function resolveTag() {
102
192
  const wanted = normalizeTag(requestedVersion);
103
193
 
104
194
  if (!tags.includes(wanted)) {
105
- console.error(`Version "${requestedVersion}" not found in ${repo}`);
195
+ fail(`Version "${requestedVersion}" not found in ${repo}`);
106
196
  process.exit(1);
107
197
  }
108
198
 
@@ -112,20 +202,30 @@ function resolveTag() {
112
202
  const stable = tags.filter((t) => /^v?\d+\.\d+\.\d+$/.test(t));
113
203
 
114
204
  if (stable.length === 0) {
115
- console.error(`No tagged releases found in ${repo}`);
205
+ fail(`No tagged releases found in ${repo}`);
116
206
  process.exit(1);
117
207
  }
118
208
 
119
209
  return stable.sort(compareSemver).pop();
120
210
  }
121
211
 
212
+ step("Locating the latest Stardrive release");
122
213
  const tag = resolveTag();
214
+ ok(`Selected ${c.bold(tag)}`);
123
215
 
124
- console.log(`\nCloning ${repo} at ${tag}...\n`);
216
+ //
217
+ // Clone
218
+ //
125
219
 
126
- execSync(`git clone --depth=1 --branch ${tag} ${repo} "${tempDir}"`, {
127
- stdio: "inherit",
128
- });
220
+ step(`Beaming ${c.bold(tag)} down from orbit`);
221
+ info(repo);
222
+
223
+ execSync(
224
+ `git clone --depth=1 --branch ${tag} --quiet ${repo} "${tempDir}"`,
225
+ {
226
+ stdio: ["ignore", "ignore", "inherit"],
227
+ }
228
+ );
129
229
 
130
230
  fs.rmSync(path.join(tempDir, ".git"), {
131
231
  recursive: true,
@@ -135,11 +235,14 @@ fs.rmSync(path.join(tempDir, ".git"), {
135
235
  fs.cpSync(tempDir, targetDir, {
136
236
  recursive: true,
137
237
  });
238
+ ok(`Landed in ${c.bold(projectName)}/`);
138
239
 
139
240
  //
140
- // Remove files not needed in the generated project
241
+ // Trim files not needed in the generated project
141
242
  //
142
243
 
244
+ step("Trimming unused boosters");
245
+
143
246
  [
144
247
  "scripts/syncVersion.js",
145
248
  "SECURITY.md",
@@ -165,11 +268,14 @@ fs.cpSync(tempDir, targetDir, {
165
268
  force: true,
166
269
  });
167
270
  });
271
+ ok("Stripped extras and stale lockfiles");
168
272
 
169
273
  //
170
274
  // Update package.json
171
275
  //
172
276
 
277
+ step("Calibrating package.json");
278
+
173
279
  const packageJsonPath = path.join(targetDir, "package.json");
174
280
 
175
281
  const pkg = JSON.parse(
@@ -199,22 +305,27 @@ fs.writeFileSync(
199
305
  packageJsonPath,
200
306
  JSON.stringify(pkg, null, 2)
201
307
  );
308
+ ok(`Named your ship ${c.bold(projectName)}`);
202
309
 
203
310
  //
204
311
  // Install dependencies
205
312
  //
206
313
 
207
314
  if (!skipInstall) {
208
- console.log(`\nInstalling dependencies using ${pm}...\n`);
315
+ step(`Loading dependencies with ${c.bold(pm)}`);
209
316
 
210
317
  execSync(`${pm} install`, {
211
318
  cwd: targetDir,
212
319
  stdio: "inherit",
213
320
  });
321
+ ok("Engines online");
322
+ } else {
323
+ step("Skipping dependency install");
324
+ info(`Run "${pm} install" inside ${projectName}/ when you're ready.`);
214
325
  }
215
326
 
216
327
  //
217
- // Next steps
328
+ // Lift off
218
329
  //
219
330
 
220
331
  const devCommands = {
@@ -225,10 +336,10 @@ const devCommands = {
225
336
  };
226
337
 
227
338
  console.log(`
228
- Done.
339
+ ${c.green("All systems go.")} ${c.dim("Pre-flight checklist complete.")}
229
340
 
230
- Next steps:
341
+ ${c.dim("$")} ${c.cyan(`cd ${projectName}`)}
342
+ ${c.dim("$")} ${c.cyan(devCommands[pm])}
231
343
 
232
- cd ${projectName}
233
- ${devCommands[pm]}
344
+ ${c.bold("Happy launching!")} ${c.magenta("🚀")}
234
345
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-stardrive",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "This is the launchpad application to create the Astro Stardrive boilerplate.",
5
5
  "bin": {
6
6
  "create-stardrive": "bin/index.js"