bunkit-cli 0.4.3 → 0.5.0

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.
Files changed (2) hide show
  1. package/dist/index.js +1297 -141
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -23958,7 +23958,17 @@ var ProjectConfigSchema = exports_external.object({
23958
23958
  path: exports_external.string(),
23959
23959
  features: exports_external.array(exports_external.string()).optional(),
23960
23960
  git: exports_external.boolean().default(true),
23961
- install: exports_external.boolean().default(true)
23961
+ install: exports_external.boolean().default(true),
23962
+ database: exports_external.enum(["postgres-drizzle", "supabase", "sqlite-drizzle", "none"]).optional(),
23963
+ codeQuality: exports_external.enum(["ultracite", "biome"]).default("ultracite"),
23964
+ tsStrictness: exports_external.enum(["strict", "moderate", "loose"]).default("strict"),
23965
+ uiLibrary: exports_external.enum(["shadcn", "none"]).optional(),
23966
+ cssFramework: exports_external.enum(["tailwind", "vanilla", "css-modules"]).optional(),
23967
+ testing: exports_external.enum(["bun-test", "vitest", "none"]).default("bun-test"),
23968
+ docker: exports_external.boolean().default(false),
23969
+ cicd: exports_external.boolean().default(false),
23970
+ envExample: exports_external.boolean().default(true),
23971
+ pathAliases: exports_external.boolean().default(true)
23962
23972
  });
23963
23973
  var FeatureConfigSchema = exports_external.object({
23964
23974
  name: exports_external.enum(["auth", "database", "ui", "payments", "email", "storage"]),
@@ -24094,7 +24104,17 @@ function createTemplateContext(config) {
24094
24104
  description: `Project created with bunkit`,
24095
24105
  license: "MIT",
24096
24106
  features: config.features || [],
24097
- supportsTypeScript: true
24107
+ supportsTypeScript: true,
24108
+ database: config.database,
24109
+ codeQuality: config.codeQuality,
24110
+ tsStrictness: config.tsStrictness,
24111
+ uiLibrary: config.uiLibrary,
24112
+ cssFramework: config.cssFramework,
24113
+ testing: config.testing,
24114
+ docker: config.docker,
24115
+ cicd: config.cicd,
24116
+ envExample: config.envExample,
24117
+ pathAliases: config.pathAliases
24098
24118
  };
24099
24119
  }
24100
24120
  // ../core/src/install.ts
@@ -24112,7 +24132,7 @@ async function installDependencies(cwd, packages) {
24112
24132
  throw error;
24113
24133
  }
24114
24134
  }
24115
- // src/commands/init.real.ts
24135
+ // src/commands/init.enhanced.ts
24116
24136
  var import_picocolors4 = __toESM(require_picocolors(), 1);
24117
24137
 
24118
24138
  // ../templates/src/render.ts
