create-macostack 0.2.0 → 0.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/create-macostack.ts +48 -35
- package/package.json +1 -1
package/bin/create-macostack.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import {
|
|
3
3
|
cancel,
|
|
4
|
-
confirm,
|
|
5
4
|
intro,
|
|
6
5
|
isCancel,
|
|
7
|
-
log,
|
|
8
6
|
multiselect,
|
|
9
7
|
note,
|
|
10
8
|
outro,
|
|
@@ -12,11 +10,9 @@ import {
|
|
|
12
10
|
text,
|
|
13
11
|
} from "@clack/prompts";
|
|
14
12
|
import {
|
|
15
|
-
closeSync,
|
|
16
13
|
existsSync,
|
|
17
14
|
mkdirSync,
|
|
18
15
|
mkdtempSync,
|
|
19
|
-
openSync,
|
|
20
16
|
readdirSync,
|
|
21
17
|
rmSync,
|
|
22
18
|
} from "node:fs";
|
|
@@ -127,29 +123,6 @@ const answered = <T>(value: T | symbol): T => {
|
|
|
127
123
|
return value as T;
|
|
128
124
|
};
|
|
129
125
|
|
|
130
|
-
// The setup wizards downstream are interactive. Under `bun create` / `bunx`,
|
|
131
|
-
// stdin often arrives as a pipe (not a TTY) even though a human is right
|
|
132
|
-
// there — so we hand children the real terminal via /dev/tty.
|
|
133
|
-
const openTty = (): number | null => {
|
|
134
|
-
try {
|
|
135
|
-
return openSync("/dev/tty", "r");
|
|
136
|
-
} catch {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const run = (command: string[], cwd: string) => {
|
|
142
|
-
const ttyFd = openTty();
|
|
143
|
-
const result = Bun.spawnSync(command, {
|
|
144
|
-
cwd,
|
|
145
|
-
stdin: ttyFd ?? "inherit",
|
|
146
|
-
stdout: "inherit",
|
|
147
|
-
stderr: "inherit",
|
|
148
|
-
});
|
|
149
|
-
if (ttyFd !== null) closeSync(ttyFd);
|
|
150
|
-
return result.exitCode;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
126
|
const githubToken = (): string | null => {
|
|
154
127
|
const fromEnv = Bun.env.GITHUB_TOKEN ?? Bun.env.GH_TOKEN;
|
|
155
128
|
if (fromEnv) return fromEnv;
|
|
@@ -299,19 +272,59 @@ for (const p of selected) {
|
|
|
299
272
|
);
|
|
300
273
|
}
|
|
301
274
|
|
|
302
|
-
//
|
|
275
|
+
// Configure the template. It OWNS its questions — we fetch them as data
|
|
276
|
+
// (`setup --questions`), ask them in THIS wizard (nested interactive
|
|
277
|
+
// prompts don't get keyboard control under `bun create`), and apply the
|
|
278
|
+
// answers non-interactively (`setup --yes`).
|
|
303
279
|
const pkg = existsSync(resolve(dest, "package.json"))
|
|
304
280
|
? await Bun.file(resolve(dest, "package.json")).json()
|
|
305
281
|
: null;
|
|
306
282
|
if (!flags["skip-setup"] && pkg?.scripts?.setup) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
283
|
+
let questions: { modules?: { options: string[]; initial: string[]; exclusive?: string[][] } } | null = null;
|
|
284
|
+
if (!flags.yes) {
|
|
285
|
+
const q = Bun.spawnSync(["bun", "run", "setup", "--questions"], {
|
|
286
|
+
cwd: dest,
|
|
287
|
+
stdout: "pipe",
|
|
288
|
+
stderr: "pipe",
|
|
289
|
+
});
|
|
290
|
+
if (q.exitCode === 0) {
|
|
291
|
+
try {
|
|
292
|
+
questions = JSON.parse(q.stdout.toString().trim().split("\n").at(-1) ?? "");
|
|
293
|
+
} catch {}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const setupArgs = ["run", "setup", "--yes", "--name", appName];
|
|
298
|
+
|
|
299
|
+
if (questions?.modules) {
|
|
300
|
+
let chosen: string[];
|
|
301
|
+
for (;;) {
|
|
302
|
+
chosen = answered(
|
|
303
|
+
await multiselect({
|
|
304
|
+
message: `${p.label} — which modules should start ON? (everything stays in the code; off = dormant, ready when you need it)`,
|
|
305
|
+
options: questions.modules.options.map((m) => ({ value: m, label: m })),
|
|
306
|
+
initialValues: questions.modules.initial,
|
|
307
|
+
required: false,
|
|
308
|
+
}),
|
|
309
|
+
);
|
|
310
|
+
const clash = (questions.modules.exclusive ?? []).find((pair) =>
|
|
311
|
+
pair.every((m) => chosen.includes(m)),
|
|
312
|
+
);
|
|
313
|
+
if (!clash) break;
|
|
314
|
+
note(`${clash.join(" and ")} can't both be on — keep one of the two.`, "One thing");
|
|
315
|
+
}
|
|
316
|
+
setupArgs.push("--modules", chosen.join(","));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const s3 = spinner();
|
|
320
|
+
s3.start(`${p.label} — applying your setup (modules, fresh secrets, git)`);
|
|
321
|
+
const setup = Bun.spawnSync(["bun", ...setupArgs], { cwd: dest, stdout: "pipe", stderr: "pipe" });
|
|
322
|
+
if (setup.exitCode === 0) {
|
|
323
|
+
s3.stop(`${p.label} — configured`);
|
|
324
|
+
} else {
|
|
325
|
+
s3.stop(`${p.label} — setup didn't finish`);
|
|
313
326
|
note(
|
|
314
|
-
`
|
|
327
|
+
`Everything downloaded fine — just finish it yourself:\n cd ${appName}/${p.folder} && bun run setup`,
|
|
315
328
|
"Heads up",
|
|
316
329
|
);
|
|
317
330
|
}
|