create-kide-app 0.1.9 → 0.2.2

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 (3) hide show
  1. package/README.md +6 -4
  2. package/index.js +105 -67
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -11,17 +11,19 @@ pnpm create kide-app my-project
11
11
  The CLI asks for:
12
12
 
13
13
  1. **Project name** — directory to create
14
- 2. **Deploy target** — Local/Node.js or Cloudflare
15
- 3. **Seed demo content** — local target only
14
+ 2. **Distribution mode** — Package (recommended: thin project + `@kidecms/core` npm dependency; `pnpm exec kide eject` converts to embedded later, one-way) or Embedded (full CMS source in `src/cms/`, yours to modify)
15
+ 3. **Deploy target** — Local/Node.js or Cloudflare
16
+
17
+ Every project starts bare-bones: one `pages` collection (`title`, `slug`, `body`) and a minimal public page — no demo content, no seeds.
16
18
 
17
19
  ## What it does
18
20
 
19
- - Clones the latest Kide CMS from GitHub.
21
+ - Clones the latest Kide CMS release from GitHub (package mode then swaps the embedded runtime for the published `@kidecms/core` at the same version).
20
22
  - Applies target-specific configuration (Node.js adapter, or Cloudflare D1/R2/Workers).
21
23
  - Installs dependencies with pnpm.
22
24
  - Optionally creates a GitHub repo (if the `gh` CLI is installed and authenticated).
23
25
  - Generates the CMS schema.
24
- - For **local**: optionally seeds demo content, then starts the dev server.
26
+ - For **local**: starts the dev server.
25
27
  - For **Cloudflare**: logs into wrangler (if needed), creates a D1 database and R2 bucket, applies migrations, builds and deploys, and prints the live URL + admin URL.
26
28
 
27
29
  ## Requirements
