create-stardrive 1.1.0 → 1.2.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.
- package/bin/index.js +248 -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,18 @@ 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 = [
|
|
393
|
+
var TRIM_ENTRIES = [
|
|
394
|
+
"scripts/syncVersion.js",
|
|
395
|
+
".ai/TRIMMING_GUIDE.md",
|
|
396
|
+
"SECURITY.md",
|
|
397
|
+
"CHANGELOG.md",
|
|
398
|
+
"repository-header.png",
|
|
399
|
+
".github"
|
|
400
|
+
];
|
|
176
401
|
var STALE_LOCKFILES = [
|
|
177
402
|
"package-lock.json",
|
|
178
403
|
"pnpm-lock.yaml",
|
|
@@ -180,7 +405,7 @@ var STALE_LOCKFILES = [
|
|
|
180
405
|
"bun.lockb"
|
|
181
406
|
];
|
|
182
407
|
function cloneRepo(tag2, targetDir2, projectName2) {
|
|
183
|
-
const tempDir =
|
|
408
|
+
const tempDir = fs3.mkdtempSync(path3.join(os.tmpdir(), "create-app-"));
|
|
184
409
|
step(`Beaming ${c.bold(tag2)} down from orbit`);
|
|
185
410
|
info(STARDRIVE_REPO);
|
|
186
411
|
execSync2(
|
|
@@ -189,11 +414,11 @@ function cloneRepo(tag2, targetDir2, projectName2) {
|
|
|
189
414
|
stdio: ["ignore", "ignore", "inherit"]
|
|
190
415
|
}
|
|
191
416
|
);
|
|
192
|
-
|
|
417
|
+
fs3.rmSync(path3.join(tempDir, ".git"), {
|
|
193
418
|
recursive: true,
|
|
194
419
|
force: true
|
|
195
420
|
});
|
|
196
|
-
|
|
421
|
+
fs3.cpSync(tempDir, targetDir2, {
|
|
197
422
|
recursive: true
|
|
198
423
|
});
|
|
199
424
|
ok(`Landed in ${c.bold(projectName2)}/`);
|
|
@@ -201,13 +426,13 @@ function cloneRepo(tag2, targetDir2, projectName2) {
|
|
|
201
426
|
function trimProject(targetDir2) {
|
|
202
427
|
step("Trimming unused boosters");
|
|
203
428
|
for (const entry of TRIM_ENTRIES) {
|
|
204
|
-
|
|
429
|
+
fs3.rmSync(path3.join(targetDir2, entry), {
|
|
205
430
|
recursive: true,
|
|
206
431
|
force: true
|
|
207
432
|
});
|
|
208
433
|
}
|
|
209
434
|
for (const file of STALE_LOCKFILES) {
|
|
210
|
-
|
|
435
|
+
fs3.rmSync(path3.join(targetDir2, file), {
|
|
211
436
|
force: true
|
|
212
437
|
});
|
|
213
438
|
}
|
|
@@ -215,9 +440,9 @@ function trimProject(targetDir2) {
|
|
|
215
440
|
}
|
|
216
441
|
function calibratePackageJson(targetDir2, projectName2, pm2) {
|
|
217
442
|
step("Calibrating package.json");
|
|
218
|
-
const packageJsonPath =
|
|
443
|
+
const packageJsonPath = path3.join(targetDir2, "package.json");
|
|
219
444
|
const pkg = JSON.parse(
|
|
220
|
-
|
|
445
|
+
fs3.readFileSync(packageJsonPath, "utf8")
|
|
221
446
|
);
|
|
222
447
|
pkg.name = projectName2;
|
|
223
448
|
if (pkg.scripts) {
|
|
@@ -230,7 +455,7 @@ function calibratePackageJson(targetDir2, projectName2, pm2) {
|
|
|
230
455
|
if (pm2 === "bun") {
|
|
231
456
|
pkg.packageManager = `bun@${execSync2("bun --version").toString().trim()}`;
|
|
232
457
|
}
|
|
233
|
-
|
|
458
|
+
fs3.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2));
|
|
234
459
|
ok(`Named your ship ${c.bold(projectName2)}`);
|
|
235
460
|
}
|
|
236
461
|
function installDependencies(targetDir2, projectName2, pm2, skipInstall2) {
|
|
@@ -256,13 +481,14 @@ if (!commandExists(pm)) {
|
|
|
256
481
|
}
|
|
257
482
|
console.log(banner);
|
|
258
483
|
var projectName = await askProjectName(positional[0]);
|
|
259
|
-
var targetDir =
|
|
484
|
+
var targetDir = path4.resolve(projectName);
|
|
260
485
|
step("Locating the latest Stardrive release");
|
|
261
486
|
var tag = resolveTag(requestedVersion);
|
|
262
487
|
ok(`Selected ${c.bold(tag)}`);
|
|
263
488
|
cloneRepo(tag, targetDir, projectName);
|
|
264
489
|
trimProject(targetDir);
|
|
265
490
|
calibratePackageJson(targetDir, projectName, pm);
|
|
491
|
+
await configureFeatures(targetDir);
|
|
266
492
|
installDependencies(targetDir, projectName, pm, skipInstall);
|
|
267
493
|
console.log(`
|
|
268
494
|
${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.1
|
|
3
|
+
"version": "1.2.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"
|
|
@@ -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
|
}
|