create-kide-app 0.0.36 → 0.1.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.
Files changed (50) hide show
  1. package/README.md +12 -11
  2. package/index.js +81 -445
  3. package/package.json +2 -3
  4. package/templates/base/.env.example +0 -2
  5. package/templates/base/CLAUDE.md +0 -73
  6. package/templates/base/gitignore +0 -8
  7. package/templates/base/package.json +0 -39
  8. package/templates/base/src/cms/adapters/db.ts +0 -49
  9. package/templates/base/src/cms/adapters/email.ts +0 -34
  10. package/templates/base/src/cms/adapters/storage.ts +0 -38
  11. package/templates/base/src/cms/cms.config.ts +0 -7
  12. package/templates/base/src/cms/collections/users.ts +0 -20
  13. package/templates/base/src/cms/internals/create-admin.ts +0 -40
  14. package/templates/base/src/cms/internals/generator.ts +0 -10
  15. package/templates/base/src/cms/internals/runtime.ts +0 -76
  16. package/templates/base/src/components/BlockRenderer.astro +0 -28
  17. package/templates/base/src/env.d.ts +0 -12
  18. package/templates/base/src/pages/index.astro +0 -11
  19. package/templates/base/src/styles/admin.css +0 -253
  20. package/templates/base/src/styles/public.css +0 -2
  21. package/templates/base/tsconfig.json +0 -11
  22. package/templates/cloudflare/astro.config.mjs +0 -16
  23. package/templates/cloudflare/db.ts +0 -23
  24. package/templates/cloudflare/drizzle.config.ts +0 -24
  25. package/templates/cloudflare/storage.ts +0 -25
  26. package/templates/cloudflare/uploads-route.ts +0 -36
  27. package/templates/cloudflare/wrangler.toml +0 -17
  28. package/templates/demo/src/cms/cms.config.ts +0 -17
  29. package/templates/demo/src/cms/collections/authors.ts +0 -20
  30. package/templates/demo/src/cms/collections/front-page.ts +0 -37
  31. package/templates/demo/src/cms/collections/menus.ts +0 -18
  32. package/templates/demo/src/cms/collections/pages.ts +0 -59
  33. package/templates/demo/src/cms/collections/posts.ts +0 -55
  34. package/templates/demo/src/cms/collections/taxonomies.ts +0 -18
  35. package/templates/demo/src/cms/collections/users.ts +0 -20
  36. package/templates/demo/src/cms/internals/seed.data.ts +0 -506
  37. package/templates/demo/src/cms/internals/seed.ts +0 -7
  38. package/templates/demo/src/components/BlockRenderer.astro +0 -32
  39. package/templates/demo/src/components/RichTextContent.astro +0 -18
  40. package/templates/demo/src/components/blocks/Faq.astro +0 -22
  41. package/templates/demo/src/components/blocks/Hero.astro +0 -19
  42. package/templates/demo/src/components/blocks/Image.astro +0 -25
  43. package/templates/demo/src/components/blocks/Text.astro +0 -16
  44. package/templates/demo/src/layouts/PublicLayout.astro +0 -53
  45. package/templates/demo/src/pages/[...slug].astro +0 -31
  46. package/templates/demo/src/pages/blog/[slug].astro +0 -41
  47. package/templates/demo/src/pages/index.astro +0 -51
  48. package/templates/local/astro.config.mjs +0 -23
  49. package/templates/local/db.ts +0 -48
  50. package/templates/local/drizzle.config.ts +0 -15
