create-stardrive 1.0.2 → 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 +4 -4
  2. package/bin/index.js +180 -251
  3. package/package.json +12 -3
package/README.md CHANGED
@@ -9,25 +9,25 @@ It helps you getting Stardrive installed and also auto-adjusts some details for
9
9
  Simply run:
10
10
 
11
11
  ```sh
12
- npm create stardrive
12
+ npm create stardrive@latest
13
13
  ```
14
14
 
15
15
  *or*
16
16
 
17
17
  ```sh
18
- pnpm create stardrive
18
+ pnpm create stardrive@latest
19
19
  ```
20
20
 
21
21
  *or*
22
22
 
23
23
  ```sh
24
- yarn create stardrive
24
+ yarn create stardrive@latest
25
25
  ```
26
26
 
27
27
  *or*
28
28
 
29
29
  ```sh
30
- bun create stardrive
30
+ bun create stardrive@latest
31
31
  ```
32
32
 
33
33
  <br />
package/bin/index.js CHANGED
@@ -1,148 +1,129 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import fs from "node:fs";
4
- import path from "node:path";
5
- import os from "node:os";
6
- import readline from "node:readline/promises";
7
- import { stdin as input, stdout as output } from "node:process";
8
- import { execSync, spawnSync } from "node:child_process";
9
-
10
- const args = process.argv.slice(2);
11
-
12
- const skipInstall = args.includes("--no-install");
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;
3
+ // src/index.ts
4
+ import path3 from "node:path";
5
+
6
+ // src/args.ts
7
+ function parseArgs(argv) {
8
+ const args = argv.slice(2);
9
+ const skipInstall2 = args.includes("--no-install");
10
+ const parseFlag = (name) => {
11
+ const eqIndex = args.findIndex((a) => a.startsWith(`${name}=`));
12
+ if (eqIndex !== -1) {
13
+ return args[eqIndex].slice(name.length + 1);
14
+ }
15
+ const i = args.indexOf(name);
16
+ const next = args[i + 1];
17
+ if (i !== -1 && next && !next.startsWith("-")) {
18
+ return next;
19
+ }
20
+ return void 0;
21
+ };
22
+ const requestedVersion2 = parseFlag("--version") ?? parseFlag("-v");
23
+ const positional2 = args.filter((a, i) => {
24
+ if (a.startsWith("-")) return false;
25
+ const prev = args[i - 1];
26
+ if (prev === "--version" || prev === "-v") return false;
27
+ return true;
28
+ });
29
+ return { positional: positional2, requestedVersion: requestedVersion2, skipInstall: skipInstall2 };
30
+ }
23
31
 
