UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

1,054 lines (905 loc) โ€ข 27.1 kB
"use strict"; /** * Bun Backend Template Base Generator * Shared functionality for all Bun web frameworks */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.BunBackendGenerator = void 0; const backend_template_generator_1 = require("../shared/backend-template-generator"); const fs_1 = require("fs"); const path = __importStar(require("path")); class BunBackendGenerator extends backend_template_generator_1.BackendTemplateGenerator { constructor(framework) { const config = { language: 'TypeScript', framework, packageManager: 'bun', buildTool: 'bun', testFramework: 'bun test', features: [ 'Blazing fast runtime', 'TypeScript by default', 'JSX support', 'Built-in bundler', 'Native ESM', 'Built-in test runner', 'Hot reloading', 'WebSocket support', 'SQLite built-in', 'Web APIs compatible' ], dependencies: {}, devDependencies: {}, scripts: { 'dev': 'bun run --watch src/index.ts', 'start': 'bun run src/index.ts', 'build': 'bun build src/index.ts --target=bun --outdir=dist', 'test': 'bun test', 'test:watch': 'bun test --watch', 'typecheck': 'tsc --noEmit', 'lint': 'eslint src --ext .ts,.tsx', 'format': 'prettier --write .', 'docker:build': 'docker build -t {{projectName}} .', 'docker:run': 'docker run -p {{port}}:{{port}} {{projectName}}' }, dockerConfig: { baseImage: 'oven/bun:1.0-alpine', workDir: '/app', exposedPorts: [3000], buildSteps: [ 'COPY package.json bun.lockb* ./', 'RUN bun install --frozen-lockfile', 'COPY . .', 'RUN bun run build' ], runCommand: 'bun run dist/index.js', multistage: true }, envVars: { 'PORT': '3000', 'HOST': '0.0.0.0', 'NODE_ENV': 'development', 'LOG_LEVEL': 'info', 'DATABASE_URL': 'sqlite://./data/app.db', 'JWT_SECRET': 'your-secret-key', 'CORS_ORIGIN': '*' } }; super(config); } async generateLanguageFiles(projectPath, options) { // Generate package.json await this.generatePackageJson(projectPath, options); // Generate TypeScript config await this.generateTsConfig(projectPath); // Generate bunfig.toml await this.generateBunConfig(projectPath); // Generate .gitignore await this.generateBunGitignore(projectPath); // Generate VS Code settings await this.generateVSCodeSettings(projectPath); // Create directory structure const directories = [ 'src', 'src/controllers', 'src/services', 'src/models', 'src/middleware', 'src/utils', 'src/config', 'src/types', 'src/routes', 'tests', 'tests/unit', 'tests/integration', 'scripts', 'data' ]; for (const dir of directories) { await fs_1.promises.mkdir(path.join(projectPath, dir), { recursive: true }); } } async generatePackageJson(projectPath, options) { const packageJson = { name: options.name, version: "1.0.0", description: `${this.config.framework} API built with Bun`, main: "src/index.ts", type: "module", scripts: this.config.scripts, dependencies: { ...this.config.dependencies, "@types/bun": "latest", "zod": "^3.22.4", "dotenv": "^16.4.5" }, devDependencies: { ...this.config.devDependencies, "typescript": "^5.4.5", "@typescript-eslint/eslint-plugin": "^7.7.1", "@typescript-eslint/parser": "^7.7.1", "eslint": "^8.57.0", "prettier": "^3.2.5", "@types/node": "^20.12.7" }, engines: { "bun": ">=1.0.0" } }; await fs_1.promises.writeFile(path.join(projectPath, 'package.json'), JSON.stringify(packageJson, null, 2)); } async generateTsConfig(projectPath) { const tsConfig = { compilerOptions: { lib: ["ESNext"], module: "esnext", target: "esnext", moduleResolution: "bundler", moduleDetection: "force", allowImportingTsExtensions: true, noEmit: true, composite: true, strict: true, downlevelIteration: true, skipLibCheck: true, jsx: "react-jsx", allowSyntheticDefaultImports: true, forceConsistentCasingInFileNames: true, allowJs: true, types: ["bun-types"], esModuleInterop: true, resolveJsonModule: true, isolatedModules: true, baseUrl: ".", paths: { "@/*": ["src/*"] } }, include: ["src/**/*", "tests/**/*"], exclude: ["node_modules", "dist"] }; await fs_1.promises.writeFile(path.join(projectPath, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2)); } async generateBunConfig(projectPath) { const bunConfig = `# Bun configuration file # https://bun.sh/docs/runtime/bunfig # Telemetry telemetry = false # Test runner [test] # Test timeout in milliseconds timeout = 5000 # Run tests in watch mode by default watch = false # Coverage reporting coverage = true coverageThreshold = 80 # Install configuration [install] # Use exact versions exact = true # Save peer dependencies peer = true # Production mode production = false # Install cache [install.cache] # Cache directory dir = "~/.bun/install/cache" # Disable cache disable = false # Registry configuration [install.registry] default = "https://registry.npmjs.org" # Development server [debug] # Enable source maps sourcemap = "external" # Macros [macros] # Define compile-time constants NODE_ENV = "development" # Bundle configuration [bundle] # Bundle target target = "bun" # Enable source maps sourcemap = "external" # Minify output minify = false `; await fs_1.promises.writeFile(path.join(projectPath, 'bunfig.toml'), bunConfig); } async generateBunGitignore(projectPath) { const gitignoreContent = `# Dependencies node_modules/ bun.lockb # Build output dist/ build/ *.tsbuildinfo # Environment .env .env.local .env.*.local # Editor .vscode/* !.vscode/settings.json !.vscode/extensions.json .idea/ *.swp *.swo *~ # OS .DS_Store Thumbs.db # Logs *.log logs/ bun-error.log # Test coverage coverage/ .coverage/ *.lcov # Temporary files tmp/ temp/ .tmp/ # Database data/*.db data/*.db-journal data/*.db-wal *.sqlite # Debug npm-debug.log* yarn-debug.log* yarn-error.log* # Misc .cache/ `; await fs_1.promises.writeFile(path.join(projectPath, '.gitignore'), gitignoreContent); } async generateVSCodeSettings(projectPath) { await fs_1.promises.mkdir(path.join(projectPath, '.vscode'), { recursive: true }); const settings = { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "typescript.tsdk": "node_modules/typescript/lib", "typescript.enablePromptUseWorkspaceTsdk": true, "files.exclude": { "**/node_modules": true, "**/dist": true, "**/.coverage": true }, "search.exclude": { "**/node_modules": true, "**/dist": true, "**/bun.lockb": true } }; await fs_1.promises.writeFile(path.join(projectPath, '.vscode', 'settings.json'), JSON.stringify(settings, null, 2)); const extensions = { recommendations: [ "oven.bun-vscode", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "ms-vscode.vscode-typescript-next" ] }; await fs_1.promises.writeFile(path.join(projectPath, '.vscode', 'extensions.json'), JSON.stringify(extensions, null, 2)); } async generateCommonFiles(projectPath, options) { await super.generateCommonFiles(projectPath, options); // Generate Bun-specific common files await this.generateMakefile(projectPath); await this.generateDevContainer(projectPath); } async generateMakefile(projectPath) { const makefileContent = `.PHONY: dev start test test-watch build typecheck lint format clean help install docker-build docker-run # Default target .DEFAULT_GOAL := help # Help command help: @echo "Available commands:" @echo " make install - Install dependencies" @echo " make dev - Run development server with hot reload" @echo " make start - Run production server" @echo " make test - Run tests" @echo " make test-watch - Run tests in watch mode" @echo " make build - Build for production" @echo " make typecheck - Run TypeScript type checking" @echo " make lint - Lint code" @echo " make format - Format code" @echo " make clean - Clean build artifacts" @echo " make docker-build - Build Docker image" @echo " make docker-run - Run Docker container" install: bun install dev: bun run dev start: bun run start test: bun test test-watch: bun test --watch build: bun run build typecheck: bun run typecheck lint: bun run lint format: bun run format clean: rm -rf dist coverage .coverage bun.lockb node_modules docker-build: docker build -t ${this.config.framework.toLowerCase()}-app . docker-run: docker run -p 3000:3000 ${this.config.framework.toLowerCase()}-app `; await fs_1.promises.writeFile(path.join(projectPath, 'Makefile'), makefileContent); } async generateDevContainer(projectPath) { await fs_1.promises.mkdir(path.join(projectPath, '.devcontainer'), { recursive: true }); const devContainerConfig = { name: `${this.config.framework} Bun Development`, image: "oven/bun:1.0", customizations: { vscode: { extensions: [ "oven.bun-vscode", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode" ], settings: { "terminal.integrated.defaultProfile.linux": "bash" } } }, features: { "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers/features/github-cli:1": {} }, postCreateCommand: "bun install", forwardPorts: [3000], remoteUser: "bun" }; await fs_1.promises.writeFile(path.join(projectPath, '.devcontainer', 'devcontainer.json'), JSON.stringify(devContainerConfig, null, 2)); } async generateTestStructure(projectPath, options) { // Generate test utilities const testUtilsContent = `import { expect, test, describe, beforeEach, afterEach, mock } from 'bun:test'; export { expect, test, describe, beforeEach, afterEach, mock }; // Test helpers export async function setupTestDb() { // Setup test database const db = new Database(':memory:'); // Run migrations return db; } export async function cleanupTestDb(db: any) { // Cleanup test database db.close(); } export function createTestUser() { return { id: crypto.randomUUID(), email: 'test@example.com', name: 'Test User', createdAt: new Date(), updatedAt: new Date() }; } // HTTP test helpers export async function testRequest( path: string, options?: RequestInit ): Promise<Response> { const baseUrl = process.env.TEST_URL || 'http://localhost:3000'; return fetch(\`\${baseUrl}\${path}\`, options); } `; await fs_1.promises.writeFile(path.join(projectPath, 'tests', 'test-utils.ts'), testUtilsContent); // Generate example test const exampleTestContent = `import { expect, test, describe } from './test-utils'; describe('Example Test Suite', () => { test('should pass a simple test', () => { expect(1 + 1).toBe(2); }); test('should handle async operations', async () => { const result = await Promise.resolve('success'); expect(result).toBe('success'); }); test('should test API endpoint', async () => { const response = await fetch('http://localhost:3000/health'); expect(response.status).toBe(200); const data = await response.json(); expect(data.status).toBe('healthy'); }); }); `; await fs_1.promises.writeFile(path.join(projectPath, 'tests', 'example.test.ts'), exampleTestContent); } async generateHealthCheck(projectPath) { const healthCheckContent = `import type { Context } from '../types'; export async function healthCheck(ctx: Context) { const health = { status: 'healthy', timestamp: new Date().toISOString(), version: '1.0.0', service: '${this.config.framework.toLowerCase()}-service', runtime: 'Bun v' + Bun.version, uptime: process.uptime(), memory: process.memoryUsage() }; return ctx.json(health); } export async function readinessCheck(ctx: Context) { try { // Add your readiness checks here // e.g., database connection, external services return ctx.json({ status: 'ready', checks: { database: 'ok', cache: 'ok' } }); } catch (error) { return ctx.json({ status: 'not ready', error: error.message }, 503); } } `; await fs_1.promises.writeFile(path.join(projectPath, 'src', 'controllers', 'health.ts'), healthCheckContent); } async generateAPIDocs(projectPath) { const apiDocsContent = `# API Documentation ## Overview This is a RESTful API built with ${this.config.framework} on Bun runtime. ## Base URL \`\`\` http://localhost:3000/api/v1 \`\`\` ## Authentication The API uses JWT (JSON Web Tokens) for authentication. Include the token in the Authorization header: \`\`\` Authorization: Bearer <your-jwt-token> \`\`\` ## Endpoints ### Health Check \`\`\`http GET /health \`\`\` Returns the health status of the API. **Response:** \`\`\`json { "status": "healthy", "timestamp": "2024-01-01T00:00:00.000Z", "version": "1.0.0", "service": "${this.config.framework.toLowerCase()}-service", "runtime": "Bun v1.0.0", "uptime": 3600, "memory": { "rss": 123456789, "heapTotal": 123456789, "heapUsed": 123456789 } } \`\`\` ### Authentication #### Register \`\`\`http POST /api/v1/auth/register Content-Type: application/json { "email": "user@example.com", "password": "SecurePass123", "name": "John Doe" } \`\`\` #### Login \`\`\`http POST /api/v1/auth/login Content-Type: application/json { "email": "user@example.com", "password": "SecurePass123" } \`\`\` ### Users #### Get Current User \`\`\`http GET /api/v1/users/me Authorization: Bearer <access-token> \`\`\` #### Update User \`\`\`http PUT /api/v1/users/:id Authorization: Bearer <access-token> Content-Type: application/json { "name": "Jane Doe", "email": "jane@example.com" } \`\`\` ## Error Responses All error responses follow this format: \`\`\`json { "error": { "code": "ERROR_CODE", "message": "Human-readable error message", "details": {} } } \`\`\` ### Common Error Codes - \`UNAUTHORIZED\` - Authentication required - \`FORBIDDEN\` - Insufficient permissions - \`NOT_FOUND\` - Resource not found - \`VALIDATION_ERROR\` - Request validation failed - \`INTERNAL_ERROR\` - Internal server error ## Rate Limiting The API implements rate limiting: - 100 requests per minute for authenticated users - 20 requests per minute for unauthenticated users ## Performance Bun provides exceptional performance: - Fast startup times - Low memory footprint - Native TypeScript execution - Built-in SQLite support `; await fs_1.promises.writeFile(path.join(projectPath, 'docs', 'API.md'), apiDocsContent); } async generateDockerFiles(projectPath, options) { const dockerContent = `# Multi-stage Dockerfile for Bun ${this.config.framework} application # Build stage FROM oven/bun:1.0-alpine AS builder WORKDIR /app # Copy package files COPY package.json bun.lockb* ./ # Install dependencies RUN bun install --frozen-lockfile --production # Copy source code COPY . . # Build application RUN bun run build # Runtime stage FROM oven/bun:1.0-alpine # Install dumb-init for proper signal handling RUN apk add --no-cache dumb-init # Create non-root user RUN addgroup -g 1001 bunapp && \\ adduser -u 1001 -G bunapp -s /bin/sh -D bunapp WORKDIR /app # Copy built application COPY --from=builder --chown=bunapp:bunapp /app/dist ./dist COPY --from=builder --chown=bunapp:bunapp /app/node_modules ./node_modules COPY --from=builder --chown=bunapp:bunapp /app/package.json ./ # Create data directory RUN mkdir -p /app/data && chown -R bunapp:bunapp /app/data # Switch to non-root user USER bunapp # Expose port EXPOSE ${options.port || 3000} # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\ CMD bun run healthcheck.js || exit 1 # Use dumb-init to handle signals properly ENTRYPOINT ["dumb-init", "--"] # Run the application CMD ["bun", "run", "dist/index.js"] `; await fs_1.promises.writeFile(path.join(projectPath, 'Dockerfile'), dockerContent); // Docker Compose const dockerComposeContent = `version: '3.8' services: app: build: . ports: - "\${PORT:-3000}:3000" environment: - NODE_ENV=production - PORT=3000 - DATABASE_URL=sqlite:///app/data/app.db - JWT_SECRET=\${JWT_SECRET} volumes: - app-data:/app/data restart: unless-stopped healthcheck: test: ["CMD", "bun", "run", "healthcheck.js"] interval: 30s timeout: 3s retries: 3 start_period: 5s # Optional: Add Redis for caching redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redis-data:/data restart: unless-stopped volumes: app-data: redis-data: `; await fs_1.promises.writeFile(path.join(projectPath, 'docker-compose.yml'), dockerComposeContent); // Health check script const healthCheckContent = `// Health check script for Docker const response = await fetch('http://localhost:3000/health'); if (response.ok) { process.exit(0); } else { process.exit(1); } `; await fs_1.promises.writeFile(path.join(projectPath, 'healthcheck.js'), healthCheckContent); // .dockerignore const dockerignoreContent = `node_modules .git .gitignore .env .env.* coverage .coverage *.log .DS_Store .vscode .idea *.swp *.swo README.md docs tests .devcontainer Makefile `; await fs_1.promises.writeFile(path.join(projectPath, '.dockerignore'), dockerignoreContent); } async generateDocumentation(projectPath, options) { const readmeContent = `# ${options.name} A ${this.config.framework} web application built with Bun. ## ๐Ÿš€ Features - โšก Blazing fast Bun runtime - ๐Ÿ”ฅ Hot reload in development - ๐Ÿ“˜ TypeScript by default - ๐Ÿงช Built-in test runner - ๐Ÿ”’ JWT authentication - ๐Ÿ“ฆ SQLite built-in - ๐Ÿณ Docker support - ๐ŸŒ Production ready ## ๐Ÿ“‹ Prerequisites - Bun 1.0.0 or higher ## ๐Ÿ› ๏ธ Installation 1. Clone the repository: \`\`\`bash git clone <repository-url> cd ${options.name} \`\`\` 2. Install Bun (if not already installed): \`\`\`bash # macOS/Linux curl -fsSL https://bun.sh/install | bash # Windows powershell -c "irm bun.sh/install.ps1 | iex" \`\`\` 3. Install dependencies: \`\`\`bash bun install \`\`\` 4. Copy environment variables: \`\`\`bash cp .env.example .env \`\`\` ## ๐Ÿƒ Running the Application ### Development \`\`\`bash bun run dev # or make dev \`\`\` The application will start at http://localhost:${options.port || 3000} with hot reload enabled. ### Production \`\`\`bash bun run build bun run start # or make build && make start \`\`\` ### Docker \`\`\`bash # Build and run with Docker Compose docker-compose up # Or build and run manually docker build -t ${options.name} . docker run -p ${options.port || 3000}:${options.port || 3000} ${options.name} \`\`\` ## ๐Ÿงช Testing \`\`\`bash # Run all tests bun test # Run tests in watch mode bun test --watch # Run with coverage bun test --coverage \`\`\` ## ๐Ÿ“ Available Scripts - \`bun run dev\` - Start development server with hot reload - \`bun run start\` - Start production server - \`bun run build\` - Build for production - \`bun test\` - Run tests - \`bun run typecheck\` - Check TypeScript types - \`bun run lint\` - Lint code - \`bun run format\` - Format code ## ๐Ÿš€ Deployment ### Railway \`\`\`bash # Install Railway CLI bun add -g @railway/cli # Deploy railway login railway init railway up \`\`\` ### Fly.io \`\`\`bash # Install Fly CLI curl -L https://fly.io/install.sh | sh # Deploy fly launch fly deploy \`\`\` ### Docker \`\`\`bash # Build image docker build -t ${options.name} . # Push to registry docker tag ${options.name} your-registry/${options.name}:latest docker push your-registry/${options.name}:latest \`\`\` ## ๐Ÿ“ Project Structure \`\`\` . โ”œโ”€โ”€ src/ # Source code โ”‚ โ”œโ”€โ”€ index.ts # Application entry point โ”‚ โ”œโ”€โ”€ controllers/ # Route controllers โ”‚ โ”œโ”€โ”€ services/ # Business logic โ”‚ โ”œโ”€โ”€ models/ # Data models โ”‚ โ”œโ”€โ”€ middleware/ # Middleware functions โ”‚ โ”œโ”€โ”€ routes/ # Route definitions โ”‚ โ”œโ”€โ”€ utils/ # Utility functions โ”‚ โ”œโ”€โ”€ config/ # Configuration โ”‚ โ””โ”€โ”€ types/ # TypeScript types โ”œโ”€โ”€ tests/ # Test files โ”œโ”€โ”€ data/ # SQLite database โ”œโ”€โ”€ scripts/ # Utility scripts โ””โ”€โ”€ docs/ # Documentation \`\`\` ## ๐Ÿ”ง Configuration The application uses environment variables for configuration. See \`.env.example\` for available options. ## ๐Ÿค Contributing 1. Fork the repository 2. Create your feature branch (\`git checkout -b feature/amazing-feature\`) 3. Run tests (\`bun test\`) 4. Commit your changes (\`git commit -m 'Add amazing feature'\`) 5. Push to the branch (\`git push origin feature/amazing-feature\`) 6. Open a Pull Request ## ๐Ÿ“ License This project is licensed under the MIT License. --- Built with โค๏ธ using [Bun](https://bun.sh) and [${this.config.framework}](https://github.com/${this.config.framework.toLowerCase()}) `; await fs_1.promises.writeFile(path.join(projectPath, 'README.md'), readmeContent); } getLanguageSpecificIgnorePatterns() { return [ 'bun.lockb', 'node_modules/', 'dist/', 'coverage/', '.coverage/', 'data/*.db', 'data/*.db-journal', 'data/*.db-wal' ]; } getLanguagePrerequisites() { return 'Bun 1.0.0+'; } getInstallCommand() { return 'bun install'; } getDevCommand() { return 'bun run dev'; } getProdCommand() { return 'bun run start'; } getTestCommand() { return 'bun test'; } getCoverageCommand() { return 'bun test --coverage'; } getLintCommand() { return 'bun run lint'; } getBuildCommand() { return 'bun run build'; } getSetupAction() { return 'oven-sh/setup-bun@v1'; } async generateBuildScript(projectPath, options) { const buildScriptContent = `#!/usr/bin/env bun // Build script for ${this.config.framework} application import { $ } from 'bun'; import { mkdir, rm } from 'fs/promises'; import { existsSync } from 'fs'; console.log('๐Ÿ”จ Building ${this.config.framework} application...'); // Clean dist directory if (existsSync('./dist')) { await rm('./dist', { recursive: true }); } await mkdir('./dist', { recursive: true }); // Run TypeScript compiler for type checking console.log('๐Ÿ“ Type checking...'); await $\`bun run typecheck\`; // Build the application console.log('๐Ÿ“ฆ Building application...'); await $\`bun build src/index.ts --target=bun --outdir=dist --minify\`; // Copy static files if needed // await $\`cp -r public dist/\`; console.log('โœ… Build complete!'); `; await fs_1.promises.writeFile(path.join(projectPath, 'scripts', 'build.ts'), buildScriptContent); await fs_1.promises.chmod(path.join(projectPath, 'scripts', 'build.ts'), 0o755); } } exports.BunBackendGenerator = BunBackendGenerator;