create-kyro 0.1.0 → 0.1.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/dist/index.js +186 -172
- package/my-kyro-app/README.md +14 -0
- package/my-kyro-app/SPEC.md +21 -0
- package/my-kyro-app/astro.config.mjs +23 -0
- package/my-kyro-app/kyro.config.ts +19 -0
- package/my-kyro-app/package.json +30 -0
- package/package.json +9 -4
- package/templates/blog/collections.ts +7 -6
- package/src/generators/astro.ts +0 -38
- package/src/generators/config.ts +0 -80
- package/src/generators/files.ts +0 -159
- package/src/generators/packagejson.ts +0 -72
- package/src/index.ts +0 -97
- package/src/prompts.ts +0 -164
- package/src/utils/logger.ts +0 -60
- package/src/validators.ts +0 -36
- package/tsconfig.json +0 -15
- package/tsup.config.ts +0 -11
package/dist/index.js
CHANGED
|
@@ -27,168 +27,169 @@ function validateProjectName(name) {
|
|
|
27
27
|
|
|
28
28
|
// src/prompts.ts
|
|
29
29
|
async function promptUser() {
|
|
30
|
-
const response = await prompts(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
30
|
+
const response = await prompts(
|
|
31
|
+
[
|
|
32
|
+
{
|
|
33
|
+
type: "text",
|
|
34
|
+
name: "projectName",
|
|
35
|
+
message: "Project name:",
|
|
36
|
+
initial: "my-kyro-app",
|
|
37
|
+
validate: validateProjectName
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
type: "select",
|
|
41
|
+
name: "database",
|
|
42
|
+
message: "Database:",
|
|
43
|
+
hint: " ",
|
|
44
|
+
choices: [
|
|
45
|
+
{
|
|
46
|
+
title: "SQLite (local-first, zero config)",
|
|
47
|
+
description: "Best for development and small projects. No setup required.",
|
|
48
|
+
value: "sqlite"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
title: "PostgreSQL",
|
|
52
|
+
description: "Recommended for production. Robust and scalable.",
|
|
53
|
+
value: "postgres"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
title: "MySQL",
|
|
57
|
+
description: "Popular choice for web applications.",
|
|
58
|
+
value: "mysql"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
title: "MongoDB",
|
|
62
|
+
description: "Best for flexible, document-based schemas.",
|
|
63
|
+
value: "mongodb"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: "multiselect",
|
|
69
|
+
name: "apis",
|
|
70
|
+
message: "API protocols (select multiple):",
|
|
71
|
+
hint: "Space to select, Enter to confirm",
|
|
72
|
+
instructions: false,
|
|
73
|
+
choices: [
|
|
74
|
+
{
|
|
75
|
+
title: "REST",
|
|
76
|
+
description: "Simple HTTP API, great for any client",
|
|
77
|
+
value: "rest",
|
|
78
|
+
selected: true
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
title: "GraphQL",
|
|
82
|
+
description: "Flexible query language, great for complex data",
|
|
83
|
+
value: "graphql",
|
|
84
|
+
selected: true
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
title: "tRPC",
|
|
88
|
+
description: "End-to-end typesafe APIs, great for TypeScript",
|
|
89
|
+
value: "trpc"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
title: "WebSocket",
|
|
93
|
+
description: "Real-time bidirectional communication",
|
|
94
|
+
value: "websocket"
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: "select",
|
|
100
|
+
name: "styling",
|
|
101
|
+
message: "Styling:",
|
|
102
|
+
hint: " ",
|
|
103
|
+
choices: [
|
|
104
|
+
{
|
|
105
|
+
title: "Tailwind CSS",
|
|
106
|
+
description: "Utility-first CSS framework, excellent DX",
|
|
107
|
+
value: "tailwind"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
title: "CSS Modules",
|
|
111
|
+
description: "Scoped CSS, no extra dependencies",
|
|
112
|
+
value: "cssmodules"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
title: "Styled Components",
|
|
116
|
+
description: "CSS-in-JS with tagged template literals",
|
|
117
|
+
value: "styled"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
title: "None",
|
|
121
|
+
description: "Bring your own styling solution",
|
|
122
|
+
value: "none"
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
type: "toggle",
|
|
128
|
+
name: "auth",
|
|
129
|
+
message: "Add authentication (JWT)?",
|
|
130
|
+
active: "Yes",
|
|
131
|
+
inactive: "No"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
type: "toggle",
|
|
135
|
+
name: "versioning",
|
|
136
|
+
message: "Add versioning/drafts?",
|
|
137
|
+
active: "Yes",
|
|
138
|
+
inactive: "No"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
type: "toggle",
|
|
142
|
+
name: "admin",
|
|
143
|
+
message: "Include admin dashboard?",
|
|
144
|
+
initial: true,
|
|
145
|
+
active: "Yes",
|
|
146
|
+
inactive: "No"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
type: "select",
|
|
150
|
+
name: "template",
|
|
151
|
+
message: "Starting template:",
|
|
152
|
+
hint: " ",
|
|
153
|
+
choices: [
|
|
154
|
+
{
|
|
155
|
+
title: "Minimal",
|
|
156
|
+
description: "Basic configuration with one example collection + core settings",
|
|
157
|
+
value: "minimal"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
title: "Blog",
|
|
161
|
+
description: "Posts, categories, media library + core settings",
|
|
162
|
+
value: "blog"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
title: "E-commerce",
|
|
166
|
+
description: "Products, orders, customers, coupons + core + store/payment settings",
|
|
167
|
+
value: "ecommerce"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
title: "Kitchen Sink",
|
|
171
|
+
description: "Everything: pages, navigation, blog, e-commerce + all settings",
|
|
172
|
+
value: "kitchen-sink"
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
}
|
|
176
|
+
],
|
|
147
177
|
{
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
choices: [
|
|
153
|
-
{
|
|
154
|
-
title: "Minimal",
|
|
155
|
-
description: "Basic configuration with one example collection",
|
|
156
|
-
value: "minimal"
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
title: "Blog",
|
|
160
|
-
description: "Posts, categories, tags, and media library",
|
|
161
|
-
value: "blog"
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
title: "E-commerce",
|
|
165
|
-
description: "Products, orders, customers, inventory, and coupons",
|
|
166
|
-
value: "ecommerce"
|
|
167
|
-
}
|
|
168
|
-
]
|
|
169
|
-
}
|
|
170
|
-
], {
|
|
171
|
-
onCancel: () => {
|
|
172
|
-
console.log("\nCancelled.");
|
|
173
|
-
process.exit(1);
|
|
178
|
+
onCancel: () => {
|
|
179
|
+
console.log("\nCancelled.");
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
174
182
|
}
|
|
175
|
-
|
|
183
|
+
);
|
|
176
184
|
return response;
|
|
177
185
|
}
|
|
178
186
|
|
|
179
187
|
// src/utils/logger.ts
|
|
180
|
-
import
|
|
181
|
-
var { cyan, green, yellow, red, bold, dim } = kolorist;
|
|
188
|
+
import { cyan, green, yellow, red, bold, dim } from "kolorist";
|
|
182
189
|
var logger = {
|
|
183
190
|
intro: (name, version) => {
|
|
184
191
|
console.log(`
|
|
185
|
-
${bold(cyan(
|
|
186
|
-
${bold(cyan(`\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D`))}
|
|
187
|
-
${bold(cyan(`\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557`))}
|
|
188
|
-
${bold(cyan(`\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551`))}
|
|
189
|
-
${bold(cyan(`\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551`))}
|
|
190
|
-
${bold(cyan(`\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D`))}
|
|
191
|
-
${dim(`v${version}`)}
|
|
192
|
+
${bold(cyan(`Kyro CMS`))} ${dim(`v${version}`)}
|
|
192
193
|
${dim("Astro-native headless CMS")}
|
|
193
194
|
`);
|
|
194
195
|
},
|
|
@@ -287,7 +288,9 @@ function formatPackageJson(pkg) {
|
|
|
287
288
|
|
|
288
289
|
// src/generators/config.ts
|
|
289
290
|
function generateKyroConfig(answers) {
|
|
290
|
-
const imports = [
|
|
291
|
+
const imports = [
|
|
292
|
+
"import { defineConfig, createTemplateConfig } from '@kyro-cms/core';"
|
|
293
|
+
];
|
|
291
294
|
const adapterLines = [];
|
|
292
295
|
if (answers.database === "sqlite") {
|
|
293
296
|
imports.push("import { localAdapter } from '@kyro-cms/core';");
|
|
@@ -328,28 +331,39 @@ function generateKyroConfig(answers) {
|
|
|
328
331
|
if (answers.versioning) {
|
|
329
332
|
features.push(" versioning: true,");
|
|
330
333
|
}
|
|
331
|
-
let
|
|
332
|
-
let
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
334
|
+
let templateCollections = [];
|
|
335
|
+
let templateGlobals = [];
|
|
336
|
+
switch (answers.template) {
|
|
337
|
+
case "minimal":
|
|
338
|
+
templateCollections.push("...minimalCollections,");
|
|
339
|
+
break;
|
|
340
|
+
case "blog":
|
|
341
|
+
templateCollections.push("...blogCollections,");
|
|
342
|
+
break;
|
|
343
|
+
case "ecommerce":
|
|
344
|
+
templateCollections.push("...ecommerceCollections,");
|
|
345
|
+
break;
|
|
346
|
+
case "kitchen-sink":
|
|
347
|
+
templateCollections.push(
|
|
348
|
+
"...minimalCollections,",
|
|
349
|
+
"...blogCollections,",
|
|
350
|
+
"...ecommerceCollections,",
|
|
351
|
+
"...kitchenSinkCollections,"
|
|
352
|
+
);
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
const collectionsBlock = templateCollections.length > 0 ? ` collections: [
|
|
356
|
+
${templateCollections.join("\n ")}
|
|
357
|
+
],` : "";
|
|
358
|
+
const globalsBlock = templateGlobals.length > 0 ? ` globals: [
|
|
359
|
+
${templateGlobals.join("\n ")}
|
|
360
|
+
],` : "";
|
|
343
361
|
const config = `${imports.join("\n")}
|
|
344
|
-
${templateImports}
|
|
345
362
|
|
|
346
363
|
export default defineConfig({
|
|
347
364
|
name: '${answers.projectName}',
|
|
348
365
|
prefix: '/api',${adapterLines.length > 0 ? "\n" + adapterLines.join("\n") : ""}
|
|
349
|
-
|
|
350
|
-
collections: {
|
|
351
|
-
${collectionsLine}
|
|
352
|
-
},${features.length > 0 ? "\n" + features.join("\n") : ""}
|
|
366
|
+
${collectionsBlock ? "\n" + collectionsBlock : ""}${globalsBlock ? "\n" + globalsBlock : ""}${features.length > 0 ? "\n" + features.join("\n") : ""}
|
|
353
367
|
|
|
354
368
|
api: {
|
|
355
369
|
${apiConfig.join("\n")}
|
|
@@ -532,7 +546,7 @@ import config from '../kyro.config';
|
|
|
532
546
|
import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync } from "fs";
|
|
533
547
|
import { join as join2 } from "path";
|
|
534
548
|
import { execSync } from "child_process";
|
|
535
|
-
var VERSION = "0.1.
|
|
549
|
+
var VERSION = "0.1.1";
|
|
536
550
|
async function main() {
|
|
537
551
|
logger.intro("create-kyro", VERSION);
|
|
538
552
|
const answers = await promptUser();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# my-kyro-app
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This project uses Kyro CMS - an Astro-native headless CMS.
|
|
6
|
+
|
|
7
|
+
## Configuration
|
|
8
|
+
|
|
9
|
+
- **Database**: SQLite (local-first)
|
|
10
|
+
- **APIs**: rest, graphql
|
|
11
|
+
- **Styling**: tailwind
|
|
12
|
+
- **Auth**: Disabled
|
|
13
|
+
- **Versioning**: Enabled
|
|
14
|
+
- **Admin**: Included
|
|
15
|
+
- **Template**: blog
|
|
16
|
+
|
|
17
|
+
## Collections
|
|
18
|
+
|
|
19
|
+
- Posts
|
|
20
|
+
- Categories
|
|
21
|
+
- Media
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineConfig } from 'astro/config';
|
|
2
|
+
import react from '@astrojs/react';
|
|
3
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
output: 'server',
|
|
7
|
+
adapter: node({
|
|
8
|
+
mode: 'standalone'
|
|
9
|
+
}),
|
|
10
|
+
|
|
11
|
+
integrations: [
|
|
12
|
+
react(),
|
|
13
|
+
],
|
|
14
|
+
vite: {
|
|
15
|
+
plugins: [
|
|
16
|
+
tailwindcss(),
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
server: {
|
|
20
|
+
port: 4321,
|
|
21
|
+
host: true,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineConfig } from '@kyro-cms/core';
|
|
2
|
+
import { localAdapter } from '@kyro-cms/core';
|
|
3
|
+
import { blogCollections } from '@kyro-cms/core';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
name: 'my-kyro-app',
|
|
7
|
+
prefix: '/api',
|
|
8
|
+
adapter: localAdapter({ path: './data.db' }),
|
|
9
|
+
|
|
10
|
+
collections: {
|
|
11
|
+
...blogCollections,
|
|
12
|
+
},
|
|
13
|
+
versioning: true,
|
|
14
|
+
|
|
15
|
+
api: {
|
|
16
|
+
rest: true,
|
|
17
|
+
graphql: true,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-kyro-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": true,
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "astro dev",
|
|
8
|
+
"build": "astro build",
|
|
9
|
+
"preview": "astro preview",
|
|
10
|
+
"db:generate": "kyro generate",
|
|
11
|
+
"db:push": "kyro push",
|
|
12
|
+
"db:studio": "kyro studio"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@kyro-cms/core": "latest",
|
|
16
|
+
"astro": "^5.4.0",
|
|
17
|
+
"@astrojs/react": "^4.2.0",
|
|
18
|
+
"react": "^19.0.0",
|
|
19
|
+
"react-dom": "^19.0.0",
|
|
20
|
+
"tailwindcss": "^4.0.0",
|
|
21
|
+
"@tailwindcss/vite": "^4.0.0",
|
|
22
|
+
"@kyro-cms/admin": "latest",
|
|
23
|
+
"lucide-react": "^0.475.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"typescript": "^5.7.3",
|
|
27
|
+
"@types/react": "^19.0.0",
|
|
28
|
+
"@types/react-dom": "^19.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-kyro",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Interactive scaffolding for Kyro CMS projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"create-kyro": "./bin.js"
|
|
@@ -27,9 +27,14 @@
|
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"kyro",
|
|
30
|
+
"kyro-cms",
|
|
30
31
|
"cms",
|
|
31
32
|
"scaffold",
|
|
32
|
-
"create"
|
|
33
|
+
"create",
|
|
34
|
+
"astro"
|
|
33
35
|
],
|
|
34
|
-
"license": "MIT"
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
}
|
|
35
40
|
}
|
|
@@ -39,9 +39,7 @@ export const blogCollections: Record<string, CollectionConfig> = {
|
|
|
39
39
|
name: 'featuredImage',
|
|
40
40
|
type: 'upload',
|
|
41
41
|
label: 'Featured Image',
|
|
42
|
-
|
|
43
|
-
mimeTypes: ['image/*']
|
|
44
|
-
}
|
|
42
|
+
relationTo: 'media'
|
|
45
43
|
},
|
|
46
44
|
{
|
|
47
45
|
name: 'category',
|
|
@@ -61,7 +59,10 @@ export const blogCollections: Record<string, CollectionConfig> = {
|
|
|
61
59
|
name: 'status',
|
|
62
60
|
type: 'select',
|
|
63
61
|
label: 'Status',
|
|
64
|
-
options: [
|
|
62
|
+
options: [
|
|
63
|
+
{ label: 'Draft', value: 'draft' },
|
|
64
|
+
{ label: 'Published', value: 'published' }
|
|
65
|
+
],
|
|
65
66
|
defaultValue: 'draft'
|
|
66
67
|
},
|
|
67
68
|
{
|
|
@@ -166,8 +167,8 @@ export const blogCollections: Record<string, CollectionConfig> = {
|
|
|
166
167
|
};
|
|
167
168
|
|
|
168
169
|
export const blogGlobals = {
|
|
169
|
-
|
|
170
|
-
name: '
|
|
170
|
+
'site-settings': {
|
|
171
|
+
name: 'site-settings',
|
|
171
172
|
label: 'Site Settings',
|
|
172
173
|
fields: {
|
|
173
174
|
siteName: { type: 'text', defaultValue: 'My Blog' },
|
package/src/generators/astro.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { Answers } from '../prompts.js';
|
|
2
|
-
|
|
3
|
-
export function generateAstroConfig(answers: Answers): string {
|
|
4
|
-
const integrations: string[] = [];
|
|
5
|
-
const vitePlugins: string[] = [];
|
|
6
|
-
|
|
7
|
-
if (answers.styling === 'tailwind') {
|
|
8
|
-
integrations.push(" react(),");
|
|
9
|
-
vitePlugins.push(" tailwindcss(),");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const adapter = answers.admin ? `
|
|
13
|
-
adapter: node({
|
|
14
|
-
mode: 'standalone'
|
|
15
|
-
}),` : '';
|
|
16
|
-
|
|
17
|
-
const config = `import { defineConfig } from 'astro/config';
|
|
18
|
-
${answers.styling === 'tailwind' ? "import react from '@astrojs/react';\nimport tailwindcss from '@tailwindcss/vite';" : ''}
|
|
19
|
-
|
|
20
|
-
export default defineConfig({
|
|
21
|
-
output: 'server',${adapter}
|
|
22
|
-
|
|
23
|
-
integrations: [
|
|
24
|
-
${integrations.join('\n')}
|
|
25
|
-
],${vitePlugins.length > 0 ? `
|
|
26
|
-
vite: {
|
|
27
|
-
plugins: [
|
|
28
|
-
${vitePlugins.join('\n')}
|
|
29
|
-
],
|
|
30
|
-
},` : ''}
|
|
31
|
-
server: {
|
|
32
|
-
port: 4321,
|
|
33
|
-
host: true,
|
|
34
|
-
},
|
|
35
|
-
});`;
|
|
36
|
-
|
|
37
|
-
return config;
|
|
38
|
-
}
|
package/src/generators/config.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import type { Answers } from '../prompts.js';
|
|
2
|
-
|
|
3
|
-
export function generateKyroConfig(answers: Answers): string {
|
|
4
|
-
const imports: string[] = ["import { defineConfig } from '@kyro-cms/core';"];
|
|
5
|
-
const adapterLines: string[] = [];
|
|
6
|
-
|
|
7
|
-
if (answers.database === 'sqlite') {
|
|
8
|
-
imports.push("import { localAdapter } from '@kyro-cms/core';");
|
|
9
|
-
adapterLines.push(` adapter: localAdapter({ path: './data.db' }),`);
|
|
10
|
-
} else if (answers.database === 'postgres') {
|
|
11
|
-
imports.push("import { drizzleAdapter } from '@kyro-cms/core';");
|
|
12
|
-
adapterLines.push(` adapter: drizzleAdapter({`);
|
|
13
|
-
adapterLines.push(` connectionString: process.env.DATABASE_URL,`);
|
|
14
|
-
adapterLines.push(` }),`);
|
|
15
|
-
} else if (answers.database === 'mysql') {
|
|
16
|
-
imports.push("import { drizzleAdapter } from '@kyro-cms/core';");
|
|
17
|
-
adapterLines.push(` adapter: drizzleAdapter({`);
|
|
18
|
-
adapterLines.push(` connectionString: process.env.DATABASE_URL,`);
|
|
19
|
-
adapterLines.push(` }),`);
|
|
20
|
-
} else if (answers.database === 'mongodb') {
|
|
21
|
-
imports.push("import { mongoAdapter } from '@kyro-cms/core';");
|
|
22
|
-
adapterLines.push(` adapter: mongoAdapter({`);
|
|
23
|
-
adapterLines.push(` connectionString: process.env.MONGODB_URI,`);
|
|
24
|
-
adapterLines.push(` }),`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const apiConfig: string[] = [];
|
|
28
|
-
if (answers.apis.includes('rest')) {
|
|
29
|
-
apiConfig.push(" rest: true,");
|
|
30
|
-
}
|
|
31
|
-
if (answers.apis.includes('graphql')) {
|
|
32
|
-
apiConfig.push(" graphql: true,");
|
|
33
|
-
}
|
|
34
|
-
if (answers.apis.includes('trpc')) {
|
|
35
|
-
apiConfig.push(" trpc: true,");
|
|
36
|
-
}
|
|
37
|
-
if (answers.apis.includes('websocket')) {
|
|
38
|
-
apiConfig.push(" websocket: true,");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const features: string[] = [];
|
|
42
|
-
if (answers.auth) {
|
|
43
|
-
features.push(" auth: true,");
|
|
44
|
-
}
|
|
45
|
-
if (answers.versioning) {
|
|
46
|
-
features.push(" versioning: true,");
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let templateImports = '';
|
|
50
|
-
let collectionsLine = '';
|
|
51
|
-
|
|
52
|
-
if (answers.template === 'minimal') {
|
|
53
|
-
templateImports = `import { minimalCollections } from '@kyro-cms/core';`;
|
|
54
|
-
collectionsLine = ' ...minimalCollections,';
|
|
55
|
-
} else if (answers.template === 'blog') {
|
|
56
|
-
templateImports = `import { blogCollections } from '@kyro-cms/core';`;
|
|
57
|
-
collectionsLine = ' ...blogCollections,';
|
|
58
|
-
} else if (answers.template === 'ecommerce') {
|
|
59
|
-
templateImports = `import { ecommerceCollections } from '@kyro-cms/core';`;
|
|
60
|
-
collectionsLine = ' ...ecommerceCollections,';
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const config = `${imports.join('\n')}
|
|
64
|
-
${templateImports}
|
|
65
|
-
|
|
66
|
-
export default defineConfig({
|
|
67
|
-
name: '${answers.projectName}',
|
|
68
|
-
prefix: '/api',${adapterLines.length > 0 ? '\n' + adapterLines.join('\n') : ''}
|
|
69
|
-
|
|
70
|
-
collections: {
|
|
71
|
-
${collectionsLine}
|
|
72
|
-
},${features.length > 0 ? '\n' + features.join('\n') : ''}
|
|
73
|
-
|
|
74
|
-
api: {
|
|
75
|
-
${apiConfig.join('\n')}
|
|
76
|
-
},
|
|
77
|
-
});`;
|
|
78
|
-
|
|
79
|
-
return config;
|
|
80
|
-
}
|
package/src/generators/files.ts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import type { Answers } from '../prompts.js';
|
|
2
|
-
import { writeFileSync, mkdirSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
|
|
5
|
-
export function generateProjectFiles(answers: Answers, projectDir: string): void {
|
|
6
|
-
const srcDir = join(projectDir, 'src');
|
|
7
|
-
const pagesDir = join(srcDir, 'pages');
|
|
8
|
-
const publicDir = join(projectDir, 'public');
|
|
9
|
-
|
|
10
|
-
mkdirSync(pagesDir, { recursive: true });
|
|
11
|
-
mkdirSync(publicDir, { recursive: true });
|
|
12
|
-
|
|
13
|
-
if (answers.database === 'sqlite') {
|
|
14
|
-
mkdirSync(join(projectDir, 'data'), { recursive: true });
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const tsconfig = `{
|
|
18
|
-
"extends": "astro/tsconfigs/strict",
|
|
19
|
-
"compilerOptions": {
|
|
20
|
-
"baseUrl": ".",
|
|
21
|
-
"paths": {
|
|
22
|
-
"@/*": ["./src/*"]
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}`;
|
|
26
|
-
|
|
27
|
-
writeFileSync(join(projectDir, 'tsconfig.json'), tsconfig);
|
|
28
|
-
|
|
29
|
-
const gitignore = `node_modules/
|
|
30
|
-
dist/
|
|
31
|
-
.astro/
|
|
32
|
-
data/
|
|
33
|
-
*.db
|
|
34
|
-
.env
|
|
35
|
-
.env.local
|
|
36
|
-
.DS_Store
|
|
37
|
-
`;
|
|
38
|
-
|
|
39
|
-
writeFileSync(join(projectDir, '.gitignore'), gitignore);
|
|
40
|
-
|
|
41
|
-
const readme = `# ${answers.projectName}
|
|
42
|
-
|
|
43
|
-
A Kyro CMS project.
|
|
44
|
-
|
|
45
|
-
## Getting Started
|
|
46
|
-
|
|
47
|
-
\`\`\`bash
|
|
48
|
-
npm install
|
|
49
|
-
npm run dev
|
|
50
|
-
\`\`\`
|
|
51
|
-
|
|
52
|
-
## Documentation
|
|
53
|
-
|
|
54
|
-
Visit [https://kyro.cms](https://kyro.cms) for full documentation.
|
|
55
|
-
`;
|
|
56
|
-
|
|
57
|
-
writeFileSync(join(projectDir, 'README.md'), readme);
|
|
58
|
-
|
|
59
|
-
const spec = `# ${answers.projectName}
|
|
60
|
-
|
|
61
|
-
## Overview
|
|
62
|
-
|
|
63
|
-
This project uses Kyro CMS - an Astro-native headless CMS.
|
|
64
|
-
|
|
65
|
-
## Configuration
|
|
66
|
-
|
|
67
|
-
- **Database**: ${answers.database === 'sqlite' ? 'SQLite (local-first)' : answers.database}
|
|
68
|
-
- **APIs**: ${answers.apis.join(', ')}
|
|
69
|
-
- **Styling**: ${answers.styling}
|
|
70
|
-
- **Auth**: ${answers.auth ? 'Enabled' : 'Disabled'}
|
|
71
|
-
- **Versioning**: ${answers.versioning ? 'Enabled' : 'Disabled'}
|
|
72
|
-
- **Admin**: ${answers.admin ? 'Included' : 'Not included'}
|
|
73
|
-
- **Template**: ${answers.template}
|
|
74
|
-
|
|
75
|
-
## Collections
|
|
76
|
-
|
|
77
|
-
${
|
|
78
|
-
answers.template === 'minimal' ? '- Posts' :
|
|
79
|
-
answers.template === 'blog' ? '- Posts\n- Categories\n- Media' :
|
|
80
|
-
'- Products\n- Categories\n- Customers\n- Orders\n- Coupons'
|
|
81
|
-
}
|
|
82
|
-
`;
|
|
83
|
-
|
|
84
|
-
writeFileSync(join(projectDir, 'SPEC.md'), spec);
|
|
85
|
-
|
|
86
|
-
const indexPage = `---
|
|
87
|
-
const title = "${answers.projectName}";
|
|
88
|
-
---
|
|
89
|
-
<!DOCTYPE html>
|
|
90
|
-
<html lang="en">
|
|
91
|
-
<head>
|
|
92
|
-
<meta charset="UTF-8" />
|
|
93
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
94
|
-
<title>${answers.projectName}</title>
|
|
95
|
-
</head>
|
|
96
|
-
<body>
|
|
97
|
-
<main>
|
|
98
|
-
<h1>Welcome to ${answers.projectName}</h1>
|
|
99
|
-
<p>Your Kyro CMS is ready.</p>
|
|
100
|
-
${
|
|
101
|
-
answers.admin
|
|
102
|
-
? '<p><a href="/admin">Go to Admin Dashboard →</a></p>'
|
|
103
|
-
: ''
|
|
104
|
-
}
|
|
105
|
-
</main>
|
|
106
|
-
</body>
|
|
107
|
-
</html>
|
|
108
|
-
|
|
109
|
-
<style>
|
|
110
|
-
main {
|
|
111
|
-
max-width: 800px;
|
|
112
|
-
margin: 4rem auto;
|
|
113
|
-
padding: 2rem;
|
|
114
|
-
text-align: center;
|
|
115
|
-
font-family: system-ui, sans-serif;
|
|
116
|
-
}
|
|
117
|
-
h1 {
|
|
118
|
-
font-size: 2.5rem;
|
|
119
|
-
margin-bottom: 1rem;
|
|
120
|
-
}
|
|
121
|
-
p {
|
|
122
|
-
color: #666;
|
|
123
|
-
}
|
|
124
|
-
a {
|
|
125
|
-
color: #6366f1;
|
|
126
|
-
text-decoration: none;
|
|
127
|
-
}
|
|
128
|
-
a:hover {
|
|
129
|
-
text-decoration: underline;
|
|
130
|
-
}
|
|
131
|
-
</style>
|
|
132
|
-
`;
|
|
133
|
-
|
|
134
|
-
writeFileSync(join(pagesDir, 'index.astro'), indexPage);
|
|
135
|
-
|
|
136
|
-
if (answers.admin) {
|
|
137
|
-
const adminDir = join(srcDir, 'admin');
|
|
138
|
-
mkdirSync(adminDir, { recursive: true });
|
|
139
|
-
|
|
140
|
-
const adminIndex = `---
|
|
141
|
-
import { Admin } from '@kyro-cms/admin';
|
|
142
|
-
import config from '../kyro.config';
|
|
143
|
-
---
|
|
144
|
-
<!DOCTYPE html>
|
|
145
|
-
<html lang="en">
|
|
146
|
-
<head>
|
|
147
|
-
<meta charset="UTF-8" />
|
|
148
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
149
|
-
<title>Admin - ${answers.projectName}</title>
|
|
150
|
-
</head>
|
|
151
|
-
<body>
|
|
152
|
-
<Admin client:load config={config} />
|
|
153
|
-
</body>
|
|
154
|
-
</html>
|
|
155
|
-
`;
|
|
156
|
-
|
|
157
|
-
writeFileSync(join(adminDir, 'index.astro'), adminIndex);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { Answers } from '../prompts.js';
|
|
2
|
-
|
|
3
|
-
export interface PackageJson {
|
|
4
|
-
name: string;
|
|
5
|
-
version: string;
|
|
6
|
-
type: string;
|
|
7
|
-
private: boolean;
|
|
8
|
-
scripts: Record<string, string>;
|
|
9
|
-
dependencies: Record<string, string>;
|
|
10
|
-
devDependencies: Record<string, string>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function generatePackageJson(answers: Answers, projectDir: string): PackageJson {
|
|
14
|
-
const deps: Record<string, string> = {
|
|
15
|
-
'@kyro-cms/core': 'latest',
|
|
16
|
-
'astro': '^5.4.0'
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const devDeps: Record<string, string> = {
|
|
20
|
-
'typescript': '^5.7.3'
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
if (answers.styling === 'tailwind') {
|
|
24
|
-
deps['@astrojs/react'] = '^4.2.0';
|
|
25
|
-
deps['react'] = '^19.0.0';
|
|
26
|
-
deps['react-dom'] = '^19.0.0';
|
|
27
|
-
deps['tailwindcss'] = '^4.0.0';
|
|
28
|
-
deps['@tailwindcss/vite'] = '^4.0.0';
|
|
29
|
-
devDeps['@types/react'] = '^19.0.0';
|
|
30
|
-
devDeps['@types/react-dom'] = '^19.0.0';
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (answers.database === 'postgres') {
|
|
34
|
-
deps['pg'] = '^8.13.1';
|
|
35
|
-
deps['@types/pg'] = '^8.11.0';
|
|
36
|
-
} else if (answers.database === 'mysql') {
|
|
37
|
-
deps['mysql2'] = '^3.12.0';
|
|
38
|
-
} else if (answers.database === 'mongodb') {
|
|
39
|
-
deps['mongodb'] = '^6.12.0';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (answers.admin) {
|
|
43
|
-
deps['@kyro-cms/admin'] = 'latest';
|
|
44
|
-
deps['lucide-react'] = '^0.475.0';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const scripts: Record<string, string> = {
|
|
48
|
-
'dev': 'astro dev',
|
|
49
|
-
'build': 'astro build',
|
|
50
|
-
'preview': 'astro preview'
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
if (answers.database === 'sqlite') {
|
|
54
|
-
scripts['db:generate'] = 'kyro generate';
|
|
55
|
-
scripts['db:push'] = 'kyro push';
|
|
56
|
-
scripts['db:studio'] = 'kyro studio';
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
name: answers.projectName,
|
|
61
|
-
version: '0.1.0',
|
|
62
|
-
type: 'module',
|
|
63
|
-
private: true,
|
|
64
|
-
scripts,
|
|
65
|
-
dependencies: deps,
|
|
66
|
-
devDependencies: devDeps
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function formatPackageJson(pkg: PackageJson): string {
|
|
71
|
-
return JSON.stringify(pkg, null, 2);
|
|
72
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { promptUser, type Answers } from './prompts.js';
|
|
2
|
-
import { logger } from './utils/logger.js';
|
|
3
|
-
import { generatePackageJson, formatPackageJson } from './generators/packagejson.js';
|
|
4
|
-
import { generateKyroConfig } from './generators/config.js';
|
|
5
|
-
import { generateAstroConfig } from './generators/astro.js';
|
|
6
|
-
import { generateProjectFiles } from './generators/files.js';
|
|
7
|
-
import { writeFileSync, mkdirSync, cpSync, existsSync } from 'fs';
|
|
8
|
-
import { join } from 'path';
|
|
9
|
-
import { execSync } from 'child_process';
|
|
10
|
-
|
|
11
|
-
const VERSION = '0.1.0';
|
|
12
|
-
|
|
13
|
-
async function main() {
|
|
14
|
-
logger.intro('create-kyro', VERSION);
|
|
15
|
-
|
|
16
|
-
const answers = await promptUser();
|
|
17
|
-
const projectDir = join(process.cwd(), answers.projectName);
|
|
18
|
-
|
|
19
|
-
if (existsSync(projectDir)) {
|
|
20
|
-
logger.error(`Directory "${answers.projectName}" already exists.`);
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const steps = [
|
|
25
|
-
'Creating project directory',
|
|
26
|
-
'Generating configuration files',
|
|
27
|
-
'Installing dependencies',
|
|
28
|
-
'Initializing git repository',
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
logger.step(1, steps.length, steps[0]);
|
|
32
|
-
mkdirSync(projectDir, { recursive: true });
|
|
33
|
-
logger.success('Project directory created');
|
|
34
|
-
|
|
35
|
-
logger.step(2, steps.length, steps[1]);
|
|
36
|
-
|
|
37
|
-
const pkg = generatePackageJson(answers, projectDir);
|
|
38
|
-
writeFileSync(
|
|
39
|
-
join(projectDir, 'package.json'),
|
|
40
|
-
formatPackageJson(pkg)
|
|
41
|
-
);
|
|
42
|
-
logger.success('package.json generated');
|
|
43
|
-
|
|
44
|
-
const kyroConfig = generateKyroConfig(answers);
|
|
45
|
-
writeFileSync(join(projectDir, 'kyro.config.ts'), kyroConfig);
|
|
46
|
-
logger.success('kyro.config.ts generated');
|
|
47
|
-
|
|
48
|
-
const astroConfig = generateAstroConfig(answers);
|
|
49
|
-
writeFileSync(join(projectDir, 'astro.config.mjs'), astroConfig);
|
|
50
|
-
logger.success('astro.config.mjs generated');
|
|
51
|
-
|
|
52
|
-
generateProjectFiles(answers, projectDir);
|
|
53
|
-
logger.success('Project files generated');
|
|
54
|
-
|
|
55
|
-
logger.step(3, steps.length, steps[2]);
|
|
56
|
-
console.log(' Installing dependencies...');
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
execSync('npm install', {
|
|
60
|
-
cwd: projectDir,
|
|
61
|
-
stdio: 'inherit',
|
|
62
|
-
env: { ...process.env, npm_config_loglevel: 'warn' }
|
|
63
|
-
});
|
|
64
|
-
logger.success('Dependencies installed');
|
|
65
|
-
} catch (error) {
|
|
66
|
-
logger.error('Failed to install dependencies');
|
|
67
|
-
process.exit(1);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
logger.step(4, steps.length, steps[3]);
|
|
71
|
-
try {
|
|
72
|
-
execSync('git init && git add . && git commit -m "Initial commit - created with create-kyro"', {
|
|
73
|
-
cwd: projectDir,
|
|
74
|
-
stdio: 'pipe'
|
|
75
|
-
});
|
|
76
|
-
logger.success('Git repository initialized');
|
|
77
|
-
} catch {
|
|
78
|
-
logger.warning('Could not initialize git repository');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
logger.done();
|
|
82
|
-
|
|
83
|
-
console.log(' To get started:');
|
|
84
|
-
console.log(` ${logger ? '\x1b[36m' : ''}cd ${answers.projectName}${logger ? '\x1b[0m' : ''}`);
|
|
85
|
-
console.log(` ${logger ? '\x1b[36m' : ''}npm run dev${logger ? '\x1b[0m' : ''}`);
|
|
86
|
-
console.log('');
|
|
87
|
-
console.log(' Visit http://localhost:4321 to see your app.');
|
|
88
|
-
if (answers.admin) {
|
|
89
|
-
console.log(' Visit http://localhost:4321/admin for the admin dashboard.');
|
|
90
|
-
}
|
|
91
|
-
console.log('');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
main().catch((error) => {
|
|
95
|
-
logger.error(`Unexpected error: ${error.message}`);
|
|
96
|
-
process.exit(1);
|
|
97
|
-
});
|
package/src/prompts.ts
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import prompts from 'prompts';
|
|
2
|
-
import { validateProjectName } from './validators.js';
|
|
3
|
-
|
|
4
|
-
export interface Answers {
|
|
5
|
-
projectName: string;
|
|
6
|
-
database: 'sqlite' | 'postgres' | 'mysql' | 'mongodb';
|
|
7
|
-
apis: ('rest' | 'graphql' | 'trpc' | 'websocket')[];
|
|
8
|
-
styling: 'tailwind' | 'cssmodules' | 'styled' | 'none';
|
|
9
|
-
auth: boolean;
|
|
10
|
-
versioning: boolean;
|
|
11
|
-
admin: boolean;
|
|
12
|
-
template: 'minimal' | 'blog' | 'ecommerce';
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function promptUser(): Promise<Answers> {
|
|
16
|
-
const response = await prompts([
|
|
17
|
-
{
|
|
18
|
-
type: 'text',
|
|
19
|
-
name: 'projectName',
|
|
20
|
-
message: 'Project name:',
|
|
21
|
-
initial: 'my-kyro-app',
|
|
22
|
-
validate: validateProjectName
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
type: 'select',
|
|
26
|
-
name: 'database',
|
|
27
|
-
message: 'Database:',
|
|
28
|
-
hint: ' ',
|
|
29
|
-
choices: [
|
|
30
|
-
{
|
|
31
|
-
title: 'SQLite (local-first, zero config)',
|
|
32
|
-
description: 'Best for development and small projects. No setup required.',
|
|
33
|
-
value: 'sqlite'
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
title: 'PostgreSQL',
|
|
37
|
-
description: 'Recommended for production. Robust and scalable.',
|
|
38
|
-
value: 'postgres'
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
title: 'MySQL',
|
|
42
|
-
description: 'Popular choice for web applications.',
|
|
43
|
-
value: 'mysql'
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
title: 'MongoDB',
|
|
47
|
-
description: 'Best for flexible, document-based schemas.',
|
|
48
|
-
value: 'mongodb'
|
|
49
|
-
}
|
|
50
|
-
]
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
type: 'multiselect',
|
|
54
|
-
name: 'apis',
|
|
55
|
-
message: 'API protocols (select multiple):',
|
|
56
|
-
hint: 'Space to select, Enter to confirm',
|
|
57
|
-
instructions: false,
|
|
58
|
-
choices: [
|
|
59
|
-
{
|
|
60
|
-
title: 'REST',
|
|
61
|
-
description: 'Simple HTTP API, great for any client',
|
|
62
|
-
value: 'rest',
|
|
63
|
-
selected: true
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
title: 'GraphQL',
|
|
67
|
-
description: 'Flexible query language, great for complex data',
|
|
68
|
-
value: 'graphql',
|
|
69
|
-
selected: true
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
title: 'tRPC',
|
|
73
|
-
description: 'End-to-end typesafe APIs, great for TypeScript',
|
|
74
|
-
value: 'trpc'
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
title: 'WebSocket',
|
|
78
|
-
description: 'Real-time bidirectional communication',
|
|
79
|
-
value: 'websocket'
|
|
80
|
-
}
|
|
81
|
-
]
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
type: 'select',
|
|
85
|
-
name: 'styling',
|
|
86
|
-
message: 'Styling:',
|
|
87
|
-
hint: ' ',
|
|
88
|
-
choices: [
|
|
89
|
-
{
|
|
90
|
-
title: 'Tailwind CSS',
|
|
91
|
-
description: 'Utility-first CSS framework, excellent DX',
|
|
92
|
-
value: 'tailwind'
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
title: 'CSS Modules',
|
|
96
|
-
description: 'Scoped CSS, no extra dependencies',
|
|
97
|
-
value: 'cssmodules'
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
title: 'Styled Components',
|
|
101
|
-
description: 'CSS-in-JS with tagged template literals',
|
|
102
|
-
value: 'styled'
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
title: 'None',
|
|
106
|
-
description: 'Bring your own styling solution',
|
|
107
|
-
value: 'none'
|
|
108
|
-
}
|
|
109
|
-
]
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
type: 'toggle',
|
|
113
|
-
name: 'auth',
|
|
114
|
-
message: 'Add authentication (JWT)?',
|
|
115
|
-
active: 'Yes',
|
|
116
|
-
inactive: 'No'
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
type: 'toggle',
|
|
120
|
-
name: 'versioning',
|
|
121
|
-
message: 'Add versioning/drafts?',
|
|
122
|
-
active: 'Yes',
|
|
123
|
-
inactive: 'No'
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
type: 'toggle',
|
|
127
|
-
name: 'admin',
|
|
128
|
-
message: 'Include admin dashboard?',
|
|
129
|
-
initial: true,
|
|
130
|
-
active: 'Yes',
|
|
131
|
-
inactive: 'No'
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
type: 'select',
|
|
135
|
-
name: 'template',
|
|
136
|
-
message: 'Starting template:',
|
|
137
|
-
hint: ' ',
|
|
138
|
-
choices: [
|
|
139
|
-
{
|
|
140
|
-
title: 'Minimal',
|
|
141
|
-
description: 'Basic configuration with one example collection',
|
|
142
|
-
value: 'minimal'
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
title: 'Blog',
|
|
146
|
-
description: 'Posts, categories, tags, and media library',
|
|
147
|
-
value: 'blog'
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
title: 'E-commerce',
|
|
151
|
-
description: 'Products, orders, customers, inventory, and coupons',
|
|
152
|
-
value: 'ecommerce'
|
|
153
|
-
}
|
|
154
|
-
]
|
|
155
|
-
}
|
|
156
|
-
], {
|
|
157
|
-
onCancel: () => {
|
|
158
|
-
console.log('\nCancelled.');
|
|
159
|
-
process.exit(1);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
return response as Answers;
|
|
164
|
-
}
|
package/src/utils/logger.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import kolorist from 'kolorist';
|
|
2
|
-
|
|
3
|
-
const { cyan, green, yellow, red, bold, dim } = kolorist;
|
|
4
|
-
|
|
5
|
-
export const logger = {
|
|
6
|
-
intro: (name: string, version: string) => {
|
|
7
|
-
console.log(`
|
|
8
|
-
${bold(cyan(`██████╗ ███████╗██╗ ██╗██████╗ ███████╗`))}
|
|
9
|
-
${bold(cyan(`██╔══██╗██╔════╝██║ ██║██╔══██╗██╔════╝`))}
|
|
10
|
-
${bold(cyan(`██████╔╝███████╗██║ ██║██████╔╝███████╗`))}
|
|
11
|
-
${bold(cyan(`██╔═══╝ ╚════██║██║ ██║██╔═══╝ ╚════██║`))}
|
|
12
|
-
${bold(cyan(`██║ ███████║╚██████╔╝██║ ███████║`))}
|
|
13
|
-
${bold(cyan(`╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚══════╝`))}
|
|
14
|
-
${dim(`v${version}`)}
|
|
15
|
-
${dim('Astro-native headless CMS')}
|
|
16
|
-
`);
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
success: (msg: string) => {
|
|
20
|
-
console.log(`${green('✓')} ${msg}`);
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
error: (msg: string) => {
|
|
24
|
-
console.log(`${red('✗')} ${msg}`);
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
warning: (msg: string) => {
|
|
28
|
-
console.log(`${yellow('⚠')} ${msg}`);
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
info: (msg: string) => {
|
|
32
|
-
console.log(`${cyan('ℹ')} ${msg}`);
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
step: (num: number, total: number, msg: string) => {
|
|
36
|
-
console.log(`${dim(`[${num}/${total}]`)} ${msg}`);
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
heading: (msg: string) => {
|
|
40
|
-
console.log(`\n${bold(cyan(msg))}`);
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
subheading: (msg: string) => {
|
|
44
|
-
console.log(`\n${bold(msg)}`);
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
list: (items: string[]) => {
|
|
48
|
-
items.forEach(item => {
|
|
49
|
-
console.log(` ${green('•')} ${item}`);
|
|
50
|
-
});
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
done: () => {
|
|
54
|
-
console.log(`\n${green(bold('Done!'))} Project created successfully.\n`);
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
prompt: (msg: string) => {
|
|
58
|
-
console.log(`\n${cyan('?')} ${bold(msg)}`);
|
|
59
|
-
}
|
|
60
|
-
};
|
package/src/validators.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export function validateProjectName(name: string): string | true {
|
|
2
|
-
if (!name) {
|
|
3
|
-
return 'Project name is required';
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
7
|
-
return 'Use lowercase letters, numbers, and hyphens only';
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
if (name.length < 2) {
|
|
11
|
-
return 'Project name must be at least 2 characters';
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (name.length > 50) {
|
|
15
|
-
return 'Project name must be less than 50 characters';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (/^[-0-9]/.test(name)) {
|
|
19
|
-
return 'Project name cannot start with a number or hyphen';
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const reserved = ['node_modules', 'dist', 'build', 'public', 'src', 'test', 'tests'];
|
|
23
|
-
if (reserved.includes(name)) {
|
|
24
|
-
return `"${name}" is a reserved name`;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function validateEmail(email: string): string | true {
|
|
31
|
-
if (!email) return true;
|
|
32
|
-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
33
|
-
return 'Invalid email address';
|
|
34
|
-
}
|
|
35
|
-
return true;
|
|
36
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"strict": true,
|
|
7
|
-
"esModuleInterop": true,
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"outDir": "./dist",
|
|
10
|
-
"declaration": true,
|
|
11
|
-
"lib": ["ES2022"]
|
|
12
|
-
},
|
|
13
|
-
"include": ["src/**/*"],
|
|
14
|
-
"exclude": ["node_modules", "dist"]
|
|
15
|
-
}
|