package/index.js CHANGED
@@ -5,7 +5,6 @@ import { execFileSync, execSync, spawn } from "node:child_process";
5
5
  import {
6
6
  cpSync,
7
7
  existsSync,
8
- mkdirSync,
9
8
  readFileSync,
10
9
  rmSync,
11
10
  writeFileSync,
@@ -52,6 +51,8 @@ const CLEANUP = [
52
51
  "docs",
53
52
  "CLAUDE.md",
54
53
  ".claude/settings.local.json",
54
+ ".github/workflows", // upstream CI + @kidecms/core release pipeline — wrong in any scaffold
55
+ "scripts/verify-cloudflare.mjs", // needs the adapters/ overlay, which every scaffold deletes
55
56
  "data",
56
57
  ".cms-data",
57
58
  "dist",
@@ -59,6 +60,20 @@ const CLEANUP = [
59
60
  ".DS_Store",
60
61
  ];
61
62
 
63
+ // Managed runtime dirs inside src/cms — the contents of the @kidecms/core
64
+ // package. Deleted in package mode (the npm dependency provides them);
65
+ // everything else in src/cms (cms.config, collections, adapters, fields,
66
+ // runtime.ts, migrations) is project-owned and stays in both modes.
67
+ const MANAGED_DIRS = [
68
+ "admin",
69
+ "client",
70
+ "core",
71
+ "internals",
72
+ "middleware",
73
+ "platform",
74
+ "routes",
75
+ ];
76
+
62
77
  // The project name reaches shell commands (git, wrangler) and path.resolve, so it is
63
78
  // validated before either. Restricting it to one path segment of safe characters keeps
64
79
  // `$(...)`, backticks, `;` and separators out of those commands and out of the target
@@ -127,7 +142,30 @@ async function main() {
127
142
  process.exit(1);
128
143
  }
129
144
 
130
- // 2. Deploy target
145
+ // 2. Distribution mode — package is the recommended default; embedded stays
146
+ // first-class for teams that want to own and modify the runtime source.
147
+ const mode = await p.select({
148
+ message: "How do you want the CMS runtime?",
149
+ options: [
150
+ {
151
+ label: "Package (recommended)",
152
+ value: "package",
153
+ hint: "thin project + @kidecms/core dependency — most updates are a version bump; eject to embedded later",
154
+ },
155
+ {
156
+ label: "Embedded",
157
+ value: "embedded",
158
+ hint: "full CMS source in src/cms/ — modify internals, audit everything; upgrades arrive as release packets",
159
+ },
160
+ ],
161
+ });
162
+
163
+ if (p.isCancel(mode)) {
164
+ p.cancel("Setup cancelled.");
165
+ process.exit(0);
166
+ }
167
+
168
+ // 3. Deploy target
131
169
  const target = await p.select({
132
170
  message: "Where will you deploy?",
133
171
  options: [
@@ -141,21 +179,6 @@ async function main() {
141
179
  process.exit(0);
142
180
  }
143
181
 
144
- // 3. Demo content (local only — Cloudflare uses remote D1)
145
- let seedDemo = false;
146
- if (target === "local") {
147
- const seed = await p.confirm({
148
- message: "Seed database with demo content?",
149
- initialValue: false,
150
- });
151
-
152
- if (p.isCancel(seed)) {
153
- p.cancel("Setup cancelled.");
154
- process.exit(0);
155
- }
156
- seedDemo = seed;
157
- }
158
-
159
182
  const s = p.spinner();
160
183
 
161
184
  // --- Scaffold via git clone ---
@@ -198,10 +221,46 @@ async function main() {
198
221
  rmSync(path.join(projectDir, f), { recursive: true, force: true });
199
222
  }
200
223
 
224
+ // Both modes scaffold the same template at the same tag; package mode then
225
+ // deletes the managed runtime dirs and swaps the workspace link for the
226
+ // published @kidecms/core at exactly that version — same source either way.
227
+ const corePkgPath = path.join(projectDir, "src/cms/package.json");
228
+ let coreVersion = null;
229
+ if (existsSync(corePkgPath)) {
230
+ coreVersion = JSON.parse(readFileSync(corePkgPath, "utf-8")).version;
231
+ }
232
+
233
+ if (mode === "package") {
234
+ if (!coreVersion) {
235
+ s.stop("Scaffolding failed");
236
+ p.cancel(
237
+ "This template release predates package mode — choose Embedded, or wait for the next release.",
238
+ );
239
+ process.exit(1);
240
+ }
241
+ for (const managed of MANAGED_DIRS) {
242
+ rmSync(path.join(projectDir, "src/cms", managed), {
243
+ recursive: true,
244
+ force: true,
245
+ });
246
+ }
247
+ rmSync(corePkgPath, { force: true });
248
+ rmSync(path.join(projectDir, "pnpm-workspace.yaml"), { force: true });
249
+ // Upstream distribution tooling that assumes the embedded workspace package.
250
+ rmSync(path.join(projectDir, "scripts/verify-pack.mjs"), { force: true });
251
+ rmSync(path.join(projectDir, "scripts/verify-package-mode.mjs"), {
252
+ force: true,
253
+ });
254
+ // Worker tests and the Cloudflare type profile live in the deleted
255
+ // src/cms/platform — their configs would match zero files.
256
+ rmSync(path.join(projectDir, "vitest.workers.config.ts"), { force: true });
257
+ rmSync(path.join(projectDir, "tsconfig.cloudflare.json"), { force: true });
258
+ }
259
+
201
260
  s.stop(
202
261
  templateRef
203
- ? `Project scaffolded from ${templateRef}`
204
- : "Project scaffolded",
262
+ ? `Project scaffolded from ${templateRef} (${mode})`
263
+ : `Project scaffolded (${mode})`,
205
264
  );
206
265
 
207
266
  // --- Apply target-specific files ---
@@ -225,12 +284,18 @@ async function main() {
225
284
  );
226
285
  writeFileSync(
227
286
  path.join(projectDir, "src/cms/adapters/db.ts"),
228
- 'export * from "../platform/cloudflare/database";\n',
287
+ 'export * from "@kidecms/core/platform/cloudflare/database";\n',
229
288
  );
230
289
  writeFileSync(
231
290
  path.join(projectDir, "src/cms/adapters/storage.ts"),
232
- 'export * from "../platform/cloudflare/storage";\n',
291
+ 'export * from "@kidecms/core/platform/cloudflare/storage";\n',
233
292
  );
293
+ // The adapter test asserts Node filesystem storage — meaningless (and failing)
294
+ // once the adapter re-exports the R2 profile.
295
+ rmSync(path.join(projectDir, "src/cms/adapters/__tests__"), {
296
+ recursive: true,
297
+ force: true,
298
+ });
234
299
  }
235
300
 
236
301
  const pkgPath = path.join(projectDir, "package.json");
@@ -238,6 +303,22 @@ async function main() {
238
303
 
239
304
  pkg.name = projectName;
240
305
 
306
+ // Upstream-repo tooling that no scaffold can run (needs the deleted adapters/ overlay).
307
+ delete pkg.scripts["verify:cloudflare"];
308
+
309
+ if (mode === "package" && pkg.dependencies["@kidecms/core"]) {
310
+ pkg.dependencies["@kidecms/core"] = `^${coreVersion}`;
311
+ delete pkg.scripts["verify:pack"];
312
+ delete pkg.scripts["verify:package"];
313
+ // The runtime (and its worker tests + Cloudflare type profile) now lives in
314
+ // node_modules — keep check/test scoped to project code, and let vitest pass
315
+ // until the project has tests of its own.
316
+ delete pkg.scripts["test:workers"];
317
+ delete pkg.scripts["check:cloudflare"];
318
+ pkg.scripts.check = "astro check && eslint .";
319
+ pkg.scripts.test = "pnpm cms:generate && vitest run --passWithNoTests";
320
+ }
321
+
241
322
  if (target === "cloudflare") {
242
323
  delete pkg.dependencies["@astrojs/node"];
243
324
  pkg.dependencies["@astrojs/cloudflare"] = "~14.1.7";
@@ -269,7 +350,7 @@ async function main() {
269
350
  );
270
351
  writeFileSync(path.join(projectDir, "wrangler.toml"), wranglerContent);
271
352
 
272
- pkg.devDependencies.wrangler = "^4.83.0";
353
+ pkg.devDependencies.wrangler = "^4.121.0";
273
354
 
274
355
  pkg.scripts.dev = "astro dev";
275
356
  pkg.scripts.build = "astro build";
@@ -309,10 +390,11 @@ async function main() {
309
390
  }
310
391
  const versionStamp = {
311
392
  template: REPO.replace(/\.git$/, ""),
312
- kideVersion: pkg.version ?? null,
393
+ kideVersion: coreVersion ?? pkg.version ?? null,
313
394
  ref: templateRef ?? "HEAD",
314
395
  commit: templateCommit,
315
396
  target,
397
+ mode,
316
398
  corePath: "src/cms",
317
399
  scaffoldedAt: new Date().toISOString(),
318
400
  createKideApp: cliVersion,
@@ -476,50 +558,6 @@ async function main() {
476
558
  s.stop("Schema generation failed — run `cms:generate` manually");
477
559
  }
478
560
 
479
- // --- Seed demo content ---
480
-
481
- if (seedDemo && target === "local") {
482
- s.start("Pushing schema to database");
483
- // Ensure data/ directory exists — drizzle-kit push silently exits 0 if it can't open the file
484
- mkdirSync(path.join(projectDir, "data"), { recursive: true });
485
- let pushOk = false;
486
- try {
487
- const out = execSync(`${pm.exec} drizzle-kit push --force`, {
488
- cwd: projectDir,
489
- stdio: "pipe",
490
- }).toString();
491
- pushOk = !out.includes("Error:") && out.includes("Changes applied");
492
- s.stop(
493
- pushOk
494
- ? "Schema pushed"
495
- : "Schema push failed — run `pnpm exec drizzle-kit push` manually",
496
- );
497
- } catch {
498
- s.stop("Schema will be set up on first dev start");
499
- }
500
- s.start("Seeding demo content");
501
- try {
502
- execSync(`${pm.run} cms:seed`, { cwd: projectDir, stdio: "pipe" });
503
- s.stop("Demo content seeded");
504
- } catch (err) {
505
- s.stop("Seeding failed — run `pnpm cms:seed` manually");
506
- if (err.stderr) console.error(err.stderr.toString());
507
- if (err.stdout) console.error(err.stdout.toString());
508
- }
509
- } else if (seedDemo && target === "cloudflare") {
510
- p.note(
511
- [
512
- "Seeding for Cloudflare requires a D1 database.",
513
- "",
514
- ` ${pm.dlx} wrangler d1 create ${projectName}-db`,
515
- " # then replace the placeholder database_id in wrangler.toml",
516
- ` ${pm.dlx} wrangler d1 migrations apply ${projectName}-db --local`,
517
- ` ${pm.run} cms:seed`,
518
- ].join("\n"),
519
- "Seed manually",
520
- );
521
- }
522
-
523
561
  // --- Cloudflare resource setup ---
524
562
 
525
563
  const cf = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kide-app",
3
- "version": "0.1.9",
3
+ "version": "0.2.2",
4
4
  "description": "Scaffold a new Kide CMS project",
5
5
  "author": "Matti Hernesniemi",
6
6
  "license": "MIT",