24
- const c = {
32
+ // src/logger.ts
33
+ import { stdout as output } from "node:process";
34
+ var supportsColor = output.isTTY && process.env["TERM"] !== "dumb" && !process.env["NO_COLOR"];
35
+ var paint = (code, text) => supportsColor ? `\x1B[${code}m${text}\x1B[0m` : text;
36
+ var c = {
25
37
  dim: (t) => paint("2", t),
26
38
  bold: (t) => paint("1", t),
27
39
  cyan: (t) => paint("36", t),
28
40
  magenta: (t) => paint("35", t),
29
41
  green: (t) => paint("32", t),
30
42
  yellow: (t) => paint("33", t),
31
- red: (t) => paint("31", t),
43
+ red: (t) => paint("31", t)
32
44
  };
33
-
34
- const banner = `
35
- ${c.magenta("★")} ${c.bold(c.cyan("Stardrive Launchpad"))} ${c.magenta("★")}
45
+ var banner = `
46
+ ${c.magenta("\u2605")} ${c.bold(c.cyan("Stardrive Launchpad"))} ${c.magenta("\u2605")}
36
47
  ${c.dim("Fueling your next Astro project...")}
37
48
  `;
49
+ var stepNum = 0;
50
+ var step = (msg) => {
51
+ console.log(`
52
+ ${c.cyan(`[${++stepNum}]`)} ${c.bold(msg)}`);
53
+ };
54
+ var info = (msg) => {
55
+ console.log(` ${c.dim(msg)}`);
56
+ };
57
+ var ok = (msg) => {
58
+ console.log(` ${c.green("\u2713")} ${msg}`);
59
+ };
60
+ var fail = (msg) => {
61
+ console.error(`
62
+ ${c.red("\u2717")} ${msg}
63
+ `);
64
+ };
38
65
 
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
-
50
- function parseFlag(name) {
51
- const eqIndex = args.findIndex((a) => a.startsWith(`${name}=`));
52
-
53
- if (eqIndex !== -1) {
54
- return args[eqIndex].slice(name.length + 1);
55
- }
56
-
57
- const i = args.indexOf(name);
58
-
59
- if (i !== -1 && args[i + 1] && !args[i + 1].startsWith("-")) {
60
- return args[i + 1];
61
- }
62
-
63
- return undefined;
64
- }
65
-
66
- const requestedVersion = parseFlag("--version") || parseFlag("-v");
67
-
68
- const positional = args.filter((a, i) => {
69
- if (a.startsWith("-")) return false;
70
- const prev = args[i - 1];
71
- if (prev === "--version" || prev === "-v") return false;
72
- return true;
73
- });
74
-
66
+ // src/package-manager.ts
67
+ import { spawnSync } from "node:child_process";
75
68
  function detectPackageManager() {
76
- const ua = process.env.npm_config_user_agent || "";
77
-
69
+ const ua = process.env["npm_config_user_agent"] ?? "";
78
70
  if (ua.startsWith("pnpm")) return "pnpm";
79
71
  if (ua.startsWith("yarn")) return "yarn";
80
72
  if (ua.startsWith("bun")) return "bun";
81
-
82
73
  return "npm";
83
74
  }
84
-
85
75
  function commandExists(cmd) {
86
76
  return spawnSync(cmd, ["--version"], {
87
- stdio: "ignore",
77
+ stdio: "ignore"
88
78
  }).status === 0;
89
79
  }
80
+ var devCommands = {
81
+ npm: "npm run dev",
82
+ pnpm: "pnpm dev",
83
+ yarn: "yarn dev",
84
+ bun: "bun run dev"
85
+ };
90
86
 
91
- const pm = detectPackageManager();
92
-
93
- if (!commandExists(pm)) {
94
- fail(`${pm} is not installed`);
95
- process.exit(1);
96
- }
97
-
98
- //
99
- // Project name (interactive if not provided)
100
- //
101
-
87
+ // src/project-name.ts
88
+ import fs from "node:fs";
89
+ import path from "node:path";
90
+ import readline from "node:readline/promises";
91
+ import { stdin as input, stdout as output2 } from "node:process";
102
92
  function isValidProjectName(name) {
103
93
  return /^[a-z0-9._-]+$/i.test(name) && !/^[._]/.test(name);
104
94
  }
105
-
106
95
  async function askProjectName(initial) {
107
96
  if (initial && isValidProjectName(initial)) {
108
- const targetDir = path.resolve(initial);
109
- if (!fs.existsSync(targetDir)) return initial;
97
+ const targetDir2 = path.resolve(initial);
98
+ if (!fs.existsSync(targetDir2)) return initial;
110
99
  fail(`Directory "${initial}" already exists.`);
111
100
  }
112
-
113
101
  if (!input.isTTY) {
114
102
  fail(
115
- `Project name "${initial || ""}" is missing or invalid and stdin is not interactive.`
103
+ `Project name "${initial ?? ""}" is missing or invalid and stdin is not interactive.`
116
104
  );
117
105
  process.exit(1);
118
106
  }
119
-
120
- const rl = readline.createInterface({ input, output });
121
-
107
+ const rl = readline.createInterface({ input, output: output2 });
122
108
  try {
123
109
  while (true) {
124
- const answer = (
125
- await rl.question(
126
- `${c.cyan("?")} ${c.bold("Project name:")} ${c.dim("(my-stardrive) ")}`
127
- )
128
- ).trim();
129
-
110
+ const answer = (await rl.question(
111
+ `${c.cyan("?")} ${c.bold("Project name:")} ${c.dim("(my-stardrive) ")}`
112
+ )).trim();
130
113
  const name = answer || "my-stardrive";
131
-
132
114
  if (!isValidProjectName(name)) {
133
115
  console.log(
134
116
  ` ${c.yellow("!")} Use letters, digits, dots, dashes or underscores.`
135
117
  );
136
118
  continue;
137
119
  }
138
-
139
- const targetDir = path.resolve(name);
140
-
141
- if (fs.existsSync(targetDir)) {
142
- console.log(` ${c.yellow("!")} "${name}" already exists. Try another.`);
120
+ const targetDir2 = path.resolve(name);
121
+ if (fs.existsSync(targetDir2)) {
122
+ console.log(
123
+ ` ${c.yellow("!")} "${name}" already exists. Try another.`
124
+ );
143
125
  continue;
144
126
  }
145
-
146
127
  return name;
147
128
  }
148
129
  } finally {
@@ -150,196 +131,144 @@ async function askProjectName(initial) {
150
131
  }
151
132
  }
152
133
 
153
- console.log(banner);
154
-
155
- const projectName = await askProjectName(positional[0]);
156
- const targetDir = path.resolve(projectName);
157
-
158
- //
159
- // Resolve which tag to clone
160
- //
161
-
162
- const repo = "https://github.com/Peltmonger/stardrive.git";
163
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "create-app-"));
164
-
165
- function normalizeTag(tag) {
166
- return tag.startsWith("v") ? tag : `v${tag}`;
134
+ // src/release.ts
135
+ import { execSync } from "node:child_process";
136
+ var STARDRIVE_REPO = "https://github.com/Peltmonger/stardrive.git";
137
+ function normalizeTag(tag2) {
138
+ return tag2.startsWith("v") ? tag2 : `v${tag2}`;
167
139
  }
168
-
169
140
  function compareSemver(a, b) {
170
141
  const pa = a.replace(/^v/, "").split("-")[0].split(".").map(Number);
171
142
  const pb = b.replace(/^v/, "").split("-")[0].split(".").map(Number);
172
-
173
143
  for (let i = 0; i < 3; i++) {
174
- const diff = (pa[i] || 0) - (pb[i] || 0);
144
+ const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
175
145
  if (diff !== 0) return diff;
176
146
  }
177
-
178
147
  return 0;
179
148
  }
180
-
181
- function resolveTag() {
182
- const output = execSync(`git ls-remote --tags --refs ${repo}`, {
183
- encoding: "utf8",
149
+ function resolveTag(requestedVersion2) {
150
+ const output3 = execSync(`git ls-remote --tags --refs ${STARDRIVE_REPO}`, {
151
+ encoding: "utf8"
184
152
  });
185
-
186
- const tags = output
187
- .split("\n")
188
- .map((line) => line.split("refs/tags/")[1])
189
- .filter(Boolean);
190
-
191
- if (requestedVersion) {
192
- const wanted = normalizeTag(requestedVersion);
193
-
153
+ const tags = output3.split("\n").map((line) => line.split("refs/tags/")[1]).filter((t) => Boolean(t));
154
+ if (requestedVersion2) {
155
+ const wanted = normalizeTag(requestedVersion2);
194
156
  if (!tags.includes(wanted)) {
195
- fail(`Version "${requestedVersion}" not found in ${repo}`);
157
+ fail(`Version "${requestedVersion2}" not found in ${STARDRIVE_REPO}`);
196
158
  process.exit(1);
197
159
  }
198
-
199
160
  return wanted;
200
161
  }
201
-
202
162
  const stable = tags.filter((t) => /^v?\d+\.\d+\.\d+$/.test(t));
203
-
204
163
  if (stable.length === 0) {
205
- fail(`No tagged releases found in ${repo}`);
164
+ fail(`No tagged releases found in ${STARDRIVE_REPO}`);
206
165
  process.exit(1);
207
166
  }
208
-
209
167
  return stable.sort(compareSemver).pop();
210
168
  }
211
169
 
212
- step("Locating the latest Stardrive release");
213
- const tag = resolveTag();
214
- ok(`Selected ${c.bold(tag)}`);
215
-
216
- //
217
- // Clone
218
- //
219
-
220
- step(`Beaming ${c.bold(tag)} down from orbit`);
221
- info(repo);
222
-
223
- execSync(
224
- `git -c advice.detachedHead=false clone --depth=1 --branch ${tag} --quiet ${repo} "${tempDir}"`,
225
- {
226
- stdio: ["ignore", "ignore", "inherit"],
227
- }
228
- );
229
-
230
- fs.rmSync(path.join(tempDir, ".git"), {
231
- recursive: true,
232
- force: true,
233
- });
234
-
235
- fs.cpSync(tempDir, targetDir, {
236
- recursive: true,
237
- });
238
- ok(`Landed in ${c.bold(projectName)}/`);
239
-
240
- //
241
- // Trim files not needed in the generated project
242
- //
243
-
244
- step("Trimming unused boosters");
245
-
246
- [
247
- "scripts/syncVersion.js",
248
- "SECURITY.md",
249
- ".github",
250
- ].forEach((entry) => {
251
- fs.rmSync(path.join(targetDir, entry), {
252
- recursive: true,
253
- force: true,
254
- });
255
- });
256
-
257
- //
258
- // Remove all lockfiles (should not be there, but just to be safe)
259
- //
260
-
261
- [
170
+ // src/scaffold.ts
171
+ import fs2 from "node:fs";
172
+ import os from "node:os";
173
+ import path2 from "node:path";
174
+ import { execSync as execSync2 } from "node:child_process";
175
+ var TRIM_ENTRIES = ["scripts/syncVersion.js", "SECURITY.md", ".github"];
176
+ var STALE_LOCKFILES = [
262
177
  "package-lock.json",
263
178
  "pnpm-lock.yaml",
264
179
  "yarn.lock",
265
- "bun.lockb",
266
- ].forEach((file) => {
267
- fs.rmSync(path.join(targetDir, file), {
268
- force: true,
180
+ "bun.lockb"
181
+ ];
182
+ function cloneRepo(tag2, targetDir2, projectName2) {
183
+ const tempDir = fs2.mkdtempSync(path2.join(os.tmpdir(), "create-app-"));
184
+ step(`Beaming ${c.bold(tag2)} down from orbit`);
185
+ info(STARDRIVE_REPO);
186
+ execSync2(
187
+ `git -c advice.detachedHead=false clone --depth=1 --branch ${tag2} --quiet ${STARDRIVE_REPO} "${tempDir}"`,
188
+ {
189
+ stdio: ["ignore", "ignore", "inherit"]
190
+ }
191
+ );
192
+ fs2.rmSync(path2.join(tempDir, ".git"), {
193
+ recursive: true,
194
+ force: true
269
195
  });
270
- });
271
- ok("Stripped extras and stale lockfiles");
272
-
273
- //
274
- // Update package.json
275
- //
276
-
277
- step("Calibrating package.json");
278
-
279
- const packageJsonPath = path.join(targetDir, "package.json");
280
-
281
- const pkg = JSON.parse(
282
- fs.readFileSync(packageJsonPath, "utf8")
283
- );
284
-
285
- pkg.name = projectName;
286
-
287
- if (pkg.scripts) {
288
- delete pkg.scripts["sync-version"];
289
- delete pkg.scripts.prebuild;
196
+ fs2.cpSync(tempDir, targetDir2, {
197
+ recursive: true
198
+ });
199
+ ok(`Landed in ${c.bold(projectName2)}/`);
290
200
  }
291
-
292
- if (pm === "pnpm") {
293
- pkg.packageManager = `pnpm@${execSync("pnpm --version")
294
- .toString()
295
- .trim()}`;
201
+ function trimProject(targetDir2) {
202
+ step("Trimming unused boosters");
203
+ for (const entry of TRIM_ENTRIES) {
204
+ fs2.rmSync(path2.join(targetDir2, entry), {
205
+ recursive: true,
206
+ force: true
207
+ });
208
+ }
209
+ for (const file of STALE_LOCKFILES) {
210
+ fs2.rmSync(path2.join(targetDir2, file), {
211
+ force: true
212
+ });
213
+ }
214
+ ok("Stripped extras and stale lockfiles");
296
215
  }
297
-
298
- if (pm === "bun") {
299
- pkg.packageManager = `bun@${execSync("bun --version")
300
- .toString()
301
- .trim()}`;
216
+ function calibratePackageJson(targetDir2, projectName2, pm2) {
217
+ step("Calibrating package.json");
218
+ const packageJsonPath = path2.join(targetDir2, "package.json");
219
+ const pkg = JSON.parse(
220
+ fs2.readFileSync(packageJsonPath, "utf8")
221
+ );
222
+ pkg.name = projectName2;
223
+ if (pkg.scripts) {
224
+ delete pkg.scripts["sync-version"];
225
+ delete pkg.scripts["prebuild"];
226
+ }
227
+ if (pm2 === "pnpm") {
228
+ pkg.packageManager = `pnpm@${execSync2("pnpm --version").toString().trim()}`;
229
+ }
230
+ if (pm2 === "bun") {
231
+ pkg.packageManager = `bun@${execSync2("bun --version").toString().trim()}`;
232
+ }
233
+ fs2.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2));
234
+ ok(`Named your ship ${c.bold(projectName2)}`);
302
235
  }
303
-
304
- fs.writeFileSync(
305
- packageJsonPath,
306
- JSON.stringify(pkg, null, 2)
307
- );
308
- ok(`Named your ship ${c.bold(projectName)}`);
309
-
310
- //
311
- // Install dependencies
312
- //
313
-
314
- if (!skipInstall) {
315
- step(`Loading dependencies with ${c.bold(pm)}`);
316
-
317
- execSync(`${pm} install`, {
318
- cwd: targetDir,
319
- stdio: "inherit",
236
+ function installDependencies(targetDir2, projectName2, pm2, skipInstall2) {
237
+ if (skipInstall2) {
238
+ step("Skipping dependency install");
239
+ info(`Run "${pm2} install" inside ${projectName2}/ when you're ready.`);
240
+ return;
241
+ }
242
+ step(`Loading dependencies with ${c.bold(pm2)}`);
243
+ execSync2(`${pm2} install`, {
244
+ cwd: targetDir2,
245
+ stdio: "inherit"
320
246
  });
