@beechcms/cms 0.4.0-preview.2 → 0.4.0-preview.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/bin/cli.mjs +41 -17
- package/bin/create.mjs +55 -41
- package/package.json +2 -2
package/bin/cli.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// @ts-check
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { spawnSync } from 'node:child_process'
|
|
5
5
|
import { existsSync } from 'node:fs'
|
|
6
6
|
import { resolve } from 'node:path'
|
|
7
|
+
import { pathToFileURL } from 'node:url'
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
const [,, command, ...args] = process.argv
|
|
@@ -30,24 +31,43 @@ function help() {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
function cmdBuild() {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
console.log(
|
|
35
|
+
'\nNo build step needed for BeechCMS projects.\n' +
|
|
36
|
+
'Edit seeds.ts then run `npx beech seed:load` to sync schema changes to D1.\n'
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function tryLoadLocalRegistry() {
|
|
41
|
+
const cwd = process.cwd()
|
|
42
|
+
|
|
43
|
+
// Try compiled JS first
|
|
44
|
+
for (const name of ['seeds.js', 'seeds.mjs']) {
|
|
45
|
+
const p = resolve(cwd, name)
|
|
46
|
+
if (existsSync(p)) {
|
|
47
|
+
try {
|
|
48
|
+
const mod = await import(pathToFileURL(p).href)
|
|
49
|
+
if (mod.SEED_REGISTRY && typeof mod.SEED_REGISTRY === 'object') {
|
|
50
|
+
return mod.SEED_REGISTRY
|
|
51
|
+
}
|
|
52
|
+
} catch {}
|
|
53
|
+
}
|
|
41
54
|
}
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
// Try seeds.ts via Node's experimental strip-types (Node 22.6+)
|
|
57
|
+
const tsPath = resolve(cwd, 'seeds.ts')
|
|
58
|
+
if (existsSync(tsPath)) {
|
|
59
|
+
const result = spawnSync(process.execPath, [
|
|
60
|
+
'--experimental-strip-types',
|
|
61
|
+
'--input-type=module',
|
|
62
|
+
'--eval',
|
|
63
|
+
`import { SEED_REGISTRY } from ${JSON.stringify(pathToFileURL(tsPath).href)}; process.stdout.write(JSON.stringify(SEED_REGISTRY))`,
|
|
64
|
+
], { encoding: 'utf-8' })
|
|
65
|
+
if (result.status === 0 && result.stdout) {
|
|
66
|
+
try { return JSON.parse(result.stdout) } catch {}
|
|
67
|
+
}
|
|
50
68
|
}
|
|
69
|
+
|
|
70
|
+
return null
|
|
51
71
|
}
|
|
52
72
|
|
|
53
73
|
async function cmdSeedLoad(args) {
|
|
@@ -57,14 +77,18 @@ async function cmdSeedLoad(args) {
|
|
|
57
77
|
const dbIdx = args.indexOf('--db')
|
|
58
78
|
const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
|
|
59
79
|
|
|
80
|
+
const registry = await tryLoadLocalRegistry()
|
|
81
|
+
|
|
60
82
|
const { seedLoad } = await import('@beechcms/cli')
|
|
61
|
-
await seedLoad({ dryRun, diff, local: !remote, db })
|
|
83
|
+
await seedLoad({ dryRun, diff, local: !remote, db, registry })
|
|
62
84
|
}
|
|
63
85
|
|
|
64
86
|
const handler = COMMANDS[command]
|
|
65
87
|
if (!handler) {
|
|
66
88
|
help()
|
|
67
89
|
if (command) process.exit(1)
|
|
90
|
+
} else if (args.includes('--help') || args.includes('-h')) {
|
|
91
|
+
help()
|
|
68
92
|
} else {
|
|
69
93
|
await handler(args)
|
|
70
94
|
}
|
package/bin/create.mjs
CHANGED
|
@@ -108,8 +108,8 @@ function buildPackageJson(name) {
|
|
|
108
108
|
'db:reset:local': 'node -e "require(\'fs\').rmSync(\'.wrangler/state\',{recursive:true,force:true})" && npm run db:migrate:local',
|
|
109
109
|
},
|
|
110
110
|
dependencies: {
|
|
111
|
-
'@beechcms/api': '^0.4.0-preview.
|
|
112
|
-
'@beechcms/core': '^0.4.0-preview.
|
|
111
|
+
'@beechcms/api': '^0.4.0-preview.3',
|
|
112
|
+
'@beechcms/core': '^0.4.0-preview.3',
|
|
113
113
|
},
|
|
114
114
|
devDependencies: {
|
|
115
115
|
'@cloudflare/workers-types': '^4.0.0',
|
|
@@ -272,54 +272,68 @@ async function askCloudflareConfig(name) {
|
|
|
272
272
|
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
273
273
|
|
|
274
274
|
async function main() {
|
|
275
|
+
const argv = process.argv.slice(2)
|
|
276
|
+
const silent = argv.includes('--yes') || argv.includes('-y') || !process.stdout.isTTY
|
|
277
|
+
|
|
275
278
|
console.log()
|
|
276
279
|
p.intro(pc.bgGreen(pc.black(' @beechcms/cms ')))
|
|
277
280
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
281
|
+
let name, selectedTemplates, cloudflare
|
|
282
|
+
|
|
283
|
+
if (silent) {
|
|
284
|
+
// Non-interactive: use first positional arg or default name, no templates, skip Cloudflare
|
|
285
|
+
const positional = argv.find((a) => !a.startsWith('-'))
|
|
286
|
+
name = positional ?? 'my-beech-project'
|
|
287
|
+
selectedTemplates = []
|
|
288
|
+
cloudflare = null
|
|
289
|
+
console.log(pc.dim(` Running in non-interactive mode. Project name: ${name}`))
|
|
290
|
+
} else {
|
|
291
|
+
// Project name
|
|
292
|
+
const projectName = await p.text({
|
|
293
|
+
message: 'Project name',
|
|
294
|
+
placeholder: 'my-website',
|
|
295
|
+
validate: (v) => {
|
|
296
|
+
if (!v.trim()) return 'Required'
|
|
297
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(v.trim())) return 'Lowercase letters, numbers and hyphens only'
|
|
298
|
+
},
|
|
299
|
+
})
|
|
300
|
+
if (p.isCancel(projectName)) { p.cancel('Cancelled'); process.exit(0) }
|
|
301
|
+
name = projectName.trim()
|
|
302
|
+
|
|
303
|
+
// Content types
|
|
304
|
+
const tmpl = await p.multiselect({
|
|
305
|
+
message: 'Which content types do you need?',
|
|
306
|
+
hint: 'Space to select, Enter to confirm. You can add more later in seeds.ts',
|
|
307
|
+
options: [
|
|
308
|
+
{ value: 'blog', label: 'Blog', hint: 'posts with rich text, cover image, tags and authors' },
|
|
309
|
+
{ value: 'gallery', label: 'Gallery', hint: 'media items with image, tags and featured flag' },
|
|
310
|
+
{ value: 'contact', label: 'Contact', hint: 'public form submissions with masked email and read status' },
|
|
311
|
+
],
|
|
312
|
+
required: false,
|
|
313
|
+
})
|
|
314
|
+
if (p.isCancel(tmpl)) { p.cancel('Cancelled'); process.exit(0) }
|
|
315
|
+
selectedTemplates = tmpl
|
|
316
|
+
|
|
317
|
+
// Cloudflare now or later?
|
|
318
|
+
const configureNow = await p.confirm({
|
|
319
|
+
message: 'Configure Cloudflare credentials now?',
|
|
320
|
+
hint: 'Choose "No" to scaffold the project and fill in the values later',
|
|
321
|
+
initialValue: true,
|
|
322
|
+
})
|
|
323
|
+
if (p.isCancel(configureNow)) { p.cancel('Cancelled'); process.exit(0) }
|
|
324
|
+
|
|
325
|
+
if (configureNow) {
|
|
326
|
+
cloudflare = await askCloudflareConfig(name)
|
|
327
|
+
if (!cloudflare) { p.cancel('Cancelled'); process.exit(0) }
|
|
328
|
+
}
|
|
329
|
+
}
|
|
290
330
|
|
|
331
|
+
const targetDir = resolve(process.cwd(), name)
|
|
291
332
|
if (existsSync(targetDir)) {
|
|
292
333
|
p.cancel(`Directory '${name}' already exists. Choose a different name or delete the folder.`)
|
|
293
334
|
process.exit(1)
|
|
294
335
|
}
|
|
295
336
|
|
|
296
|
-
// Content types
|
|
297
|
-
const selectedTemplates = await p.multiselect({
|
|
298
|
-
message: 'Which content types do you need?',
|
|
299
|
-
hint: 'Space to select, Enter to confirm. You can add more later in seeds.ts',
|
|
300
|
-
options: [
|
|
301
|
-
{ value: 'blog', label: 'Blog', hint: 'posts with rich text, cover image, tags and authors' },
|
|
302
|
-
{ value: 'gallery', label: 'Gallery', hint: 'media items with image, tags and featured flag' },
|
|
303
|
-
{ value: 'contact', label: 'Contact', hint: 'public form submissions with masked email and read status' },
|
|
304
|
-
],
|
|
305
|
-
required: false,
|
|
306
|
-
})
|
|
307
|
-
if (p.isCancel(selectedTemplates)) { p.cancel('Cancelled'); process.exit(0) }
|
|
308
|
-
|
|
309
|
-
// Cloudflare now or later?
|
|
310
|
-
const configureNow = await p.confirm({
|
|
311
|
-
message: 'Configure Cloudflare credentials now?',
|
|
312
|
-
hint: 'Choose "No" to scaffold the project and fill in the values later',
|
|
313
|
-
initialValue: true,
|
|
314
|
-
})
|
|
315
|
-
if (p.isCancel(configureNow)) { p.cancel('Cancelled'); process.exit(0) }
|
|
316
|
-
|
|
317
|
-
let cloudflare = null
|
|
318
|
-
if (configureNow) {
|
|
319
|
-
cloudflare = await askCloudflareConfig(name)
|
|
320
|
-
if (!cloudflare) { p.cancel('Cancelled'); process.exit(0) }
|
|
321
|
-
}
|
|
322
|
-
|
|
323
337
|
const jwtSecret = generateSecret(32)
|
|
324
338
|
const publicReadKey = generateSecret(16)
|
|
325
339
|
const publicWriteKey = generateSecret(16)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beechcms/cms",
|
|
3
|
-
"version": "0.4.0-preview.
|
|
3
|
+
"version": "0.4.0-preview.3",
|
|
4
4
|
"packageManager": "npm@11.9.0",
|
|
5
5
|
"description": "Edge-Native, Schema-Driven Headless CMS built on Cloudflare Workers, D1, and R2. Features the Botanical Engine for alias-stable field management, a modular widget layer, and a React + Vite admin dashboard.",
|
|
6
6
|
"keywords": [
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"typescript": "^5.9.3"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@beechcms/cli": "^0.4.0-preview.
|
|
57
|
+
"@beechcms/cli": "^0.4.0-preview.3",
|
|
58
58
|
"@clack/prompts": "^0.9.1",
|
|
59
59
|
"picocolors": "^1.1.1"
|
|
60
60
|
}
|