create-kofi-stack 2.0.23 → 2.1.1
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 +200 -52
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -85,76 +85,185 @@ async function runPrompts(projectNameArg, options) {
|
|
|
85
85
|
if (p.isCancel(structure)) throw new Error("cancelled");
|
|
86
86
|
let marketingSite = "none";
|
|
87
87
|
if (structure === "monorepo") {
|
|
88
|
+
const marketingOptions = [
|
|
89
|
+
{ value: "none", label: "None", hint: "Skip marketing site" },
|
|
90
|
+
{ value: "nextjs", label: "Next.js", hint: "Simple marketing pages" },
|
|
91
|
+
{ value: "payload", label: "Payload CMS", hint: "Full CMS for content" }
|
|
92
|
+
];
|
|
88
93
|
marketingSite = await p.select({
|
|
89
94
|
message: "Add a marketing site?",
|
|
90
|
-
options:
|
|
91
|
-
{ value: "none", label: "None", hint: "Skip marketing site" },
|
|
92
|
-
{ value: "nextjs", label: "Next.js", hint: "Simple marketing pages" },
|
|
93
|
-
{ value: "payload", label: "Payload CMS", hint: "Full CMS for content" }
|
|
94
|
-
],
|
|
95
|
+
options: marketingOptions,
|
|
95
96
|
initialValue: "none"
|
|
96
97
|
});
|
|
97
98
|
if (p.isCancel(marketingSite)) throw new Error("cancelled");
|
|
98
99
|
}
|
|
100
|
+
const componentLibraryOptions = [
|
|
101
|
+
{ value: "base", label: "Base UI", hint: "Recommended - Headless, unstyled primitives" },
|
|
102
|
+
{ value: "radix", label: "Radix UI", hint: "Full-featured accessible primitives" }
|
|
103
|
+
];
|
|
104
|
+
const componentLibrary = await p.select({
|
|
105
|
+
message: "Select a component library",
|
|
106
|
+
options: componentLibraryOptions,
|
|
107
|
+
initialValue: "base"
|
|
108
|
+
});
|
|
109
|
+
if (p.isCancel(componentLibrary)) throw new Error("cancelled");
|
|
110
|
+
const styleOptions = [
|
|
111
|
+
{ value: "nova", label: "Nova", hint: "Modern, clean design" },
|
|
112
|
+
{ value: "vega", label: "Vega", hint: "Bold, vibrant" },
|
|
113
|
+
{ value: "maia", label: "Maia", hint: "Soft, organic" },
|
|
114
|
+
{ value: "lyra", label: "Lyra", hint: "Minimal, refined" },
|
|
115
|
+
{ value: "mira", label: "Mira", hint: "Elegant, classic" }
|
|
116
|
+
];
|
|
117
|
+
const style = await p.select({
|
|
118
|
+
message: "What style do you want?",
|
|
119
|
+
options: styleOptions,
|
|
120
|
+
initialValue: "nova"
|
|
121
|
+
});
|
|
122
|
+
if (p.isCancel(style)) throw new Error("cancelled");
|
|
123
|
+
const baseColorOptions = [
|
|
124
|
+
{ value: "neutral", label: "Neutral" },
|
|
125
|
+
{ value: "stone", label: "Stone" },
|
|
126
|
+
{ value: "zinc", label: "Zinc" },
|
|
127
|
+
{ value: "gray", label: "Gray" }
|
|
128
|
+
];
|
|
99
129
|
const baseColor = await p.select({
|
|
100
|
-
message: "
|
|
101
|
-
options:
|
|
102
|
-
{ value: "neutral", label: "Neutral" },
|
|
103
|
-
{ value: "slate", label: "Slate" },
|
|
104
|
-
{ value: "zinc", label: "Zinc" },
|
|
105
|
-
{ value: "stone", label: "Stone" },
|
|
106
|
-
{ value: "gray", label: "Gray" }
|
|
107
|
-
],
|
|
130
|
+
message: "Choose your base color",
|
|
131
|
+
options: baseColorOptions,
|
|
108
132
|
initialValue: "neutral"
|
|
109
133
|
});
|
|
110
134
|
if (p.isCancel(baseColor)) throw new Error("cancelled");
|
|
135
|
+
const themeColorOptions = [
|
|
136
|
+
{ value: "neutral", label: "Neutral" },
|
|
137
|
+
{ value: "blue", label: "Blue" },
|
|
138
|
+
{ value: "green", label: "Green" },
|
|
139
|
+
{ value: "purple", label: "Purple" },
|
|
140
|
+
{ value: "red", label: "Red" },
|
|
141
|
+
{ value: "orange", label: "Orange" },
|
|
142
|
+
{ value: "amber", label: "Amber" },
|
|
143
|
+
{ value: "cyan", label: "Cyan" },
|
|
144
|
+
{ value: "emerald", label: "Emerald" },
|
|
145
|
+
{ value: "fuchsia", label: "Fuchsia" },
|
|
146
|
+
{ value: "indigo", label: "Indigo" },
|
|
147
|
+
{ value: "lime", label: "Lime" },
|
|
148
|
+
{ value: "pink", label: "Pink" },
|
|
149
|
+
{ value: "rose", label: "Rose" },
|
|
150
|
+
{ value: "sky", label: "Sky" },
|
|
151
|
+
{ value: "teal", label: "Teal" },
|
|
152
|
+
{ value: "violet", label: "Violet" },
|
|
153
|
+
{ value: "yellow", label: "Yellow" }
|
|
154
|
+
];
|
|
155
|
+
const themeColor = await p.select({
|
|
156
|
+
message: "Choose your theme color",
|
|
157
|
+
options: themeColorOptions,
|
|
158
|
+
initialValue: "neutral"
|
|
159
|
+
});
|
|
160
|
+
if (p.isCancel(themeColor)) throw new Error("cancelled");
|
|
161
|
+
const iconLibraryOptions = [
|
|
162
|
+
{ value: "hugeicons", label: "HugeIcons", hint: "Recommended - 4000+ icons" },
|
|
163
|
+
{ value: "lucide", label: "Lucide", hint: "Popular, lightweight" },
|
|
164
|
+
{ value: "tabler", label: "Tabler Icons", hint: "Free, open source" },
|
|
165
|
+
{ value: "phosphor", label: "Phosphor Icons", hint: "Flexible, consistent" }
|
|
166
|
+
];
|
|
167
|
+
const iconLibrary = await p.select({
|
|
168
|
+
message: "Choose your icon library",
|
|
169
|
+
options: iconLibraryOptions,
|
|
170
|
+
initialValue: "hugeicons"
|
|
171
|
+
});
|
|
172
|
+
if (p.isCancel(iconLibrary)) throw new Error("cancelled");
|
|
173
|
+
const fontOptions = [
|
|
174
|
+
{ value: "inter", label: "Inter", hint: "Recommended - Clean, modern" },
|
|
175
|
+
{ value: "dm-sans", label: "DM Sans", hint: "Geometric, friendly" },
|
|
176
|
+
{ value: "figtree", label: "Figtree", hint: "Playful, readable" },
|
|
177
|
+
{ value: "outfit", label: "Outfit", hint: "Modern, versatile" },
|
|
178
|
+
{ value: "public-sans", label: "Public Sans", hint: "Neutral, accessible" },
|
|
179
|
+
{ value: "roboto", label: "Roboto", hint: "Classic, reliable" },
|
|
180
|
+
{ value: "raleway", label: "Raleway", hint: "Elegant, stylish" },
|
|
181
|
+
{ value: "noto-sans", label: "Noto Sans", hint: "Global, inclusive" },
|
|
182
|
+
{ value: "nunito-sans", label: "Nunito Sans", hint: "Rounded, friendly" },
|
|
183
|
+
{ value: "jetbrains-mono", label: "JetBrains Mono", hint: "Code-focused" }
|
|
184
|
+
];
|
|
185
|
+
const font = await p.select({
|
|
186
|
+
message: "Select your font",
|
|
187
|
+
options: fontOptions,
|
|
188
|
+
initialValue: "inter"
|
|
189
|
+
});
|
|
190
|
+
if (p.isCancel(font)) throw new Error("cancelled");
|
|
191
|
+
const menuAccentOptions = [
|
|
192
|
+
{ value: "subtle", label: "Subtle", hint: "Muted accent colors" },
|
|
193
|
+
{ value: "bold", label: "Bold", hint: "Strong accent colors" }
|
|
194
|
+
];
|
|
195
|
+
const menuAccent = await p.select({
|
|
196
|
+
message: "Menu accent",
|
|
197
|
+
options: menuAccentOptions,
|
|
198
|
+
initialValue: "subtle"
|
|
199
|
+
});
|
|
200
|
+
if (p.isCancel(menuAccent)) throw new Error("cancelled");
|
|
201
|
+
const menuColorOptions = [
|
|
202
|
+
{ value: "default", label: "Default", hint: "Standard colors" },
|
|
203
|
+
{ value: "inverted", label: "Inverted", hint: "Dark sidebar" }
|
|
204
|
+
];
|
|
205
|
+
const menuColor = await p.select({
|
|
206
|
+
message: "Menu color",
|
|
207
|
+
options: menuColorOptions,
|
|
208
|
+
initialValue: "default"
|
|
209
|
+
});
|
|
210
|
+
if (p.isCancel(menuColor)) throw new Error("cancelled");
|
|
211
|
+
const radiusOptions = [
|
|
212
|
+
{ value: "default", label: "Default" },
|
|
213
|
+
{ value: "none", label: "None", hint: "Sharp corners" },
|
|
214
|
+
{ value: "small", label: "Small" },
|
|
215
|
+
{ value: "medium", label: "Medium" },
|
|
216
|
+
{ value: "large", label: "Large", hint: "Rounded corners" }
|
|
217
|
+
];
|
|
218
|
+
const radius = await p.select({
|
|
219
|
+
message: "Border radius",
|
|
220
|
+
options: radiusOptions,
|
|
221
|
+
initialValue: "default"
|
|
222
|
+
});
|
|
223
|
+
if (p.isCancel(radius)) throw new Error("cancelled");
|
|
224
|
+
const analyticsOptions = [
|
|
225
|
+
{ value: "none", label: "None", hint: "Skip analytics" },
|
|
226
|
+
{ value: "posthog", label: "PostHog", hint: "Product analytics" },
|
|
227
|
+
{ value: "vercel", label: "Vercel Analytics", hint: "Simple analytics" }
|
|
228
|
+
];
|
|
111
229
|
const analytics = await p.select({
|
|
112
230
|
message: "Analytics provider?",
|
|
113
|
-
options:
|
|
114
|
-
{ value: "none", label: "None", hint: "Skip analytics" },
|
|
115
|
-
{ value: "posthog", label: "PostHog", hint: "Product analytics" },
|
|
116
|
-
{ value: "vercel", label: "Vercel Analytics", hint: "Simple analytics" }
|
|
117
|
-
],
|
|
231
|
+
options: analyticsOptions,
|
|
118
232
|
initialValue: "none"
|
|
119
233
|
});
|
|
120
234
|
if (p.isCancel(analytics)) throw new Error("cancelled");
|
|
235
|
+
const uploadsOptions = [
|
|
236
|
+
{ value: "convex-fs", label: "Convex FS", hint: "Built-in Convex storage (Recommended)" },
|
|
237
|
+
{ value: "r2", label: "Cloudflare R2", hint: "S3-compatible edge storage" },
|
|
238
|
+
{ value: "uploadthing", label: "UploadThing", hint: "Easy file uploads" },
|
|
239
|
+
{ value: "s3", label: "AWS S3", hint: "S3-compatible storage" },
|
|
240
|
+
{ value: "vercel-blob", label: "Vercel Blob", hint: "Vercel storage" },
|
|
241
|
+
{ value: "none", label: "None", hint: "Skip file storage" }
|
|
242
|
+
];
|
|
121
243
|
const uploads = await p.select({
|
|
122
244
|
message: "File storage provider?",
|
|
123
|
-
options:
|
|
124
|
-
{ value: "convex-fs", label: "Convex FS", hint: "Built-in Convex storage (Recommended)" },
|
|
125
|
-
{ value: "r2", label: "Cloudflare R2", hint: "S3-compatible edge storage" },
|
|
126
|
-
{ value: "uploadthing", label: "UploadThing", hint: "Easy file uploads" },
|
|
127
|
-
{ value: "s3", label: "AWS S3", hint: "S3-compatible storage" },
|
|
128
|
-
{ value: "vercel-blob", label: "Vercel Blob", hint: "Vercel storage" },
|
|
129
|
-
{ value: "none", label: "None", hint: "Skip file storage" }
|
|
130
|
-
],
|
|
245
|
+
options: uploadsOptions,
|
|
131
246
|
initialValue: "convex-fs"
|
|
132
247
|
});
|
|
133
248
|
if (p.isCancel(uploads)) throw new Error("cancelled");
|
|
249
|
+
const paymentsOptions = [
|
|
250
|
+
{ value: "none", label: "None", hint: "Skip payments" },
|
|
251
|
+
{ value: "stripe", label: "Stripe", hint: "Full payment platform" },
|
|
252
|
+
{ value: "polar", label: "Polar", hint: "Open source monetization" }
|
|
253
|
+
];
|
|
134
254
|
const payments = await p.select({
|
|
135
255
|
message: "Payment provider?",
|
|
136
|
-
options:
|
|
137
|
-
{ value: "none", label: "None", hint: "Skip payments" },
|
|
138
|
-
{ value: "stripe", label: "Stripe", hint: "Full payment platform" },
|
|
139
|
-
{ value: "polar", label: "Polar", hint: "Open source monetization" }
|
|
140
|
-
],
|
|
256
|
+
options: paymentsOptions,
|
|
141
257
|
initialValue: "none"
|
|
142
258
|
});
|
|
143
259
|
if (p.isCancel(payments)) throw new Error("cancelled");
|
|
260
|
+
const addonsOptions = [
|
|
261
|
+
{ value: "rate-limiting", label: "Rate Limiting", hint: "Convex Rate Limiter (Recommended)" },
|
|
262
|
+
{ value: "monitoring", label: "Error Monitoring", hint: "Sentry integration" }
|
|
263
|
+
];
|
|
144
264
|
const addonsSelected = await p.multiselect({
|
|
145
265
|
message: "Additional features?",
|
|
146
|
-
options:
|
|
147
|
-
{
|
|
148
|
-
value: "rate-limiting",
|
|
149
|
-
label: "Rate Limiting",
|
|
150
|
-
hint: "Convex Rate Limiter (Recommended)"
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
value: "monitoring",
|
|
154
|
-
label: "Error Monitoring",
|
|
155
|
-
hint: "Sentry integration"
|
|
156
|
-
}
|
|
157
|
-
],
|
|
266
|
+
options: addonsOptions,
|
|
158
267
|
required: false
|
|
159
268
|
});
|
|
160
269
|
if (p.isCancel(addonsSelected)) throw new Error("cancelled");
|
|
@@ -164,7 +273,17 @@ async function runPrompts(projectNameArg, options) {
|
|
|
164
273
|
`${pc.cyan("Project:")} ${name}`,
|
|
165
274
|
`${pc.cyan("Structure:")} ${structure}`,
|
|
166
275
|
structure === "monorepo" ? `${pc.cyan("Marketing:")} ${marketingSite}` : null,
|
|
276
|
+
"",
|
|
277
|
+
pc.dim("\u2500\u2500\u2500 Design System \u2500\u2500\u2500"),
|
|
278
|
+
`${pc.cyan("Component Library:")} ${componentLibrary}`,
|
|
279
|
+
`${pc.cyan("Style:")} ${style}`,
|
|
167
280
|
`${pc.cyan("Base Color:")} ${baseColor}`,
|
|
281
|
+
`${pc.cyan("Theme Color:")} ${themeColor}`,
|
|
282
|
+
`${pc.cyan("Icon Library:")} ${iconLibrary}`,
|
|
283
|
+
`${pc.cyan("Font:")} ${font}`,
|
|
284
|
+
`${pc.cyan("Radius:")} ${radius}`,
|
|
285
|
+
"",
|
|
286
|
+
pc.dim("\u2500\u2500\u2500 Integrations \u2500\u2500\u2500"),
|
|
168
287
|
`${pc.cyan("Analytics:")} ${analytics}`,
|
|
169
288
|
`${pc.cyan("Storage:")} ${uploads}`,
|
|
170
289
|
`${pc.cyan("Payments:")} ${payments}`,
|
|
@@ -187,14 +306,15 @@ async function runPrompts(projectNameArg, options) {
|
|
|
187
306
|
organizations: false
|
|
188
307
|
},
|
|
189
308
|
shadcn: {
|
|
309
|
+
componentLibrary,
|
|
310
|
+
style,
|
|
190
311
|
baseColor,
|
|
191
|
-
themeColor
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
spacingScale: "default"
|
|
312
|
+
themeColor,
|
|
313
|
+
iconLibrary,
|
|
314
|
+
font,
|
|
315
|
+
menuAccent,
|
|
316
|
+
menuColor,
|
|
317
|
+
radius
|
|
198
318
|
},
|
|
199
319
|
integrations: {
|
|
200
320
|
analytics,
|
|
@@ -251,6 +371,22 @@ function generateSecret() {
|
|
|
251
371
|
return crypto.randomBytes(32).toString("base64");
|
|
252
372
|
}
|
|
253
373
|
}
|
|
374
|
+
function buildShadcnPresetUrl(config) {
|
|
375
|
+
const { shadcn } = config;
|
|
376
|
+
const params = new URLSearchParams({
|
|
377
|
+
base: shadcn.componentLibrary,
|
|
378
|
+
style: shadcn.style,
|
|
379
|
+
baseColor: shadcn.baseColor,
|
|
380
|
+
theme: shadcn.themeColor,
|
|
381
|
+
iconLibrary: shadcn.iconLibrary,
|
|
382
|
+
font: shadcn.font,
|
|
383
|
+
menuAccent: shadcn.menuAccent,
|
|
384
|
+
menuColor: shadcn.menuColor,
|
|
385
|
+
radius: shadcn.radius,
|
|
386
|
+
template: "next"
|
|
387
|
+
});
|
|
388
|
+
return `https://ui.shadcn.com/init?${params.toString()}`;
|
|
389
|
+
}
|
|
254
390
|
async function updateEnvWithSecrets(envPath, secrets) {
|
|
255
391
|
if (!await fs.pathExists(envPath)) {
|
|
256
392
|
return;
|
|
@@ -314,15 +450,27 @@ async function generateProject(config, options = {}) {
|
|
|
314
450
|
spinner.warn("Failed to install dependencies. Run pnpm install manually.");
|
|
315
451
|
}
|
|
316
452
|
const shadcnDir = config.structure === "monorepo" ? path2.join(config.targetDir, "apps/web") : config.targetDir;
|
|
453
|
+
const presetUrl = buildShadcnPresetUrl(config);
|
|
454
|
+
spinner.start("Initializing shadcn/ui...");
|
|
455
|
+
try {
|
|
456
|
+
await execa("pnpm", ["dlx", "shadcn@latest", "init", "--yes", "--preset", presetUrl], {
|
|
457
|
+
cwd: shadcnDir,
|
|
458
|
+
stdio: "pipe"
|
|
459
|
+
});
|
|
460
|
+
spinner.succeed("shadcn/ui initialized");
|
|
461
|
+
} catch {
|
|
462
|
+
spinner.warn(`Failed to initialize shadcn. Run manually:
|
|
463
|
+
pnpm dlx shadcn@latest init --preset "${presetUrl}"`);
|
|
464
|
+
}
|
|
317
465
|
spinner.start("Installing shadcn/ui components...");
|
|
318
466
|
try {
|
|
319
|
-
await execa("pnpm", ["dlx", "shadcn@latest", "add", "--all", "
|
|
467
|
+
await execa("pnpm", ["dlx", "shadcn@latest", "add", "--all", "--yes"], {
|
|
320
468
|
cwd: shadcnDir,
|
|
321
469
|
stdio: "pipe"
|
|
322
470
|
});
|
|
323
471
|
spinner.succeed("shadcn/ui components installed");
|
|
324
472
|
} catch {
|
|
325
|
-
spinner.warn("Failed to install shadcn/ui components. Run pnpm dlx shadcn@latest add --all
|
|
473
|
+
spinner.warn("Failed to install shadcn/ui components. Run: pnpm dlx shadcn@latest add --all");
|
|
326
474
|
}
|
|
327
475
|
if (!skipPrompts) {
|
|
328
476
|
console.log();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-kofi-stack",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Scaffold opinionated full-stack projects with Next.js, Convex, Better-Auth, and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"gradient-string": "^2.0.2",
|
|
18
18
|
"ora": "^8.0.0",
|
|
19
19
|
"picocolors": "^1.0.0",
|
|
20
|
-
"kofi-stack-
|
|
21
|
-
"kofi-stack-
|
|
20
|
+
"kofi-stack-template-generator": "^2.1.0",
|
|
21
|
+
"kofi-stack-types": "^2.1.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/fs-extra": "^11.0.4",
|