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.
- package/dist/index.js +1358 -159
- 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
|
|
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 =
|
|
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
|
|
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 (
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
24238
|
-
|
|
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: "
|
|
24832
|
+
jsx: "react-jsx",
|
|
24251
24833
|
incremental: true,
|
|
24252
24834
|
plugins: [{ name: "next" }],
|
|
24253
|
-
paths: {
|
|
24254
|
-
|
|
24255
|
-
|
|
24256
|
-
|
|
24257
|
-
|
|
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
|
-
|
|
24262
|
-
|
|
24263
|
-
|
|
24264
|
-
|
|
24265
|
-
|
|
24266
|
-
|
|
24267
|
-
|
|
24268
|
-
|
|
24269
|
-
|
|
24270
|
-
|
|
24271
|
-
|
|
24272
|
-
|
|
24273
|
-
|
|
24274
|
-
|
|
24275
|
-
|
|
24276
|
-
|
|
24277
|
-
|
|
24278
|
-
|
|
24279
|
-
|
|
24280
|
-
|
|
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
|
|
24358
|
-
|
|
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
|
-
|
|
24371
|
-
|
|
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": "
|
|
25262
|
+
"@biomejs/biome": "catalog:",
|
|
24404
25263
|
"@types/bun": "latest",
|
|
24405
|
-
typescript: "
|
|
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": "
|
|
24439
|
-
"@types/react-dom": "
|
|
24440
|
-
"@types/node": "
|
|
24441
|
-
typescript: "
|
|
24442
|
-
tailwindcss: "
|
|
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": "
|
|
24463
|
-
"@types/react-dom": "
|
|
24464
|
-
"@types/node": "
|
|
24465
|
-
typescript: "
|
|
24466
|
-
tailwindcss: "
|
|
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: "
|
|
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
|
|
24624
|
-
|
|
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: "
|
|
25500
|
+
jsx: "react-jsx",
|
|
24637
25501
|
incremental: true,
|
|
24638
|
-
plugins: [
|
|
24639
|
-
|
|
24640
|
-
|
|
24641
|
-
|
|
24642
|
-
|
|
24643
|
-
|
|
24644
|
-
|
|
24645
|
-
|
|
24646
|
-
|
|
24647
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
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-
|
|
24982
|
-
hint: "
|
|
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
|
-
}
|
|
24991
|
-
|
|
24992
|
-
|
|
24993
|
-
|
|
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
|
-
|
|
25029
|
-
|
|
25030
|
-
|
|
25031
|
-
|
|
25032
|
-
|
|
25033
|
-
|
|
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
|
|
25066
|
-
if (
|
|
25067
|
-
|
|
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:
|
|
26311
|
+
title: "\uD83D\uDE80 Next steps",
|
|
25101
26312
|
titleAlignment: "left",
|
|
25102
|
-
borderColor: "
|
|
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
|
|
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
|
|
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);
|