@@ -24161,6 +24181,551 @@ coverage = true
24161
24181
  `;
24162
24182
  await writeFile(join(projectPath, "bunfig.toml"), bunfigContent);
24163
24183
  }
24184
+ // ../templates/src/generators/ultracite.ts
24185
+ async function setupUltracite(projectPath, context) {
24186
+ const biomeConfig = `{
24187
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
24188
+ "extends": [
24189
+ "ultracite/core",
24190
+ ${context.cssFramework === "tailwind" ? `"ultracite/react",
24191
+ "ultracite/next"` : '"ultracite/react"'}
24192
+ ],
24193
+ "vcs": {
24194
+ "enabled": true,
24195
+ "clientKind": "git",
24196
+ "useIgnoreFile": true
24197
+ },
24198
+ "files": {
24199
+ "ignore": [
24200
+ "node_modules",
24201
+ "dist",
24202
+ "build",
24203
+ ".next",
24204
+ ".turbo",
24205
+ "coverage"
24206
+ ]
24207
+ },
24208
+ "formatter": {
24209
+ "enabled": true,
24210
+ "formatWithErrors": false,
24211
+ "indentStyle": "space",
24212
+ "indentWidth": 2,
24213
+ "lineWidth": 100,
24214
+ "lineEnding": "lf"
24215
+ },
24216
+ "linter": {
24217
+ "enabled": true,
24218
+ "rules": {
24219
+ "recommended": true
24220
+ }
24221
+ },
24222
+ "javascript": {
24223
+ "formatter": {
24224
+ "quoteStyle": "single",
24225
+ "trailingCommas": "es5",
24226
+ "semicolons": "always",
24227
+ "arrowParentheses": "always",
24228
+ "bracketSpacing": true,
24229
+ "jsxQuoteStyle": "double",
24230
+ "quoteProperties": "asNeeded"
24231
+ }
24232
+ },
24233
+ "json": {
24234
+ "formatter": {
24235
+ "enabled": true,
24236
+ "indentWidth": 2
24237
+ }
24238
+ }
24239
+ }
24240
+ `;
24241
+ await writeFile(join(projectPath, "biome.jsonc"), biomeConfig);
24242
+ const cursorRules = `# ${context.projectName} - Cursor AI Rules
24243
+
24244
+ ## Code Style (Ultracite)
24245
+ - Follow Biome configuration in biome.jsonc
24246
+ - Use single quotes for JavaScript/TypeScript
24247
+ - Use double quotes for JSX attributes
24248
+ - Always use semicolons
24249
+ - 2-space indentation
24250
+ - 100 character line width
24251
+ - ES5 trailing commas
24252
+
24253
+ ## TypeScript
24254
+ - Strict mode: ${context.tsStrictness === "strict" ? "enabled" : context.tsStrictness === "moderate" ? "moderate" : "disabled"}
24255
+ - Always define types explicitly for function parameters and return values
24256
+ - Use \`type\` for object types, \`interface\` for contracts
24257
+ - Prefer \`const\` assertions for literal types
24258
+
24259
+ ## React/Next.js Best Practices
24260
+ - Use Server Components by default (Next.js 16)
24261
+ - Add 'use client' only when needed (hooks, browser APIs, interactivity)
24262
+ - Always await \`params\` and \`searchParams\` in Next.js 16
24263
+ - Use descriptive variable names (no \`c\`, \`ctx\`, \`req\`, \`res\`)
24264
+
24265
+ ${context.cssFramework === "tailwind" ? `## Tailwind CSS
24266
+ - Use utility classes for styling
24267
+ - Follow mobile-first responsive design
24268
+ - Use OKLCH colors from theme when available
24269
+ - Avoid arbitrary values unless necessary` : ""}
24270
+
24271
+ ${context.database && context.database !== "none" ? `## Database (${context.database})
24272
+ - Always use Drizzle ORM for type-safe queries
24273
+ - Define schema in separate files by domain
24274
+ - Use transactions for multi-step operations
24275
+ - Always handle database errors gracefully` : ""}
24276
+
24277
+ ## File Naming
24278
+ - Pages: \`page.tsx\`, \`layout.tsx\`
24279
+ - Components: PascalCase (\`UserCard.tsx\`)
24280
+ - Utilities: camelCase (\`formatDate.ts\`)
24281
+ - Types: \`*.types.ts\` or in \`types/\` directory
24282
+
24283
+ ## Testing
24284
+ - Write tests for critical business logic
24285
+ - Use Bun's built-in test runner
24286
+ - Follow AAA pattern: Arrange, Act, Assert
24287
+ - Mock external dependencies
24288
+
24289
+ ## AI Code Generation Guidelines
24290
+ - Always generate complete, working code
24291
+ - Include error handling
24292
+ - Add TypeScript types
24293
+ - Write self-documenting code with clear names
24294
+ - Add comments only for complex logic
24295
+ - Follow the project's existing patterns
24296
+ `;
24297
+ await writeFile(join(projectPath, ".cursorrules"), cursorRules);
24298
+ const windsurfRules = `# ${context.projectName} - Windsurf AI Rules
24299
+
24300
+ This project uses **Ultracite** for code quality - an AI-optimized Biome preset.
24301
+
24302
+ ## Code Quality Rules
24303
+ - Lint: \`bun run lint\`
24304
+ - Format: \`bun run format\`
24305
+ - Config: See \`biome.jsonc\`
24306
+
24307
+ ## Key Conventions
24308
+ 1. **TypeScript**: ${context.tsStrictness} mode
24309
+ 2. **Quotes**: Single quotes (JS/TS), double quotes (JSX)
24310
+ 3. **Semicolons**: Always required
24311
+ 4. **Indentation**: 2 spaces
24312
+ 5. **Line width**: 100 characters
24313
+
24314
+ ## Framework-Specific
24315
+ ${context.cssFramework === "tailwind" ? `- **Tailwind CSS**: Utility-first styling
24316
+ ` : ""}${context.database && context.database !== "none" ? `- **Database**: ${context.database} with Drizzle ORM
24317
+ ` : ""}- **Testing**: ${context.testing}
24318
+
24319
+ ## Variable Naming (CRITICAL)
24320
+ \u274C NEVER use: \`c\`, \`ctx\`, \`e\`, \`req\`, \`res\`, \`data\`, \`temp\`
24321
+ \u2705 ALWAYS use: \`context\`, \`error\`, \`request\`, \`response\`, \`userData\`, \`temporaryBuffer\`
24322
+
24323
+ ## React/Next.js 16
24324
+ - Server Components by default
24325
+ - \`'use client'\` only when necessary
24326
+ - Always \`await params\` and \`await searchParams\`
24327
+
24328
+ Sync with \`.cursorrules\` for full AI guidelines.
24329
+ `;
24330
+ await writeFile(join(projectPath, ".windsurfrules"), windsurfRules);
24331
+ const claudeMd = `# ${context.projectName}
24332
+
24333
+ AI-assisted development with Ultracite code quality.
24334
+
24335
+ ## Quick Start
24336
+
24337
+ \`\`\`bash
24338
+ bun install
24339
+ bun dev
24340
+ \`\`\`
24341
+
24342
+ ## Code Quality
24343
+
24344
+ This project uses **Ultracite** - an AI-optimized Biome preset that keeps code consistent across:
24345
+ - Cursor
24346
+ - Windsurf
24347
+ - Claude Code
24348
+ - Zed
24349
+
24350
+ ### Commands
24351
+
24352
+ \`\`\`bash
24353
+ bun run lint # Check code quality
24354
+ bun run format # Auto-fix issues
24355
+ \`\`\`
24356
+
24357
+ ## AI Development Guidelines
24358
+
24359
+ See \`.cursorrules\` and \`.windsurfrules\` for comprehensive AI coding guidelines.
24360
+
24361
+ ### Critical Rules
24362
+
24363
+ 1. **Descriptive Names**: No \`c\`, \`ctx\`, \`e\` - use \`context\`, \`error\`, etc.
24364
+ 2. **TypeScript**: ${context.tsStrictness} mode
24365
+ 3. **Server Components**: Default in Next.js 16
24366
+ 4. **Error Handling**: Always handle errors gracefully
24367
+
24368
+ ## Tech Stack
24369
+
24370
+ - **Runtime**: Bun
24371
+ - **Framework**: ${context.preset === "web" || context.preset === "full" ? "Next.js 16 + React 19" : context.preset === "api" ? "Hono" : "Minimal"}
24372
+ ${context.cssFramework === "tailwind" ? `- **Styling**: Tailwind CSS 4
24373
+ ` : ""}${context.database && context.database !== "none" ? `- **Database**: ${context.database}
24374
+ ` : ""}${context.uiLibrary === "shadcn" ? `- **UI**: shadcn/ui
24375
+ ` : ""}- **Code Quality**: Ultracite (Biome)
24376
+ - **Testing**: ${context.testing}
24377
+ `;
24378
+ await writeFile(join(projectPath, "CLAUDE.md"), claudeMd);
24379
+ }
24380
+ async function setupBiome(projectPath, context) {
24381
+ const biomeConfig = {
24382
+ $schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
24383
+ vcs: {
24384
+ enabled: true,
24385
+ clientKind: "git",
24386
+ useIgnoreFile: true
24387
+ },
24388
+ files: {
24389
+ ignore: [
24390
+ "node_modules",
24391
+ "dist",
24392
+ "build",
24393
+ ".next",
24394
+ ".turbo",
24395
+ "coverage"
24396
+ ]
24397
+ },
24398
+ formatter: {
24399
+ enabled: true,
24400
+ indentStyle: "space",
24401
+ indentWidth: 2,
24402
+ lineWidth: 100
24403
+ },
24404
+ linter: {
24405
+ enabled: true,
24406
+ rules: {
24407
+ recommended: true
24408
+ }
24409
+ },
24410
+ javascript: {
24411
+ formatter: {
24412
+ quoteStyle: "single",
24413
+ trailingCommas: "es5",
24414
+ semicolons: "always"
24415
+ }
24416
+ }
24417
+ };
24418
+ await writeFile(join(projectPath, "biome.json"), JSON.stringify(biomeConfig, null, 2));
24419
+ }
24420
+ function getCodeQualityDependencies(codeQuality) {
24421
+ if (codeQuality === "ultracite") {
24422
+ return ["ultracite", "@biomejs/biome"];
24423
+ }
24424
+ return ["@biomejs/biome"];
24425
+ }
24426
+
24427
+ // ../templates/src/generators/docker.ts
24428
+ async function setupDocker(projectPath, context) {
24429
+ const dockerfile = `# Use Bun official image
24430
+ FROM oven/bun:1 AS base
24431
+
24432
+ # Install dependencies
24433
+ FROM base AS deps
24434
+ WORKDIR /app
24435
+ COPY package.json bun.lock* ./
24436
+ RUN bun install --frozen-lockfile
24437
+
24438
+ # Build application
24439
+ FROM base AS builder
24440
+ WORKDIR /app
24441
+ COPY --from=deps /app/node_modules ./node_modules
24442
+ COPY . .
24443
+
24444
+ ${context.preset === "web" || context.preset === "full" ? `# Build Next.js app
24445
+ RUN bun run build
24446
+ ` : "# No build step needed for API/minimal"}
24447
+ # Production image
24448
+ FROM base AS runner
24449
+ WORKDIR /app
24450
+
24451
+ # Create non-root user
24452
+ RUN addgroup --system --gid 1001 bunuser && \\
24453
+ adduser --system --uid 1001 bunuser
24454
+
24455
+ ${context.preset === "web" || context.preset === "full" ? `# Copy Next.js build
24456
+ COPY --from=builder --chown=bunuser:bunuser /app/.next/standalone ./
24457
+ COPY --from=builder --chown=bunuser:bunuser /app/.next/static ./.next/static
24458
+ COPY --from=builder --chown=bunuser:bunuser /app/public ./public
24459
+ ` : `# Copy application
24460
+ COPY --from=builder --chown=bunuser:bunuser /app/src ./src
24461
+ COPY --from=builder --chown=bunuser:bunuser /app/package.json ./package.json
24462
+ `}
24463
+ USER bunuser
24464
+
24465
+ EXPOSE 3000
24466
+
24467
+ ENV PORT=3000
24468
+ ENV NODE_ENV=production
24469
+
24470
+ ${context.preset === "web" || context.preset === "full" ? 'CMD ["bun", "run", "start"]' : 'CMD ["bun", "run", "src/index.ts"]'}
24471
+ `;
24472
+ await writeFile(join(projectPath, "Dockerfile"), dockerfile);
24473
+ const dockerCompose = `version: '3.8'
24474
+
24475
+ services:
24476
+ app:
24477
+ build:
24478
+ context: .
24479
+ dockerfile: Dockerfile
24480
+ ports:
24481
+ - "3000:3000"
24482
+ environment:
24483
+ - NODE_ENV=production
24484
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? "- DATABASE_URL=${DATABASE_URL:-postgres://postgres:postgres@db:5432/${context.projectName}}" : ""}
24485
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `depends_on:
24486
+ - db` : ""}
24487
+ restart: unless-stopped
24488
+
24489
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `db:
24490
+ image: ${context.database === "sqlite-drizzle" ? "alpine:latest" : "postgres:16-alpine"}
24491
+ ${context.database !== "sqlite-drizzle" ? `environment:
24492
+ - POSTGRES_USER=postgres
24493
+ - POSTGRES_PASSWORD=postgres
24494
+ - POSTGRES_DB=${context.projectName}
24495
+ volumes:
24496
+ - postgres_data:/var/lib/postgresql/data
24497
+ ports:
24498
+ - "5432:5432"` : `volumes:
24499
+ - sqlite_data:/data`}
24500
+ restart: unless-stopped
24501
+ ` : ""}
24502
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `volumes:
24503
+ ${context.database === "sqlite-drizzle" ? "sqlite_data:" : "postgres_data:"}` : ""}
24504
+ `;
24505
+ await writeFile(join(projectPath, "docker-compose.yml"), dockerCompose);
24506
+ const dockerignore = `# Dependencies
24507
+ node_modules/
24508
+ bun.lock
24509
+
24510
+ # Build
24511
+ dist/
24512
+ build/
24513
+ .next/
24514
+ .turbo/
24515
+
24516
+ # Environment
24517
+ .env
24518
+ .env*.local
24519
+
24520
+ # Logs
24521
+ *.log
24522
+ npm-debug.log*
24523
+
24524
+ # OS
24525
+ .DS_Store
24526
+
24527
+ # IDEs
24528
+ .vscode/
24529
+ .idea/
24530
+
24531
+ # Git
24532
+ .git/
24533
+ .gitignore
24534
+
24535
+ # Documentation
24536
+ README.md
24537
+ CLAUDE.md
24538
+ .cursorrules
24539
+ .windsurfrules
24540
+
24541
+ # Tests
24542
+ *.test.ts
24543
+ *.test.tsx
24544
+ *.spec.ts
24545
+ *.spec.tsx
24546
+ coverage/
24547
+ `;
24548
+ await writeFile(join(projectPath, ".dockerignore"), dockerignore);
24549
+ }
24550
+
24551
+ // ../templates/src/generators/cicd.ts
24552
+ async function setupGitHubActions(projectPath, context) {
24553
+ await ensureDirectory(join(projectPath, ".github/workflows"));
24554
+ const ciWorkflow = `name: CI
24555
+
24556
+ on:
24557
+ push:
24558
+ branches: [main, develop]
24559
+ pull_request:
24560
+ branches: [main, develop]
24561
+
24562
+ jobs:
24563
+ lint:
24564
+ name: Lint & Format Check
24565
+ runs-on: ubuntu-latest
24566
+
24567
+ steps:
24568
+ - name: Checkout code
24569
+ uses: actions/checkout@v4
24570
+
24571
+ - name: Setup Bun
24572
+ uses: oven-sh/setup-bun@v2
24573
+ with:
24574
+ bun-version: latest
24575
+
24576
+ - name: Install dependencies
24577
+ run: bun install --frozen-lockfile
24578
+
24579
+ - name: Run linter
24580
+ run: bun run lint
24581
+
24582
+ typecheck:
24583
+ name: Type Check
24584
+ runs-on: ubuntu-latest
24585
+
24586
+ steps:
24587
+ - name: Checkout code
24588
+ uses: actions/checkout@v4
24589
+
24590
+ - name: Setup Bun
24591
+ uses: oven-sh/setup-bun@v2
24592
+ with:
24593
+ bun-version: latest
24594
+
24595
+ - name: Install dependencies
24596
+ run: bun install --frozen-lockfile
24597
+
24598
+ - name: Type check
24599
+ run: ${context.preset === "full" ? "bun run --filter '*' typecheck" : "bun run typecheck"}
24600
+
24601
+ ${context.testing !== "none" ? `test:
24602
+ name: Run Tests
24603
+ runs-on: ubuntu-latest
24604
+
24605
+ steps:
24606
+ - name: Checkout code
24607
+ uses: actions/checkout@v4
24608
+
24609
+ - name: Setup Bun
24610
+ uses: oven-sh/setup-bun@v2
24611
+ with:
24612
+ bun-version: latest
24613
+
24614
+ - name: Install dependencies
24615
+ run: bun install --frozen-lockfile
24616
+
24617
+ - name: Run tests
24618
+ run: ${context.testing === "bun-test" ? "bun test" : "bun run test"}
24619
+ ${context.database && context.database !== "none" ? `env:
24620
+ DATABASE_URL: sqlite://test.db` : ""}
24621
+ ` : ""}
24622
+ build:
24623
+ name: Build
24624
+ runs-on: ubuntu-latest
24625
+ needs: [lint, typecheck${context.testing !== "none" ? ", test" : ""}]
24626
+
24627
+ steps:
24628
+ - name: Checkout code
24629
+ uses: actions/checkout@v4
24630
+
24631
+ - name: Setup Bun
24632
+ uses: oven-sh/setup-bun@v2
24633
+ with:
24634
+ bun-version: latest
24635
+
24636
+ - name: Install dependencies
24637
+ run: bun install --frozen-lockfile
24638
+
24639
+ ${context.preset === "web" || context.preset === "full" ? `- name: Build application
24640
+ run: bun run build
24641
+ env:
24642
+ ${context.database === "supabase" ? `NEXT_PUBLIC_SUPABASE_URL: \${{ secrets.SUPABASE_URL }}
24643
+ NEXT_PUBLIC_SUPABASE_ANON_KEY: \${{ secrets.SUPABASE_ANON_KEY }}
24644
+ ` : ""}NODE_ENV: production` : ""}
24645
+
24646
+ ${context.docker ? `- name: Set up Docker Buildx
24647
+ uses: docker/setup-buildx-action@v3
24648
+
24649
+ - name: Build Docker image
24650
+ uses: docker/build-push-action@v5
24651
+ with:
24652
+ context: .
24653
+ push: false
24654
+ tags: ${context.projectName}:latest
24655
+ cache-from: type=gha
24656
+ cache-to: type=gha,mode=max` : ""}
24657
+ `;
24658
+ await writeFile(join(projectPath, ".github/workflows/ci.yml"), ciWorkflow);
24659
+ const deployWorkflow = `# name: Deploy
24660
+ #
24661
+ # on:
24662
+ # push:
24663
+ # branches: [main]
24664
+ #
24665
+ # jobs:
24666
+ # deploy:
24667
+ # name: Deploy to Production
24668
+ # runs-on: ubuntu-latest
24669
+ # environment: production
24670
+ #
24671
+ # steps:
24672
+ # - name: Checkout code
24673
+ # uses: actions/checkout@v4
24674
+ #
24675
+ # - name: Setup Bun
24676
+ # uses: oven-sh/setup-bun@v2
24677
+ # with:
24678
+ # bun-version: latest
24679
+ #
24680
+ # - name: Install dependencies
24681
+ # run: bun install --frozen-lockfile
24682
+ #
24683
+ # - name: Build
24684
+ # run: bun run build
24685
+ # env:
24686
+ # NODE_ENV: production
24687
+ # ${context.database === "supabase" ? `NEXT_PUBLIC_SUPABASE_URL: \${{ secrets.SUPABASE_URL }}
24688
+ # NEXT_PUBLIC_SUPABASE_ANON_KEY: \${{ secrets.SUPABASE_ANON_KEY }}` : ""}
24689
+ #
24690
+ # # Add your deployment steps here
24691
+ # # Examples:
24692
+ # # - Deploy to Vercel
24693
+ # # - Deploy to Railway
24694
+ # # - Deploy to your own server via SSH
24695
+ # # - Push Docker image to registry
24696
+ `;
24697
+ await writeFile(join(projectPath, ".github/workflows/deploy.yml.example"), deployWorkflow);
24698
+ const dependabotConfig = `version: 2
24699
+ updates:
24700
+ - package-ecosystem: "npm"
24701
+ directory: "/"
24702
+ schedule:
24703
+ interval: "weekly"
24704
+ open-pull-requests-limit: 10
24705
+ reviewers:
24706
+ - "your-github-username"
24707
+ labels:
24708
+ - "dependencies"
24709
+
24710
+ ${context.docker ? `- package-ecosystem: "docker"
24711
+ directory: "/"
24712
+ schedule:
24713
+ interval: "weekly"
24714
+ labels:
24715
+ - "dependencies"
24716
+ - "docker"` : ""}
24717
+
24718
+ - package-ecosystem: "github-actions"
24719
+ directory: "/"
24720
+ schedule:
24721
+ interval: "weekly"
24722
+ labels:
24723
+ - "dependencies"
24724
+ - "ci"
24725
+ `;
24726
+ await writeFile(join(projectPath, ".github/dependabot.yml"), dependabotConfig);
24727
+ }
24728
+
24164
24729
  // ../templates/src/builders/web.ts
24165
24730
  async function buildWebPreset(projectPath, context) {
24166
24731
  await ensureDirectory(join(projectPath, "src/app"));
@@ -24234,51 +24799,226 @@ const config: Config = {
24234
24799
  export default config;
24235
24800
  `;
24236
24801
  await writeFile(join(projectPath, "tailwind.config.ts"), tailwindConfigContent);
24237
- const tsconfigContent = {
24238
- compilerOptions: {
24802
+ const getTsCompilerOptions = () => {
24803
+ const baseOptions = {
24239
24804
  target: "ES2017",
24240
24805
  lib: ["dom", "dom.iterable", "esnext"],
24241
24806
  allowJs: true,
24242
24807
  skipLibCheck: true,
24243
- strict: true,
24244
24808
  noEmit: true,
24245
24809
  esModuleInterop: true,
24246
24810
  module: "esnext",
24247
24811
  moduleResolution: "bundler",
24248
24812
  resolveJsonModule: true,
24249
24813
  isolatedModules: true,
24250
- jsx: "preserve",
24814
+ jsx: "react-jsx",
24251
24815
  incremental: true,
24252
24816
  plugins: [{ name: "next" }],
24253
- paths: {
24254
- "@/*": ["./src/*"]
24255
- }
24256
- },
24257
- include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
24817
+ paths: context.pathAliases ? { "@/*": ["./src/*"] } : undefined
24818
+ };
24819
+ if (context.tsStrictness === "strict") {
24820
+ return {
24821
+ ...baseOptions,
24822
+ strict: true,
24823
+ noUnusedLocals: true,
24824
+ noUnusedParameters: true,
24825
+ noFallthroughCasesInSwitch: true,
24826
+ noImplicitReturns: true
24827
+ };
24828
+ }
24829
+ if (context.tsStrictness === "moderate") {
24830
+ return {
24831
+ ...baseOptions,
24832
+ strict: true,
24833
+ noUnusedLocals: false,
24834
+ noUnusedParameters: false
24835
+ };
24836
+ }
24837
+ return {
24838
+ ...baseOptions,
24839
+ strict: false,
24840
+ noImplicitAny: false
24841
+ };
24842
+ };
24843
+ const tsconfigContent = {
24844
+ compilerOptions: getTsCompilerOptions(),
24845
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/dev/types/**/*.ts"],
24258
24846
  exclude: ["node_modules"]
24259
24847
  };
24260
24848
  await writeFile(join(projectPath, "tsconfig.json"), JSON.stringify(tsconfigContent, null, 2));
24261
- const biomeContent = {
24262
- $schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
24263
- vcs: {
24264
- enabled: true,
24265
- clientKind: "git",
24266
- useIgnoreFile: true
24267
- },
24268
- formatter: {
24269
- enabled: true,
24270
- indentStyle: "space",
24271
- indentWidth: 2
24272
- },
24273
- linter: {
24274
- enabled: true,
24275
- rules: {
24276
- recommended: true
24277
- }
24278
- }
24279
- };
24280
- await writeFile(join(projectPath, "biome.json"), JSON.stringify(biomeContent, null, 2));
24849
+ if (context.codeQuality === "ultracite") {
24850
+ await setupUltracite(projectPath, context);
24851
+ } else {
24852
+ await setupBiome(projectPath, context);
24853
+ }
24854
+ if (context.docker) {
24855
+ await setupDocker(projectPath, context);
24856
+ }
24857
+ if (context.cicd) {
24858
+ await setupGitHubActions(projectPath, context);
24859
+ }
24860
+ }
24861
+ // ../templates/src/generators/database.ts
24862
+ async function setupPostgresDrizzle(projectPath, context, isMonorepo = false) {
24863
+ const dbPath = isMonorepo ? join(projectPath, "packages/db") : join(projectPath, "src/db");
24864
+ await ensureDirectory(join(dbPath, "schema"));
24865
+ const drizzleConfig = `import { defineConfig } from 'drizzle-kit';
24866
+
24867
+ export default defineConfig({
24868
+ schema: './src/schema/index.ts',
24869
+ out: './drizzle',
24870
+ dialect: 'postgresql',
24871
+ dbCredentials: {
24872
+ url: process.env.DATABASE_URL!,
24873
+ },
24874
+ });
24875
+ `;
24876
+ await writeFile(join(isMonorepo ? join(projectPath, "packages/db") : projectPath, "drizzle.config.ts"), drizzleConfig);
24877
+ const clientContent = `import { drizzle } from 'drizzle-orm/bun-postgres';
24878
+ import { Database } from 'bun:postgres';
24879
+ import * as schema from './schema';
24880
+
24881
+ const client = new Database(process.env.DATABASE_URL!);
24882
+ export const db = drizzle(client, { schema });
24883
+ `;
24884
+ await writeFile(join(dbPath, "index.ts"), clientContent);
24885
+ const schemaContent = `import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
24886
+
24887
+ export const users = pgTable('users', {
24888
+ id: uuid('id').primaryKey().defaultRandom(),
24889
+ email: text('email').notNull().unique(),
24890
+ name: text('name'),
24891
+ createdAt: timestamp('created_at').defaultNow().notNull(),
24892
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
24893
+ });
24894
+ `;
24895
+ await writeFile(join(dbPath, "schema/index.ts"), schemaContent);
24896
+ const envExample = `# Database
24897
+ DATABASE_URL=postgresql://postgres:postgres@localhost:5432/${context.projectName}
24898
+ `;
24899
+ await writeFile(join(projectPath, ".env.example"), envExample);
24900
+ }
24901
+ async function setupSupabase(projectPath, context, isMonorepo = false) {
24902
+ const dbPath = isMonorepo ? join(projectPath, "packages/db") : join(projectPath, "src/db");
24903
+ await ensureDirectory(join(dbPath, "schema"));
24904
+ const drizzleConfig = `import { defineConfig } from 'drizzle-kit';
24905
+
24906
+ export default defineConfig({
24907
+ schema: './src/schema/index.ts',
24908
+ out: './supabase/migrations',
24909
+ dialect: 'postgresql',
24910
+ dbCredentials: {
24911
+ url: process.env.DATABASE_URL!,
24912
+ },
24913
+ });
24914
+ `;
24915
+ await writeFile(join(isMonorepo ? join(projectPath, "packages/db") : projectPath, "drizzle.config.ts"), drizzleConfig);
24916
+ const clientContent = `import { createClient } from '@supabase/supabase-js';
24917
+ import { drizzle } from 'drizzle-orm/postgres-js';
24918
+ import postgres from 'postgres';
24919
+ import * as schema from './schema';
24920
+
24921
+ // Supabase client for auth, storage, realtime
24922
+ export const supabase = createClient(
24923
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
24924
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
24925
+ );
24926
+
24927
+ // Drizzle client for type-safe database queries
24928
+ const queryClient = postgres(process.env.DATABASE_URL!);
24929
+ export const db = drizzle(queryClient, { schema });
24930
+ `;
24931
+ await writeFile(join(dbPath, "index.ts"), clientContent);
24932
+ const schemaContent = `import { pgTable, text, timestamp, uuid, boolean } from 'drizzle-orm/pg-core';
24933
+
24934
+ export const users = pgTable('users', {
24935
+ id: uuid('id').primaryKey(),
24936
+ email: text('email').notNull().unique(),
24937
+ name: text('name'),
24938
+ avatarUrl: text('avatar_url'),
24939
+ createdAt: timestamp('created_at').defaultNow().notNull(),
24940
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
24941
+ });
24942
+
24943
+ export const profiles = pgTable('profiles', {
24944
+ id: uuid('id').primaryKey().references(() => users.id),
24945
+ username: text('username').unique(),
24946
+ bio: text('bio'),
24947
+ website: text('website'),
24948
+ isPublic: boolean('is_public').default(true),
24949
+ createdAt: timestamp('created_at').defaultNow().notNull(),
24950
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
24951
+ });
24952
+ `;
24953
+ await writeFile(join(dbPath, "schema/index.ts"), schemaContent);
24954
+ await ensureDirectory(join(projectPath, "supabase/migrations"));
24955
+ const envExample = `# Supabase
24956
+ NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
24957
+ NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
24958
+ DATABASE_URL=postgresql://postgres:[password]@db.your-project.supabase.co:5432/postgres
24959
+ `;
24960
+ await writeFile(join(projectPath, ".env.example"), envExample);
24961
+ }
24962
+ async function setupSQLiteDrizzle(projectPath, context, isMonorepo = false) {
24963
+ const dbPath = isMonorepo ? join(projectPath, "packages/db") : join(projectPath, "src/db");
24964
+ await ensureDirectory(join(dbPath, "schema"));
24965
+ const drizzleConfig = `import { defineConfig } from 'drizzle-kit';
24966
+
24967
+ export default defineConfig({
24968
+ schema: './src/schema/index.ts',
24969
+ out: './drizzle',
24970
+ dialect: 'sqlite',
24971
+ dbCredentials: {
24972
+ url: process.env.DATABASE_URL || './local.db',
24973
+ },
24974
+ });
24975
+ `;
24976
+ await writeFile(join(isMonorepo ? join(projectPath, "packages/db") : projectPath, "drizzle.config.ts"), drizzleConfig);
24977
+ const clientContent = `import { drizzle } from 'drizzle-orm/bun-sqlite';
24978
+ import { Database } from 'bun:sqlite';
24979
+ import * as schema from './schema';
24980
+
24981
+ const sqlite = new Database(process.env.DATABASE_URL || './local.db');
24982
+ export const db = drizzle(sqlite, { schema });
24983
+ `;
24984
+ await writeFile(join(dbPath, "index.ts"), clientContent);
24985
+ const schemaContent = `import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
24986
+ import { sql } from 'drizzle-orm';
24987
+
24988
+ export const users = sqliteTable('users', {
24989
+ id: integer('id').primaryKey({ autoIncrement: true }),
24990
+ email: text('email').notNull().unique(),
24991
+ name: text('name'),
24992
+ createdAt: integer('created_at', { mode: 'timestamp' }).default(sql\`(unixepoch())\`).notNull(),
24993
+ updatedAt: integer('updated_at', { mode: 'timestamp' }).default(sql\`(unixepoch())\`).notNull(),
24994
+ });
24995
+ `;
24996
+ await writeFile(join(dbPath, "schema/index.ts"), schemaContent);
24997
+ const envExample = `# Database
24998
+ DATABASE_URL=./local.db
24999
+ `;
25000
+ await writeFile(join(projectPath, ".env.example"), envExample);
25001
+ const gitignoreAddition = `
25002
+ # SQLite
25003
+ *.db
25004
+ *.db-shm
25005
+ *.db-wal
25006
+ `;
25007
+ await writeFile(join(projectPath, ".gitignore.db"), gitignoreAddition);
24281
25008
  }
25009
+ function getDatabaseDependencies(databaseType) {
25010
+ switch (databaseType) {
25011
+ case "postgres-drizzle":
25012
+ return ["drizzle-orm", "drizzle-kit"];
25013
+ case "supabase":
25014
+ return ["@supabase/supabase-js", "drizzle-orm", "drizzle-kit", "postgres"];
25015
+ case "sqlite-drizzle":
25016
+ return ["drizzle-orm", "drizzle-kit"];
25017
+ default:
25018
+ return [];
25019
+ }
25020
+ }
25021
+
24282
25022
  // ../templates/src/builders/api.ts
24283
25023
  async function buildApiPreset(projectPath, context) {
24284
25024
  await ensureDirectory(join(projectPath, "src/routes"));
@@ -24287,6 +25027,9 @@ async function buildApiPreset(projectPath, context) {
24287
25027
  import { serve } from 'bun';
24288
25028
  import { logger } from 'hono/logger';
24289
25029
  import { cors } from 'hono/cors';
25030
+ ${context.database && context.database !== "none" ? `import { db } from './db';
25031
+ import { users } from './db/schema';
25032
+ import { eq } from 'drizzle-orm';` : ""}
24290
25033
 
24291
25034
  const app = new Hono();
24292
25035
 
@@ -24299,6 +25042,7 @@ app.get('/', (context) => {
24299
25042
  return context.json({
24300
25043
  message: 'Welcome to ${context.projectName} API \uD83C\uDF5E',
24301
25044
  version: '1.0.0',
25045
+ database: '${context.database || "none"}',
24302
25046
  });
24303
25047
  });
24304
25048
 
@@ -24309,6 +25053,44 @@ app.get('/health', (context) => {
24309
25053
  });
24310
25054
  });
24311
25055
 
25056
+ ${context.database && context.database !== "none" ? `// Database example routes
25057
+ app.get('/users', async (context) => {
25058
+ try {
25059
+ const allUsers = await db.select().from(users);
25060
+ return context.json({ users: allUsers });
25061
+ } catch (error) {
25062
+ console.error('Database error:', error);
25063
+ return context.json({ error: 'Failed to fetch users' }, 500);
25064
+ }
25065
+ });
25066
+
25067
+ app.get('/users/:id', async (context) => {
25068
+ try {
25069
+ const id = context.req.param('id');
25070
+ const user = await db.select().from(users).where(eq(users.id, id)).limit(1);
25071
+
25072
+ if (!user.length) {
25073
+ return context.json({ error: 'User not found' }, 404);
25074
+ }
25075
+
25076
+ return context.json({ user: user[0] });
25077
+ } catch (error) {
25078
+ console.error('Database error:', error);
25079
+ return context.json({ error: 'Failed to fetch user' }, 500);
25080
+ }
25081
+ });
25082
+
25083
+ app.post('/users', async (context) => {
25084
+ try {
25085
+ const body = await context.req.json();
25086
+ const newUser = await db.insert(users).values(body).returning();
25087
+ return context.json({ user: newUser[0] }, 201);
25088
+ } catch (error) {
25089
+ console.error('Database error:', error);
25090
+ return context.json({ error: 'Failed to create user' }, 500);
25091
+ }
25092
+ });
25093
+ ` : ""}
24312
25094
  // 404 handler
24313
25095
  app.notFound((context) => {
24314
25096
  return context.json({ error: 'Not found' }, 404);
@@ -24330,7 +25112,8 @@ serve({
24330
25112
  },
24331
25113
  });
24332
25114
 
24333
- console.log('\uD83D\uDE80 API running on http://localhost:3001');
25115
+ console.log('\uD83D\uDE80 ${context.projectName} API running on http://localhost:3001');
25116
+ ${context.database && context.database !== "none" ? "console.log('\uD83D\uDCCA Database:', process.env.DATABASE_URL || 'Not configured');" : ""}
24334
25117
  `;
24335
25118
  await writeFile(join(projectPath, "src/index.ts"), indexContent);
24336
25119
  const usersRouteContent = `import { Hono } from 'hono';
@@ -24354,8 +25137,8 @@ users.get('/:id', (context) => {
24354
25137
  export default users;
24355
25138
  `;
24356
25139
  await writeFile(join(projectPath, "src/routes/users.ts"), usersRouteContent);
24357
- const tsconfigContent = {
24358
- compilerOptions: {
25140
+ const getTsCompilerOptions = () => {
25141
+ const baseOptions = {
24359
25142
  lib: ["ESNext"],
24360
25143
  target: "ESNext",
24361
25144
  module: "ESNext",
@@ -24365,20 +25148,63 @@ export default users;
24365
25148
  allowImportingTsExtensions: true,
24366
25149
  verbatimModuleSyntax: true,
24367
25150
  noEmit: true,
24368
- strict: true,
24369
25151
  skipLibCheck: true,
24370
- noFallthroughCasesInSwitch: true,
24371
- types: ["bun-types"]
25152
+ types: ["bun"],
25153
+ paths: context.pathAliases ? { "@/*": ["./src/*"] } : undefined
25154
+ };
25155
+ if (context.tsStrictness === "strict") {
25156
+ return {
25157
+ ...baseOptions,
25158
+ strict: true,
25159
+ noUnusedLocals: true,
25160
+ noUnusedParameters: true,
25161
+ noFallthroughCasesInSwitch: true,
25162
+ noImplicitReturns: true
25163
+ };
25164
+ }
25165
+ if (context.tsStrictness === "moderate") {
25166
+ return {
25167
+ ...baseOptions,
25168
+ strict: true,
25169
+ noFallthroughCasesInSwitch: true
25170
+ };
24372
25171
  }
25172
+ return {
25173
+ ...baseOptions,
25174
+ strict: false
25175
+ };
25176
+ };
25177
+ const tsconfigContent = {
25178
+ compilerOptions: getTsCompilerOptions()
24373
25179
  };
24374
25180
  await writeFile(join(projectPath, "tsconfig.json"), JSON.stringify(tsconfigContent, null, 2));
24375
25181
  const bunfigContent = `[install]
24376
25182
  frozenLockfile = false
24377
25183
 
24378
- [test]
25184
+ ${context.testing !== "none" ? `[test]
24379
25185
  coverage = true
24380
- `;
25186
+ ` : ""}`;
24381
25187
  await writeFile(join(projectPath, "bunfig.toml"), bunfigContent);
25188
+ if (context.database && context.database !== "none") {
25189
+ if (context.database === "postgres-drizzle") {
25190
+ await setupPostgresDrizzle(projectPath, context, false);
25191
+ } else if (context.database === "supabase") {
25192
+ await setupSupabase(projectPath, context, false);
25193
+ } else if (context.database === "sqlite-drizzle") {
25194
+ await setupSQLiteDrizzle(projectPath, context, false);
25195
+ }
25196
+ }
25197
+ if (context.codeQuality === "ultracite") {
25198
+ await setupUltracite(projectPath, context);
25199
+ } else {
25200
+ await setupBiome(projectPath, context);
25201
+ }
25202
+ if (context.docker) {
25203
+ await setupDocker(projectPath, context);
25204
+ }
25205
+ if (context.cicd) {
25206
+ await setupGitHubActions(projectPath, context);
25207
+ }
24382
25208
  }
24383
25209
  // ../templates/src/builders/full.ts
24384
25210
  async function buildFullPreset(projectPath, context) {
@@ -24387,6 +25213,9 @@ async function buildFullPreset(projectPath, context) {
24387
25213
  await ensureDirectory(join(projectPath, "apps/api"));
24388
25214
  await ensureDirectory(join(projectPath, "packages/types"));
24389
25215
  await ensureDirectory(join(projectPath, "packages/utils"));
25216
+ if (context.database && context.database !== "none") {
25217
+ await ensureDirectory(join(projectPath, "packages/db"));
25218
+ }
24390
25219
  const rootPackageJson = {
24391
25220
  name: `${context.packageName}-monorepo`,
24392
25221
  version: "0.0.0",
@@ -24620,31 +25449,50 @@ const config: Config = {
24620
25449
  export default config;
24621
25450
  `;
24622
25451
  await writeFile(join(projectPath, "apps/web/tailwind.config.ts"), webTailwindConfigContent);
24623
- const webTsconfigContent = {
24624
- compilerOptions: {
25452
+ const getWebTsConfig = () => {
25453
+ const baseOptions = {
24625
25454
  target: "ES2017",
24626
25455
  lib: ["dom", "dom.iterable", "esnext"],
24627
25456
  allowJs: true,
24628
25457
  skipLibCheck: true,
24629
- strict: true,
24630
25458
  noEmit: true,
24631
25459
  esModuleInterop: true,
24632
25460
  module: "esnext",
24633
25461
  moduleResolution: "bundler",
24634
25462
  resolveJsonModule: true,
24635
25463
  isolatedModules: true,
24636
- jsx: "preserve",
25464
+ jsx: "react-jsx",
24637
25465
  incremental: true,
24638
- plugins: [
24639
- {
24640
- name: "next"
24641
- }
24642
- ],
24643
- paths: {
24644
- "@/*": ["./src/*"]
24645
- }
24646
- },
24647
- include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25466
+ plugins: [{ name: "next" }],
25467
+ paths: context.pathAliases ? { "@/*": ["./src/*"] } : undefined
25468
+ };
25469
+ if (context.tsStrictness === "strict") {
25470
+ return {
25471
+ ...baseOptions,
25472
+ strict: true,
25473
+ noUnusedLocals: true,
25474
+ noUnusedParameters: true,
25475
+ noFallthroughCasesInSwitch: true,
25476
+ noImplicitReturns: true
25477
+ };
25478
+ }
25479
+ if (context.tsStrictness === "moderate") {
25480
+ return {
25481
+ ...baseOptions,
25482
+ strict: true,
25483
+ noUnusedLocals: false,
25484
+ noUnusedParameters: false
25485
+ };
25486
+ }
25487
+ return {
25488
+ ...baseOptions,
25489
+ strict: false,
25490
+ noImplicitAny: false
25491
+ };
25492
+ };
25493
+ const webTsconfigContent = {
25494
+ compilerOptions: getWebTsConfig(),
25495
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/dev/types/**/*.ts"],
24648
25496
  exclude: ["node_modules"]
24649
25497
  };
24650
25498
  await writeFile(join(projectPath, "apps/web/tsconfig.json"), JSON.stringify(webTsconfigContent, null, 2));
@@ -24734,30 +25582,8 @@ export default config;
24734
25582
  `;
24735
25583
  await writeFile(join(projectPath, "apps/platform/tailwind.config.ts"), platformTailwindConfigContent);
24736
25584
  const platformTsconfigContent = {
24737
- compilerOptions: {
24738
- target: "ES2017",
24739
- lib: ["dom", "dom.iterable", "esnext"],
24740
- allowJs: true,
24741
- skipLibCheck: true,
24742
- strict: true,
24743
- noEmit: true,
24744
- esModuleInterop: true,
24745
- module: "esnext",
24746
- moduleResolution: "bundler",
24747
- resolveJsonModule: true,
24748
- isolatedModules: true,
24749
- jsx: "preserve",
24750
- incremental: true,
24751
- plugins: [
24752
- {
24753
- name: "next"
24754
- }
24755
- ],
24756
- paths: {
24757
- "@/*": ["./src/*"]
24758
- }
24759
- },
24760
- include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25585
+ compilerOptions: getWebTsConfig(),
25586
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/dev/types/**/*.ts"],
24761
25587
  exclude: ["node_modules"]
24762
25588
  };
24763
25589
  await writeFile(join(projectPath, "apps/platform/tsconfig.json"), JSON.stringify(platformTsconfigContent, null, 2));
@@ -24914,8 +25740,107 @@ Built with \u2764\uFE0F using Bun monorepo features
24914
25740
  exclude: ["node_modules"]
24915
25741
  };
24916
25742
  await writeFile(join(projectPath, "tsconfig.json"), JSON.stringify(tsconfigContent, null, 2));
25743
+ if (context.database && context.database !== "none") {
25744
+ const dbPackagePath = join(projectPath, "packages/db");
25745
+ const dbPackageJson = {
25746
+ name: `@${context.packageName}/db`,
25747
+ version: "0.0.0",
25748
+ private: true,
25749
+ main: "./src/index.ts",
25750
+ types: "./src/index.ts",
25751
+ dependencies: context.database === "supabase" ? {
25752
+ "@supabase/supabase-js": "^2.48.1",
25753
+ "drizzle-orm": "^0.38.0",
25754
+ postgres: "^3.4.5"
25755
+ } : {
25756
+ "drizzle-orm": "^0.38.0"
25757
+ },
25758
+ devDependencies: {
25759
+ "drizzle-kit": "^0.30.1",
25760
+ "@types/bun": "latest",
25761
+ typescript: "^5.7.2"
25762
+ }
25763
+ };
25764
+ await writeFile(join(dbPackagePath, "package.json"), JSON.stringify(dbPackageJson, null, 2));
25765
+ if (context.database === "postgres-drizzle") {
25766
+ await setupPostgresDrizzle(dbPackagePath, context, true);
25767
+ } else if (context.database === "supabase") {
25768
+ await setupSupabase(dbPackagePath, context, true);
25769
+ } else if (context.database === "sqlite-drizzle") {
25770
+ await setupSQLiteDrizzle(dbPackagePath, context, true);
25771
+ }
25772
+ const apiPackageJson2 = JSON.parse(await Bun.file(join(projectPath, "apps/api/package.json")).text());
25773
+ apiPackageJson2.dependencies[`@${context.packageName}/db`] = "workspace:*";
25774
+ await writeFile(join(projectPath, "apps/api/package.json"), JSON.stringify(apiPackageJson2, null, 2));
25775
+ }
25776
+ if (context.codeQuality === "ultracite") {
25777
+ await setupUltracite(projectPath, context);
25778
+ } else {
25779
+ await setupBiome(projectPath, context);
25780
+ }
25781
+ if (context.docker) {
25782
+ await setupDocker(projectPath, context);
25783
+ const dockerCompose = `version: '3.8'
25784
+
25785
+ services:
25786
+ web:
25787
+ build:
25788
+ context: ./apps/web
25789
+ dockerfile: ../../Dockerfile
25790
+ ports:
25791
+ - "3000:3000"
25792
+ environment:
25793
+ - NODE_ENV=production
25794
+ ${context.database === "supabase" ? "- NEXT_PUBLIC_SUPABASE_URL=${SUPABASE_URL}\n - NEXT_PUBLIC_SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}" : ""}
25795
+ restart: unless-stopped
25796
+
25797
+ platform:
25798
+ build:
25799
+ context: ./apps/platform
25800
+ dockerfile: ../../Dockerfile
25801
+ ports:
25802
+ - "3001:3000"
25803
+ environment:
25804
+ - NODE_ENV=production
25805
+ ${context.database === "supabase" ? "- NEXT_PUBLIC_SUPABASE_URL=${SUPABASE_URL}\n - NEXT_PUBLIC_SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}" : ""}
25806
+ restart: unless-stopped
25807
+
25808
+ api:
25809
+ build:
25810
+ context: ./apps/api
25811
+ dockerfile: ../../Dockerfile
25812
+ ports:
25813
+ - "3002:3001"
25814
+ environment:
25815
+ - NODE_ENV=production
25816
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `- DATABASE_URL=\${DATABASE_URL}` : ""}
25817
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `depends_on:
25818
+ - db` : ""}
25819
+ restart: unless-stopped
25820
+
25821
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `db:
25822
+ image: ${context.database === "sqlite-drizzle" ? "alpine:latest" : "postgres:16-alpine"}
25823
+ ${context.database !== "sqlite-drizzle" ? `environment:
25824
+ - POSTGRES_USER=postgres
25825
+ - POSTGRES_PASSWORD=postgres
25826
+ - POSTGRES_DB=${context.projectName}
25827
+ volumes:
25828
+ - postgres_data:/var/lib/postgresql/data
25829
+ ports:
25830
+ - "5432:5432"` : `volumes:
25831
+ - sqlite_data:/data`}
25832
+ restart: unless-stopped
25833
+ ` : ""}
25834
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `volumes:
25835
+ ${context.database === "sqlite-drizzle" ? "sqlite_data:" : "postgres_data:"}` : ""}
25836
+ `;
25837
+ await writeFile(join(projectPath, "docker-compose.yml"), dockerCompose);
25838
+ }
25839
+ if (context.cicd) {
25840
+ await setupGitHubActions(projectPath, context);
25841
+ }
24917
25842
  }
24918
- // src/commands/init.real.ts
25843
+ // src/commands/init.enhanced.ts
24919
25844
  function getOptionValue(envVar, option, defaultValue) {
24920
25845
  const envValue = process.env[envVar];
24921
25846
  if (envValue !== undefined) {
@@ -24927,7 +25852,7 @@ function getOptionValue(envVar, option, defaultValue) {
24927
25852
  }
24928
25853
  return option ?? defaultValue;
24929
25854
  }
24930
- async function initCommand(options = {}) {
25855
+ async function enhancedInitCommand(options = {}) {
24931
25856
  const isNonInteractive = process.env.BUNKIT_NON_INTERACTIVE === "true" || options.nonInteractive === true;
24932
25857
  let projectName = getOptionValue("BUNKIT_PROJECT_NAME", options.name);
24933
25858
  if (!projectName) {
@@ -24956,7 +25881,7 @@ async function initCommand(options = {}) {
24956
25881
  let preset = getOptionValue("BUNKIT_PRESET", options.preset);
24957
25882
  if (!preset) {
24958
25883
  if (isNonInteractive) {
24959
- throw new Error("Preset is required in non-interactive mode. " + "Provide it via BUNKIT_PRESET env var or --preset flag. " + "Valid values: minimal, web, api, full");
25884
+ throw new Error("Preset is required in non-interactive mode. " + "Provide it via BUNKIT_PRESET env var or --preset flag.");
24960
25885
  }
24961
25886
  preset = await ve({
24962
25887
  message: "\uD83C\uDFA8 Which preset would you like?",
@@ -24964,22 +25889,22 @@ async function initCommand(options = {}) {
24964
25889
  {
24965
25890
  value: "minimal",
24966
25891
  label: "\u26A1 Minimal",
24967
- hint: "Single repo, clean start"
25892
+ hint: "Single file, clean start - perfect for learning Bun"
24968
25893
  },
24969
25894
  {
24970
25895
  value: "web",
24971
- label: "\uD83C\uDF10 Web",
24972
- hint: "Next.js 16 + React 19"
25896
+ label: "\uD83C\uDF10 Web App",
25897
+ hint: "Next.js 16 + React 19 - full-stack web application"
24973
25898
  },
24974
25899
  {
24975
25900
  value: "api",
24976
- label: "\uD83D\uDE80 API",
24977
- hint: "Hono + Bun.serve()"
25901
+ label: "\uD83D\uDE80 API Server",
25902
+ hint: "Hono + Bun.serve() - ultra-fast REST API"
24978
25903
  },
24979
25904
  {
24980
25905
  value: "full",
24981
- label: "\uD83D\uDCE6 Full-stack",
24982
- hint: "Monorepo with everything"
25906
+ label: "\uD83D\uDCE6 Full-Stack Monorepo",
25907
+ hint: "Web + Platform + API - enterprise SaaS setup"
24983
25908
  }
24984
25909
  ]
24985
25910
  });
@@ -24987,17 +25912,221 @@ async function initCommand(options = {}) {
24987
25912
  xe("Operation cancelled.");
24988
25913
  process.exit(0);
24989
25914
  }
24990
- } else {
24991
- const validPresets = ["minimal", "web", "api", "full"];
24992
- if (!validPresets.includes(preset)) {
24993
- throw new Error(`Invalid preset: ${preset}. Valid values: ${validPresets.join(", ")}`);
25915
+ }
25916
+ let database = getOptionValue("BUNKIT_DATABASE", options.database);
25917
+ if (!database && (preset === "api" || preset === "full")) {
25918
+ if (!isNonInteractive) {
25919
+ database = await ve({
25920
+ message: "\uD83D\uDDC4\uFE0F Database setup?",
25921
+ options: [
25922
+ {
25923
+ value: "postgres-drizzle",
25924
+ label: "PostgreSQL + Drizzle ORM",
25925
+ hint: "Production-ready, type-safe SQL database"
25926
+ },
25927
+ {
25928
+ value: "supabase",
25929
+ label: "Supabase",
25930
+ hint: "PostgreSQL + Auth + Storage + Realtime - all-in-one"
25931
+ },
25932
+ {
25933
+ value: "sqlite-drizzle",
25934
+ label: "SQLite + Drizzle ORM",
25935
+ hint: "Local-first, embedded database - perfect for prototypes"
25936
+ },
25937
+ {
25938
+ value: "none",
25939
+ label: "None",
25940
+ hint: "I'll add it later"
25941
+ }
25942
+ ]
25943
+ });
25944
+ if (pD(database)) {
25945
+ xe("Operation cancelled.");
25946
+ process.exit(0);
25947
+ }
25948
+ } else {
25949
+ database = "none";
25950
+ }
25951
+ }
25952
+ let codeQuality = getOptionValue("BUNKIT_CODE_QUALITY", options.codeQuality, "ultracite");
25953
+ if (!codeQuality) {
25954
+ if (!isNonInteractive) {
25955
+ codeQuality = await ve({
25956
+ message: "\uD83E\uDD16 Code quality setup?",
25957
+ options: [
25958
+ {
25959
+ value: "ultracite",
25960
+ label: "Ultracite (Recommended)",
25961
+ hint: "AI-optimized Biome preset - syncs rules for Cursor, Claude, Windsurf"
25962
+ },
25963
+ {
25964
+ value: "biome",
25965
+ label: "Biome",
25966
+ hint: "Standard Biome with good defaults - fast and reliable"
25967
+ }
25968
+ ]
25969
+ });
25970
+ if (pD(codeQuality)) {
25971
+ xe("Operation cancelled.");
25972
+ process.exit(0);
25973
+ }
25974
+ } else {
25975
+ codeQuality = "ultracite";
25976
+ }
25977
+ }
25978
+ let tsStrictness = getOptionValue("BUNKIT_TS_STRICTNESS", options.tsStrictness, "strict");
25979
+ if (!tsStrictness) {
25980
+ if (!isNonInteractive) {
25981
+ tsStrictness = await ve({
25982
+ message: "\uD83D\uDD12 TypeScript strictness?",
25983
+ options: [
25984
+ {
25985
+ value: "strict",
25986
+ label: "Strict (Recommended)",
25987
+ hint: "Maximum type safety - catches bugs early, prevents headaches"
25988
+ },
25989
+ {
25990
+ value: "moderate",
25991
+ label: "Moderate",
25992
+ hint: "Balanced - good safety without being too rigid"
25993
+ },
25994
+ {
25995
+ value: "loose",
25996
+ label: "Loose",
25997
+ hint: "Minimal checks - quick prototyping, migrate from JavaScript"
25998
+ }
25999
+ ]
26000
+ });
26001
+ if (pD(tsStrictness)) {
26002
+ xe("Operation cancelled.");
26003
+ process.exit(0);
26004
+ }
26005
+ } else {
26006
+ tsStrictness = "strict";
26007
+ }
26008
+ }
26009
+ let cssFramework = getOptionValue("BUNKIT_CSS_FRAMEWORK", options.cssFramework);
26010
+ if (!cssFramework && (preset === "web" || preset === "full")) {
26011
+ if (!isNonInteractive) {
26012
+ cssFramework = await ve({
26013
+ message: "\uD83C\uDFA8 CSS framework?",
26014
+ options: [
26015
+ {
26016
+ value: "tailwind",
26017
+ label: "Tailwind CSS 4 (Recommended)",
26018
+ hint: "Utility-first, fast, modern - with OKLCH colors and @theme"
26019
+ },
26020
+ {
26021
+ value: "vanilla",
26022
+ label: "Vanilla CSS",
26023
+ hint: "Plain CSS files - full control, no framework"
26024
+ },
26025
+ {
26026
+ value: "css-modules",
26027
+ label: "CSS Modules",
26028
+ hint: "Scoped CSS - automatic class name generation"
26029
+ }
26030
+ ]
26031
+ });
26032
+ if (pD(cssFramework)) {
26033
+ xe("Operation cancelled.");
26034
+ process.exit(0);
26035
+ }
26036
+ } else {
26037
+ cssFramework = "tailwind";
26038
+ }
26039
+ }
26040
+ let uiLibrary = getOptionValue("BUNKIT_UI_LIBRARY", options.uiLibrary);
26041
+ if (!uiLibrary && (preset === "web" || preset === "full") && cssFramework === "tailwind") {
26042
+ if (!isNonInteractive) {
26043
+ uiLibrary = await ve({
26044
+ message: "\uD83E\uDDE9 UI component library?",
26045
+ options: [
26046
+ {
26047
+ value: "shadcn",
26048
+ label: "shadcn/ui (Recommended)",
26049
+ hint: "64+ components, accessible, customizable - production-ready"
26050
+ },
26051
+ {
26052
+ value: "none",
26053
+ label: "None",
26054
+ hint: "I'll build my own components"
26055
+ }
26056
+ ]
26057
+ });
26058
+ if (pD(uiLibrary)) {
26059
+ xe("Operation cancelled.");
26060
+ process.exit(0);
26061
+ }
26062
+ } else {
26063
+ uiLibrary = "shadcn";
26064
+ }
26065
+ }
26066
+ let testing = getOptionValue("BUNKIT_TESTING", options.testing, "bun-test");
26067
+ if (!testing) {
26068
+ if (!isNonInteractive) {
26069
+ testing = await ve({
26070
+ message: "\uD83E\uDDEA Testing framework?",
26071
+ options: [
26072
+ {
26073
+ value: "bun-test",
26074
+ label: "Bun Test (Recommended)",
26075
+ hint: "Built-in, fast, Jest-compatible - no extra dependencies"
26076
+ },
26077
+ {
26078
+ value: "vitest",
26079
+ label: "Vitest",
26080
+ hint: "Vite-powered, fast, ESM-first - popular in ecosystem"
26081
+ },
26082
+ {
26083
+ value: "none",
26084
+ label: "None",
26085
+ hint: "I'll add testing later"
26086
+ }
26087
+ ]
26088
+ });
26089
+ if (pD(testing)) {
26090
+ xe("Operation cancelled.");
26091
+ process.exit(0);
26092
+ }
26093
+ } else {
26094
+ testing = "bun-test";
26095
+ }
26096
+ }
26097
+ let docker = getOptionValue("BUNKIT_DOCKER", options.docker, false);
26098
+ if (docker === undefined) {
26099
+ if (!isNonInteractive) {
26100
+ docker = await ye({
26101
+ message: "\uD83D\uDC33 Add Docker configuration?",
26102
+ initialValue: false
26103
+ });
26104
+ if (pD(docker)) {
26105
+ xe("Operation cancelled.");
26106
+ process.exit(0);
26107
+ }
26108
+ } else {
26109
+ docker = false;
26110
+ }
26111
+ }
26112
+ let cicd = getOptionValue("BUNKIT_CICD", options.cicd, false);
26113
+ if (cicd === undefined) {
26114
+ if (!isNonInteractive) {
26115
+ cicd = await ye({
26116
+ message: "\u2699\uFE0F Add GitHub Actions CI/CD?",
26117
+ initialValue: false
26118
+ });
26119
+ if (pD(cicd)) {
26120
+ xe("Operation cancelled.");
26121
+ process.exit(0);
26122
+ }
26123
+ } else {
26124
+ cicd = false;
24994
26125
  }
24995
26126
  }
24996
26127
  let shouldInstall = getOptionValue("BUNKIT_INSTALL", options.install, true);
24997
26128
  if (shouldInstall === undefined) {
24998
- if (isNonInteractive) {
24999
- shouldInstall = true;
25000
- } else {
26129
+ if (!isNonInteractive) {
25001
26130
  shouldInstall = await ye({
25002
26131
  message: "\uD83D\uDCE5 Install dependencies?",
25003
26132
  initialValue: true
@@ -25006,13 +26135,13 @@ async function initCommand(options = {}) {
25006
26135
  xe("Operation cancelled.");
25007
26136
  process.exit(0);
25008
26137
  }
26138
+ } else {
26139
+ shouldInstall = true;
25009
26140
  }
25010
26141
  }
25011
26142
  let shouldInitGit = getOptionValue("BUNKIT_GIT", options.git, true);
25012
26143
  if (shouldInitGit === undefined) {
25013
- if (isNonInteractive) {
25014
- shouldInitGit = true;
25015
- } else {
26144
+ if (!isNonInteractive) {
25016
26145
  shouldInitGit = await ye({
25017
26146
  message: "\uD83D\uDD27 Initialize git repository?",
25018
26147
  initialValue: true
@@ -25021,16 +26150,39 @@ async function initCommand(options = {}) {
25021
26150
  xe("Operation cancelled.");
25022
26151
  process.exit(0);
25023
26152
  }
26153
+ } else {
26154
+ shouldInitGit = true;
25024
26155
  }
25025
26156
  }
25026
- if (isNonInteractive) {
26157
+ if (!isNonInteractive) {
25027
26158
  console.log(`
25028
- \uD83D\uDCCB Configuration:`);
25029
- console.log(` Project: ${import_picocolors4.default.cyan(projectName)}`);
25030
- console.log(` Preset: ${import_picocolors4.default.cyan(preset)}`);
25031
- console.log(` Install dependencies: ${import_picocolors4.default.cyan(shouldInstall ? "yes" : "no")}`);
25032
- console.log(` Initialize git: ${import_picocolors4.default.cyan(shouldInitGit ? "yes" : "no")}`);
25033
- console.log("");
26159
+ ` + boxen([
26160
+ `${source_default.bold("Project:")} ${source_default.cyan(projectName)}`,
26161
+ `${source_default.bold("Preset:")} ${source_default.cyan(preset)}`,
26162
+ database ? `${source_default.bold("Database:")} ${source_default.cyan(database)}` : "",
26163
+ `${source_default.bold("Code Quality:")} ${source_default.cyan(codeQuality)}`,
26164
+ `${source_default.bold("TypeScript:")} ${source_default.cyan(tsStrictness)}`,
26165
+ cssFramework ? `${source_default.bold("CSS:")} ${source_default.cyan(cssFramework)}` : "",
26166
+ uiLibrary ? `${source_default.bold("UI Library:")} ${source_default.cyan(uiLibrary)}` : "",
26167
+ `${source_default.bold("Testing:")} ${source_default.cyan(testing)}`,
26168
+ docker ? `${source_default.bold("Docker:")} ${source_default.green("\u2713")}` : "",
26169
+ cicd ? `${source_default.bold("CI/CD:")} ${source_default.green("\u2713")}` : ""
26170
+ ].filter(Boolean).join(`
26171
+ `), {
26172
+ padding: 1,
26173
+ title: "\uD83D\uDCCB Configuration",
26174
+ titleAlignment: "left",
26175
+ borderColor: "cyan",
26176
+ borderStyle: "round"
26177
+ }));
26178
+ const confirm = await ye({
26179
+ message: "Proceed with this configuration?",
26180
+ initialValue: true
26181
+ });
26182
+ if (pD(confirm) || !confirm) {
26183
+ xe("Operation cancelled.");
26184
+ process.exit(0);
26185
+ }
25034
26186
  }
25035
26187
  const s = Y2();
25036
26188
  s.start("\uD83D\uDD28 Creating project structure...");
@@ -25040,7 +26192,17 @@ async function initCommand(options = {}) {
25040
26192
  preset,
25041
26193
  path: projectName,
25042
26194
  git: shouldInitGit,
25043
- install: shouldInstall
26195
+ install: shouldInstall,
26196
+ database,
26197
+ codeQuality,
26198
+ tsStrictness,
26199
+ uiLibrary,
26200
+ cssFramework,
26201
+ testing,
26202
+ docker,
26203
+ cicd,
26204
+ envExample: true,
26205
+ pathAliases: true
25044
26206
  };
25045
26207
  await createProject(config);
25046
26208
  const projectPath = join(process.cwd(), config.path);
@@ -25060,34 +26222,40 @@ async function initCommand(options = {}) {
25060
26222
  await buildFullPreset(projectPath, context);
25061
26223
  break;
25062
26224
  }
26225
+ if (database && database !== "none") {
26226
+ s.message(`\uD83D\uDDC4\uFE0F Setting up ${database}...`);
26227
+ }
26228
+ if (codeQuality === "ultracite") {
26229
+ s.message("\uD83E\uDD16 Configuring Ultracite for AI editors...");
26230
+ }
26231
+ if (docker) {
26232
+ s.message("\uD83D\uDC33 Adding Docker configuration...");
26233
+ }
26234
+ if (cicd) {
26235
+ s.message("\u2699\uFE0F Adding GitHub Actions...");
26236
+ }
25063
26237
  s.stop("\u2705 Project created!");
25064
26238
  if (shouldInstall) {
25065
- const deps = getDependenciesForPreset(preset);
25066
- if (deps.length > 0) {
25067
- await installDependencies(projectPath, deps);
25068
- } else {
25069
- await installDependencies(projectPath);
26239
+ const additionalDeps = [];
26240
+ if (database && database !== "none") {
26241
+ additionalDeps.push(...getDatabaseDependencies(database));
25070
26242
  }
26243
+ if (codeQuality) {
26244
+ additionalDeps.push(...getCodeQualityDependencies(codeQuality));
26245
+ }
26246
+ if (testing === "vitest") {
26247
+ additionalDeps.push("vitest", "@vitest/ui");
26248
+ }
26249
+ if (uiLibrary === "shadcn") {
26250
+ additionalDeps.push("class-variance-authority", "clsx", "tailwind-merge");
26251
+ }
26252
+ await installDependencies(projectPath, additionalDeps);
25071
26253
  }
25072
26254
  const getDevCommand = () => {
25073
26255
  if (preset === "full" || preset === "web")
25074
26256
  return "bun dev";
25075
26257
  return "bun run dev";
25076
26258
  };
25077
- const getPresetEmoji = () => {
25078
- switch (preset) {
25079
- case "minimal":
25080
- return "\u26A1";
25081
- case "web":
25082
- return "\uD83C\uDF10";
25083
- case "api":
25084
- return "\uD83D\uDE80";
25085
- case "full":
25086
- return "\uD83D\uDCE6";
25087
- default:
25088
- return "\uD83C\uDF5E";
25089
- }
25090
- };
25091
26259
  const nextSteps = [
25092
26260
  `${source_default.cyan("cd")} ${projectName}`,
25093
26261
  shouldInstall ? "" : `${source_default.cyan("bun install")}`,
@@ -25097,9 +26265,9 @@ async function initCommand(options = {}) {
25097
26265
  console.log(`
25098
26266
  ` + boxen(nextSteps, {
25099
26267
  padding: 1,
25100
- title: `${getPresetEmoji()} Next steps`,
26268
+ title: "\uD83D\uDE80 Next steps",
25101
26269
  titleAlignment: "left",
25102
- borderColor: "cyan",
26270
+ borderColor: "green",
25103
26271
  borderStyle: "round"
25104
26272
  }));
25105
26273
  } catch (error) {
@@ -25108,18 +26276,6 @@ async function initCommand(options = {}) {
25108
26276
  process.exit(1);
25109
26277
  }
25110
26278
  }
25111
- function getDependenciesForPreset(preset) {
25112
- switch (preset) {
25113
- case "web":
25114
- return ["react@19.1.0", "react-dom@19.1.0", "next@16.0.0", "tailwindcss@4.1.7"];
25115
- case "api":
25116
- return ["hono@4.7.12"];
25117
- case "full":
25118
- return [];
25119
- default:
25120
- return [];
25121
- }
25122
- }
25123
26279
 
25124
26280
  // src/commands/create.ts
25125
26281
  async function createCommand2(preset, name, options) {
@@ -25156,10 +26312,10 @@ var packageJson = await Bun.file(new URL("../package.json", import.meta.url)).js
25156
26312
  var VERSION = packageJson.version;
25157
26313
  var program2 = new Command;
25158
26314
  program2.name("bunkit").description("Bake production-ready apps in seconds").version(VERSION);
25159
- program2.command("init").description("Create a new project interactively").option("--name <name>", "Project name").option("--preset <preset>", "Preset type (minimal, web, api, full)").option("--no-git", "Skip git initialization").option("--no-install", "Skip dependency installation").option("--non-interactive", "Run without prompts (requires all options)").action(async (options) => {
26315
+ program2.command("init").description("Create a new project with full customization").option("--name <name>", "Project name").option("--preset <preset>", "Preset type (minimal, web, api, full)").option("--database <database>", "Database (postgres-drizzle, supabase, sqlite-drizzle, none)").option("--code-quality <quality>", "Code quality (ultracite, biome)").option("--ts-strictness <strictness>", "TypeScript strictness (strict, moderate, loose)").option("--ui-library <library>", "UI library (shadcn, none)").option("--css-framework <framework>", "CSS framework (tailwind, vanilla, css-modules)").option("--testing <framework>", "Testing framework (bun-test, vitest, none)").option("--docker", "Add Docker configuration").option("--cicd", "Add GitHub Actions CI/CD").option("--no-git", "Skip git initialization").option("--no-install", "Skip dependency installation").option("--non-interactive", "Run without prompts (requires all options)").action(async (options) => {
25160
26316
  showBanner(VERSION);
25161
26317
  try {
25162
- await initCommand(options);
26318
+ await enhancedInitCommand(options);
25163
26319
  Se(import_picocolors5.default.green("\u2728 Done! Your project is ready to bake! \uD83C\uDF5E"));
25164
26320
  } catch (error) {
25165
26321
  M2.error(error.message);