package/README.md CHANGED
@@ -5,27 +5,28 @@ Scaffold a new [Kide CMS](https://github.com/mhernesniemi/kide-cms) project.
5
5
  ## Usage
6
6
 
7
7
  ```bash
8
- npm create kide-app my-project
9
- # or
10
8
  pnpm create kide-app my-project
11
9
  # or
12
- bunx create-kide-app my-project
10
+ npm create kide-app my-project
13
11
  ```
14
12
 
15
- The CLI will guide you through:
13
+ The CLI asks for:
16
14
 
17
15
  1. **Project name** — directory to create
18
- 2. **Deploy target** — Local/Node.js or Cloudflare
19
- 3. **Demo content** — optionally seed the database
16
+ 2. **Deploy target** — Node.js (default) or Cloudflare
20
17
 
21
18
  ## What it does
22
19
 
23
- - Downloads the latest Kide CMS template from GitHub
24
- - Applies platform-specific configuration (Node.js adapter, Cloudflare D1/R2, etc.)
25
- - Installs dependencies
26
- - Generates the CMS schema
27
- - Optionally seeds demo content
20
+ 1. Clones the latest `kide-cms` from GitHub (shallow).
21
+ 2. Removes the embedded `.git` and renames the project in `package.json`.
22
+ 3. If Cloudflare is selected, overlays the files from `adapters/cloudflare/` and patches `package.json` (swaps `@astrojs/node` → `@astrojs/cloudflare`, adds `wrangler`, removes `sharp`, etc.).
23
+ 4. Removes the `adapters/` scaffolding directory.
24
+ 5. Runs `pnpm install`.
25
+
26
+ For Cloudflare projects, the CLI prints the remaining manual steps (create D1/R2, apply migrations).
28
27
 
29
28
  ## Requirements
30
29
 
31
30
  - Node.js >= 22.12.0
31
+ - `git` on PATH
32
+ - `pnpm`
package/index.js CHANGED
@@ -1,51 +1,43 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import * as p from "@clack/prompts";
4
- import { execSync, spawn } from "node:child_process";
5
- import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { execSync } from "node:child_process";
5
+ import { cpSync, existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
6
6
  import path from "node:path";
7
- import { fileURLToPath } from "node:url";
8
7
 
9
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
- const TEMPLATES_DIR = path.join(__dirname, "templates");
8
+ const REPO = "https://github.com/mhernesniemi/kide-cms.git";
11
9
 
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
- err.stdout = stdout;
26
- reject(err);
27
- }
28
- });
29
- });
30
-
31
- // --- Package manager detection ---
32
-
33
- const pm = { name: "pnpm", exec: "pnpm exec", dlx: "pnpm dlx", run: "pnpm", install: "pnpm install" };
34
-
35
- // --- Main ---
10
+ function applyPackagePatch(pkg, patch) {
11
+ for (const [k, v] of Object.entries(patch.dependencies?.add ?? {})) {
12
+ pkg.dependencies[k] = v;
13
+ }
14
+ for (const k of patch.dependencies?.remove ?? []) {
15
+ delete pkg.dependencies[k];
16
+ }
17
+ pkg.devDependencies ??= {};
18
+ for (const [k, v] of Object.entries(patch.devDependencies?.add ?? {})) {
19
+ pkg.devDependencies[k] = v;
20
+ }
21
+ for (const k of patch.devDependencies?.moveFromDependencies ?? []) {
22
+ if (pkg.dependencies[k]) {
23
+ pkg.devDependencies[k] = pkg.dependencies[k];
24
+ delete pkg.dependencies[k];
25
+ }
26
+ }
27
+ for (const [k, v] of Object.entries(patch.scripts ?? {})) {
28
+ pkg.scripts[k] = v;
29
+ }
30
+ }
36
31
 
