create-kide-app 0.3.4 → 0.3.6
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/index.js +49 -24
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as p from "@clack/prompts";
|
|
4
|
-
import {
|
|
4
|
+
import { execSync, spawn } from "node:child_process";
|
|
5
5
|
import {
|
|
6
6
|
cpSync,
|
|
7
7
|
existsSync,
|
|
@@ -31,6 +31,27 @@ const runAsync = (cmd, cwd) =>
|
|
|
31
31
|
});
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
// Argv-based variant: no shell, so untrusted values (like the project directory)
|
|
35
|
+
// are passed as single arguments and can never be reinterpreted as commands.
|
|
36
|
+
const runFileAsync = (file, args, cwd) =>
|
|
37
|
+
new Promise((resolve, reject) => {
|
|
38
|
+
const child = spawn(file, args, { cwd });
|
|
39
|
+
let stdout = "";
|
|
40
|
+
let stderr = "";
|
|
41
|
+
child.stdout.on("data", (d) => (stdout += d.toString()));
|
|
42
|
+
child.stderr.on("data", (d) => (stderr += d.toString()));
|
|
43
|
+
child.on("error", reject);
|
|
44
|
+
child.on("close", (code) => {
|
|
45
|
+
if (code === 0) resolve(stdout);
|
|
46
|
+
else {
|
|
47
|
+
const err = new Error(`Command failed: ${file} ${args.join(" ")}`);
|
|
48
|
+
err.stderr = stderr;
|
|
49
|
+
err.stdout = stdout;
|
|
50
|
+
reject(err);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
34
55
|
// --- Package manager detection ---
|
|
35
56
|
|
|
36
57
|
const pm = {
|
|
@@ -97,13 +118,17 @@ const validateProjectName = (value) => {
|
|
|
97
118
|
|
|
98
119
|
// Resolve the latest release tag (v-prefixed semver) so scaffolds pin to a
|
|
99
120
|
// deliberate release instead of whatever HEAD happens to be. Returns null when
|
|
100
|
-
// the repo has no tags (falls back to the default branch).
|
|
101
|
-
|
|
121
|
+
// the repo has no tags (falls back to the default branch). Async so the
|
|
122
|
+
// "Downloading template" spinner keeps animating during the network call.
|
|
123
|
+
const resolveLatestTag = async () => {
|
|
102
124
|
try {
|
|
103
|
-
const output =
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
125
|
+
const output = await runFileAsync("git", [
|
|
126
|
+
"ls-remote",
|
|
127
|
+
"--tags",
|
|
128
|
+
"--sort=-v:refname",
|
|
129
|
+
REPO,
|
|
130
|
+
"v*",
|
|
131
|
+
]);
|
|
107
132
|
for (const line of output.split("\n")) {
|
|
108
133
|
const match = line.match(/refs\/tags\/(v[0-9][^^\s]*)$/);
|
|
109
134
|
if (match) return match[1];
|
|
@@ -195,27 +220,14 @@ async function main() {
|
|
|
195
220
|
|
|
196
221
|
s.start("Downloading template");
|
|
197
222
|
|
|
198
|
-
const templateRef = resolveLatestTag();
|
|
223
|
+
const templateRef = await resolveLatestTag();
|
|
199
224
|
let templateCommit = null;
|
|
200
225
|
|
|
201
226
|
try {
|
|
202
227
|
const branchArgs = templateRef ? ["--branch", templateRef] : [];
|
|
203
|
-
|
|
204
|
-
// and can never be reinterpreted as a command regardless of what it contains.
|
|
205
|
-
execFileSync(
|
|
206
|
-
"git",
|
|
207
|
-
["clone", "--depth", "1", ...branchArgs, REPO, projectDir],
|
|
208
|
-
{
|
|
209
|
-
stdio: "pipe",
|
|
210
|
-
},
|
|
211
|
-
);
|
|
228
|
+
await runFileAsync("git", ["clone", "--depth", "1", ...branchArgs, REPO, projectDir]);
|
|
212
229
|
try {
|
|
213
|
-
templateCommit =
|
|
214
|
-
cwd: projectDir,
|
|
215
|
-
stdio: "pipe",
|
|
216
|
-
})
|
|
217
|
-
.toString()
|
|
218
|
-
.trim();
|
|
230
|
+
templateCommit = (await runFileAsync("git", ["rev-parse", "HEAD"], projectDir)).trim();
|
|
219
231
|
} catch {
|
|
220
232
|
// best-effort — stamp without a commit hash
|
|
221
233
|
}
|
|
@@ -723,12 +735,25 @@ async function main() {
|
|
|
723
735
|
s.stop("Schema generation failed — run `cms:generate` manually");
|
|
724
736
|
}
|
|
725
737
|
|
|
738
|
+
// --- Apply schema to the local database (local target only; Cloudflare's D1
|
|
739
|
+
// needs Worker bindings). Without this, only `pnpm dev` (which pushes on boot)
|
|
740
|
+
// works out of the box — `pnpm build && pnpm preview` would hit a table-less DB.
|
|
741
|
+
|
|
742
|
+
if (target === "local") {
|
|
743
|
+
s.start("Preparing database");
|
|
744
|
+
try {
|
|
745
|
+
await runAsync(`${pm.run} cms:push`, projectDir);
|
|
746
|
+
s.stop("Database ready");
|
|
747
|
+
} catch {
|
|
748
|
+
s.stop("Database push failed — run `pnpm cms:push` manually");
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
726
752
|
// --- Seed starter content (local target only) ---
|
|
727
753
|
|
|
728
754
|
if (starter && seedRequested && target === "local") {
|
|
729
755
|
s.start("Seeding example content");
|
|
730
756
|
try {
|
|
731
|
-
await runAsync(`${pm.run} cms:push`, projectDir);
|
|
732
757
|
await runAsync(`${pm.exec} kide seed`, projectDir);
|
|
733
758
|
s.stop("Example content seeded");
|
|
734
759
|
} catch {
|