create-kide-app 0.0.16 → 0.0.18
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 +80 -26
- package/package.json +1 -1
- package/templates/base/gitignore +8 -0
- package/templates/cloudflare/drizzle.config.ts +12 -6
package/index.js
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as p from "@clack/prompts";
|
|
4
|
-
import { execSync } from "node:child_process";
|
|
5
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { execSync, spawn } from "node:child_process";
|
|
5
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
|
|
9
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
const TEMPLATES_DIR = path.join(__dirname, "templates");
|
|
11
11
|
|
|
12
|
+
// Async spawn wrapper so long-running commands don't block clack spinners
|
|
13
|
+
const runAsync = (cmd, cwd) =>
|
|
14
|
+
new Promise((resolve, reject) => {
|
|
15
|
+
const child = spawn(cmd, { cwd, shell: true });
|
|
16
|
+
let stdout = "";
|
|
17
|
+
let stderr = "";
|
|
18
|
+
child.stdout.on("data", (d) => (stdout += d.toString()));
|
|
19
|
+
child.stderr.on("data", (d) => (stderr += d.toString()));
|
|
20
|
+
child.on("close", (code) => {
|
|
21
|
+
if (code === 0) resolve(stdout);
|
|
22
|
+
else {
|
|
23
|
+
const err = new Error(`Command failed: ${cmd}`);
|
|
24
|
+
err.stderr = stderr;
|
|
25
|
+
reject(err);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
12
30
|
// --- Package manager detection ---
|
|
13
31
|
|
|
14
32
|
const pm = { name: "pnpm", exec: "pnpm dlx", run: "pnpm", install: "pnpm install" };
|
|
@@ -77,6 +95,13 @@ async function main() {
|
|
|
77
95
|
|
|
78
96
|
cpSync(path.join(TEMPLATES_DIR, "base"), projectDir, { recursive: true });
|
|
79
97
|
|
|
98
|
+
// Rename gitignore → .gitignore (npm strips dotfiles from published tarballs)
|
|
99
|
+
const gitignoreSrc = path.join(projectDir, "gitignore");
|
|
100
|
+
if (existsSync(gitignoreSrc)) {
|
|
101
|
+
cpSync(gitignoreSrc, path.join(projectDir, ".gitignore"));
|
|
102
|
+
rmSync(gitignoreSrc);
|
|
103
|
+
}
|
|
104
|
+
|
|
80
105
|
// Apply demo schema and seed data if selected
|
|
81
106
|
if (seedDemo) {
|
|
82
107
|
cpSync(path.join(TEMPLATES_DIR, "demo"), projectDir, { recursive: true });
|
|
@@ -133,9 +158,8 @@ async function main() {
|
|
|
133
158
|
|
|
134
159
|
if (target === "cloudflare") {
|
|
135
160
|
const gitignorePath = path.join(projectDir, ".gitignore");
|
|
136
|
-
|
|
137
|
-
gitignore
|
|
138
|
-
writeFileSync(gitignorePath, gitignore);
|
|
161
|
+
const gitignore = existsSync(gitignorePath) ? readFileSync(gitignorePath, "utf-8") : "";
|
|
162
|
+
writeFileSync(gitignorePath, gitignore + "\n# Cloudflare\n.wrangler/\n");
|
|
139
163
|
}
|
|
140
164
|
|
|
141
165
|
s.stop("Configuration applied");
|
|
@@ -144,7 +168,7 @@ async function main() {
|
|
|
144
168
|
|
|
145
169
|
s.start("Installing dependencies");
|
|
146
170
|
try {
|
|
147
|
-
|
|
171
|
+
await runAsync(pm.install, projectDir);
|
|
148
172
|
s.stop("Dependencies installed");
|
|
149
173
|
} catch {
|
|
150
174
|
s.stop(`${pm.install} failed — run it manually`);
|
|
@@ -193,7 +217,7 @@ async function main() {
|
|
|
193
217
|
|
|
194
218
|
// --- Cloudflare resource setup ---
|
|
195
219
|
|
|
196
|
-
const cf = { d1Created: false, r2Created: false, migrationsApplied: false };
|
|
220
|
+
const cf = { d1Created: false, r2Created: false, migrationsApplied: false, deployed: false, url: null };
|
|
197
221
|
if (target === "cloudflare") {
|
|
198
222
|
const setupNow = await p.confirm({
|
|
199
223
|
message: "Set up Cloudflare resources now? (creates D1 database and R2 bucket)",
|
|
@@ -293,6 +317,7 @@ async function main() {
|
|
|
293
317
|
execSync(`${pm.exec} wrangler d1 migrations apply ${projectName}-db --remote`, {
|
|
294
318
|
cwd: projectDir,
|
|
295
319
|
stdio: "pipe",
|
|
320
|
+
input: "y\n",
|
|
296
321
|
});
|
|
297
322
|
cf.migrationsApplied = true;
|
|
298
323
|
s.stop("Migrations applied");
|
|
@@ -300,6 +325,27 @@ async function main() {
|
|
|
300
325
|
s.stop("Migration apply failed — run manually with: wrangler d1 migrations apply --remote");
|
|
301
326
|
}
|
|
302
327
|
}
|
|
328
|
+
|
|
329
|
+
// Deploy to Cloudflare
|
|
330
|
+
if (cf.migrationsApplied) {
|
|
331
|
+
const doDeploy = await p.confirm({
|
|
332
|
+
message: "Deploy to Cloudflare now?",
|
|
333
|
+
initialValue: true,
|
|
334
|
+
});
|
|
335
|
+
if (!p.isCancel(doDeploy) && doDeploy) {
|
|
336
|
+
s.start("Building and deploying to Cloudflare");
|
|
337
|
+
try {
|
|
338
|
+
const deployOutput = await runAsync(`${pm.run} deploy`, projectDir);
|
|
339
|
+
const urlMatch = deployOutput.match(/https:\/\/[^\s]+\.workers\.dev/);
|
|
340
|
+
if (urlMatch) cf.url = urlMatch[0];
|
|
341
|
+
cf.deployed = true;
|
|
342
|
+
s.stop("Deployed to Cloudflare");
|
|
343
|
+
} catch (err) {
|
|
344
|
+
s.stop("Deploy failed — run manually with: pnpm run deploy");
|
|
345
|
+
if (err.stderr) console.error(err.stderr.slice(-500));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
303
349
|
}
|
|
304
350
|
}
|
|
305
351
|
}
|
|
@@ -315,26 +361,34 @@ async function main() {
|
|
|
315
361
|
console.log(` To start again: cd ${projectName} && pnpm dev\n`);
|
|
316
362
|
}
|
|
317
363
|
} else {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
364
|
+
if (cf.deployed && cf.url) {
|
|
365
|
+
p.note([`Live at: ${cf.url}`, `Admin: ${cf.url}/admin`, "", `cd ${projectName}`, "", "Local development:", ` ${pm.run} dev`, "", "Redeploy:", " pnpm run deploy"].join("\n"), "🎉 Your Kide CMS is live");
|
|
366
|
+
p.outro("Project created!");
|
|
367
|
+
} else {
|
|
368
|
+
const lines = [`cd ${projectName}`];
|
|
369
|
+
const remaining = [];
|
|
370
|
+
if (!cf.d1Created) {
|
|
371
|
+
remaining.push(
|
|
372
|
+
` ${pm.exec} wrangler d1 create ${projectName}-db`,
|
|
373
|
+
" # Copy the database_id to wrangler.toml",
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
if (!cf.r2Created) {
|
|
377
|
+
remaining.push(` ${pm.exec} wrangler r2 bucket create ${projectName}-assets`);
|
|
378
|
+
}
|
|
379
|
+
if (!cf.migrationsApplied) {
|
|
380
|
+
remaining.push(` ${pm.exec} wrangler d1 migrations apply ${projectName}-db --remote`);
|
|
381
|
+
}
|
|
382
|
+
if (!cf.deployed) {
|
|
383
|
+
remaining.push(` ${pm.run} run deploy`);
|
|
384
|
+
}
|
|
385
|
+
if (remaining.length > 0) {
|
|
386
|
+
lines.push("", "Remaining setup:", ...remaining);
|
|
387
|
+
}
|
|
388
|
+
lines.push("", "Local development:", ` ${pm.run} dev`);
|
|
389
|
+
p.note(lines.join("\n"), "Next steps");
|
|
390
|
+
p.outro("Project created!");
|
|
334
391
|
}
|
|
335
|
-
lines.push("", "Local development:", ` ${pm.run} dev`, "", "Deploy:", " pnpm run deploy");
|
|
336
|
-
p.note(lines.join("\n"), "Next steps");
|
|
337
|
-
p.outro("Project created!");
|
|
338
392
|
}
|
|
339
393
|
}
|
|
340
394
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import { readdirSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
2
|
import { defineConfig } from "drizzle-kit";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// Look for the local D1 sqlite file (created by wrangler after first run).
|
|
5
|
+
// Returns null if it doesn't exist yet — `drizzle-kit generate` doesn't need it.
|
|
6
|
+
function getLocalD1Path(): string | null {
|
|
5
7
|
const dir = ".wrangler/state/v3/d1/miniflare-D1DatabaseObject";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
if (!existsSync(dir)) return null;
|
|
9
|
+
try {
|
|
10
|
+
const file = readdirSync(dir).find((f) => f.endsWith(".sqlite") && f !== "*.sqlite");
|
|
11
|
+
return file ? `${dir}/${file}` : null;
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
9
15
|
}
|
|
10
16
|
|
|
11
17
|
export default defineConfig({
|
|
@@ -13,6 +19,6 @@ export default defineConfig({
|
|
|
13
19
|
out: "./src/cms/migrations",
|
|
14
20
|
dialect: "sqlite",
|
|
15
21
|
dbCredentials: {
|
|
16
|
-
url: getLocalD1Path(),
|
|
22
|
+
url: getLocalD1Path() ?? ":memory:",
|
|
17
23
|
},
|
|
18
24
|
});
|