@mvp-kit/create 0.0.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/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # create-mvpkit
2
+
3
+ > The official CLI for creating MVPKit applications
4
+
5
+ [![npm version](https://badge.fury.io/js/create-mvpkit.svg)](https://www.npmjs.com/package/create-mvpkit)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Quick Start
9
+
10
+ ```bash
11
+ # Create a new MVPKit application
12
+ npx create-mvpkit my-app
13
+
14
+ # Or with pnpm
15
+ pnpm create mvpkit my-app
16
+
17
+ # Or with yarn
18
+ yarn create mvpkit my-app
19
+ ```
20
+
21
+ ## Features
22
+
23
+ 🆓 **Free Core Template**
24
+ - React 19 with modern concurrent features
25
+ - Cloudflare Workers for edge computing
26
+ - tRPC for end-to-end type safety
27
+ - Drizzle ORM with D1 database
28
+ - Better Auth for authentication
29
+ - Tailwind CSS + shadcn/ui components
30
+
31
+ 💰 **Premium Templates** (License Required)
32
+ - **SaaS**: Payments, subscriptions, multi-tenancy
33
+ - **AI**: LLM integrations, vector databases, AI components
34
+
35
+ ## Usage
36
+
37
+ ### Basic Commands
38
+
39
+ ```bash
40
+ # List available templates
41
+ npx create-mvpkit list
42
+
43
+ # Create with specific template
44
+ npx create-mvpkit my-app --template core
45
+ npx create-mvpkit my-saas --template saas --license YOUR_LICENSE
46
+ npx create-mvpkit my-ai --template ai --license YOUR_LICENSE
47
+
48
+ # Interactive mode
49
+ npx create-mvpkit my-app --interactive
50
+ ```
51
+
52
+ ### Options
53
+
54
+ | Option | Description |
55
+ |--------|-------------|
56
+ | `--template <name>` | Template to use (core, saas, ai) |
57
+ | `--license <key>` | License key for paid templates |
58
+ | `--description <text>` | Project description |
59
+ | `--domain <domain>` | Domain name for the application |
60
+ | `--no-install` | Skip dependency installation |
61
+ | `--no-git` | Skip git initialization |
62
+ | `--setup-db` | Setup database and run migrations |
63
+ | `--seed-db` | Seed database with sample data |
64
+ | `--no-interactive` | Skip interactive mode (use defaults) |
65
+ | `--local` | Use local template files (development) |
66
+
67
+ ### Examples
68
+
69
+ ```bash
70
+ # Minimal setup
71
+ npx create-mvpkit my-app --no-install --no-git
72
+
73
+ # Full setup with database
74
+ npx create-mvpkit my-app --setup-db --seed-db
75
+
76
+ # Non-interactive with custom options
77
+ npx create-mvpkit my-app \
78
+ --description "My awesome app" \
79
+ --domain "myapp.com" \
80
+ --no-interactive
81
+ ```
82
+
83
+ ## Templates
84
+
85
+ ### 🆓 Core (Free)
86
+ Perfect for MVPs and small projects:
87
+ - **Frontend**: React 19, TanStack Router, TanStack Query
88
+ - **Backend**: Cloudflare Workers, Hono, tRPC
89
+ - **Database**: D1 with Drizzle ORM
90
+ - **Auth**: Better Auth with social providers
91
+ - **Styling**: Tailwind CSS v4 + shadcn/ui
92
+ - **Deployment**: Cloudflare Pages + Workers
93
+
94
+ ### 💰 SaaS (Premium)
95
+ Everything in Core plus:
96
+ - Stripe payments integration
97
+ - Subscription management
98
+ - Multi-tenant architecture
99
+ - Admin dashboard
100
+ - User management
101
+ - Billing portal
102
+
103
+ ### 💰 AI (Premium)
104
+ Everything in Core plus:
105
+ - OpenAI API integration
106
+ - Vector database (Pinecone/Weaviate)
107
+ - AI chat components
108
+ - Document processing
109
+ - Embeddings management
110
+ - AI workflow tools
111
+
112
+ ## Get a License
113
+
114
+ Visit [mvpkit.dev](https://mvpkit.dev) to purchase licenses for premium templates.
115
+
116
+ ## Development
117
+
118
+ For local development of the CLI itself:
119
+
120
+ ```bash
121
+ # Clone the repository
122
+ git clone https://github.com/mvp-kit/cli.git
123
+ cd cli
124
+
125
+ # Install dependencies
126
+ pnpm install
127
+
128
+ # Build the CLI
129
+ pnpm build
130
+
131
+ # Test locally
132
+ npx create-mvpkit test-app --local
133
+ ```
134
+
135
+ See [CLI_TESTING_GUIDE.md](./CLI_TESTING_GUIDE.md) for detailed development instructions.
136
+
137
+ ## Support
138
+
139
+ - 🌐 [Website](https://mvpkit.dev)
140
+ - 📖 [Documentation](https://docs.mvpkit.dev)
141
+ - 🔗 [Examples](https://github.com/mvp-kit/core/examples)
142
+
143
+ ## License
144
+
145
+ MIT © [MVPKit](https://mvpkit.dev)
package/dist/create.js ADDED
@@ -0,0 +1,549 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { execSync } from 'child_process';
4
+ import chalk from 'chalk';
5
+ import ora from 'ora';
6
+ import inquirer from 'inquirer';
7
+ import { glob } from 'glob';
8
+ // Enhanced Box System
9
+ const BOX_WIDTH = 72;
10
+ const BOX_INDENT = ' ';
11
+ function drawBox(content, title) {
12
+ const lines = [];
13
+ // Top border
14
+ lines.push(BOX_INDENT + '┌' + '─'.repeat(BOX_WIDTH - 2) + '┐');
15
+ // Title if provided
16
+ if (title) {
17
+ lines.push(drawBoxLine(title, true));
18
+ lines.push(drawBoxLine(''));
19
+ }
20
+ // Content lines with wrapping
21
+ content.forEach(line => {
22
+ const wrappedLines = wrapText(line, BOX_WIDTH - 4);
23
+ wrappedLines.forEach(wrappedLine => {
24
+ lines.push(drawBoxLine(wrappedLine));
25
+ });
26
+ });
27
+ // Add empty line if we have content
28
+ if (content.length > 0) {
29
+ lines.push(drawBoxLine(''));
30
+ }
31
+ // Bottom border
32
+ lines.push(BOX_INDENT + '└' + '─'.repeat(BOX_WIDTH - 2) + '┘');
33
+ return lines.join('\n');
34
+ }
35
+ function wrapText(text, maxWidth) {
36
+ if (text.length <= maxWidth) {
37
+ return [text];
38
+ }
39
+ const words = text.split(' ');
40
+ const lines = [];
41
+ let currentLine = '';
42
+ for (const word of words) {
43
+ if ((currentLine + (currentLine ? ' ' : '') + word).length <= maxWidth) {
44
+ currentLine += (currentLine ? ' ' : '') + word;
45
+ }
46
+ else {
47
+ if (currentLine)
48
+ lines.push(currentLine);
49
+ currentLine = word;
50
+ }
51
+ }
52
+ if (currentLine)
53
+ lines.push(currentLine);
54
+ return lines;
55
+ }
56
+ function drawBoxLine(content, isTitle = false) {
57
+ const availableWidth = BOX_WIDTH - 4; // Account for borders and spaces
58
+ const padding = availableWidth - content.length;
59
+ const coloredText = isTitle ? chalk.bold.cyan(content) : chalk.white(content);
60
+ return BOX_INDENT + '│ ' + coloredText + ' '.repeat(padding) + ' │';
61
+ }
62
+ import { generateTemplateVariables, getCoreRepositoryPath, shouldSkipFile, DEFAULT_SKIP_PATTERNS, transformFileName } from './utils.js';
63
+ import { getFileTransformFunction } from './template.js';
64
+ import { setupDatabase } from './database.js';
65
+ import { TemplateDownloader, TEMPLATE_REGISTRY } from './template-downloader.js';
66
+ function detectPackageManager(dir) {
67
+ // Check how CLI was invoked (highest priority)
68
+ const invocation = detectInvocationMethod();
69
+ if (invocation) {
70
+ return invocation;
71
+ }
72
+ // Check for lock files in priority order
73
+ if (fs.existsSync(path.join(dir, 'bun.lockb'))) {
74
+ return { pm: 'bun', version: getPackageVersion('bun') };
75
+ }
76
+ if (fs.existsSync(path.join(dir, 'pnpm-lock.yaml'))) {
77
+ return { pm: 'pnpm', version: getPackageVersion('pnpm') };
78
+ }
79
+ // Default to pnpm
80
+ return { pm: 'pnpm', version: '10.14.0' };
81
+ }
82
+ function getPackageVersion(pm) {
83
+ try {
84
+ const result = execSync(`${pm} --version`, { encoding: 'utf8', stdio: 'pipe' });
85
+ return result.trim();
86
+ }
87
+ catch {
88
+ return '10.14.0';
89
+ }
90
+ }
91
+ function detectInvocationMethod() {
92
+ // Check process.execArgv and process.argv to see how we were invoked
93
+ const execPath = process.execPath;
94
+ const argv = process.argv;
95
+ const userAgent = process.env.npm_config_user_agent || '';
96
+ // Check if invoked via bunx
97
+ if (execPath.includes('bun') || userAgent.startsWith('bun')) {
98
+ return { pm: 'bun', version: extractVersion(userAgent) || 'latest' };
99
+ }
100
+ // Check argv for package manager indicators
101
+ const commandLine = argv.join(' ');
102
+ if (commandLine.includes('bunx') || commandLine.includes('bun run')) {
103
+ return { pm: 'bun', version: extractVersion(userAgent) || 'latest' };
104
+ }
105
+ if (commandLine.includes('pnpm') || commandLine.includes('pnpx')) {
106
+ return { pm: 'pnpm', version: extractVersion(userAgent) || '10.14.0' };
107
+ }
108
+ if (commandLine.includes('yarn') || commandLine.includes('yarn create') || commandLine.includes('npx') || commandLine.includes('npm create')) {
109
+ return { pm: 'pnpm', version: '10.14.0' };
110
+ }
111
+ // Check user agent string
112
+ if (userAgent.startsWith('pnpm'))
113
+ return { pm: 'pnpm', version: extractVersion(userAgent) || '10.14.0' };
114
+ if (userAgent.startsWith('bun'))
115
+ return { pm: 'bun', version: extractVersion(userAgent) || 'latest' };
116
+ if (userAgent.startsWith('yarn') || userAgent.startsWith('npm'))
117
+ return { pm: 'pnpm', version: '10.14.0' };
118
+ return null;
119
+ }
120
+ function extractVersion(userAgent) {
121
+ // Extract version from user agent string
122
+ // Examples: "npm/8.19.2 node/v18.17.0 linux x64", "pnpm/8.6.0", "yarn/1.22.19", "bun/1.0.0"
123
+ const match = userAgent.match(/^(npm|pnpm|yarn|bun)\/([^\s]+)/);
124
+ return match ? match[2] : '10.14.0';
125
+ }
126
+ function getPackageManagerCommand(pmInfo) {
127
+ const commands = {
128
+ npm: 'npm',
129
+ yarn: 'yarn',
130
+ pnpm: 'pnpm',
131
+ bun: 'bun'
132
+ };
133
+ return commands[pmInfo.pm];
134
+ }
135
+ export async function createProject(projectName, options = {}) {
136
+ const targetDir = path.resolve(projectName);
137
+ // Show MVPKit branding
138
+ console.log();
139
+ const logoWidth = BOX_WIDTH - 2; // Account for box borders and padding
140
+ const logoIndent = ' ';
141
+ console.log(chalk.cyan.bold(logoIndent + '╔' + '═'.repeat(logoWidth) + '╗'));
142
+ console.log(chalk.cyan.bold(logoIndent + '║') + chalk.bold.white(' '.repeat(Math.floor((logoWidth - 6) / 2)) + 'MVPKit' + ' '.repeat(Math.ceil((logoWidth - 6) / 2))) + chalk.cyan.bold('║'));
143
+ console.log(chalk.cyan.bold(logoIndent + '║') + chalk.gray(' '.repeat(Math.floor((logoWidth - 35) / 2)) + 'Cloudflare-native React Starter Kit' + ' '.repeat(Math.ceil((logoWidth - 35) / 2))) + chalk.cyan.bold('║'));
144
+ console.log(chalk.cyan.bold(logoIndent + '╚' + '═'.repeat(logoWidth) + '╝'));
145
+ console.log();
146
+ console.log(chalk.blue.bold(' 🚀 Ship production apps in minutes'));
147
+ console.log();
148
+ // Validate project name and directory
149
+ if (fs.existsSync(targetDir)) {
150
+ throw new Error(`Directory ${projectName} already exists`);
151
+ }
152
+ // Validate template
153
+ const templateName = options.template || 'core';
154
+ const template = TEMPLATE_REGISTRY[templateName];
155
+ if (!template) {
156
+ throw new Error(`Unknown template: ${templateName}`);
157
+ }
158
+ // Detect package manager (we need this early for setup files)
159
+ const packageManagerInfo = detectPackageManager(process.cwd());
160
+ const pmCommand = getPackageManagerCommand(packageManagerInfo);
161
+ // Show template info
162
+ console.log(drawBox([
163
+ `${template.license === 'free' ? '🆓' : '💰'} ${template.displayName}`,
164
+ template.description,
165
+ `Features: ${template.features.slice(0, 2).join(' • ')}`
166
+ ], 'Template Selected'));
167
+ console.log();
168
+ // Generate template variables
169
+ let variables = generateTemplateVariables(projectName, options, packageManagerInfo.pm, packageManagerInfo.version);
170
+ // Interactive mode for project customization (default unless explicitly disabled)
171
+ if (options.interactive !== false) {
172
+ const { updatedVariables, updatedOptions } = await runInteractivePrompts(projectName, variables, options);
173
+ variables = updatedVariables;
174
+ Object.assign(options, updatedOptions);
175
+ }
176
+ else {
177
+ // Set opinionated defaults for non-interactive mode
178
+ options.install = true; // Always install dependencies
179
+ options.git = options.git !== false; // Create git repo unless explicitly disabled
180
+ options.setupDb = true; // Always setup database locally
181
+ options.setupShadcn = true; // Setup shadcn by default
182
+ }
183
+ // Project creation message
184
+ console.log(drawBox([
185
+ `🎯 Project: ${variables.projectDisplayName}`,
186
+ `📁 Location: ${targetDir}`,
187
+ `📋 Template: ${template.displayName}`,
188
+ '',
189
+ '🚀 Steps: Download → Git → Install → Build → Setup'
190
+ ], 'Creating Your Project'));
191
+ // Step 1: Download and copy template
192
+ const downloader = new TemplateDownloader();
193
+ const templatePath = await downloader.downloadTemplate(templateName, options.license, options.local);
194
+ const copySpinner = ora('Copying template files...').start();
195
+ try {
196
+ await copyAndTransformTemplate(targetDir, variables, templatePath, packageManagerInfo);
197
+ copySpinner.succeed('Template files copied and transformed');
198
+ }
199
+ catch (error) {
200
+ copySpinner.fail('Failed to copy template files');
201
+ const errorMessage = error instanceof Error ? error.message : String(error);
202
+ console.error(chalk.red('Error details:'), errorMessage);
203
+ throw new Error(`Template copy failed: ${errorMessage}`);
204
+ }
205
+ // Update package manager for target directory
206
+ const targetPackageManagerInfo = detectPackageManager(targetDir);
207
+ const targetPmCommand = getPackageManagerCommand(targetPackageManagerInfo);
208
+ // Step 2: Initialize git (if requested)
209
+ if (options.git) {
210
+ const gitSpinner = ora('Initializing git repository...').start();
211
+ try {
212
+ execSync('git init', { cwd: targetDir, stdio: 'ignore' });
213
+ execSync('git add .', { cwd: targetDir, stdio: 'ignore' });
214
+ execSync('git commit -m "Initial commit from MVPKit"', { cwd: targetDir, stdio: 'ignore' });
215
+ gitSpinner.succeed('Git repository initialized');
216
+ }
217
+ catch (error) {
218
+ gitSpinner.warn('Git initialization failed (optional)');
219
+ }
220
+ }
221
+ // Step 3: Install dependencies (always install unless explicitly disabled)
222
+ if (options.install !== false) {
223
+ const installSpinner = ora('Installing dependencies...').start();
224
+ try {
225
+ execSync(`${targetPmCommand} install`, { cwd: targetDir, stdio: 'ignore' });
226
+ installSpinner.succeed('Dependencies installed');
227
+ }
228
+ catch (error) {
229
+ installSpinner.fail('Failed to install dependencies');
230
+ const errorMessage = error instanceof Error ? error.message : String(error);
231
+ console.log(chalk.yellow(`💡 You can install them manually with: ${targetPmCommand} install`));
232
+ console.log(chalk.gray(` Error: ${errorMessage}`));
233
+ }
234
+ }
235
+ // Step 4: Setup database
236
+ let dbSetupCompleted = false;
237
+ if (options.setupDb !== false && !options.noDb) {
238
+ dbSetupCompleted = await setupDatabase(targetDir, variables, false);
239
+ }
240
+ // Step 5: Setup shadcn/ui
241
+ await setupShadcn(targetDir, targetPackageManagerInfo.pm);
242
+ // Cloudflare setup is documented in README for when users are ready to deploy
243
+ // Success message
244
+ console.log();
245
+ console.log(drawBox([
246
+ `🎉 ${variables.projectDisplayName} is ready!`,
247
+ '',
248
+ '🎨 Your Cloudflare-native MVP awaits!',
249
+ '⚡ Powered by MVPKit'
250
+ ], 'Project Complete!'));
251
+ console.log();
252
+ printNextSteps(projectName, options, dbSetupCompleted, targetPackageManagerInfo.pm);
253
+ }
254
+ async function runInteractivePrompts(projectName, variables, options) {
255
+ console.log();
256
+ console.log(drawBox([
257
+ 'Just a few quick questions to get you started'
258
+ ], 'Configure Your Project'));
259
+ console.log();
260
+ const answers = await inquirer.prompt([
261
+ {
262
+ type: 'input',
263
+ name: 'description',
264
+ message: `Brief description for "${projectName}":`,
265
+ default: variables.projectDescription
266
+ },
267
+ {
268
+ type: 'input',
269
+ name: 'domain',
270
+ message: 'Production domain name (e.g. myapp.com, leave empty to skip):',
271
+ default: variables.domainName || ''
272
+ },
273
+ {
274
+ type: 'input',
275
+ name: 'packageScope',
276
+ message: 'Package namespace (e.g. @yourcompany):',
277
+ default: variables.packageScope,
278
+ validate: (input) => {
279
+ if (!input.startsWith('@')) {
280
+ return 'Must start with @ (e.g. @yourcompany)';
281
+ }
282
+ return true;
283
+ }
284
+ },
285
+ {
286
+ type: 'confirm',
287
+ name: 'git',
288
+ message: 'Create git repository?',
289
+ default: true
290
+ },
291
+ ]);
292
+ console.log(); // Add spacing after prompts
293
+ const updatedVariables = {
294
+ ...variables,
295
+ projectDescription: answers.description,
296
+ domainName: answers.domain || undefined,
297
+ packageScope: answers.packageScope,
298
+ frontendPackageName: `${answers.packageScope}/frontend`,
299
+ backendPackageName: `${answers.packageScope}/backend`,
300
+ apiPackageName: `${answers.packageScope}/api`,
301
+ configPackageName: `${answers.packageScope}/config`
302
+ };
303
+ const updatedOptions = {
304
+ ...options,
305
+ install: true, // Always install dependencies
306
+ git: answers.git,
307
+ setupDb: true, // Always setup database locally
308
+ setupShadcn: true // Always setup shadcn
309
+ };
310
+ return { updatedVariables, updatedOptions };
311
+ }
312
+ async function copyAndTransformTemplate(targetDir, variables, templatePath, packageManagerInfo) {
313
+ const coreDir = templatePath || getCoreRepositoryPath();
314
+ if (!fs.existsSync(coreDir)) {
315
+ throw new Error(`Template directory not found at ${coreDir}`);
316
+ }
317
+ // Get all files from core directory with optimized glob pattern
318
+ const files = await glob('**/*', {
319
+ cwd: coreDir,
320
+ dot: true,
321
+ nodir: true,
322
+ absolute: false
323
+ });
324
+ // Process each file
325
+ for (const sourceFile of files) {
326
+ const sourcePath = path.join(coreDir, sourceFile);
327
+ const relativePath = sourceFile;
328
+ // Skip files we don't want to copy
329
+ if (shouldSkipFile(relativePath, DEFAULT_SKIP_PATTERNS)) {
330
+ continue;
331
+ }
332
+ // Transform the file path if needed
333
+ const transformedRelativePath = transformFileName(relativePath, variables);
334
+ const targetFile = path.join(targetDir, transformedRelativePath);
335
+ // Ensure target directory exists
336
+ await fs.ensureDir(path.dirname(targetFile));
337
+ try {
338
+ // Read source file
339
+ const content = await fs.readFile(sourcePath, 'utf8');
340
+ // Get appropriate transformation function
341
+ const fileName = path.basename(sourceFile);
342
+ const transformFunction = getFileTransformFunction(fileName);
343
+ // Transform content
344
+ const transformedContent = transformFunction(content, variables);
345
+ // Write transformed content
346
+ await fs.writeFile(targetFile, transformedContent);
347
+ }
348
+ catch (error) {
349
+ // If file is binary or can't be read as text, copy as-is
350
+ await fs.copy(sourcePath, targetFile);
351
+ }
352
+ }
353
+ // Create additional setup files
354
+ if (packageManagerInfo) {
355
+ await createSetupFiles(targetDir, variables, packageManagerInfo);
356
+ }
357
+ }
358
+ async function createSetupFiles(targetDir, variables, packageManagerInfo) {
359
+ // Create .env.example files with proper variable names
360
+ const frontendEnvExample = `# Frontend Environment Variables
361
+ VITE_API_URL=http://localhost:8787
362
+ VITE_APP_NAME="${variables.projectDisplayName}"
363
+ ${variables.domainName ? `VITE_APP_DOMAIN=${variables.domainName}` : '# VITE_APP_DOMAIN=your-domain.com'}
364
+ `;
365
+ const backendDevVarsExample = `# Backend Development Variables
366
+ # Database
367
+ DATABASE_URL="file:./dev.db"
368
+
369
+ # Auth (generate your own secrets)
370
+ # AUTH_SECRET="your-secret-key-here"
371
+ # GITHUB_CLIENT_ID="your-github-client-id"
372
+ # GITHUB_CLIENT_SECRET="your-github-client-secret"
373
+
374
+ # Email (optional)
375
+ # SMTP_HOST="smtp.gmail.com"
376
+ # SMTP_PORT="587"
377
+ # SMTP_USER="your-email@gmail.com"
378
+ # SMTP_PASSWORD="your-password"
379
+ `;
380
+ await fs.writeFile(path.join(targetDir, 'apps/frontend/.env.example'), frontendEnvExample);
381
+ await fs.writeFile(path.join(targetDir, 'apps/backend/.dev.vars.example'), backendDevVarsExample);
382
+ // Create a project-specific README
383
+ const readmeContent = `# ${variables.projectDisplayName}
384
+
385
+ ${variables.projectDescription}
386
+
387
+ Built with [MVPKit Core](https://mvpkit.dev) - A production-ready Cloudflare-native starter.
388
+
389
+ ## Quick Start
390
+
391
+ \`\`\`bash
392
+ # Install dependencies
393
+ ${getPackageManagerCommand(packageManagerInfo)} install
394
+
395
+ # Set up environment variables
396
+ cp apps/frontend/.env.example apps/frontend/.env.local
397
+ cp apps/backend/.dev.vars.example apps/backend/.dev.vars
398
+
399
+ # Set up database (if not done during project creation)
400
+ # cd apps/backend
401
+ # ${getPackageManagerCommand(packageManagerInfo)} db:migrate:local
402
+ # ${getPackageManagerCommand(packageManagerInfo)} db:seed:local
403
+
404
+ # Start development servers
405
+ ${getPackageManagerCommand(packageManagerInfo)} dev
406
+ \`\`\`
407
+
408
+ Visit [http://localhost:5173](http://localhost:5173) to see your application!
409
+
410
+ ## Project Structure
411
+
412
+ - \`apps/frontend/\` - React application with Vite
413
+ - \`apps/backend/\` - Cloudflare Workers API with Hono
414
+ - \`packages/api/\` - Shared API types
415
+ - \`packages/config/\` - Shared configuration
416
+
417
+ ## Features
418
+
419
+ - 🚀 **Cloudflare Stack**: Workers, Pages, D1, KV, R2
420
+ - ⚡ **Modern Frontend**: React 19, TanStack Router & Query
421
+ - 🎨 **Styling**: Tailwind CSS v4 + shadcn/ui components
422
+ - 🔐 **Authentication**: Better Auth with social providers
423
+ - 🗄️ **Database**: D1 with Drizzle ORM
424
+ - 📡 **Type-Safe APIs**: tRPC for end-to-end type safety
425
+ - 🏗️ **Monorepo**: Turbo for fast builds and caching
426
+
427
+ ## Development Commands
428
+
429
+ \`\`\`bash
430
+ ${getPackageManagerCommand(packageManagerInfo)} dev # Start development servers
431
+ ${getPackageManagerCommand(packageManagerInfo)} build # Build all packages
432
+ ${getPackageManagerCommand(packageManagerInfo)} lint # Lint all packages
433
+ ${getPackageManagerCommand(packageManagerInfo)} typecheck # Type check all packages
434
+ ${getPackageManagerCommand(packageManagerInfo)} test # Run tests
435
+ \`\`\`
436
+
437
+ ## Deployment
438
+
439
+ \`\`\`bash
440
+ # Deploy backend to Cloudflare Workers
441
+ ${getPackageManagerCommand(packageManagerInfo)} deploy:backend
442
+
443
+ # Deploy frontend to Cloudflare Pages
444
+ ${getPackageManagerCommand(packageManagerInfo)} deploy:frontend
445
+
446
+ # Deploy both
447
+ ${getPackageManagerCommand(packageManagerInfo)} deploy:apps
448
+ \`\`\`
449
+
450
+ ## Learn More
451
+
452
+ - [MVPKit Website](https://mvpkit.dev)
453
+ - [MVPKit Documentation](https://docs.mvpkit.dev)
454
+ - [MVPKit Examples](https://github.com/mvp-kit/core/examples)
455
+ `;
456
+ await fs.writeFile(path.join(targetDir, 'README.md'), readmeContent);
457
+ }
458
+ async function setupShadcn(targetDir, packageManager) {
459
+ console.log();
460
+ console.log(drawBox([
461
+ 'Setting up shadcn/ui component library',
462
+ 'Configure your preferences, then we\'ll install essential components'
463
+ ], 'Component Setup'));
464
+ console.log();
465
+ const shadcnSpinner = ora('Setting up components...').start();
466
+ try {
467
+ const frontendDir = path.join(targetDir, 'apps', 'frontend');
468
+ // Initialize shadcn interactively
469
+ try {
470
+ execSync('npx shadcn@latest init', {
471
+ cwd: frontendDir,
472
+ stdio: 'inherit'
473
+ });
474
+ }
475
+ catch (error) {
476
+ const errorMessage = error instanceof Error ? error.message : String(error);
477
+ console.log(chalk.yellow('⚠️ shadcn init had issues, continuing with component installation'));
478
+ console.log(chalk.gray(` Error: ${errorMessage}`));
479
+ }
480
+ // Install shadcn components
481
+ const componentSpinner = ora('Installing components...').start();
482
+ const components = ['alert', 'badge', 'button', 'card', 'input', 'label'];
483
+ for (const component of components) {
484
+ try {
485
+ execSync(`npx shadcn@latest add ${component} --overwrite`, {
486
+ cwd: frontendDir,
487
+ stdio: 'ignore'
488
+ });
489
+ }
490
+ catch {
491
+ try {
492
+ execSync(`npx shadcn@latest add ${component}`, {
493
+ cwd: frontendDir,
494
+ stdio: 'ignore'
495
+ });
496
+ }
497
+ catch (error) {
498
+ const errorMessage = error instanceof Error ? error.message : String(error);
499
+ console.log(chalk.yellow(`⚠️ Failed to install ${component}`));
500
+ console.log(chalk.gray(` Error: ${errorMessage}`));
501
+ }
502
+ }
503
+ }
504
+ componentSpinner.succeed('Components installed');
505
+ shadcnSpinner.succeed('Setup completed');
506
+ }
507
+ catch (error) {
508
+ shadcnSpinner.fail('Setup failed');
509
+ const errorMessage = error instanceof Error ? error.message : String(error);
510
+ console.log(chalk.yellow('💡 Run manually: npx shadcn@latest init'));
511
+ console.log(chalk.gray(` Error: ${errorMessage}`));
512
+ }
513
+ }
514
+ function printNextSteps(projectName, options, dbSetupCompleted = false, packageManager = 'pnpm') {
515
+ const pmCommand = getPackageManagerCommand({ pm: packageManager, version: packageManager === 'pnpm' ? '10.14.0' : 'latest' });
516
+ // Build next steps list
517
+ const steps = [`1. cd ${projectName}`];
518
+ let step = 2;
519
+ if (!options.install) {
520
+ steps.push(`${step}. ${pmCommand} install`);
521
+ step++;
522
+ }
523
+ steps.push(`${step}. cp apps/frontend/.env.example apps/frontend/.env.local`);
524
+ step++;
525
+ steps.push(`${step}. cp apps/backend/.dev.vars.example apps/backend/.dev.vars`);
526
+ step++;
527
+ if (!dbSetupCompleted) {
528
+ steps.push(`${step}. cd apps/backend`);
529
+ step++;
530
+ steps.push(`${step}. ${pmCommand} db:generate`);
531
+ step++;
532
+ steps.push(`${step}. ${pmCommand} db:migrate:local`);
533
+ step++;
534
+ steps.push(`${step}. cd ../..`);
535
+ step++;
536
+ }
537
+ steps.push(`${step}. ${pmCommand} dev`);
538
+ console.log(drawBox(steps, 'Next Steps'));
539
+ console.log();
540
+ console.log(drawBox([
541
+ '🌐 Website: https://mvpkit.dev',
542
+ '📖 Documentation: https://docs.mvpkit.dev',
543
+ '🔗 Examples: https://github.com/mvp-kit/core/examples'
544
+ ], 'Resources'));
545
+ console.log();
546
+ console.log(chalk.bold.green('🚀 Happy building with MVPKit!'));
547
+ console.log();
548
+ }
549
+ //# sourceMappingURL=create.js.map