321
247
  ok("Engines online");
322
- } else {
323
- step("Skipping dependency install");
324
- info(`Run "${pm} install" inside ${projectName}/ when you're ready.`);
325
248
  }
326
249
 
327
- //
328
- // Lift off
329
- //
330
-
331
- const devCommands = {
332
- npm: "npm run dev",
333
- pnpm: "pnpm dev",
334
- yarn: "yarn dev",
335
- bun: "bun run dev",
336
- };
337
-
250
+ // src/index.ts
251
+ var { positional, requestedVersion, skipInstall } = parseArgs(process.argv);
252
+ var pm = detectPackageManager();
253
+ if (!commandExists(pm)) {
254
+ fail(`${pm} is not installed`);
255
+ process.exit(1);
256
+ }
257
+ console.log(banner);
258
+ var projectName = await askProjectName(positional[0]);
259
+ var targetDir = path3.resolve(projectName);
260
+ step("Locating the latest Stardrive release");
261
+ var tag = resolveTag(requestedVersion);
262
+ ok(`Selected ${c.bold(tag)}`);
263
+ cloneRepo(tag, targetDir, projectName);
264
+ trimProject(targetDir);
265
+ calibratePackageJson(targetDir, projectName, pm);
266
+ installDependencies(targetDir, projectName, pm, skipInstall);
338
267
  console.log(`
339
268
  ${c.green("All systems go.")} ${c.dim("Pre-flight checklist complete.")}
340
269
 
341
270
  ${c.dim("$")} ${c.cyan(`cd ${projectName}`)}
342
271
  ${c.dim("$")} ${c.cyan(devCommands[pm])}
343
272
 
344
- ${c.bold("Happy launching!")} ${c.magenta("🚀")}
273
+ ${c.bold("Happy launching!")} ${c.magenta("\u{1F680}")}
345
274
  `);
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "create-stardrive",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "This is the launchpad application to create the Astro Stardrive boilerplate.",
5
5
  "bin": {
6
6
  "create-stardrive": "bin/index.js"
7
7
  },
8
8
  "files": [
9
- "index.js"
9
+ "bin"
10
10
  ],
11
11
  "keywords": [
12
12
  "astro",
@@ -44,6 +44,15 @@
44
44
  "funding": "https://github.com/peltmonger/stardrive?sponsor=1",
45
45
  "type": "module",
46
46
  "engines": {
47
- "node": ">=14"
47
+ "node": ">=18"
48
+ },
49
+ "scripts": {
50
+ "build": "node build.mjs",
51
+ "typecheck": "tsc --noEmit"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^22.10.2",
55
+ "esbuild": "^0.24.2",
56
+ "typescript": "^5.7.2"
48
57
  }
49
58
  }