create-stardrive 1.0.2 → 1.2.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 +398 -250
  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,347 @@
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 path4 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);
87
+ // src/features.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";
92
+ function removePaths(targetDir2, entries) {
93
+ for (const entry of entries) {
94
+ fs.rmSync(path.join(targetDir2, entry), {
95
+ recursive: true,
96
+ force: true
97
+ });
98
+ }
99
+ }
100
+ function editFile(targetDir2, relativePath, transform) {
101
+ const filePath = path.join(targetDir2, relativePath);
102
+ if (!fs.existsSync(filePath)) return;
103
+ const before = fs.readFileSync(filePath, "utf8");
104
+ const after = transform(before);
105
+ if (after !== before) fs.writeFileSync(filePath, after);
106
+ }
107
+ function removeLines(source, pattern) {
108
+ return source.split("\n").filter((line) => !pattern.test(line)).join("\n");
109
+ }
110
+ function removeBraceBlock(source, startPattern) {
111
+ const match = startPattern.exec(source);
112
+ if (!match) return source;
113
+ const start = match.index;
114
+ let i = source.indexOf("{", start);
115
+ if (i === -1) return source;
116
+ let depth = 0;
117
+ let end = -1;
118
+ for (; i < source.length; i++) {
119
+ const ch = source[i];
120
+ if (ch === "{") depth++;
121
+ else if (ch === "}") {
122
+ depth--;
123
+ if (depth === 0) {
124
+ end = i;
125
+ break;
126
+ }
127
+ }
128
+ }
129
+ if (end === -1) return source;
130
+ let after = end + 1;
131
+ while (after < source.length && /[);,]/.test(source[after])) after++;
132
+ if (source[after] === "\n") after++;
133
+ let head = start;
134
+ while (head > 0 && /[ \t]/.test(source[head - 1])) head--;
135
+ return source.slice(0, head) + source.slice(after);
136
+ }
137
+ function removeCollectionFromExport(source, name) {
138
+ return source.replace(
139
+ /(export const collections\s*=\s*\{)([^}]*)(\};?)/,
140
+ (_full, open, body, close) => {
141
+ const items = body.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0 && entry !== name);
142
+ return `${open} ${items.join(", ")} ${close}`;
143
+ }
144
+ );
145
+ }
146
+ function removeBlog(targetDir2) {
147
+ removePaths(targetDir2, [
148
+ "src/utils/blog.ts",
149
+ "src/utils/reading-time.ts",
150
+ "src/styles/blog.css",
151
+ "src/pages/rss.xml.js",
152
+ "src/pages/[lang]/rss.xml.js",
153
+ "src/pages/blog",
154
+ "src/pages/[lang]/blog",
155
+ "src/layouts/article.astro",
156
+ "src/images/content/articles-fallback.jpg",
157
+ "src/images/content/articles",
158
+ "src/content/articles",
159
+ "src/components/structured/article.astro",
160
+ "src/components/blog",
161
+ "scripts/processSocialImages.js",
162
+ "public/data/articles"
163
+ ]);
164
+ editFile(
165
+ targetDir2,
166
+ "scripts/postbuild.js",
167
+ (content) => removeLines(content, /await import\(['"]\.\/processSocialImages\.js['"]\);/)
168
+ );
169
+ editFile(targetDir2, "src/content.config.ts", (content) => {
170
+ const withoutDecl = removeBraceBlock(content, /const articles = defineCollection\(/);
171
+ return removeCollectionFromExport(withoutDecl, "articles");
172
+ });
173
+ editFile(targetDir2, "theme.config.ts", (content) => {
174
+ const withoutSection = removeBraceBlock(content, /^[ \t]*articles:\s*\{/m);
175
+ const withoutComment = removeLines(withoutSection, /^\s*\/\/ content\/article settings\s*$/);
176
+ return removeLines(withoutComment, /^\s*addArticles:/);
177
+ });
178
+ }
179
+ function removeFaq(targetDir2) {
180
+ removePaths(targetDir2, [
181
+ "src/content/faq-answers",
182
+ "src/pages/faq.astro",
183
+ "src/pages/[lang]/faq.astro"
184
+ ]);
185
+ editFile(targetDir2, "src/content.config.ts", (content) => {
186
+ const withoutDecl = removeBraceBlock(content, /const faq_answers = defineCollection\(/);
187
+ return removeCollectionFromExport(withoutDecl, "faq_answers");
188
+ });
189
+ editFile(
190
+ targetDir2,
191
+ "theme.config.ts",
192
+ (content) => removeLines(content, /^\s*addFAQ:/)
193
+ );
194
+ }
195
+ function removeIntegration(targetDir2) {
196
+ removePaths(targetDir2, [
197
+ "src/images/content/integration",
198
+ "src/content/integration-options",
199
+ "src/pages/integration",
200
+ "src/pages/[lang]/integration",
201
+ "src/components/integration-list.astro"
202
+ ]);
203
+ editFile(targetDir2, "src/content.config.ts", (content) => {
204
+ const withoutDecl = removeBraceBlock(
205
+ content,
206
+ /const integration_options = defineCollection\(/
207
+ );
208
+ return removeCollectionFromExport(withoutDecl, "integration_options");
209
+ });
210
+ }
211
+ function cleanupNav(targetDir2, removed) {
212
+ const routes = [
213
+ removed.blog ? "blog" : null,
214
+ removed.faq ? "faq" : null,
215
+ removed.integration ? "integration" : null
216
+ ].filter((route) => route !== null);
217
+ if (routes.length === 0) return;
218
+ const pattern = new RegExp(
219
+ `getLocaleUrl\\(['"](?:${routes.join("|")})['"]`
220
+ );
221
+ editFile(
222
+ targetDir2,
223
+ "src/components/layout/nav/footer-nav.astro",
224
+ (content) => removeLines(content, pattern)
225
+ );
226
+ }
227
+ function removeCloudflare(targetDir2) {
228
+ removePaths(targetDir2, [
229
+ "scripts/purgeCloudflareCache.js",
230
+ "worker-configuration.d.ts",
231
+ "wrangler.jsonc",
232
+ "public/_headers"
233
+ ]);
234
+ editFile(targetDir2, "astro.config.ts", (content) => {
235
+ const withoutImport = removeLines(
236
+ content,
237
+ /^import cloudflare from ['"]@astrojs\/cloudflare['"];/
238
+ );
239
+ return removeLines(withoutImport, /^\s*adapter: cloudflare\(/);
240
+ });
241
+ const packageJsonPath = path.join(targetDir2, "package.json");
242
+ if (fs.existsSync(packageJsonPath)) {
243
+ const pkg = JSON.parse(
244
+ fs.readFileSync(packageJsonPath, "utf8")
245
+ );
246
+ if (pkg.scripts) delete pkg.scripts["purge:cloudflare"];
247
+ if (pkg.dependencies) {
248
+ delete pkg.dependencies["@astrojs/cloudflare"];
249
+ delete pkg.dependencies["wrangler"];
250
+ }
251
+ if (pkg.devDependencies) {
252
+ delete pkg.devDependencies["@astrojs/cloudflare"];
253
+ delete pkg.devDependencies["wrangler"];
254
+ }
255
+ fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2));
256
+ }
257
+ }
258
+ async function confirm(rl, question, defaultYes = true) {
259
+ const hint = defaultYes ? "(Y/n)" : "(y/N)";
260
+ const answer = (await rl.question(`${c.cyan("?")} ${c.bold(question)} ${c.dim(`${hint} `)}`)).trim().toLowerCase();
261
+ if (!answer) return defaultYes;
262
+ return answer === "y" || answer === "yes";
263
+ }
264
+ async function configureFeatures(targetDir2) {
265
+ step("Configuring optional features");
266
+ if (!input.isTTY) {
267
+ info("Non-interactive shell detected; keeping all features.");
268
+ return;
269
+ }
270
+ const rl = readline.createInterface({ input, output: output2 });
271
+ try {
272
+ const keepBlog = await confirm(rl, "Keep the blog feature?");
273
+ if (!keepBlog) {
274
+ removeBlog(targetDir2);
275
+ ok("Removed the blog feature");
276
+ }
277
+ const keepFaq = await confirm(rl, "Keep the FAQ feature?");
278
+ if (!keepFaq) {
279
+ removeFaq(targetDir2);
280
+ ok("Removed the FAQ feature");
281
+ }
282
+ const keepIntegration = await confirm(rl, "Keep the integration catalog?");
283
+ if (!keepIntegration) {
284
+ removeIntegration(targetDir2);
285
+ ok("Removed the integration catalog");
286
+ }
287
+ cleanupNav(targetDir2, {
288
+ blog: !keepBlog,
289
+ faq: !keepFaq,
290
+ integration: !keepIntegration
291
+ });
292
+ const useCloudflare = await confirm(
293
+ rl,
294
+ "Will you host on Cloudflare Workers?"
295
+ );
296
+ if (!useCloudflare) {
297
+ removeCloudflare(targetDir2);
298
+ ok("Removed Cloudflare-specific setup");
299
+ }
300
+ } finally {
301
+ rl.close();
302
+ }
96
303
  }
97
304
 
98
- //
99
- // Project name (interactive if not provided)
100
- //
101
-
305
+ // src/project-name.ts
306
+ import fs2 from "node:fs";
307
+ import path2 from "node:path";
308
+ import readline2 from "node:readline/promises";
309
+ import { stdin as input2, stdout as output3 } from "node:process";
102
310
  function isValidProjectName(name) {
103
311
  return /^[a-z0-9._-]+$/i.test(name) && !/^[._]/.test(name);
104
312
  }
105
-
106
313
  async function askProjectName(initial) {
107
314
  if (initial && isValidProjectName(initial)) {
108
- const targetDir = path.resolve(initial);
109
- if (!fs.existsSync(targetDir)) return initial;
315
+ const targetDir2 = path2.resolve(initial);
316
+ if (!fs2.existsSync(targetDir2)) return initial;
110
317
  fail(`Directory "${initial}" already exists.`);
111
318
  }
112
-
113
- if (!input.isTTY) {
319
+ if (!input2.isTTY) {
114
320
  fail(
115
- `Project name "${initial || ""}" is missing or invalid and stdin is not interactive.`
321
+ `Project name "${initial ?? ""}" is missing or invalid and stdin is not interactive.`
116
322
  );
117
323
  process.exit(1);
118
324
  }
119
-
120
- const rl = readline.createInterface({ input, output });
121
-
325
+ const rl = readline2.createInterface({ input: input2, output: output3 });
122
326
  try {
123
327
  while (true) {
124
- const answer = (
125
- await rl.question(
126
- `${c.cyan("?")} ${c.bold("Project name:")} ${c.dim("(my-stardrive) ")}`
127
- )
128
- ).trim();
129
-
328
+ const answer = (await rl.question(
329
+ `${c.cyan("?")} ${c.bold("Project name:")} ${c.dim("(my-stardrive) ")}`
330
+ )).trim();
130
331
  const name = answer || "my-stardrive";
131
-
132
332
  if (!isValidProjectName(name)) {
133
333
  console.log(
134
334
  ` ${c.yellow("!")} Use letters, digits, dots, dashes or underscores.`
135
335
  );
136
336
  continue;
137
337
  }
138
-
139
- const targetDir = path.resolve(name);
140
-
141
- if (fs.existsSync(targetDir)) {
142
- console.log(` ${c.yellow("!")} "${name}" already exists. Try another.`);
338
+ const targetDir2 = path2.resolve(name);
339
+ if (fs2.existsSync(targetDir2)) {
340
+ console.log(
341
+ ` ${c.yellow("!")} "${name}" already exists. Try another.`
342
+ );
143
343
  continue;
144
344
  }
145
-
146
345
  return name;
147
346
  }
148
347
  } finally {
@@ -150,196 +349,145 @@ async function askProjectName(initial) {
150
349
  }
151
350
  }
152
351
 
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}`;
352
+ // src/release.ts
353
+ import { execSync } from "node:child_process";
354
+ var STARDRIVE_REPO = "https://github.com/Peltmonger/stardrive.git";
355
+ function normalizeTag(tag2) {
356
+ return tag2.startsWith("v") ? tag2 : `v${tag2}`;
167
357
  }
168
-
169
358
  function compareSemver(a, b) {
170
359
  const pa = a.replace(/^v/, "").split("-")[0].split(".").map(Number);
171
360
  const pb = b.replace(/^v/, "").split("-")[0].split(".").map(Number);
172
-
173
361
  for (let i = 0; i < 3; i++) {
174
- const diff = (pa[i] || 0) - (pb[i] || 0);
362
+ const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
175
363
  if (diff !== 0) return diff;
176
364
  }
177
-
178
365
  return 0;
179
366
  }
180
-
181
- function resolveTag() {
182
- const output = execSync(`git ls-remote --tags --refs ${repo}`, {
183
- encoding: "utf8",
367
+ function resolveTag(requestedVersion2) {
368
+ const output4 = execSync(`git ls-remote --tags --refs ${STARDRIVE_REPO}`, {
369
+ encoding: "utf8"
184
370
  });
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
-
371
+ const tags = output4.split("\n").map((line) => line.split("refs/tags/")[1]).filter((t) => Boolean(t));
372
+ if (requestedVersion2) {
373
+ const wanted = normalizeTag(requestedVersion2);
194
374
  if (!tags.includes(wanted)) {
195
- fail(`Version "${requestedVersion}" not found in ${repo}`);
375
+ fail(`Version "${requestedVersion2}" not found in ${STARDRIVE_REPO}`);
196
376
  process.exit(1);
197
377
  }
198
-
199
378
  return wanted;
200
379
  }
201
-
202
380
  const stable = tags.filter((t) => /^v?\d+\.\d+\.\d+$/.test(t));
203
-
204
381
  if (stable.length === 0) {
205
- fail(`No tagged releases found in ${repo}`);
382
+ fail(`No tagged releases found in ${STARDRIVE_REPO}`);
206
383
  process.exit(1);
207
384
  }
208
-
209
385
  return stable.sort(compareSemver).pop();
210
386
  }
211
387
 
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
- [
388
+ // src/scaffold.ts
389
+ import fs3 from "node:fs";
390
+ import os from "node:os";
391
+ import path3 from "node:path";
392
+ import { execSync as execSync2 } from "node:child_process";
393
+ var TRIM_ENTRIES = ["scripts/syncVersion.js", ".ai/TRIMMING_GUIDE.md", "SECURITY.md", ".github"];
394
+ var STALE_LOCKFILES = [
262
395
  "package-lock.json",
263
396
  "pnpm-lock.yaml",
264
397
  "yarn.lock",
265
- "bun.lockb",
266
- ].forEach((file) => {
267
- fs.rmSync(path.join(targetDir, file), {
268
- force: true,
398
+ "bun.lockb"
399
+ ];
400
+ function cloneRepo(tag2, targetDir2, projectName2) {
401
+ const tempDir = fs3.mkdtempSync(path3.join(os.tmpdir(), "create-app-"));
402
+ step(`Beaming ${c.bold(tag2)} down from orbit`);
403
+ info(STARDRIVE_REPO);
404
+ execSync2(
405
+ `git -c advice.detachedHead=false clone --depth=1 --branch ${tag2} --quiet ${STARDRIVE_REPO} "${tempDir}"`,
406
+ {
407
+ stdio: ["ignore", "ignore", "inherit"]
408
+ }
409
+ );
410
+ fs3.rmSync(path3.join(tempDir, ".git"), {
411
+ recursive: true,
412
+ force: true
269
413
  });
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;
414
+ fs3.cpSync(tempDir, targetDir2, {
415
+ recursive: true
416
+ });
417
+ ok(`Landed in ${c.bold(projectName2)}/`);
290
418
  }
291
-
292
- if (pm === "pnpm") {
293
- pkg.packageManager = `pnpm@${execSync("pnpm --version")
294
- .toString()
295
- .trim()}`;
419
+ function trimProject(targetDir2) {
420
+ step("Trimming unused boosters");
421
+ for (const entry of TRIM_ENTRIES) {
422
+ fs3.rmSync(path3.join(targetDir2, entry), {
423
+ recursive: true,
424
+ force: true
425
+ });
426
+ }
427
+ for (const file of STALE_LOCKFILES) {
428
+ fs3.rmSync(path3.join(targetDir2, file), {
429
+ force: true
430
+ });
431
+ }
432
+ ok("Stripped extras and stale lockfiles");
296
433
  }
297
-
298
- if (pm === "bun") {
299
- pkg.packageManager = `bun@${execSync("bun --version")
300
- .toString()
301
- .trim()}`;
434
+ function calibratePackageJson(targetDir2, projectName2, pm2) {
435
+ step("Calibrating package.json");
436
+ const packageJsonPath = path3.join(targetDir2, "package.json");
437
+ const pkg = JSON.parse(
438
+ fs3.readFileSync(packageJsonPath, "utf8")
439
+ );
440
+ pkg.name = projectName2;
441
+ if (pkg.scripts) {
442
+ delete pkg.scripts["sync-version"];
443
+ delete pkg.scripts["prebuild"];
444
+ }
445
+ if (pm2 === "pnpm") {
446
+ pkg.packageManager = `pnpm@${execSync2("pnpm --version").toString().trim()}`;
447
+ }
448
+ if (pm2 === "bun") {
449
+ pkg.packageManager = `bun@${execSync2("bun --version").toString().trim()}`;
450
+ }
451
+ fs3.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2));
452
+ ok(`Named your ship ${c.bold(projectName2)}`);
302
453
  }
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",
454
+ function installDependencies(targetDir2, projectName2, pm2, skipInstall2) {
455
+ if (skipInstall2) {
456
+ step("Skipping dependency install");
457
+ info(`Run "${pm2} install" inside ${projectName2}/ when you're ready.`);
458
+ return;
459
+ }
460
+ step(`Loading dependencies with ${c.bold(pm2)}`);
461
+ execSync2(`${pm2} install`, {
462
+ cwd: targetDir2,
463
+ stdio: "inherit"
320
464
  });
321
465
  ok("Engines online");
322
- } else {
323
- step("Skipping dependency install");
324
- info(`Run "${pm} install" inside ${projectName}/ when you're ready.`);
325
466
  }
326
467
 
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
-
468
+ // src/index.ts
469
+ var { positional, requestedVersion, skipInstall } = parseArgs(process.argv);
470
+ var pm = detectPackageManager();
471
+ if (!commandExists(pm)) {
472
+ fail(`${pm} is not installed`);
473
+ process.exit(1);
474
+ }
475
+ console.log(banner);
476
+ var projectName = await askProjectName(positional[0]);
477
+ var targetDir = path4.resolve(projectName);
478
+ step("Locating the latest Stardrive release");
479
+ var tag = resolveTag(requestedVersion);
480
+ ok(`Selected ${c.bold(tag)}`);
481
+ cloneRepo(tag, targetDir, projectName);
482
+ trimProject(targetDir);
483
+ calibratePackageJson(targetDir, projectName, pm);
484
+ await configureFeatures(targetDir);
485
+ installDependencies(targetDir, projectName, pm, skipInstall);
338
486
  console.log(`
339
487
  ${c.green("All systems go.")} ${c.dim("Pre-flight checklist complete.")}
340
488
 
341
489
  ${c.dim("$")} ${c.cyan(`cd ${projectName}`)}
342
490
  ${c.dim("$")} ${c.cyan(devCommands[pm])}
343
491
 
344
- ${c.bold("Happy launching!")} ${c.magenta("🚀")}
492
+ ${c.bold("Happy launching!")} ${c.magenta("\u{1F680}")}
345
493
  `);
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "create-stardrive",
3
- "version": "1.0.2",
3
+ "version": "1.2.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": "^26.0.0",
55
+ "esbuild": "^0.28.1",
56
+ "typescript": "^6.0.3"
48
57
  }
49
58
  }