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.
- package/README.md +6 -4
- package/index.js +105 -67
- 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. **
|
|
15
|
-
3. **
|
|
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**:
|
|
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.
|
|
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
|
-
:
|
|
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 "
|
|
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 "
|
|
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.
|
|
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 = {
|