create-kide-app 0.1.1 → 0.1.3
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 +200 -28
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as p from "@clack/prompts";
|
|
4
|
-
import { execSync } from "node:child_process";
|
|
4
|
+
import { exec as execCb, execSync } from "node:child_process";
|
|
5
5
|
import { cpSync, existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
|
+
import { promisify } from "node:util";
|
|
7
8
|
|
|
9
|
+
const exec = promisify(execCb);
|
|
8
10
|
const REPO = "https://github.com/mhernesniemi/kide-cms.git";
|
|
9
11
|
|
|
12
|
+
function run(cmd, cwd) {
|
|
13
|
+
return exec(cmd, { cwd });
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
function applyPackagePatch(pkg, patch) {
|
|
11
17
|
for (const [k, v] of Object.entries(patch.dependencies?.add ?? {})) {
|
|
12
18
|
pkg.dependencies[k] = v;
|
|
@@ -61,13 +67,32 @@ async function main() {
|
|
|
61
67
|
process.exit(0);
|
|
62
68
|
}
|
|
63
69
|
|
|
70
|
+
let seedDemo = false;
|
|
71
|
+
if (!cloudflare) {
|
|
72
|
+
const seed = await p.confirm({
|
|
73
|
+
message: "Seed database with demo content?",
|
|
74
|
+
initialValue: false,
|
|
75
|
+
});
|
|
76
|
+
if (p.isCancel(seed)) {
|
|
77
|
+
p.cancel("Setup cancelled.");
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
seedDemo = seed;
|
|
81
|
+
}
|
|
82
|
+
|
|
64
83
|
const s = p.spinner();
|
|
65
84
|
|
|
85
|
+
// --- Clone kide-cms ---
|
|
86
|
+
|
|
66
87
|
s.start("Cloning kide-cms");
|
|
67
|
-
|
|
88
|
+
await run(`git clone --depth 1 ${REPO} "${projectDir}"`);
|
|
68
89
|
rmSync(path.join(projectDir, ".git"), { recursive: true, force: true });
|
|
69
90
|
s.stop("Cloned");
|
|
70
91
|
|
|
92
|
+
// --- Adjust package.json and apply adapter overlay ---
|
|
93
|
+
|
|
94
|
+
s.start(cloudflare ? "Applying Cloudflare adapter" : "Configuring project");
|
|
95
|
+
|
|
71
96
|
const pkgPath = path.join(projectDir, "package.json");
|
|
72
97
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
73
98
|
pkg.name = projectName;
|
|
@@ -76,9 +101,7 @@ async function main() {
|
|
|
76
101
|
const adaptersDir = path.join(projectDir, "adapters");
|
|
77
102
|
|
|
78
103
|
if (cloudflare) {
|
|
79
|
-
s.start("Applying Cloudflare adapter");
|
|
80
104
|
const cfDir = path.join(adaptersDir, "cloudflare");
|
|
81
|
-
|
|
82
105
|
const overlayFiles = [
|
|
83
106
|
"astro.config.mjs",
|
|
84
107
|
"drizzle.config.ts",
|
|
@@ -98,45 +121,194 @@ async function main() {
|
|
|
98
121
|
|
|
99
122
|
const patch = JSON.parse(readFileSync(path.join(cfDir, "package.patch.json"), "utf-8"));
|
|
100
123
|
applyPackagePatch(pkg, patch);
|
|
101
|
-
|
|
102
|
-
s.stop("Cloudflare adapter applied");
|
|
103
124
|
}
|
|
104
125
|
|
|
105
126
|
rmSync(adaptersDir, { recursive: true, force: true });
|
|
106
127
|
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
107
128
|
|
|
129
|
+
s.stop(cloudflare ? "Cloudflare adapter applied" : "Project configured");
|
|
130
|
+
|
|
131
|
+
// --- Install dependencies ---
|
|
132
|
+
|
|
108
133
|
s.start("Installing dependencies");
|
|
109
134
|
try {
|
|
110
|
-
|
|
135
|
+
await run("pnpm install", projectDir);
|
|
111
136
|
s.stop("Dependencies installed");
|
|
112
137
|
} catch {
|
|
113
138
|
s.stop("pnpm install failed — run it manually");
|
|
114
139
|
}
|
|
115
140
|
|
|
141
|
+
// --- Seed demo content (local only) ---
|
|
142
|
+
|
|
143
|
+
if (seedDemo) {
|
|
144
|
+
s.start("Generating CMS schema");
|
|
145
|
+
try {
|
|
146
|
+
await run("pnpm cms:generate", projectDir);
|
|
147
|
+
s.stop("Schema generated");
|
|
148
|
+
} catch {
|
|
149
|
+
s.stop("Schema generation failed — run `pnpm cms:generate` manually");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
s.start("Pushing schema to database");
|
|
153
|
+
try {
|
|
154
|
+
await run("pnpm exec drizzle-kit push --force", projectDir);
|
|
155
|
+
s.stop("Schema pushed");
|
|
156
|
+
} catch {
|
|
157
|
+
s.stop("Schema push failed — will be set up on first dev start");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
s.start("Seeding demo content");
|
|
161
|
+
try {
|
|
162
|
+
await run("pnpm cms:seed", projectDir);
|
|
163
|
+
s.stop("Demo content seeded");
|
|
164
|
+
} catch {
|
|
165
|
+
s.stop("Seeding failed — run `pnpm cms:seed` manually");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// --- Cloudflare resource setup ---
|
|
170
|
+
|
|
171
|
+
const cf = { d1Created: false, r2Created: false, migrationsApplied: false };
|
|
116
172
|
if (cloudflare) {
|
|
117
|
-
p.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
"
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
"",
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
173
|
+
const setupNow = await p.confirm({
|
|
174
|
+
message: "Set up Cloudflare resources now? (creates D1 database and R2 bucket)",
|
|
175
|
+
initialValue: true,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
if (!p.isCancel(setupNow) && setupNow) {
|
|
179
|
+
// Check wrangler authentication
|
|
180
|
+
let authenticated = false;
|
|
181
|
+
try {
|
|
182
|
+
await run("pnpm wrangler whoami", projectDir);
|
|
183
|
+
authenticated = true;
|
|
184
|
+
} catch {
|
|
185
|
+
p.note("You need to log in to Cloudflare first.", "Wrangler login required");
|
|
186
|
+
const doLogin = await p.confirm({ message: "Open browser to log in?", initialValue: true });
|
|
187
|
+
if (!p.isCancel(doLogin) && doLogin) {
|
|
188
|
+
try {
|
|
189
|
+
execSync("pnpm wrangler login", { cwd: projectDir, stdio: "inherit" });
|
|
190
|
+
authenticated = true;
|
|
191
|
+
} catch {
|
|
192
|
+
// Login failed
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (authenticated) {
|
|
198
|
+
// Create D1 database
|
|
199
|
+
let databaseId = null;
|
|
200
|
+
s.start("Creating D1 database");
|
|
201
|
+
try {
|
|
202
|
+
const { stdout } = await run(`pnpm wrangler d1 create ${projectName}-db`, projectDir);
|
|
203
|
+
const match = stdout.match(/database_id\s*=\s*"([^"]+)"/);
|
|
204
|
+
if (match) databaseId = match[1];
|
|
205
|
+
cf.d1Created = true;
|
|
206
|
+
s.stop("D1 database created");
|
|
207
|
+
} catch {
|
|
208
|
+
// Already exists — look it up
|
|
209
|
+
try {
|
|
210
|
+
const { stdout } = await run("pnpm wrangler d1 list", projectDir);
|
|
211
|
+
const dbLine = stdout.split("\n").find((l) => l.includes(`${projectName}-db`));
|
|
212
|
+
if (dbLine) {
|
|
213
|
+
const idMatch = dbLine.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);
|
|
214
|
+
if (idMatch) databaseId = idMatch[0];
|
|
215
|
+
}
|
|
216
|
+
if (databaseId) {
|
|
217
|
+
cf.d1Created = true;
|
|
218
|
+
s.stop("D1 database already exists — using existing");
|
|
219
|
+
} else {
|
|
220
|
+
s.stop("D1 setup failed");
|
|
221
|
+
}
|
|
222
|
+
} catch {
|
|
223
|
+
s.stop("D1 setup failed");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Update wrangler.toml with database_id
|
|
228
|
+
if (databaseId) {
|
|
229
|
+
const wranglerPath = path.join(projectDir, "wrangler.toml");
|
|
230
|
+
let wranglerContent = readFileSync(wranglerPath, "utf-8");
|
|
231
|
+
wranglerContent = wranglerContent.replace(
|
|
232
|
+
/database_id = "" #[^\n]*/,
|
|
233
|
+
`database_id = "${databaseId}"`,
|
|
234
|
+
);
|
|
235
|
+
writeFileSync(wranglerPath, wranglerContent);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Create R2 bucket
|
|
239
|
+
s.start("Creating R2 bucket");
|
|
240
|
+
try {
|
|
241
|
+
await run(`pnpm wrangler r2 bucket create ${projectName}-assets`, projectDir);
|
|
242
|
+
cf.r2Created = true;
|
|
243
|
+
s.stop("R2 bucket created");
|
|
244
|
+
} catch {
|
|
245
|
+
// Already exists is fine
|
|
246
|
+
cf.r2Created = true;
|
|
247
|
+
s.stop("R2 bucket already exists");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Generate schema + migrations, apply to remote D1
|
|
251
|
+
if (databaseId) {
|
|
252
|
+
s.start("Generating CMS schema");
|
|
253
|
+
try {
|
|
254
|
+
await run("pnpm cms:generate", projectDir);
|
|
255
|
+
s.stop("Schema generated");
|
|
256
|
+
} catch {
|
|
257
|
+
s.stop("Schema generation failed");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
s.start("Generating database migrations");
|
|
261
|
+
try {
|
|
262
|
+
await run("pnpm exec drizzle-kit generate", projectDir);
|
|
263
|
+
s.stop("Migrations generated");
|
|
264
|
+
} catch {
|
|
265
|
+
s.stop("Migration generation failed");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
s.start("Applying migrations to remote D1");
|
|
269
|
+
try {
|
|
270
|
+
await run(`pnpm wrangler d1 migrations apply ${projectName}-db --remote`, projectDir);
|
|
271
|
+
cf.migrationsApplied = true;
|
|
272
|
+
s.stop("Migrations applied");
|
|
273
|
+
} catch {
|
|
274
|
+
s.stop("Migration apply failed — run manually with: wrangler d1 migrations apply --remote");
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// --- Done ---
|
|
282
|
+
|
|
283
|
+
if (cloudflare) {
|
|
284
|
+
const lines = [`cd ${projectName}`];
|
|
285
|
+
const remaining = [];
|
|
286
|
+
if (!cf.d1Created) {
|
|
287
|
+
remaining.push(
|
|
288
|
+
` pnpm wrangler d1 create ${projectName}-db`,
|
|
289
|
+
" # Paste the database_id into wrangler.toml",
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
if (!cf.r2Created) {
|
|
293
|
+
remaining.push(` pnpm wrangler r2 bucket create ${projectName}-assets`);
|
|
294
|
+
}
|
|
295
|
+
if (!cf.migrationsApplied) {
|
|
296
|
+
remaining.push(` pnpm wrangler d1 migrations apply ${projectName}-db --remote`);
|
|
297
|
+
}
|
|
298
|
+
if (remaining.length > 0) {
|
|
299
|
+
lines.push("", "Remaining setup:", ...remaining);
|
|
300
|
+
}
|
|
301
|
+
lines.push("", "Local development:", " pnpm dev", "", "Deploy:", " pnpm deploy");
|
|
302
|
+
p.note(lines.join("\n"), "Next steps");
|
|
136
303
|
p.outro("Project created!");
|
|
137
304
|
} else {
|
|
138
|
-
p.
|
|
139
|
-
|
|
305
|
+
p.outro("Starting dev server...");
|
|
306
|
+
try {
|
|
307
|
+
execSync("pnpm dev", { cwd: projectDir, stdio: "inherit" });
|
|
308
|
+
} catch {
|
|
309
|
+
console.log(`\n Project directory: ${projectDir}`);
|
|
310
|
+
console.log(` To start again: cd ${projectName} && pnpm dev\n`);
|
|
311
|
+
}
|
|
140
312
|
}
|
|
141
313
|
}
|
|
142
314
|
|