37
32
  async function main() {
38
- p.intro("🪐 Create Kide CMS Project");
33
+ p.intro("Create Kide CMS Project");
39
34
 
40
- // 1. Project name
41
35
  const projectName =
42
36
  process.argv[2] ||
43
37
  (await p.text({
44
38
  message: "Project name",
45
39
  placeholder: "my-cms-app",
46
- validate: (value) => {
47
- if (!value) return "Project name is required";
48
- },
40
+ validate: (value) => (value ? undefined : "Project name is required"),
49
41
  }));
50
42
 
51
43
  if (p.isCancel(projectName)) {
@@ -59,448 +51,92 @@ async function main() {
59
51
  process.exit(1);
60
52
  }
61
53
 
62
- // 2. Deploy target
63
- const target = await p.select({
64
- message: "Where will you deploy?",
65
- options: [
66
- { label: "Local / Node.js", value: "local" },
67
- { label: "Cloudflare", value: "cloudflare" },
68
- ],
54
+ const cloudflare = await p.confirm({
55
+ message: "Deploy to Cloudflare? (otherwise Node.js)",
56
+ initialValue: false,
69
57
  });
70
58
 
71
- if (p.isCancel(target)) {
59
+ if (p.isCancel(cloudflare)) {
72
60
  p.cancel("Setup cancelled.");
73
61
  process.exit(0);
74
62
  }
75
63
 
76
- // 3. Demo content (local only — Cloudflare uses remote D1)
77
- let seedDemo = false;
78
- if (target === "local") {
79
- const seed = await p.confirm({
80
- message: "Seed database with demo content?",
81
- initialValue: false,
82
- });
83
-
84
- if (p.isCancel(seed)) {
85
- p.cancel("Setup cancelled.");
86
- process.exit(0);
87
- }
88
- seedDemo = seed;
89
- }
90
-
91
64
  const s = p.spinner();
92
65
 
93
- // --- Scaffold from base template ---
94
-
95
- s.start(`Scaffolding project (using ${pm.name})`);
96
-
97
- cpSync(path.join(TEMPLATES_DIR, "base"), projectDir, { recursive: true });
98
-
99
- // Rename gitignore → .gitignore (npm strips dotfiles from published tarballs)
100
- const gitignoreSrc = path.join(projectDir, "gitignore");
101
- if (existsSync(gitignoreSrc)) {
102
- cpSync(gitignoreSrc, path.join(projectDir, ".gitignore"));
103
- rmSync(gitignoreSrc);
104
- }
105
-
106
- // Apply demo schema and seed data if selected
107
- if (seedDemo) {
108
- cpSync(path.join(TEMPLATES_DIR, "demo"), projectDir, { recursive: true });
109
- }
110
-
111
- s.stop("Project scaffolded");
112
-
113
- // --- Apply target-specific files ---
114
-
115
- s.start(`Applying ${target} configuration`);
116
-
117
- const targetDir = path.join(TEMPLATES_DIR, target);
118
-
119
- cpSync(path.join(targetDir, "astro.config.mjs"), path.join(projectDir, "astro.config.mjs"));
120
- cpSync(path.join(targetDir, "db.ts"), path.join(projectDir, "src/cms/adapters/db.ts"));
121
- cpSync(path.join(targetDir, "drizzle.config.ts"), path.join(projectDir, "drizzle.config.ts"));
122
-
123
- if (target === "cloudflare") {
124
- cpSync(path.join(targetDir, "storage.ts"), path.join(projectDir, "src/cms/adapters/storage.ts"));
125
- const uploadsRouteDir = path.join(projectDir, "src/pages/uploads");
126
- mkdirSync(uploadsRouteDir, { recursive: true });
127
- cpSync(path.join(targetDir, "uploads-route.ts"), path.join(uploadsRouteDir, "[...path].ts"));
128
- }
66
+ s.start("Cloning kide-cms");
67
+ execSync(`git clone --depth 1 ${REPO} "${projectDir}"`, { stdio: "pipe" });
68
+ rmSync(path.join(projectDir, ".git"), { recursive: true, force: true });
69
+ s.stop("Cloned");
129
70
 
130
71
  const pkgPath = path.join(projectDir, "package.json");
131
72
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
132
-
133
73
  pkg.name = projectName;
74
+ pkg.version = "0.0.1";
75
+
76
+ const adaptersDir = path.join(projectDir, "adapters");
77
+
78
+ if (cloudflare) {
79
+ s.start("Applying Cloudflare adapter");
80
+ const cfDir = path.join(adaptersDir, "cloudflare");
81
+
82
+ const overlayFiles = [
83
+ "astro.config.mjs",
84
+ "drizzle.config.ts",
85
+ "src/cms/adapters/db.ts",
86
+ "src/cms/adapters/storage.ts",
87
+ "src/pages/uploads/[...path].ts",
88
+ ];
89
+ for (const f of overlayFiles) {
90
+ cpSync(path.join(cfDir, f), path.join(projectDir, f));
91
+ }
134
92
 
135
- if (seedDemo) {
136
- pkg.scripts["cms:seed"] = "node --import tsx src/cms/internals/seed.ts";
137
- }
138
-
139
- if (target === "cloudflare") {
140
- delete pkg.dependencies["@astrojs/node"];
141
- pkg.dependencies["@astrojs/cloudflare"] = "^13.0.0";
142
- // libsql is for local target only; Cloudflare uses D1 directly
143
- delete pkg.dependencies["@libsql/client"];
144
- delete pkg.dependencies["sharp"];
145
-
146
- let wranglerContent = readFileSync(path.join(targetDir, "wrangler.toml"), "utf-8");
147
- wranglerContent = wranglerContent.replaceAll("{{PROJECT_NAME}}", projectName);
148
- writeFileSync(path.join(projectDir, "wrangler.toml"), wranglerContent);
93
+ const wrangler = readFileSync(path.join(cfDir, "wrangler.toml"), "utf-8").replaceAll(
94
+ "{{PROJECT_NAME}}",
95
+ projectName,
96
+ );
97
+ writeFileSync(path.join(projectDir, "wrangler.toml"), wrangler);
149
98
 
150
- pkg.devDependencies.wrangler = "^4.0.0";
99
+ const patch = JSON.parse(readFileSync(path.join(cfDir, "package.patch.json"), "utf-8"));
100
+ applyPackagePatch(pkg, patch);
151
101
 
152
- pkg.scripts.dev = "astro dev";
153
- pkg.scripts.build = "astro build";
154
- pkg.scripts.preview = "astro build && wrangler dev --config dist/server/wrangler.json";
155
- pkg.scripts.deploy = "astro build && wrangler deploy --config dist/server/wrangler.json";
102
+ s.stop("Cloudflare adapter applied");
156
103
  }
157
104
 
105
+ rmSync(adaptersDir, { recursive: true, force: true });
158
106
  writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
159
107
 
160
- if (target === "cloudflare") {
161
- const gitignorePath = path.join(projectDir, ".gitignore");
162
- const gitignore = existsSync(gitignorePath) ? readFileSync(gitignorePath, "utf-8") : "";
163
- writeFileSync(gitignorePath, gitignore + "\n# Cloudflare\n.wrangler/\n");
164
- }
165
-
166
- s.stop("Configuration applied");
167
-
168
- // --- Install dependencies ---
169
-
170
108
  s.start("Installing dependencies");
171
109
  try {
172
- await runAsync(pm.install, projectDir);
110
+ execSync("pnpm install", { cwd: projectDir, stdio: "pipe" });
173
111
  s.stop("Dependencies installed");
174
112
  } catch {
175
- s.stop(`${pm.install} failed — run it manually`);
176
- }
177
-
178
- // --- Initialize git repository ---
179
-
180
- let gitInitialized = false;
181
- try {
182
- execSync("git init -q && git add . && git commit -q -m 'Initial commit from create-kide-app'", {
183
- cwd: projectDir,
184
- stdio: "pipe",
185
- });
186
- gitInitialized = true;
187
- } catch {
188
- // git not available — silently skip
189
- }
190
-
191
- // --- Optional: create GitHub repository ---
192
-
193
- if (gitInitialized) {
194
- let ghAvailable = false;
195
- try {
196
- execSync("gh --version", { stdio: "pipe" });
197
- execSync("gh auth status", { stdio: "pipe" });
198
- ghAvailable = true;
199
- } catch {
200
- // gh not installed or not authenticated — skip the prompt
201
- }
202
-
203
- if (ghAvailable) {
204
- const createRepo = await p.confirm({
205
- message: "Create a GitHub repository for this project?",
206
- initialValue: false,
207
- });
208
- if (!p.isCancel(createRepo) && createRepo) {
209
- // Get the GitHub username so we can check repo availability
210
- let ghUser = "";
211
- try {
212
- ghUser = execSync("gh api user --jq .login", { stdio: "pipe" }).toString().trim();
213
- } catch {}
214
-
215
- // Prompt for repo name, validate it doesn't already exist
216
- let repoName = null;
217
- while (true) {
218
- const input = await p.text({
219
- message: "Repository name",
220
- initialValue: projectName,
221
- validate: (value) => {
222
- if (!value) return "Repository name is required";
223
- if (!/^[a-zA-Z0-9._-]+$/.test(value)) return "Only letters, numbers, dots, hyphens, and underscores";
224
- },
225
- });
226
- if (p.isCancel(input)) break;
227
-
228
- // Check if repo already exists under the user's account
229
- if (ghUser) {
230
- try {
231
- execSync(`gh repo view ${ghUser}/${input}`, { stdio: "pipe" });
232
- p.note(`A repository named "${input}" already exists. Pick a different name.`, "Name taken");
233
- continue;
234
- } catch {
235
- // Repo doesn't exist — name is free
236
- }
237
- }
238
- repoName = input;
239
- break;
240
- }
241
-
242
- if (repoName) {
243
- const visibility = await p.select({
244
- message: "Repository visibility",
245
- options: [
246
- { label: "Private", value: "--private" },
247
- { label: "Public", value: "--public" },
248
- ],
249
- });
250
- if (!p.isCancel(visibility)) {
251
- s.start("Creating GitHub repository");
252
- try {
253
- execSync(`gh repo create ${repoName} ${visibility} --source=. --push`, {
254
- cwd: projectDir,
255
- stdio: "pipe",
256
- });
257
- s.stop("GitHub repository created and pushed");
258
- } catch (err) {
259
- s.stop("GitHub repository creation failed");
260
- if (err.stderr) console.error(err.stderr.toString().slice(-500));
261
- }
262
- }
263
- }
264
- }
265
- }
266
- }
267
-
268
- // --- Generate schema ---
269
-
270
- s.start("Generating CMS schema");
271
- try {
272
- execSync(`${pm.run} cms:generate`, { cwd: projectDir, stdio: "pipe" });
273
- s.stop("Schema generated");
274
- } catch {
275
- s.stop("Schema generation failed — run `cms:generate` manually");
113
+ s.stop("pnpm install failed — run it manually");
276
114
  }
277
115
 
278
- // --- Seed demo content ---
279
-
280
- if (seedDemo && target === "local") {
281
- s.start("Pushing schema to database");
282
- // Ensure data/ directory exists — drizzle-kit push silently exits 0 if it can't open the file
283
- mkdirSync(path.join(projectDir, "data"), { recursive: true });
284
- let pushOk = false;
285
- try {
286
- const out = execSync(`${pm.exec} drizzle-kit push --force`, {
287
- cwd: projectDir,
288
- stdio: "pipe",
289
- }).toString();
290
- // drizzle-kit exits 0 even on connection errors, so verify by checking output
291
- pushOk = !out.includes("Error:") && out.includes("Changes applied");
292
- s.stop(pushOk ? "Schema pushed" : "Schema push failed — run `pnpm exec drizzle-kit push` manually");
293
- } catch {
294
- s.stop("Schema will be set up on first dev start");
295
- }
296
- s.start("Seeding demo content");
297
- try {
298
- execSync(`${pm.run} cms:seed`, { cwd: projectDir, stdio: "pipe" });
299
- s.stop("Demo content seeded");
300
- } catch (err) {
301
- s.stop("Seeding failed — run `pnpm cms:seed` manually");
302
- if (err.stderr) console.error(err.stderr.toString());
303
- if (err.stdout) console.error(err.stdout.toString());
304
- }
305
- } else if (seedDemo && target === "cloudflare") {
116
+ if (cloudflare) {
306
117
  p.note(
307
118
  [
308
- "Seeding for Cloudflare requires a D1 database.",
119
+ `cd ${projectName}`,
120
+ "",
121
+ "1. Create Cloudflare resources:",
122
+ ` pnpm wrangler d1 create ${projectName}-db`,
123
+ " # Paste the database_id into wrangler.toml",
124
+ ` pnpm wrangler r2 bucket create ${projectName}-assets`,
125
+ "",
126
+ "2. Generate and apply migrations:",
127
+ " pnpm cms:generate && pnpm exec drizzle-kit generate",
128
+ ` pnpm wrangler d1 migrations apply ${projectName}-db --remote`,
309
129
  "",
310
- ` ${pm.dlx} wrangler d1 create ${projectName}-db`,
311
- " # Add the database_id to wrangler.toml",
312
- ` ${pm.dlx} wrangler d1 migrations apply ${projectName}-db --local`,
313
- ` ${pm.run} cms:seed`,
130
+ "3. Run locally or deploy:",
131
+ " pnpm dev",
132
+ " pnpm deploy",
314
133
  ].join("\n"),
315
- "Seed manually",
134
+ "Next steps",
316
135
  );
317
- }
318
-
319
- // --- Cloudflare resource setup ---
320
-
321
- const cf = { d1Created: false, r2Created: false, migrationsApplied: false, deployed: false, url: null };
322
- if (target === "cloudflare") {
323
- const setupNow = await p.confirm({
324
- message: "Set up Cloudflare resources now? (creates D1 database and R2 bucket)",
325
- initialValue: true,
326
- });
327
-
328
- if (!p.isCancel(setupNow) && setupNow) {
329
- // Check wrangler authentication
330
- let authenticated = false;
331
- try {
332
- execSync(`${pm.exec} wrangler whoami`, { cwd: projectDir, stdio: "pipe" });
333
- authenticated = true;
334
- } catch {
335
- p.note("You need to log in to Cloudflare first.", "Wrangler login required");
336
- const doLogin = await p.confirm({ message: "Open browser to log in?", initialValue: true });
337
- if (!p.isCancel(doLogin) && doLogin) {
338
- try {
339
- execSync(`${pm.exec} wrangler login`, { cwd: projectDir, stdio: "inherit" });
340
- authenticated = true;
341
- } catch {
342
- s.stop("Login failed");
343
- }
344
- }
345
- }
346
-
347
- if (authenticated) {
348
- // Create D1 database
349
- let databaseId = null;
350
- s.start("Creating D1 database");
351
- try {
352
- const output = execSync(`${pm.exec} wrangler d1 create ${projectName}-db`, {
353
- cwd: projectDir,
354
- stdio: "pipe",
355
- }).toString();
356
- // Parse database_id from output (looks for UUID-like string)
357
- const match = output.match(/database_id\s*=\s*"([^"]+)"/);
358
- if (match) databaseId = match[1];
359
- cf.d1Created = true;
360
- s.stop("D1 database created");
361
- } catch (err) {
362
- // Already exists — look it up
363
- try {
364
- const listOutput = execSync(`${pm.exec} wrangler d1 list`, { cwd: projectDir, stdio: "pipe" }).toString();
365
- const lines = listOutput.split("\n");
366
- const dbLine = lines.find((l) => l.includes(`${projectName}-db`));
367
- if (dbLine) {
368
- const idMatch = dbLine.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);
369
- if (idMatch) databaseId = idMatch[0];
370
- }
371
- if (databaseId) {
372
- cf.d1Created = true;
373
- s.stop("D1 database already exists — using existing");
374
- } else {
375
- s.stop("D1 setup failed");
376
- if (err.stderr) console.error(err.stderr.toString());
377
- }
378
- } catch {
379
- s.stop("D1 setup failed");
380
- }
381
- }
382
-
383
- // Update wrangler.toml with database_id
384
- if (databaseId) {
385
- const wranglerPath = path.join(projectDir, "wrangler.toml");
386
- let wranglerContent = readFileSync(wranglerPath, "utf-8");
387
- wranglerContent = wranglerContent.replace(/database_id = "" #[^\n]*/, `database_id = "${databaseId}"`);
388
- writeFileSync(wranglerPath, wranglerContent);
389
- }
390
-
391
- // Create R2 bucket
392
- s.start("Creating R2 bucket");
393
- try {
394
- execSync(`${pm.exec} wrangler r2 bucket create ${projectName}-assets`, { cwd: projectDir, stdio: "pipe" });
395
- cf.r2Created = true;
396
- s.stop("R2 bucket created");
397
- } catch {
398
- // Already exists is fine — assume it's there
399
- cf.r2Created = true;
400
- s.stop("R2 bucket already exists");
401
- }
402
-
403
- // Generate migrations and apply to remote D1
404
- if (databaseId) {
405
- s.start("Generating database migrations");
406
- try {
407
- execSync(`${pm.exec} drizzle-kit generate`, { cwd: projectDir, stdio: "pipe" });
408
- s.stop("Migrations generated");
409
- } catch (err) {
410
- s.stop("Migration generation failed");
411
- if (err.stderr) console.error(err.stderr.toString().slice(-800));
412
- if (err.stdout) console.error(err.stdout.toString().slice(-800));
413
- }
414
-
415
- s.start("Applying migrations to remote D1");
416
- try {
417
- execSync(`${pm.exec} wrangler d1 migrations apply ${projectName}-db --remote`, {
418
- cwd: projectDir,
419
- stdio: "pipe",
420
- input: "y\n",
421
- });
422
- cf.migrationsApplied = true;
423
- s.stop("Migrations applied");
424
- } catch {
425
- s.stop("Migration apply failed — run manually with: wrangler d1 migrations apply --remote");
426
- }
427
- }
428
-
429
- // Deploy to Cloudflare
430
- if (cf.migrationsApplied) {
431
- const doDeploy = await p.confirm({
432
- message: "Deploy to Cloudflare now?",
433
- initialValue: true,
434
- });
435
- if (!p.isCancel(doDeploy) && doDeploy) {
436
- s.start("Building and deploying to Cloudflare");
437
- try {
438
- const deployOutput = await runAsync(`${pm.run} run deploy`, projectDir);
439
- const urlMatch = deployOutput.match(/https:\/\/[^\s]+\.workers\.dev/);
440
- if (urlMatch) cf.url = urlMatch[0];
441
- cf.deployed = true;
442
- s.stop("Deployed to Cloudflare");
443
- } catch (err) {
444
- s.stop("Deploy failed — run manually with: pnpm run deploy");
445
- if (err.stderr) console.error(err.stderr.slice(-1500));
446
- if (err.stdout) console.error(err.stdout.slice(-1500));
447
- }
448
- }
449
- }
450
- }
451
- }
452
- }
453
-
454
- // --- Done ---
455
-
456
- if (target === "local") {
457
- p.outro("Starting dev server...");
458
- try {
459
- execSync(`${pm.run} dev`, { cwd: projectDir, stdio: "inherit" });
460
- } catch {
461
- console.log(`\n Project directory: ${projectDir}`);
462
- console.log(` To start again: cd ${projectName} && pnpm dev\n`);
463
- }
136
+ p.outro("Project created!");
464
137
  } else {
465
- if (cf.deployed && cf.url) {
466
- p.note(
467
- [
468
- `Live at: ${cf.url}`,
469
- `Admin: ${cf.url}/admin`,
470
- "",
471
- `cd ${projectName}`,
472
- "",
473
- "Local development:",
474
- ` ${pm.run} dev`,
475
- "",
476
- "Redeploy:",
477
- " pnpm run deploy",
478
- ].join("\n"),
479
- "🎉 Your Kide CMS is live",
480
- );
481
- p.outro("Project created!");
482
- } else {
483
- const lines = [`cd ${projectName}`];
484
- const remaining = [];
485
- if (!cf.d1Created) {
486
- remaining.push(` ${pm.dlx} wrangler d1 create ${projectName}-db`, " # Copy the database_id to wrangler.toml");
487
- }
488
- if (!cf.r2Created) {
489
- remaining.push(` ${pm.dlx} wrangler r2 bucket create ${projectName}-assets`);
490
- }
491
- if (!cf.migrationsApplied) {
492
- remaining.push(` ${pm.dlx} wrangler d1 migrations apply ${projectName}-db --remote`);
493
- }
494
- if (!cf.deployed) {
495
- remaining.push(` ${pm.run} run deploy`);
496
- }
497
- if (remaining.length > 0) {
498
- lines.push("", "Remaining setup:", ...remaining);
499
- }
500
- lines.push("", "Local development:", ` ${pm.run} dev`);
501
- p.note(lines.join("\n"), "Next steps");
502
- p.outro("Project created!");
503
- }
138
+ p.note([`cd ${projectName}`, "pnpm dev"].join("\n"), "Next steps");
139
+ p.outro("Project created!");
504
140
  }
505
141
  }
506
142
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kide-app",
3
- "version": "0.0.36",
3
+ "version": "0.1.1",
4
4
  "description": "Scaffold a new Kide CMS project",
5
5
  "author": "Matti Hernesniemi",
6
6
  "license": "MIT",
@@ -9,8 +9,7 @@
9
9
  "create-kide-app": "./index.js"
10
10
  },
11
11
  "files": [
12
- "index.js",
13
- "templates"
12
+ "index.js"
14
13
  ],
15
14
  "repository": {
16
15
  "type": "git",
@@ -1,2 +0,0 @@
1
- # Optional: Enable AI features
2
- # OPENAI_API_KEY=sk-...