create-stardrive 1.1.0 → 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.
- package/bin/index.js +241 -22
- package/package.json +4 -4
package/bin/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import
|
|
4
|
+
import path4 from "node:path";
|
|
5
5
|
|
|
6
6
|
// src/args.ts
|
|
7
7
|
function parseArgs(argv) {
|
|
@@ -84,27 +84,245 @@ var devCommands = {
|
|
|
84
84
|
bun: "bun run dev"
|
|
85
85
|
};
|
|
86
86
|
|
|
87
|
-
// src/
|
|
87
|
+
// src/features.ts
|
|
88
88
|
import fs from "node:fs";
|
|
89
89
|
import path from "node:path";
|
|
90
90
|
import readline from "node:readline/promises";
|
|
91
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
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
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";
|
|
92
310
|
function isValidProjectName(name) {
|
|
93
311
|
return /^[a-z0-9._-]+$/i.test(name) && !/^[._]/.test(name);
|
|
94
312
|
}
|
|
95
313
|
async function askProjectName(initial) {
|
|
96
314
|
if (initial && isValidProjectName(initial)) {
|
|
97
|
-
const targetDir2 =
|
|
98
|
-
if (!
|
|
315
|
+
const targetDir2 = path2.resolve(initial);
|
|
316
|
+
if (!fs2.existsSync(targetDir2)) return initial;
|
|
99
317
|
fail(`Directory "${initial}" already exists.`);
|
|
100
318
|
}
|
|
101
|
-
if (!
|
|
319
|
+
if (!input2.isTTY) {
|
|
102
320
|
fail(
|
|
103
321
|
`Project name "${initial ?? ""}" is missing or invalid and stdin is not interactive.`
|
|
104
322
|
);
|
|
105
323
|
process.exit(1);
|
|
106
324
|
}
|
|
107
|
-
const rl =
|
|
325
|
+
const rl = readline2.createInterface({ input: input2, output: output3 });
|
|
108
326
|
try {
|
|
109
327
|
while (true) {
|
|
110
328
|
const answer = (await rl.question(
|
|
@@ -117,8 +335,8 @@ async function askProjectName(initial) {
|
|
|
117
335
|
);
|
|
118
336
|
continue;
|
|
119
337
|
}
|
|
120
|
-
const targetDir2 =
|
|
121
|
-
if (
|
|
338
|
+
const targetDir2 = path2.resolve(name);
|
|
339
|
+
if (fs2.existsSync(targetDir2)) {
|
|
122
340
|
console.log(
|
|
123
341
|
` ${c.yellow("!")} "${name}" already exists. Try another.`
|
|
124
342
|
);
|
|
@@ -147,10 +365,10 @@ function compareSemver(a, b) {
|
|
|
147
365
|
return 0;
|
|
148
366
|
}
|
|
149
367
|
function resolveTag(requestedVersion2) {
|
|
150
|
-
const
|
|
368
|
+
const output4 = execSync(`git ls-remote --tags --refs ${STARDRIVE_REPO}`, {
|
|
151
369
|
encoding: "utf8"
|
|
152
370
|
});
|
|
153
|
-
const tags =
|
|
371
|
+
const tags = output4.split("\n").map((line) => line.split("refs/tags/")[1]).filter((t) => Boolean(t));
|
|
154
372
|
if (requestedVersion2) {
|
|
155
373
|
const wanted = normalizeTag(requestedVersion2);
|
|
156
374
|
if (!tags.includes(wanted)) {
|
|
@@ -168,11 +386,11 @@ function resolveTag(requestedVersion2) {
|
|
|
168
386
|
}
|
|
169
387
|
|
|
170
388
|
// src/scaffold.ts
|
|
171
|
-
import
|
|
389
|
+
import fs3 from "node:fs";
|
|
172
390
|
import os from "node:os";
|
|
173
|
-
import
|
|
391
|
+
import path3 from "node:path";
|
|
174
392
|
import { execSync as execSync2 } from "node:child_process";
|
|
175
|
-
var TRIM_ENTRIES = ["scripts/syncVersion.js", "SECURITY.md", ".github"];
|
|
393
|
+
var TRIM_ENTRIES = ["scripts/syncVersion.js", ".ai/TRIMMING_GUIDE.md", "SECURITY.md", ".github"];
|
|
176
394
|
var STALE_LOCKFILES = [
|
|
177
395
|
"package-lock.json",
|
|
178
396
|
"pnpm-lock.yaml",
|
|
@@ -180,7 +398,7 @@ var STALE_LOCKFILES = [
|
|
|
180
398
|
"bun.lockb"
|
|
181
399
|
];
|
|
182
400
|
function cloneRepo(tag2, targetDir2, projectName2) {
|
|
183
|
-
const tempDir =
|
|
401
|
+
const tempDir = fs3.mkdtempSync(path3.join(os.tmpdir(), "create-app-"));
|
|
184
402
|
step(`Beaming ${c.bold(tag2)} down from orbit`);
|
|
185
403
|
info(STARDRIVE_REPO);
|
|
186
404
|
execSync2(
|
|
@@ -189,11 +407,11 @@ function cloneRepo(tag2, targetDir2, projectName2) {
|
|
|
189
407
|
stdio: ["ignore", "ignore", "inherit"]
|
|
190
408
|
}
|
|
191
409
|
);
|
|
192
|
-
|
|
410
|
+
fs3.rmSync(path3.join(tempDir, ".git"), {
|
|
193
411
|
recursive: true,
|
|
194
412
|
force: true
|
|
195
413
|
});
|
|
196
|
-
|
|
414
|
+
fs3.cpSync(tempDir, targetDir2, {
|
|
197
415
|
recursive: true
|
|
198
416
|
});
|
|
199
417
|
ok(`Landed in ${c.bold(projectName2)}/`);
|
|
@@ -201,13 +419,13 @@ function cloneRepo(tag2, targetDir2, projectName2) {
|
|
|
201
419
|
function trimProject(targetDir2) {
|
|
202
420
|
step("Trimming unused boosters");
|
|
203
421
|
for (const entry of TRIM_ENTRIES) {
|
|
204
|
-
|
|
422
|
+
fs3.rmSync(path3.join(targetDir2, entry), {
|
|
205
423
|
recursive: true,
|
|
206
424
|
force: true
|
|
207
425
|
});
|
|
208
426
|
}
|
|
209
427
|
for (const file of STALE_LOCKFILES) {
|
|
210
|
-
|
|
428
|
+
fs3.rmSync(path3.join(targetDir2, file), {
|
|
211
429
|
force: true
|
|
212
430
|
});
|
|
213
431
|
}
|
|
@@ -215,9 +433,9 @@ function trimProject(targetDir2) {
|
|
|
215
433
|
}
|
|
216
434
|
function calibratePackageJson(targetDir2, projectName2, pm2) {
|
|
217
435
|
step("Calibrating package.json");
|
|
218
|
-
const packageJsonPath =
|
|
436
|
+
const packageJsonPath = path3.join(targetDir2, "package.json");
|
|
219
437
|
const pkg = JSON.parse(
|
|
220
|
-
|
|
438
|
+
fs3.readFileSync(packageJsonPath, "utf8")
|
|
221
439
|
);
|
|
222
440
|
pkg.name = projectName2;
|
|
223
441
|
if (pkg.scripts) {
|
|
@@ -230,7 +448,7 @@ function calibratePackageJson(targetDir2, projectName2, pm2) {
|
|
|
230
448
|
if (pm2 === "bun") {
|
|
231
449
|
pkg.packageManager = `bun@${execSync2("bun --version").toString().trim()}`;
|
|
232
450
|
}
|
|
233
|
-
|
|
451
|
+
fs3.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2));
|
|
234
452
|
ok(`Named your ship ${c.bold(projectName2)}`);
|
|
235
453
|
}
|
|
236
454
|
function installDependencies(targetDir2, projectName2, pm2, skipInstall2) {
|
|
@@ -256,13 +474,14 @@ if (!commandExists(pm)) {
|
|
|
256
474
|
}
|
|
257
475
|
console.log(banner);
|
|
258
476
|
var projectName = await askProjectName(positional[0]);
|
|
259
|
-
var targetDir =
|
|
477
|
+
var targetDir = path4.resolve(projectName);
|
|
260
478
|
step("Locating the latest Stardrive release");
|
|
261
479
|
var tag = resolveTag(requestedVersion);
|
|
262
480
|
ok(`Selected ${c.bold(tag)}`);
|
|
263
481
|
cloneRepo(tag, targetDir, projectName);
|
|
264
482
|
trimProject(targetDir);
|
|
265
483
|
calibratePackageJson(targetDir, projectName, pm);
|
|
484
|
+
await configureFeatures(targetDir);
|
|
266
485
|
installDependencies(targetDir, projectName, pm, skipInstall);
|
|
267
486
|
console.log(`
|
|
268
487
|
${c.green("All systems go.")} ${c.dim("Pre-flight checklist complete.")}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-stardrive",
|
|
3
|
-
"version": "1.
|
|
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"
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"typecheck": "tsc --noEmit"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@types/node": "^
|
|
55
|
-
"esbuild": "^0.
|
|
56
|
-
"typescript": "^
|
|
54
|
+
"@types/node": "^26.0.0",
|
|
55
|
+
"esbuild": "^0.28.1",
|
|
56
|
+
"typescript": "^6.0.3"
|
|
57
57
|
}
|
|
58
58
|
}
|