bunkit-cli 0.4.3 → 0.5.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.
Files changed (2) hide show
  1. package/dist/index.js +1358 -159
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10433,7 +10433,7 @@ var require_package = __commonJS((exports, module) => {
10433
10433
 
10434
10434
  // ../../node_modules/.bun/ejs@3.1.10/node_modules/ejs/lib/ejs.js
10435
10435
  var require_ejs = __commonJS((exports) => {
10436
- var fs4 = __require("fs");
10436
+ var fs5 = __require("fs");
10437
10437
  var path7 = __require("path");
10438
10438
  var utils = require_utils7();
10439
10439
  var scopeOptionWarned = false;
@@ -10461,7 +10461,7 @@ var require_ejs = __commonJS((exports) => {
10461
10461
  var _BOM = /^\uFEFF/;
10462
10462
  var _JS_IDENTIFIER = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
10463
10463
  exports.cache = utils.cache;
10464
- exports.fileLoader = fs4.readFileSync;
10464
+ exports.fileLoader = fs5.readFileSync;
10465
10465
  exports.localsName = _DEFAULT_LOCALS_NAME;
10466
10466
  exports.promiseImpl = new Function("return this;")().Promise;
10467
10467
  exports.resolveInclude = function(name, filename, isDir) {
@@ -10479,7 +10479,7 @@ var require_ejs = __commonJS((exports) => {
10479
10479
  var filePath;
10480
10480
  if (paths.some(function(v2) {
10481
10481
  filePath = exports.resolveInclude(name, v2, true);
10482
- return fs4.existsSync(filePath);
10482
+ return fs5.existsSync(filePath);
10483
10483
  })) {
10484
10484
  return filePath;
10485
10485
  }
@@ -10499,7 +10499,7 @@ var require_ejs = __commonJS((exports) => {
10499
10499
  } else {
10500
10500
  if (options.filename) {
10501
10501
  filePath = exports.resolveInclude(path8, options.filename);
10502
- if (fs4.existsSync(filePath)) {
10502
+ if (fs5.existsSync(filePath)) {
10503
10503
  includePath = filePath;
10504
10504
  }
10505
10505
  }
@@ -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"]),
@@ -23996,7 +24006,7 @@ async function createPackageJson(projectPath, config) {
23996
24006
  dependencies: {},
23997
24007
  devDependencies: {
23998
24008
  "@types/bun": "latest",
23999
- typescript: "^5.7.2"
24009
+ typescript: "^5.9.3"
24000
24010
  },
24001
24011
  ...gitUser.name && { author: gitUser.name }
24002
24012
  };
@@ -24094,14 +24104,28 @@ 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
24121
+ var import_fs_extra3 = __toESM(require_lib(), 1);
24101
24122
  async function installDependencies(cwd, packages) {
24102
24123
  logger.step("Installing dependencies...");
24103
24124
  try {
24104
- if (packages && packages.length > 0) {
24125
+ if (packages && typeof packages === "object" && !Array.isArray(packages)) {
24126
+ await addDependenciesToPackageJson(cwd, packages);
24127
+ await execa("bun", ["install"], { cwd, stdio: "inherit" });
24128
+ } else if (packages && Array.isArray(packages) && packages.length > 0) {
24105
24129
  await execa("bun", ["add", ...packages], { cwd, stdio: "inherit" });
24106
24130
  } else {
24107
24131
  await execa("bun", ["install"], { cwd, stdio: "inherit" });
@@ -24112,7 +24136,16 @@ async function installDependencies(cwd, packages) {
24112
24136
  throw error;
24113
24137
  }
24114
24138
  }
24115
- // src/commands/init.real.ts
24139
+ async function addDependenciesToPackageJson(cwd, dependencies) {
24140
+ const packageJsonPath = join(cwd, "package.json");
24141
+ const packageJson = await import_fs_extra3.default.readJson(packageJsonPath);
24142
+ packageJson.dependencies = {
24143
+ ...packageJson.dependencies,
24144
+ ...dependencies
24145
+ };
24146
+ await import_fs_extra3.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
24147
+ }
24148
+ // src/commands/init.enhanced.ts
24116
24149
  var import_picocolors4 = __toESM(require_picocolors(), 1);
24117
24150
 
24118
24151
  // ../templates/src/render.ts
@@ -24161,6 +24194,556 @@ coverage = true
24161
24194
  `;
24162
24195
  await writeFile(join(projectPath, "bunfig.toml"), bunfigContent);
24163
24196
  }
24197
+ // ../templates/src/generators/ultracite.ts
24198
+ async function setupUltracite(projectPath, context) {
24199
+ const biomeConfig = `{
24200
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
24201
+ "extends": [
24202
+ "ultracite/core",
24203
+ ${context.cssFramework === "tailwind" ? `"ultracite/react",
24204
+ "ultracite/next"` : '"ultracite/react"'}
24205
+ ],
24206
+ "vcs": {
24207
+ "enabled": true,
24208
+ "clientKind": "git",
24209
+ "useIgnoreFile": true
24210
+ },
24211
+ "files": {
24212
+ "ignore": [
24213
+ "node_modules",
24214
+ "dist",
24215
+ "build",
24216
+ ".next",
24217
+ ".turbo",
24218
+ "coverage"
24219
+ ]
24220
+ },
24221
+ "formatter": {
24222
+ "enabled": true,
24223
+ "formatWithErrors": false,
24224
+ "indentStyle": "space",
24225
+ "indentWidth": 2,
24226
+ "lineWidth": 100,
24227
+ "lineEnding": "lf"
24228
+ },
24229
+ "linter": {
24230
+ "enabled": true,
24231
+ "rules": {
24232
+ "recommended": true
24233
+ }
24234
+ },
24235
+ "javascript": {
24236
+ "formatter": {
24237
+ "quoteStyle": "single",
24238
+ "trailingCommas": "es5",
24239
+ "semicolons": "always",
24240
+ "arrowParentheses": "always",
24241
+ "bracketSpacing": true,
24242
+ "jsxQuoteStyle": "double",
24243
+ "quoteProperties": "asNeeded"
24244
+ }
24245
+ },
24246
+ "json": {
24247
+ "formatter": {
24248
+ "enabled": true,
24249
+ "indentWidth": 2
24250
+ }
24251
+ }
24252
+ }
24253
+ `;
24254
+ await writeFile(join(projectPath, "biome.jsonc"), biomeConfig);
24255
+ const cursorRules = `# ${context.projectName} - Cursor AI Rules
24256
+
24257
+ ## Code Style (Ultracite)
24258
+ - Follow Biome configuration in biome.jsonc
24259
+ - Use single quotes for JavaScript/TypeScript
24260
+ - Use double quotes for JSX attributes
24261
+ - Always use semicolons
24262
+ - 2-space indentation
24263
+ - 100 character line width
24264
+ - ES5 trailing commas
24265
+
24266
+ ## TypeScript
24267
+ - Strict mode: ${context.tsStrictness === "strict" ? "enabled" : context.tsStrictness === "moderate" ? "moderate" : "disabled"}
24268
+ - Always define types explicitly for function parameters and return values
24269
+ - Use \`type\` for object types, \`interface\` for contracts
24270
+ - Prefer \`const\` assertions for literal types
24271
+
24272
+ ## React/Next.js Best Practices
24273
+ - Use Server Components by default (Next.js 16)
24274
+ - Add 'use client' only when needed (hooks, browser APIs, interactivity)
24275
+ - Always await \`params\` and \`searchParams\` in Next.js 16
24276
+ - Use descriptive variable names (no \`c\`, \`ctx\`, \`req\`, \`res\`)
24277
+
24278
+ ${context.cssFramework === "tailwind" ? `## Tailwind CSS
24279
+ - Use utility classes for styling
24280
+ - Follow mobile-first responsive design
24281
+ - Use OKLCH colors from theme when available
24282
+ - Avoid arbitrary values unless necessary` : ""}
24283
+
24284
+ ${context.database && context.database !== "none" ? `## Database (${context.database})
24285
+ - Always use Drizzle ORM for type-safe queries
24286
+ - Define schema in separate files by domain
24287
+ - Use transactions for multi-step operations
24288
+ - Always handle database errors gracefully` : ""}
24289
+
24290
+ ## File Naming
24291
+ - Pages: \`page.tsx\`, \`layout.tsx\`
24292
+ - Components: PascalCase (\`UserCard.tsx\`)
24293
+ - Utilities: camelCase (\`formatDate.ts\`)
24294
+ - Types: \`*.types.ts\` or in \`types/\` directory
24295
+
24296
+ ## Testing
24297
+ - Write tests for critical business logic
24298
+ - Use Bun's built-in test runner
24299
+ - Follow AAA pattern: Arrange, Act, Assert
24300
+ - Mock external dependencies
24301
+
24302
+ ## AI Code Generation Guidelines
24303
+ - Always generate complete, working code
24304
+ - Include error handling
24305
+ - Add TypeScript types
24306
+ - Write self-documenting code with clear names
24307
+ - Add comments only for complex logic
24308
+ - Follow the project's existing patterns
24309
+ `;
24310
+ await writeFile(join(projectPath, ".cursorrules"), cursorRules);
24311
+ const windsurfRules = `# ${context.projectName} - Windsurf AI Rules
24312
+
24313
+ This project uses **Ultracite** for code quality - an AI-optimized Biome preset.
24314
+
24315
+ ## Code Quality Rules
24316
+ - Lint: \`bun run lint\`
24317
+ - Format: \`bun run format\`
24318
+ - Config: See \`biome.jsonc\`
24319
+
24320
+ ## Key Conventions
24321
+ 1. **TypeScript**: ${context.tsStrictness} mode
24322
+ 2. **Quotes**: Single quotes (JS/TS), double quotes (JSX)
24323
+ 3. **Semicolons**: Always required
24324
+ 4. **Indentation**: 2 spaces
24325
+ 5. **Line width**: 100 characters
24326
+
24327
+ ## Framework-Specific
24328
+ ${context.cssFramework === "tailwind" ? `- **Tailwind CSS**: Utility-first styling
24329
+ ` : ""}${context.database && context.database !== "none" ? `- **Database**: ${context.database} with Drizzle ORM
24330
+ ` : ""}- **Testing**: ${context.testing}
24331
+
24332
+ ## Variable Naming (CRITICAL)
24333
+ \u274C NEVER use: \`c\`, \`ctx\`, \`e\`, \`req\`, \`res\`, \`data\`, \`temp\`
24334
+ \u2705 ALWAYS use: \`context\`, \`error\`, \`request\`, \`response\`, \`userData\`, \`temporaryBuffer\`
24335
+
24336
+ ## React/Next.js 16
24337
+ - Server Components by default
24338
+ - \`'use client'\` only when necessary
24339
+ - Always \`await params\` and \`await searchParams\`
24340
+
24341
+ Sync with \`.cursorrules\` for full AI guidelines.
24342
+ `;
24343
+ await writeFile(join(projectPath, ".windsurfrules"), windsurfRules);
24344
+ const claudeMd = `# ${context.projectName}
24345
+
24346
+ AI-assisted development with Ultracite code quality.
24347
+
24348
+ ## Quick Start
24349
+
24350
+ \`\`\`bash
24351
+ bun install
24352
+ bun dev
24353
+ \`\`\`
24354
+
24355
+ ## Code Quality
24356
+
24357
+ This project uses **Ultracite** - an AI-optimized Biome preset that keeps code consistent across:
24358
+ - Cursor
24359
+ - Windsurf
24360
+ - Claude Code
24361
+ - Zed
24362
+
24363
+ ### Commands
24364
+
24365
+ \`\`\`bash
24366
+ bun run lint # Check code quality
24367
+ bun run format # Auto-fix issues
24368
+ \`\`\`
24369
+
24370
+ ## AI Development Guidelines
24371
+
24372
+ See \`.cursorrules\` and \`.windsurfrules\` for comprehensive AI coding guidelines.
24373
+
24374
+ ### Critical Rules
24375
+
24376
+ 1. **Descriptive Names**: No \`c\`, \`ctx\`, \`e\` - use \`context\`, \`error\`, etc.
24377
+ 2. **TypeScript**: ${context.tsStrictness} mode
24378
+ 3. **Server Components**: Default in Next.js 16
24379
+ 4. **Error Handling**: Always handle errors gracefully
24380
+
24381
+ ## Tech Stack
24382
+
24383
+ - **Runtime**: Bun
24384
+ - **Framework**: ${context.preset === "web" || context.preset === "full" ? "Next.js 16 + React 19" : context.preset === "api" ? "Hono" : "Minimal"}
24385
+ ${context.cssFramework === "tailwind" ? `- **Styling**: Tailwind CSS 4
24386
+ ` : ""}${context.database && context.database !== "none" ? `- **Database**: ${context.database}
24387
+ ` : ""}${context.uiLibrary === "shadcn" ? `- **UI**: shadcn/ui
24388
+ ` : ""}- **Code Quality**: Ultracite (Biome)
24389
+ - **Testing**: ${context.testing}
24390
+ `;
24391
+ await writeFile(join(projectPath, "CLAUDE.md"), claudeMd);
24392
+ }
24393
+ async function setupBiome(projectPath, context) {
24394
+ const biomeConfig = {
24395
+ $schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
24396
+ vcs: {
24397
+ enabled: true,
24398
+ clientKind: "git",
24399
+ useIgnoreFile: true
24400
+ },
24401
+ files: {
24402
+ ignore: [
24403
+ "node_modules",
24404
+ "dist",
24405
+ "build",
24406
+ ".next",
24407
+ ".turbo",
24408
+ "coverage"
24409
+ ]
24410
+ },
24411
+ formatter: {
24412
+ enabled: true,
24413
+ indentStyle: "space",
24414
+ indentWidth: 2,
24415
+ lineWidth: 100
24416
+ },
24417
+ linter: {
24418
+ enabled: true,
24419
+ rules: {
24420
+ recommended: true
24421
+ }
24422
+ },
24423
+ javascript: {
24424
+ formatter: {
24425
+ quoteStyle: "single",
24426
+ trailingCommas: "es5",
24427
+ semicolons: "always"
24428
+ }
24429
+ }
24430
+ };
24431
+ await writeFile(join(projectPath, "biome.json"), JSON.stringify(biomeConfig, null, 2));
24432
+ }
24433
+ function getCodeQualityDependencies(codeQuality) {
24434
+ if (codeQuality === "ultracite") {
24435
+ return {
24436
+ ultracite: "catalog:",
24437
+ "@biomejs/biome": "catalog:"
24438
+ };
24439
+ }
24440
+ return {
24441
+ "@biomejs/biome": "catalog:"
24442
+ };
24443
+ }
24444
+
24445
+ // ../templates/src/generators/docker.ts
24446
+ async function setupDocker(projectPath, context) {
24447
+ const dockerfile = `# Use Bun official image
24448
+ FROM oven/bun:1 AS base
24449
+
24450
+ # Install dependencies
24451
+ FROM base AS deps
24452
+ WORKDIR /app
24453
+ COPY package.json bun.lock* ./
24454
+ RUN bun install --frozen-lockfile
24455
+
24456
+ # Build application
24457
+ FROM base AS builder
24458
+ WORKDIR /app
24459
+ COPY --from=deps /app/node_modules ./node_modules
24460
+ COPY . .
24461
+
24462
+ ${context.preset === "web" || context.preset === "full" ? `# Build Next.js app
24463
+ RUN bun run build
24464
+ ` : "# No build step needed for API/minimal"}
24465
+ # Production image
24466
+ FROM base AS runner
24467
+ WORKDIR /app
24468
+
24469
+ # Create non-root user
24470
+ RUN addgroup --system --gid 1001 bunuser && \\
24471
+ adduser --system --uid 1001 bunuser
24472
+
24473
+ ${context.preset === "web" || context.preset === "full" ? `# Copy Next.js build
24474
+ COPY --from=builder --chown=bunuser:bunuser /app/.next/standalone ./
24475
+ COPY --from=builder --chown=bunuser:bunuser /app/.next/static ./.next/static
24476
+ COPY --from=builder --chown=bunuser:bunuser /app/public ./public
24477
+ ` : `# Copy application
24478
+ COPY --from=builder --chown=bunuser:bunuser /app/src ./src
24479
+ COPY --from=builder --chown=bunuser:bunuser /app/package.json ./package.json
24480
+ `}
24481
+ USER bunuser
24482
+
24483
+ EXPOSE 3000
24484
+
24485
+ ENV PORT=3000
24486
+ ENV NODE_ENV=production
24487
+
24488
+ ${context.preset === "web" || context.preset === "full" ? 'CMD ["bun", "run", "start"]' : 'CMD ["bun", "run", "src/index.ts"]'}
24489
+ `;
24490
+ await writeFile(join(projectPath, "Dockerfile"), dockerfile);
24491
+ const dockerCompose = `version: '3.8'
24492
+
24493
+ services:
24494
+ app:
24495
+ build:
24496
+ context: .
24497
+ dockerfile: Dockerfile
24498
+ ports:
24499
+ - "3000:3000"
24500
+ environment:
24501
+ - NODE_ENV=production
24502
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? "- DATABASE_URL=${DATABASE_URL:-postgres://postgres:postgres@db:5432/${context.projectName}}" : ""}
24503
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `depends_on:
24504
+ - db` : ""}
24505
+ restart: unless-stopped
24506
+
24507
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `db:
24508
+ image: ${context.database === "sqlite-drizzle" ? "alpine:latest" : "postgres:16-alpine"}
24509
+ ${context.database !== "sqlite-drizzle" ? `environment:
24510
+ - POSTGRES_USER=postgres
24511
+ - POSTGRES_PASSWORD=postgres
24512
+ - POSTGRES_DB=${context.projectName}
24513
+ volumes:
24514
+ - postgres_data:/var/lib/postgresql/data
24515
+ ports:
24516
+ - "5432:5432"` : `volumes:
24517
+ - sqlite_data:/data`}
24518
+ restart: unless-stopped
24519
+ ` : ""}
24520
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `volumes:
24521
+ ${context.database === "sqlite-drizzle" ? "sqlite_data:" : "postgres_data:"}` : ""}
24522
+ `;
24523
+ await writeFile(join(projectPath, "docker-compose.yml"), dockerCompose);
24524
+ const dockerignore = `# Dependencies
24525
+ node_modules/
24526
+ bun.lock
24527
+
24528
+ # Build
24529
+ dist/
24530
+ build/
24531
+ .next/
24532
+ .turbo/
24533
+
24534
+ # Environment
24535
+ .env
24536
+ .env*.local
24537
+
24538
+ # Logs
24539
+ *.log
24540
+ npm-debug.log*
24541
+
24542
+ # OS
24543
+ .DS_Store
24544
+
24545
+ # IDEs
24546
+ .vscode/
24547
+ .idea/
24548
+
24549
+ # Git
24550
+ .git/
24551
+ .gitignore
24552
+
24553
+ # Documentation
24554
+ README.md
24555
+ CLAUDE.md
24556
+ .cursorrules
24557
+ .windsurfrules
24558
+
24559
+ # Tests
24560
+ *.test.ts
24561
+ *.test.tsx
24562
+ *.spec.ts
24563
+ *.spec.tsx
24564
+ coverage/
24565
+ `;
24566
+ await writeFile(join(projectPath, ".dockerignore"), dockerignore);
24567
+ }
24568
+
24569
+ // ../templates/src/generators/cicd.ts
24570
+ async function setupGitHubActions(projectPath, context) {
24571
+ await ensureDirectory(join(projectPath, ".github/workflows"));
24572
+ const ciWorkflow = `name: CI
24573
+
24574
+ on:
24575
+ push:
24576
+ branches: [main, develop]
24577
+ pull_request:
24578
+ branches: [main, develop]
24579
+
24580
+ jobs:
24581
+ lint:
24582
+ name: Lint & Format Check
24583
+ runs-on: ubuntu-latest
24584
+
24585
+ steps:
24586
+ - name: Checkout code
24587
+ uses: actions/checkout@v4
24588
+
24589
+ - name: Setup Bun
24590
+ uses: oven-sh/setup-bun@v2
24591
+ with:
24592
+ bun-version: latest
24593
+
24594
+ - name: Install dependencies
24595
+ run: bun install --frozen-lockfile
24596
+
24597
+ - name: Run linter
24598
+ run: bun run lint
24599
+
24600
+ typecheck:
24601
+ name: Type Check
24602
+ runs-on: ubuntu-latest
24603
+
24604
+ steps:
24605
+ - name: Checkout code
24606
+ uses: actions/checkout@v4
24607
+
24608
+ - name: Setup Bun
24609
+ uses: oven-sh/setup-bun@v2
24610
+ with:
24611
+ bun-version: latest
24612
+
24613
+ - name: Install dependencies
24614
+ run: bun install --frozen-lockfile
24615
+
24616
+ - name: Type check
24617
+ run: ${context.preset === "full" ? "bun run --filter '*' typecheck" : "bun run typecheck"}
24618
+
24619
+ ${context.testing !== "none" ? `test:
24620
+ name: Run Tests
24621
+ runs-on: ubuntu-latest
24622
+
24623
+ steps:
24624
+ - name: Checkout code
24625
+ uses: actions/checkout@v4
24626
+
24627
+ - name: Setup Bun
24628
+ uses: oven-sh/setup-bun@v2
24629
+ with:
24630
+ bun-version: latest
24631
+
24632
+ - name: Install dependencies
24633
+ run: bun install --frozen-lockfile
24634
+
24635
+ - name: Run tests
24636
+ run: ${context.testing === "bun-test" ? "bun test" : "bun run test"}
24637
+ ${context.database && context.database !== "none" ? `env:
24638
+ DATABASE_URL: sqlite://test.db` : ""}
24639
+ ` : ""}
24640
+ build:
24641
+ name: Build
24642
+ runs-on: ubuntu-latest
24643
+ needs: [lint, typecheck${context.testing !== "none" ? ", test" : ""}]
24644
+
24645
+ steps:
24646
+ - name: Checkout code
24647
+ uses: actions/checkout@v4
24648
+
24649
+ - name: Setup Bun
24650
+ uses: oven-sh/setup-bun@v2
24651
+ with:
24652
+ bun-version: latest
24653
+
24654
+ - name: Install dependencies
24655
+ run: bun install --frozen-lockfile
24656
+
24657
+ ${context.preset === "web" || context.preset === "full" ? `- name: Build application
24658
+ run: bun run build
24659
+ env:
24660
+ ${context.database === "supabase" ? `NEXT_PUBLIC_SUPABASE_URL: \${{ secrets.SUPABASE_URL }}
24661
+ NEXT_PUBLIC_SUPABASE_ANON_KEY: \${{ secrets.SUPABASE_ANON_KEY }}
24662
+ ` : ""}NODE_ENV: production` : ""}
24663
+
24664
+ ${context.docker ? `- name: Set up Docker Buildx
24665
+ uses: docker/setup-buildx-action@v3
24666
+
24667
+ - name: Build Docker image
24668
+ uses: docker/build-push-action@v5
24669
+ with:
24670
+ context: .
24671
+ push: false
24672
+ tags: ${context.projectName}:latest
24673
+ cache-from: type=gha
24674
+ cache-to: type=gha,mode=max` : ""}
24675
+ `;
24676
+ await writeFile(join(projectPath, ".github/workflows/ci.yml"), ciWorkflow);
24677
+ const deployWorkflow = `# name: Deploy
24678
+ #
24679
+ # on:
24680
+ # push:
24681
+ # branches: [main]
24682
+ #
24683
+ # jobs:
24684
+ # deploy:
24685
+ # name: Deploy to Production
24686
+ # runs-on: ubuntu-latest
24687
+ # environment: production
24688
+ #
24689
+ # steps:
24690
+ # - name: Checkout code
24691
+ # uses: actions/checkout@v4
24692
+ #
24693
+ # - name: Setup Bun
24694
+ # uses: oven-sh/setup-bun@v2
24695
+ # with:
24696
+ # bun-version: latest
24697
+ #
24698
+ # - name: Install dependencies
24699
+ # run: bun install --frozen-lockfile
24700
+ #
24701
+ # - name: Build
24702
+ # run: bun run build
24703
+ # env:
24704
+ # NODE_ENV: production
24705
+ # ${context.database === "supabase" ? `NEXT_PUBLIC_SUPABASE_URL: \${{ secrets.SUPABASE_URL }}
24706
+ # NEXT_PUBLIC_SUPABASE_ANON_KEY: \${{ secrets.SUPABASE_ANON_KEY }}` : ""}
24707
+ #
24708
+ # # Add your deployment steps here
24709
+ # # Examples:
24710
+ # # - Deploy to Vercel
24711
+ # # - Deploy to Railway
24712
+ # # - Deploy to your own server via SSH
24713
+ # # - Push Docker image to registry
24714
+ `;
24715
+ await writeFile(join(projectPath, ".github/workflows/deploy.yml.example"), deployWorkflow);
24716
+ const dependabotConfig = `version: 2
24717
+ updates:
24718
+ - package-ecosystem: "npm"
24719
+ directory: "/"
24720
+ schedule:
24721
+ interval: "weekly"
24722
+ open-pull-requests-limit: 10
24723
+ reviewers:
24724
+ - "your-github-username"
24725
+ labels:
24726
+ - "dependencies"
24727
+
24728
+ ${context.docker ? `- package-ecosystem: "docker"
24729
+ directory: "/"
24730
+ schedule:
24731
+ interval: "weekly"
24732
+ labels:
24733
+ - "dependencies"
24734
+ - "docker"` : ""}
24735
+
24736
+ - package-ecosystem: "github-actions"
24737
+ directory: "/"
24738
+ schedule:
24739
+ interval: "weekly"
24740
+ labels:
24741
+ - "dependencies"
24742
+ - "ci"
24743
+ `;
24744
+ await writeFile(join(projectPath, ".github/dependabot.yml"), dependabotConfig);
24745
+ }
24746
+
24164
24747
  // ../templates/src/builders/web.ts
24165
24748
  async function buildWebPreset(projectPath, context) {
24166
24749
  await ensureDirectory(join(projectPath, "src/app"));
@@ -24234,51 +24817,238 @@ const config: Config = {
24234
24817
  export default config;
24235
24818
  `;
24236
24819
  await writeFile(join(projectPath, "tailwind.config.ts"), tailwindConfigContent);
24237
- const tsconfigContent = {
24238
- compilerOptions: {
24820
+ const getTsCompilerOptions = () => {
24821
+ const baseOptions = {
24239
24822
  target: "ES2017",
24240
24823
  lib: ["dom", "dom.iterable", "esnext"],
24241
24824
  allowJs: true,
24242
24825
  skipLibCheck: true,
24243
- strict: true,
24244
24826
  noEmit: true,
24245
24827
  esModuleInterop: true,
24246
24828
  module: "esnext",
24247
24829
  moduleResolution: "bundler",
24248
24830
  resolveJsonModule: true,
24249
24831
  isolatedModules: true,
24250
- jsx: "preserve",
24832
+ jsx: "react-jsx",
24251
24833
  incremental: true,
24252
24834
  plugins: [{ name: "next" }],
24253
- paths: {
24254
- "@/*": ["./src/*"]
24255
- }
24256
- },
24257
- include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
24835
+ paths: context.pathAliases ? { "@/*": ["./src/*"] } : undefined
24836
+ };
24837
+ if (context.tsStrictness === "strict") {
24838
+ return {
24839
+ ...baseOptions,
24840
+ strict: true,
24841
+ noUnusedLocals: true,
24842
+ noUnusedParameters: true,
24843
+ noFallthroughCasesInSwitch: true,
24844
+ noImplicitReturns: true
24845
+ };
24846
+ }
24847
+ if (context.tsStrictness === "moderate") {
24848
+ return {
24849
+ ...baseOptions,
24850
+ strict: true,
24851
+ noUnusedLocals: false,
24852
+ noUnusedParameters: false
24853
+ };
24854
+ }
24855
+ return {
24856
+ ...baseOptions,
24857
+ strict: false,
24858
+ noImplicitAny: false
24859
+ };
24860
+ };
24861
+ const tsconfigContent = {
24862
+ compilerOptions: getTsCompilerOptions(),
24863
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/dev/types/**/*.ts"],
24258
24864
  exclude: ["node_modules"]
24259
24865
  };
24260
24866
  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));
24867
+ if (context.codeQuality === "ultracite") {
24868
+ await setupUltracite(projectPath, context);
24869
+ } else {
24870
+ await setupBiome(projectPath, context);
24871
+ }
24872
+ if (context.docker) {
24873
+ await setupDocker(projectPath, context);
24874
+ }
24875
+ if (context.cicd) {
24876
+ await setupGitHubActions(projectPath, context);
24877
+ }
24878
+ }
24879
+ // ../templates/src/generators/database.ts
24880
+ async function setupPostgresDrizzle(projectPath, context, isMonorepo = false) {
24881
+ const dbPath = isMonorepo ? join(projectPath, "packages/db") : join(projectPath, "src/db");
24882
+ await ensureDirectory(join(dbPath, "schema"));
24883
+ const drizzleConfig = `import { defineConfig } from 'drizzle-kit';
24884
+
24885
+ export default defineConfig({
24886
+ schema: './src/schema/index.ts',
24887
+ out: './drizzle',
24888
+ dialect: 'postgresql',
24889
+ dbCredentials: {
24890
+ url: process.env.DATABASE_URL!,
24891
+ },
24892
+ });
24893
+ `;
24894
+ await writeFile(join(isMonorepo ? join(projectPath, "packages/db") : projectPath, "drizzle.config.ts"), drizzleConfig);
24895
+ const clientContent = `import { drizzle } from 'drizzle-orm/bun-postgres';
24896
+ import { Database } from 'bun:postgres';
24897
+ import * as schema from './schema';
24898
+
24899
+ const client = new Database(process.env.DATABASE_URL!);
24900
+ export const db = drizzle(client, { schema });
24901
+ `;
24902
+ await writeFile(join(dbPath, "index.ts"), clientContent);
24903
+ const schemaContent = `import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
24904
+
24905
+ export const users = pgTable('users', {
24906
+ id: uuid('id').primaryKey().defaultRandom(),
24907
+ email: text('email').notNull().unique(),
24908
+ name: text('name'),
24909
+ createdAt: timestamp('created_at').defaultNow().notNull(),
24910
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
24911
+ });
24912
+ `;
24913
+ await writeFile(join(dbPath, "schema/index.ts"), schemaContent);
24914
+ const envExample = `# Database
24915
+ DATABASE_URL=postgresql://postgres:postgres@localhost:5432/${context.projectName}
24916
+ `;
24917
+ await writeFile(join(projectPath, ".env.example"), envExample);
24918
+ }
24919
+ async function setupSupabase(projectPath, context, isMonorepo = false) {
24920
+ const dbPath = isMonorepo ? join(projectPath, "packages/db") : join(projectPath, "src/db");
24921
+ await ensureDirectory(join(dbPath, "schema"));
24922
+ const drizzleConfig = `import { defineConfig } from 'drizzle-kit';
24923
+
24924
+ export default defineConfig({
24925
+ schema: './src/schema/index.ts',
24926
+ out: './supabase/migrations',
24927
+ dialect: 'postgresql',
24928
+ dbCredentials: {
24929
+ url: process.env.DATABASE_URL!,
24930
+ },
24931
+ });
24932
+ `;
24933
+ await writeFile(join(isMonorepo ? join(projectPath, "packages/db") : projectPath, "drizzle.config.ts"), drizzleConfig);
24934
+ const clientContent = `import { createClient } from '@supabase/supabase-js';
24935
+ import { drizzle } from 'drizzle-orm/postgres-js';
24936
+ import postgres from 'postgres';
24937
+ import * as schema from './schema';
24938
+
24939
+ // Supabase client for auth, storage, realtime
24940
+ export const supabase = createClient(
24941
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
24942
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
24943
+ );
24944
+
24945
+ // Drizzle client for type-safe database queries
24946
+ const queryClient = postgres(process.env.DATABASE_URL!);
24947
+ export const db = drizzle(queryClient, { schema });
24948
+ `;
24949
+ await writeFile(join(dbPath, "index.ts"), clientContent);
24950
+ const schemaContent = `import { pgTable, text, timestamp, uuid, boolean } from 'drizzle-orm/pg-core';
24951
+
24952
+ export const users = pgTable('users', {
24953
+ id: uuid('id').primaryKey(),
24954
+ email: text('email').notNull().unique(),
24955
+ name: text('name'),
24956
+ avatarUrl: text('avatar_url'),
24957
+ createdAt: timestamp('created_at').defaultNow().notNull(),
24958
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
24959
+ });
24960
+
24961
+ export const profiles = pgTable('profiles', {
24962
+ id: uuid('id').primaryKey().references(() => users.id),
24963
+ username: text('username').unique(),
24964
+ bio: text('bio'),
24965
+ website: text('website'),
24966
+ isPublic: boolean('is_public').default(true),
24967
+ createdAt: timestamp('created_at').defaultNow().notNull(),
24968
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
24969
+ });
24970
+ `;
24971
+ await writeFile(join(dbPath, "schema/index.ts"), schemaContent);
24972
+ await ensureDirectory(join(projectPath, "supabase/migrations"));
24973
+ const envExample = `# Supabase
24974
+ NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
24975
+ NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
24976
+ DATABASE_URL=postgresql://postgres:[password]@db.your-project.supabase.co:5432/postgres
24977
+ `;
24978
+ await writeFile(join(projectPath, ".env.example"), envExample);
24979
+ }
24980
+ async function setupSQLiteDrizzle(projectPath, context, isMonorepo = false) {
24981
+ const dbPath = isMonorepo ? join(projectPath, "packages/db") : join(projectPath, "src/db");
24982
+ await ensureDirectory(join(dbPath, "schema"));
24983
+ const drizzleConfig = `import { defineConfig } from 'drizzle-kit';
24984
+
24985
+ export default defineConfig({
24986
+ schema: './src/schema/index.ts',
24987
+ out: './drizzle',
24988
+ dialect: 'sqlite',
24989
+ dbCredentials: {
24990
+ url: process.env.DATABASE_URL || './local.db',
24991
+ },
24992
+ });
24993
+ `;
24994
+ await writeFile(join(isMonorepo ? join(projectPath, "packages/db") : projectPath, "drizzle.config.ts"), drizzleConfig);
24995
+ const clientContent = `import { drizzle } from 'drizzle-orm/bun-sqlite';
24996
+ import { Database } from 'bun:sqlite';
24997
+ import * as schema from './schema';
24998
+
24999
+ const sqlite = new Database(process.env.DATABASE_URL || './local.db');
25000
+ export const db = drizzle(sqlite, { schema });
25001
+ `;
25002
+ await writeFile(join(dbPath, "index.ts"), clientContent);
25003
+ const schemaContent = `import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
25004
+ import { sql } from 'drizzle-orm';
25005
+
25006
+ export const users = sqliteTable('users', {
25007
+ id: integer('id').primaryKey({ autoIncrement: true }),
25008
+ email: text('email').notNull().unique(),
25009
+ name: text('name'),
25010
+ createdAt: integer('created_at', { mode: 'timestamp' }).default(sql\`(unixepoch())\`).notNull(),
25011
+ updatedAt: integer('updated_at', { mode: 'timestamp' }).default(sql\`(unixepoch())\`).notNull(),
25012
+ });
25013
+ `;
25014
+ await writeFile(join(dbPath, "schema/index.ts"), schemaContent);
25015
+ const envExample = `# Database
25016
+ DATABASE_URL=./local.db
25017
+ `;
25018
+ await writeFile(join(projectPath, ".env.example"), envExample);
25019
+ const gitignoreAddition = `
25020
+ # SQLite
25021
+ *.db
25022
+ *.db-shm
25023
+ *.db-wal
25024
+ `;
25025
+ await writeFile(join(projectPath, ".gitignore.db"), gitignoreAddition);
24281
25026
  }
25027
+ function getDatabaseDependencies(databaseType) {
25028
+ switch (databaseType) {
25029
+ case "postgres-drizzle":
25030
+ return {
25031
+ "drizzle-orm": "catalog:",
25032
+ "drizzle-kit": "catalog:",
25033
+ postgres: "catalog:"
25034
+ };
25035
+ case "supabase":
25036
+ return {
25037
+ "@supabase/supabase-js": "catalog:",
25038
+ "drizzle-orm": "catalog:",
25039
+ "drizzle-kit": "catalog:",
25040
+ postgres: "catalog:"
25041
+ };
25042
+ case "sqlite-drizzle":
25043
+ return {
25044
+ "drizzle-orm": "catalog:",
25045
+ "drizzle-kit": "catalog:"
25046
+ };
25047
+ default:
25048
+ return {};
25049
+ }
25050
+ }
25051
+
24282
25052
  // ../templates/src/builders/api.ts
24283
25053
  async function buildApiPreset(projectPath, context) {
24284
25054
  await ensureDirectory(join(projectPath, "src/routes"));
@@ -24287,6 +25057,9 @@ async function buildApiPreset(projectPath, context) {
24287
25057
  import { serve } from 'bun';
24288
25058
  import { logger } from 'hono/logger';
24289
25059
  import { cors } from 'hono/cors';
25060
+ ${context.database && context.database !== "none" ? `import { db } from './db';
25061
+ import { users } from './db/schema';
25062
+ import { eq } from 'drizzle-orm';` : ""}
24290
25063
 
24291
25064
  const app = new Hono();
24292
25065
 
@@ -24299,6 +25072,7 @@ app.get('/', (context) => {
24299
25072
  return context.json({
24300
25073
  message: 'Welcome to ${context.projectName} API \uD83C\uDF5E',
24301
25074
  version: '1.0.0',
25075
+ database: '${context.database || "none"}',
24302
25076
  });
24303
25077
  });
24304
25078
 
@@ -24309,6 +25083,44 @@ app.get('/health', (context) => {
24309
25083
  });
24310
25084
  });
24311
25085
 
25086
+ ${context.database && context.database !== "none" ? `// Database example routes
25087
+ app.get('/users', async (context) => {
25088
+ try {
25089
+ const allUsers = await db.select().from(users);
25090
+ return context.json({ users: allUsers });
25091
+ } catch (error) {
25092
+ console.error('Database error:', error);
25093
+ return context.json({ error: 'Failed to fetch users' }, 500);
25094
+ }
25095
+ });
25096
+
25097
+ app.get('/users/:id', async (context) => {
25098
+ try {
25099
+ const id = context.req.param('id');
25100
+ const user = await db.select().from(users).where(eq(users.id, id)).limit(1);
25101
+
25102
+ if (!user.length) {
25103
+ return context.json({ error: 'User not found' }, 404);
25104
+ }
25105
+
25106
+ return context.json({ user: user[0] });
25107
+ } catch (error) {
25108
+ console.error('Database error:', error);
25109
+ return context.json({ error: 'Failed to fetch user' }, 500);
25110
+ }
25111
+ });
25112
+
25113
+ app.post('/users', async (context) => {
25114
+ try {
25115
+ const body = await context.req.json();
25116
+ const newUser = await db.insert(users).values(body).returning();
25117
+ return context.json({ user: newUser[0] }, 201);
25118
+ } catch (error) {
25119
+ console.error('Database error:', error);
25120
+ return context.json({ error: 'Failed to create user' }, 500);
25121
+ }
25122
+ });
25123
+ ` : ""}
24312
25124
  // 404 handler
24313
25125
  app.notFound((context) => {
24314
25126
  return context.json({ error: 'Not found' }, 404);
@@ -24330,7 +25142,8 @@ serve({
24330
25142
  },
24331
25143
  });
24332
25144
 
24333
- console.log('\uD83D\uDE80 API running on http://localhost:3001');
25145
+ console.log('\uD83D\uDE80 ${context.projectName} API running on http://localhost:3001');
25146
+ ${context.database && context.database !== "none" ? "console.log('\uD83D\uDCCA Database:', process.env.DATABASE_URL || 'Not configured');" : ""}
24334
25147
  `;
24335
25148
  await writeFile(join(projectPath, "src/index.ts"), indexContent);
24336
25149
  const usersRouteContent = `import { Hono } from 'hono';
@@ -24354,8 +25167,8 @@ users.get('/:id', (context) => {
24354
25167
  export default users;
24355
25168
  `;
24356
25169
  await writeFile(join(projectPath, "src/routes/users.ts"), usersRouteContent);
24357
- const tsconfigContent = {
24358
- compilerOptions: {
25170
+ const getTsCompilerOptions = () => {
25171
+ const baseOptions = {
24359
25172
  lib: ["ESNext"],
24360
25173
  target: "ESNext",
24361
25174
  module: "ESNext",
@@ -24365,20 +25178,63 @@ export default users;
24365
25178
  allowImportingTsExtensions: true,
24366
25179
  verbatimModuleSyntax: true,
24367
25180
  noEmit: true,
24368
- strict: true,
24369
25181
  skipLibCheck: true,
24370
- noFallthroughCasesInSwitch: true,
24371
- types: ["bun-types"]
25182
+ types: ["bun"],
25183
+ paths: context.pathAliases ? { "@/*": ["./src/*"] } : undefined
25184
+ };
25185
+ if (context.tsStrictness === "strict") {
25186
+ return {
25187
+ ...baseOptions,
25188
+ strict: true,
25189
+ noUnusedLocals: true,
25190
+ noUnusedParameters: true,
25191
+ noFallthroughCasesInSwitch: true,
25192
+ noImplicitReturns: true
25193
+ };
25194
+ }
25195
+ if (context.tsStrictness === "moderate") {
25196
+ return {
25197
+ ...baseOptions,
25198
+ strict: true,
25199
+ noFallthroughCasesInSwitch: true
25200
+ };
24372
25201
  }
25202
+ return {
25203
+ ...baseOptions,
25204
+ strict: false
25205
+ };
25206
+ };
25207
+ const tsconfigContent = {
25208
+ compilerOptions: getTsCompilerOptions()
24373
25209
  };
24374
25210
  await writeFile(join(projectPath, "tsconfig.json"), JSON.stringify(tsconfigContent, null, 2));
24375
25211
  const bunfigContent = `[install]
24376
25212
  frozenLockfile = false
24377
25213
 
24378
- [test]
25214
+ ${context.testing !== "none" ? `[test]
24379
25215
  coverage = true
24380
- `;
25216
+ ` : ""}`;
24381
25217
  await writeFile(join(projectPath, "bunfig.toml"), bunfigContent);
25218
+ if (context.database && context.database !== "none") {
25219
+ if (context.database === "postgres-drizzle") {
25220
+ await setupPostgresDrizzle(projectPath, context, false);
25221
+ } else if (context.database === "supabase") {
25222
+ await setupSupabase(projectPath, context, false);
25223
+ } else if (context.database === "sqlite-drizzle") {
25224
+ await setupSQLiteDrizzle(projectPath, context, false);
25225
+ }
25226
+ }
25227
+ if (context.codeQuality === "ultracite") {
25228
+ await setupUltracite(projectPath, context);
25229
+ } else {
25230
+ await setupBiome(projectPath, context);
25231
+ }
25232
+ if (context.docker) {
25233
+ await setupDocker(projectPath, context);
25234
+ }
25235
+ if (context.cicd) {
25236
+ await setupGitHubActions(projectPath, context);
25237
+ }
24382
25238
  }
24383
25239
  // ../templates/src/builders/full.ts
24384
25240
  async function buildFullPreset(projectPath, context) {
@@ -24387,6 +25243,9 @@ async function buildFullPreset(projectPath, context) {
24387
25243
  await ensureDirectory(join(projectPath, "apps/api"));
24388
25244
  await ensureDirectory(join(projectPath, "packages/types"));
24389
25245
  await ensureDirectory(join(projectPath, "packages/utils"));
25246
+ if (context.database && context.database !== "none") {
25247
+ await ensureDirectory(join(projectPath, "packages/db"));
25248
+ }
24390
25249
  const rootPackageJson = {
24391
25250
  name: `${context.packageName}-monorepo`,
24392
25251
  version: "0.0.0",
@@ -24400,15 +25259,21 @@ async function buildFullPreset(projectPath, context) {
24400
25259
  test: "bun test"
24401
25260
  },
24402
25261
  devDependencies: {
24403
- "@biomejs/biome": "^2.3.0",
25262
+ "@biomejs/biome": "catalog:",
24404
25263
  "@types/bun": "latest",
24405
- typescript: "^5.7.2"
25264
+ typescript: "catalog:"
24406
25265
  },
24407
25266
  catalog: {
24408
25267
  react: "^19.1.0",
24409
25268
  "react-dom": "^19.1.0",
24410
25269
  next: "^16.0.0",
24411
- hono: "^4.7.12"
25270
+ hono: "^4.7.12",
25271
+ "@biomejs/biome": "^2.3.0",
25272
+ typescript: "^5.9.3",
25273
+ "@types/react": "^19.1.0",
25274
+ "@types/react-dom": "^19.1.0",
25275
+ "@types/node": "^22.10.6",
25276
+ tailwindcss: "^4.1.7"
24412
25277
  }
24413
25278
  };
24414
25279
  await writeFile(join(projectPath, "package.json"), JSON.stringify(rootPackageJson, null, 2));
@@ -24435,11 +25300,11 @@ coverage = true
24435
25300
  [`@${context.packageName}/types`]: "workspace:*"
24436
25301
  },
24437
25302
  devDependencies: {
24438
- "@types/react": "^19.1.0",
24439
- "@types/react-dom": "^19.1.0",
24440
- "@types/node": "^22.10.6",
24441
- typescript: "^5.7.2",
24442
- tailwindcss: "^4.1.5"
25303
+ "@types/react": "catalog:",
25304
+ "@types/react-dom": "catalog:",
25305
+ "@types/node": "catalog:",
25306
+ typescript: "catalog:",
25307
+ tailwindcss: "catalog:"
24443
25308
  }
24444
25309
  };
24445
25310
  await writeFile(join(projectPath, "apps/web/package.json"), JSON.stringify(webPackageJson, null, 2));
@@ -24459,11 +25324,11 @@ coverage = true
24459
25324
  [`@${context.packageName}/types`]: "workspace:*"
24460
25325
  },
24461
25326
  devDependencies: {
24462
- "@types/react": "^19.1.0",
24463
- "@types/react-dom": "^19.1.0",
24464
- "@types/node": "^22.10.6",
24465
- typescript: "^5.7.2",
24466
- tailwindcss: "^4.1.5"
25327
+ "@types/react": "catalog:",
25328
+ "@types/react-dom": "catalog:",
25329
+ "@types/node": "catalog:",
25330
+ typescript: "catalog:",
25331
+ tailwindcss: "catalog:"
24467
25332
  }
24468
25333
  };
24469
25334
  await writeFile(join(projectPath, "apps/platform/package.json"), JSON.stringify(platformPackageJson, null, 2));
@@ -24482,7 +25347,7 @@ coverage = true
24482
25347
  },
24483
25348
  devDependencies: {
24484
25349
  "@types/bun": "latest",
24485
- typescript: "^5.7.2"
25350
+ typescript: "catalog:"
24486
25351
  }
24487
25352
  };
24488
25353
  await writeFile(join(projectPath, "apps/api/package.json"), JSON.stringify(apiPackageJson, null, 2));
@@ -24620,31 +25485,50 @@ const config: Config = {
24620
25485
  export default config;
24621
25486
  `;
24622
25487
  await writeFile(join(projectPath, "apps/web/tailwind.config.ts"), webTailwindConfigContent);
24623
- const webTsconfigContent = {
24624
- compilerOptions: {
25488
+ const getWebTsConfig = () => {
25489
+ const baseOptions = {
24625
25490
  target: "ES2017",
24626
25491
  lib: ["dom", "dom.iterable", "esnext"],
24627
25492
  allowJs: true,
24628
25493
  skipLibCheck: true,
24629
- strict: true,
24630
25494
  noEmit: true,
24631
25495
  esModuleInterop: true,
24632
25496
  module: "esnext",
24633
25497
  moduleResolution: "bundler",
24634
25498
  resolveJsonModule: true,
24635
25499
  isolatedModules: true,
24636
- jsx: "preserve",
25500
+ jsx: "react-jsx",
24637
25501
  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"],
25502
+ plugins: [{ name: "next" }],
25503
+ paths: context.pathAliases ? { "@/*": ["./src/*"] } : undefined
25504
+ };
25505
+ if (context.tsStrictness === "strict") {
25506
+ return {
25507
+ ...baseOptions,
25508
+ strict: true,
25509
+ noUnusedLocals: true,
25510
+ noUnusedParameters: true,
25511
+ noFallthroughCasesInSwitch: true,
25512
+ noImplicitReturns: true
25513
+ };
25514
+ }
25515
+ if (context.tsStrictness === "moderate") {
25516
+ return {
25517
+ ...baseOptions,
25518
+ strict: true,
25519
+ noUnusedLocals: false,
25520
+ noUnusedParameters: false
25521
+ };
25522
+ }
25523
+ return {
25524
+ ...baseOptions,
25525
+ strict: false,
25526
+ noImplicitAny: false
25527
+ };
25528
+ };
25529
+ const webTsconfigContent = {
25530
+ compilerOptions: getWebTsConfig(),
25531
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/dev/types/**/*.ts"],
24648
25532
  exclude: ["node_modules"]
24649
25533
  };
24650
25534
  await writeFile(join(projectPath, "apps/web/tsconfig.json"), JSON.stringify(webTsconfigContent, null, 2));
@@ -24734,30 +25618,8 @@ export default config;
24734
25618
  `;
24735
25619
  await writeFile(join(projectPath, "apps/platform/tailwind.config.ts"), platformTailwindConfigContent);
24736
25620
  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"],
25621
+ compilerOptions: getWebTsConfig(),
25622
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/dev/types/**/*.ts"],
24761
25623
  exclude: ["node_modules"]
24762
25624
  };
24763
25625
  await writeFile(join(projectPath, "apps/platform/tsconfig.json"), JSON.stringify(platformTsconfigContent, null, 2));
@@ -24914,8 +25776,107 @@ Built with \u2764\uFE0F using Bun monorepo features
24914
25776
  exclude: ["node_modules"]
24915
25777
  };
24916
25778
  await writeFile(join(projectPath, "tsconfig.json"), JSON.stringify(tsconfigContent, null, 2));
25779
+ if (context.database && context.database !== "none") {
25780
+ const dbPackagePath = join(projectPath, "packages/db");
25781
+ const dbPackageJson = {
25782
+ name: `@${context.packageName}/db`,
25783
+ version: "0.0.0",
25784
+ private: true,
25785
+ main: "./src/index.ts",
25786
+ types: "./src/index.ts",
25787
+ dependencies: context.database === "supabase" ? {
25788
+ "@supabase/supabase-js": "catalog:",
25789
+ "drizzle-orm": "catalog:",
25790
+ postgres: "catalog:"
25791
+ } : {
25792
+ "drizzle-orm": "catalog:"
25793
+ },
25794
+ devDependencies: {
25795
+ "drizzle-kit": "catalog:",
25796
+ "@types/bun": "latest",
25797
+ typescript: "catalog:"
25798
+ }
25799
+ };
25800
+ await writeFile(join(dbPackagePath, "package.json"), JSON.stringify(dbPackageJson, null, 2));
25801
+ if (context.database === "postgres-drizzle") {
25802
+ await setupPostgresDrizzle(dbPackagePath, context, true);
25803
+ } else if (context.database === "supabase") {
25804
+ await setupSupabase(dbPackagePath, context, true);
25805
+ } else if (context.database === "sqlite-drizzle") {
25806
+ await setupSQLiteDrizzle(dbPackagePath, context, true);
25807
+ }
25808
+ const apiPackageJson2 = JSON.parse(await Bun.file(join(projectPath, "apps/api/package.json")).text());
25809
+ apiPackageJson2.dependencies[`@${context.packageName}/db`] = "workspace:*";
25810
+ await writeFile(join(projectPath, "apps/api/package.json"), JSON.stringify(apiPackageJson2, null, 2));
25811
+ }
25812
+ if (context.codeQuality === "ultracite") {
25813
+ await setupUltracite(projectPath, context);
25814
+ } else {
25815
+ await setupBiome(projectPath, context);
25816
+ }
25817
+ if (context.docker) {
25818
+ await setupDocker(projectPath, context);
25819
+ const dockerCompose = `version: '3.8'
25820
+
25821
+ services:
25822
+ web:
25823
+ build:
25824
+ context: ./apps/web
25825
+ dockerfile: ../../Dockerfile
25826
+ ports:
25827
+ - "3000:3000"
25828
+ environment:
25829
+ - NODE_ENV=production
25830
+ ${context.database === "supabase" ? "- NEXT_PUBLIC_SUPABASE_URL=${SUPABASE_URL}\n - NEXT_PUBLIC_SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}" : ""}
25831
+ restart: unless-stopped
25832
+
25833
+ platform:
25834
+ build:
25835
+ context: ./apps/platform
25836
+ dockerfile: ../../Dockerfile
25837
+ ports:
25838
+ - "3001:3000"
25839
+ environment:
25840
+ - NODE_ENV=production
25841
+ ${context.database === "supabase" ? "- NEXT_PUBLIC_SUPABASE_URL=${SUPABASE_URL}\n - NEXT_PUBLIC_SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}" : ""}
25842
+ restart: unless-stopped
25843
+
25844
+ api:
25845
+ build:
25846
+ context: ./apps/api
25847
+ dockerfile: ../../Dockerfile
25848
+ ports:
25849
+ - "3002:3001"
25850
+ environment:
25851
+ - NODE_ENV=production
25852
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `- DATABASE_URL=\${DATABASE_URL}` : ""}
25853
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `depends_on:
25854
+ - db` : ""}
25855
+ restart: unless-stopped
25856
+
25857
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `db:
25858
+ image: ${context.database === "sqlite-drizzle" ? "alpine:latest" : "postgres:16-alpine"}
25859
+ ${context.database !== "sqlite-drizzle" ? `environment:
25860
+ - POSTGRES_USER=postgres
25861
+ - POSTGRES_PASSWORD=postgres
25862
+ - POSTGRES_DB=${context.projectName}
25863
+ volumes:
25864
+ - postgres_data:/var/lib/postgresql/data
25865
+ ports:
25866
+ - "5432:5432"` : `volumes:
25867
+ - sqlite_data:/data`}
25868
+ restart: unless-stopped
25869
+ ` : ""}
25870
+ ${context.database && context.database !== "none" && context.database !== "supabase" ? `volumes:
25871
+ ${context.database === "sqlite-drizzle" ? "sqlite_data:" : "postgres_data:"}` : ""}
25872
+ `;
25873
+ await writeFile(join(projectPath, "docker-compose.yml"), dockerCompose);
25874
+ }
25875
+ if (context.cicd) {
25876
+ await setupGitHubActions(projectPath, context);
25877
+ }
24917
25878
  }
24918
- // src/commands/init.real.ts
25879
+ // src/commands/init.enhanced.ts
24919
25880
  function getOptionValue(envVar, option, defaultValue) {
24920
25881
  const envValue = process.env[envVar];
24921
25882
  if (envValue !== undefined) {
@@ -24927,7 +25888,7 @@ function getOptionValue(envVar, option, defaultValue) {
24927
25888
  }
24928
25889
  return option ?? defaultValue;
24929
25890
  }
24930
- async function initCommand(options = {}) {
25891
+ async function enhancedInitCommand(options = {}) {
24931
25892
  const isNonInteractive = process.env.BUNKIT_NON_INTERACTIVE === "true" || options.nonInteractive === true;
24932
25893
  let projectName = getOptionValue("BUNKIT_PROJECT_NAME", options.name);
24933
25894
  if (!projectName) {
@@ -24956,7 +25917,7 @@ async function initCommand(options = {}) {
24956
25917
  let preset = getOptionValue("BUNKIT_PRESET", options.preset);
24957
25918
  if (!preset) {
24958
25919
  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");
25920
+ throw new Error("Preset is required in non-interactive mode. " + "Provide it via BUNKIT_PRESET env var or --preset flag.");
24960
25921
  }
24961
25922
  preset = await ve({
24962
25923
  message: "\uD83C\uDFA8 Which preset would you like?",
@@ -24964,22 +25925,22 @@ async function initCommand(options = {}) {
24964
25925
  {
24965
25926
  value: "minimal",
24966
25927
  label: "\u26A1 Minimal",
24967
- hint: "Single repo, clean start"
25928
+ hint: "Single file, clean start - perfect for learning Bun"
24968
25929
  },
24969
25930
  {
24970
25931
  value: "web",
24971
- label: "\uD83C\uDF10 Web",
24972
- hint: "Next.js 16 + React 19"
25932
+ label: "\uD83C\uDF10 Web App",
25933
+ hint: "Next.js 16 + React 19 - full-stack web application"
24973
25934
  },
24974
25935
  {
24975
25936
  value: "api",
24976
- label: "\uD83D\uDE80 API",
24977
- hint: "Hono + Bun.serve()"
25937
+ label: "\uD83D\uDE80 API Server",
25938
+ hint: "Hono + Bun.serve() - ultra-fast REST API"
24978
25939
  },
24979
25940
  {
24980
25941
  value: "full",
24981
- label: "\uD83D\uDCE6 Full-stack",
24982
- hint: "Monorepo with everything"
25942
+ label: "\uD83D\uDCE6 Full-Stack Monorepo",
25943
+ hint: "Web + Platform + API - enterprise SaaS setup"
24983
25944
  }
24984
25945
  ]
24985
25946
  });
@@ -24987,17 +25948,221 @@ async function initCommand(options = {}) {
24987
25948
  xe("Operation cancelled.");
24988
25949
  process.exit(0);
24989
25950
  }
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(", ")}`);
25951
+ }
25952
+ let database = getOptionValue("BUNKIT_DATABASE", options.database);
25953
+ if (!database && (preset === "api" || preset === "full")) {
25954
+ if (!isNonInteractive) {
25955
+ database = await ve({
25956
+ message: "\uD83D\uDDC4\uFE0F Database setup?",
25957
+ options: [
25958
+ {
25959
+ value: "postgres-drizzle",
25960
+ label: "PostgreSQL + Drizzle ORM",
25961
+ hint: "Production-ready, type-safe SQL database"
25962
+ },
25963
+ {
25964
+ value: "supabase",
25965
+ label: "Supabase",
25966
+ hint: "PostgreSQL + Auth + Storage + Realtime - all-in-one"
25967
+ },
25968
+ {
25969
+ value: "sqlite-drizzle",
25970
+ label: "SQLite + Drizzle ORM",
25971
+ hint: "Local-first, embedded database - perfect for prototypes"
25972
+ },
25973
+ {
25974
+ value: "none",
25975
+ label: "None",
25976
+ hint: "I'll add it later"
25977
+ }
25978
+ ]
25979
+ });
25980
+ if (pD(database)) {
25981
+ xe("Operation cancelled.");
25982
+ process.exit(0);
25983
+ }
25984
+ } else {
25985
+ database = "none";
25986
+ }
25987
+ }
25988
+ let codeQuality = getOptionValue("BUNKIT_CODE_QUALITY", options.codeQuality, "ultracite");
25989
+ if (!codeQuality) {
25990
+ if (!isNonInteractive) {
25991
+ codeQuality = await ve({
25992
+ message: "\uD83E\uDD16 Code quality setup?",
25993
+ options: [
25994
+ {
25995
+ value: "ultracite",
25996
+ label: "Ultracite (Recommended)",
25997
+ hint: "AI-optimized Biome preset - syncs rules for Cursor, Claude, Windsurf"
25998
+ },
25999
+ {
26000
+ value: "biome",
26001
+ label: "Biome",
26002
+ hint: "Standard Biome with good defaults - fast and reliable"
26003
+ }
26004
+ ]
26005
+ });
26006
+ if (pD(codeQuality)) {
26007
+ xe("Operation cancelled.");
26008
+ process.exit(0);
26009
+ }
26010
+ } else {
26011
+ codeQuality = "ultracite";
26012
+ }
26013
+ }
26014
+ let tsStrictness = getOptionValue("BUNKIT_TS_STRICTNESS", options.tsStrictness, "strict");
26015
+ if (!tsStrictness) {
26016
+ if (!isNonInteractive) {
26017
+ tsStrictness = await ve({
26018
+ message: "\uD83D\uDD12 TypeScript strictness?",
26019
+ options: [
26020
+ {
26021
+ value: "strict",
26022
+ label: "Strict (Recommended)",
26023
+ hint: "Maximum type safety - catches bugs early, prevents headaches"
26024
+ },
26025
+ {
26026
+ value: "moderate",
26027
+ label: "Moderate",
26028
+ hint: "Balanced - good safety without being too rigid"
26029
+ },
26030
+ {
26031
+ value: "loose",
26032
+ label: "Loose",
26033
+ hint: "Minimal checks - quick prototyping, migrate from JavaScript"
26034
+ }
26035
+ ]
26036
+ });
26037
+ if (pD(tsStrictness)) {
26038
+ xe("Operation cancelled.");
26039
+ process.exit(0);
26040
+ }
26041
+ } else {
26042
+ tsStrictness = "strict";
26043
+ }
26044
+ }
26045
+ let cssFramework = getOptionValue("BUNKIT_CSS_FRAMEWORK", options.cssFramework);
26046
+ if (!cssFramework && (preset === "web" || preset === "full")) {
26047
+ if (!isNonInteractive) {
26048
+ cssFramework = await ve({
26049
+ message: "\uD83C\uDFA8 CSS framework?",
26050
+ options: [
26051
+ {
26052
+ value: "tailwind",
26053
+ label: "Tailwind CSS 4 (Recommended)",
26054
+ hint: "Utility-first, fast, modern - with OKLCH colors and @theme"
26055
+ },
26056
+ {
26057
+ value: "vanilla",
26058
+ label: "Vanilla CSS",
26059
+ hint: "Plain CSS files - full control, no framework"
26060
+ },
26061
+ {
26062
+ value: "css-modules",
26063
+ label: "CSS Modules",
26064
+ hint: "Scoped CSS - automatic class name generation"
26065
+ }
26066
+ ]
26067
+ });
26068
+ if (pD(cssFramework)) {
26069
+ xe("Operation cancelled.");
26070
+ process.exit(0);
26071
+ }
26072
+ } else {
26073
+ cssFramework = "tailwind";
26074
+ }
26075
+ }
26076
+ let uiLibrary = getOptionValue("BUNKIT_UI_LIBRARY", options.uiLibrary);
26077
+ if (!uiLibrary && (preset === "web" || preset === "full") && cssFramework === "tailwind") {
26078
+ if (!isNonInteractive) {
26079
+ uiLibrary = await ve({
26080
+ message: "\uD83E\uDDE9 UI component library?",
26081
+ options: [
26082
+ {
26083
+ value: "shadcn",
26084
+ label: "shadcn/ui (Recommended)",
26085
+ hint: "64+ components, accessible, customizable - production-ready"
26086
+ },
26087
+ {
26088
+ value: "none",
26089
+ label: "None",
26090
+ hint: "I'll build my own components"
26091
+ }
26092
+ ]
26093
+ });
26094
+ if (pD(uiLibrary)) {
26095
+ xe("Operation cancelled.");
26096
+ process.exit(0);
26097
+ }
26098
+ } else {
26099
+ uiLibrary = "shadcn";
26100
+ }
26101
+ }
26102
+ let testing = getOptionValue("BUNKIT_TESTING", options.testing, "bun-test");
26103
+ if (!testing) {
26104
+ if (!isNonInteractive) {
26105
+ testing = await ve({
26106
+ message: "\uD83E\uDDEA Testing framework?",
26107
+ options: [
26108
+ {
26109
+ value: "bun-test",
26110
+ label: "Bun Test (Recommended)",
26111
+ hint: "Built-in, fast, Jest-compatible - no extra dependencies"
26112
+ },
26113
+ {
26114
+ value: "vitest",
26115
+ label: "Vitest",
26116
+ hint: "Vite-powered, fast, ESM-first - popular in ecosystem"
26117
+ },
26118
+ {
26119
+ value: "none",
26120
+ label: "None",
26121
+ hint: "I'll add testing later"
26122
+ }
26123
+ ]
26124
+ });
26125
+ if (pD(testing)) {
26126
+ xe("Operation cancelled.");
26127
+ process.exit(0);
26128
+ }
26129
+ } else {
26130
+ testing = "bun-test";
26131
+ }
26132
+ }
26133
+ let docker = getOptionValue("BUNKIT_DOCKER", options.docker, false);
26134
+ if (docker === undefined) {
26135
+ if (!isNonInteractive) {
26136
+ docker = await ye({
26137
+ message: "\uD83D\uDC33 Add Docker configuration?",
26138
+ initialValue: false
26139
+ });
26140
+ if (pD(docker)) {
26141
+ xe("Operation cancelled.");
26142
+ process.exit(0);
26143
+ }
26144
+ } else {
26145
+ docker = false;
26146
+ }
26147
+ }
26148
+ let cicd = getOptionValue("BUNKIT_CICD", options.cicd, false);
26149
+ if (cicd === undefined) {
26150
+ if (!isNonInteractive) {
26151
+ cicd = await ye({
26152
+ message: "\u2699\uFE0F Add GitHub Actions CI/CD?",
26153
+ initialValue: false
26154
+ });
26155
+ if (pD(cicd)) {
26156
+ xe("Operation cancelled.");
26157
+ process.exit(0);
26158
+ }
26159
+ } else {
26160
+ cicd = false;
24994
26161
  }
24995
26162
  }
24996
26163
  let shouldInstall = getOptionValue("BUNKIT_INSTALL", options.install, true);
24997
26164
  if (shouldInstall === undefined) {
24998
- if (isNonInteractive) {
24999
- shouldInstall = true;
25000
- } else {
26165
+ if (!isNonInteractive) {
25001
26166
  shouldInstall = await ye({
25002
26167
  message: "\uD83D\uDCE5 Install dependencies?",
25003
26168
  initialValue: true
@@ -25006,13 +26171,13 @@ async function initCommand(options = {}) {
25006
26171
  xe("Operation cancelled.");
25007
26172
  process.exit(0);
25008
26173
  }
26174
+ } else {
26175
+ shouldInstall = true;
25009
26176
  }
25010
26177
  }
25011
26178
  let shouldInitGit = getOptionValue("BUNKIT_GIT", options.git, true);
25012
26179
  if (shouldInitGit === undefined) {
25013
- if (isNonInteractive) {
25014
- shouldInitGit = true;
25015
- } else {
26180
+ if (!isNonInteractive) {
25016
26181
  shouldInitGit = await ye({
25017
26182
  message: "\uD83D\uDD27 Initialize git repository?",
25018
26183
  initialValue: true
@@ -25021,16 +26186,39 @@ async function initCommand(options = {}) {
25021
26186
  xe("Operation cancelled.");
25022
26187
  process.exit(0);
25023
26188
  }
26189
+ } else {
26190
+ shouldInitGit = true;
25024
26191
  }
25025
26192
  }
25026
- if (isNonInteractive) {
26193
+ if (!isNonInteractive) {
25027
26194
  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("");
26195
+ ` + boxen([
26196
+ `${source_default.bold("Project:")} ${source_default.cyan(projectName)}`,
26197
+ `${source_default.bold("Preset:")} ${source_default.cyan(preset)}`,
26198
+ database ? `${source_default.bold("Database:")} ${source_default.cyan(database)}` : "",
26199
+ `${source_default.bold("Code Quality:")} ${source_default.cyan(codeQuality)}`,
26200
+ `${source_default.bold("TypeScript:")} ${source_default.cyan(tsStrictness)}`,
26201
+ cssFramework ? `${source_default.bold("CSS:")} ${source_default.cyan(cssFramework)}` : "",
26202
+ uiLibrary ? `${source_default.bold("UI Library:")} ${source_default.cyan(uiLibrary)}` : "",
26203
+ `${source_default.bold("Testing:")} ${source_default.cyan(testing)}`,
26204
+ docker ? `${source_default.bold("Docker:")} ${source_default.green("\u2713")}` : "",
26205
+ cicd ? `${source_default.bold("CI/CD:")} ${source_default.green("\u2713")}` : ""
26206
+ ].filter(Boolean).join(`
26207
+ `), {
26208
+ padding: 1,
26209
+ title: "\uD83D\uDCCB Configuration",
26210
+ titleAlignment: "left",
26211
+ borderColor: "cyan",
26212
+ borderStyle: "round"
26213
+ }));
26214
+ const confirm = await ye({
26215
+ message: "Proceed with this configuration?",
26216
+ initialValue: true
26217
+ });
26218
+ if (pD(confirm) || !confirm) {
26219
+ xe("Operation cancelled.");
26220
+ process.exit(0);
26221
+ }
25034
26222
  }
25035
26223
  const s = Y2();
25036
26224
  s.start("\uD83D\uDD28 Creating project structure...");
@@ -25040,7 +26228,17 @@ async function initCommand(options = {}) {
25040
26228
  preset,
25041
26229
  path: projectName,
25042
26230
  git: shouldInitGit,
25043
- install: shouldInstall
26231
+ install: shouldInstall,
26232
+ database,
26233
+ codeQuality,
26234
+ tsStrictness,
26235
+ uiLibrary,
26236
+ cssFramework,
26237
+ testing,
26238
+ docker,
26239
+ cicd,
26240
+ envExample: true,
26241
+ pathAliases: true
25044
26242
  };
25045
26243
  await createProject(config);
25046
26244
  const projectPath = join(process.cwd(), config.path);
@@ -25060,11 +26258,38 @@ async function initCommand(options = {}) {
25060
26258
  await buildFullPreset(projectPath, context);
25061
26259
  break;
25062
26260
  }
26261
+ if (database && database !== "none") {
26262
+ s.message(`\uD83D\uDDC4\uFE0F Setting up ${database}...`);
26263
+ }
26264
+ if (codeQuality === "ultracite") {
26265
+ s.message("\uD83E\uDD16 Configuring Ultracite for AI editors...");
26266
+ }
26267
+ if (docker) {
26268
+ s.message("\uD83D\uDC33 Adding Docker configuration...");
26269
+ }
26270
+ if (cicd) {
26271
+ s.message("\u2699\uFE0F Adding GitHub Actions...");
26272
+ }
25063
26273
  s.stop("\u2705 Project created!");
25064
26274
  if (shouldInstall) {
25065
- const deps = getDependenciesForPreset(preset);
25066
- if (deps.length > 0) {
25067
- await installDependencies(projectPath, deps);
26275
+ const additionalDeps = {};
26276
+ if (database && database !== "none") {
26277
+ Object.assign(additionalDeps, getDatabaseDependencies(database));
26278
+ }
26279
+ if (codeQuality) {
26280
+ Object.assign(additionalDeps, getCodeQualityDependencies(codeQuality));
26281
+ }
26282
+ if (testing === "vitest") {
26283
+ additionalDeps["vitest"] = "catalog:";
26284
+ additionalDeps["@vitest/ui"] = "catalog:";
26285
+ }
26286
+ if (uiLibrary === "shadcn") {
26287
+ additionalDeps["class-variance-authority"] = "catalog:";
26288
+ additionalDeps["clsx"] = "catalog:";
26289
+ additionalDeps["tailwind-merge"] = "catalog:";
26290
+ }
26291
+ if (Object.keys(additionalDeps).length > 0) {
26292
+ await installDependencies(projectPath, additionalDeps);
25068
26293
  } else {
25069
26294
  await installDependencies(projectPath);
25070
26295
  }
@@ -25074,20 +26299,6 @@ async function initCommand(options = {}) {
25074
26299
  return "bun dev";
25075
26300
  return "bun run dev";
25076
26301
  };
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
26302
  const nextSteps = [
25092
26303
  `${source_default.cyan("cd")} ${projectName}`,
25093
26304
  shouldInstall ? "" : `${source_default.cyan("bun install")}`,
@@ -25097,9 +26308,9 @@ async function initCommand(options = {}) {
25097
26308
  console.log(`
25098
26309
  ` + boxen(nextSteps, {
25099
26310
  padding: 1,
25100
- title: `${getPresetEmoji()} Next steps`,
26311
+ title: "\uD83D\uDE80 Next steps",
25101
26312
  titleAlignment: "left",
25102
- borderColor: "cyan",
26313
+ borderColor: "green",
25103
26314
  borderStyle: "round"
25104
26315
  }));
25105
26316
  } catch (error) {
@@ -25108,18 +26319,6 @@ async function initCommand(options = {}) {
25108
26319
  process.exit(1);
25109
26320
  }
25110
26321
  }
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
26322
 
25124
26323
  // src/commands/create.ts
25125
26324
  async function createCommand2(preset, name, options) {
@@ -25156,10 +26355,10 @@ var packageJson = await Bun.file(new URL("../package.json", import.meta.url)).js
25156
26355
  var VERSION = packageJson.version;
25157
26356
  var program2 = new Command;
25158
26357
  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) => {
26358
+ 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
26359
  showBanner(VERSION);
25161
26360
  try {
25162
- await initCommand(options);
26361
+ await enhancedInitCommand(options);
25163
26362
  Se(import_picocolors5.default.green("\u2728 Done! Your project is ready to bake! \uD83C\uDF5E"));
25164
26363
  } catch (error) {
25165
26364
  M2.error(error.message);