@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,285 lines (1,148 loc) • 40 kB
JavaScript
"use strict";
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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateCode = generateCode;
exports.generateTests = generateTests;
exports.generateDocumentation = generateDocumentation;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const chalk_1 = __importDefault(require("chalk"));
const monorepo_1 = require("../utils/monorepo");
const backend_template_registry_1 = __importDefault(require("../templates/backend/backend-template-registry"));
async function generateCode(name, options = {}) {
try {
const monorepoRoot = await (0, monorepo_1.findMonorepoRoot)(process.cwd());
if (!monorepoRoot) {
throw new Error('Not in a Re-Shell monorepo. Run this command from within a monorepo.');
}
if (options.spinner) {
options.spinner.text = `Generating ${options.type || 'component'}...`;
}
const generator = getGenerator(options.type || 'component');
await generator(monorepoRoot, name, options);
if (options.spinner) {
options.spinner.succeed(chalk_1.default.green(`${options.type || 'Component'} "${name}" generated successfully!`));
}
console.log('\n' + chalk_1.default.bold('Generated:'));
console.log(` Type: ${options.type || 'component'}`);
console.log(` Name: ${name}`);
console.log(` Framework: ${options.framework || 'react'}`);
}
catch (error) {
if (options.spinner) {
options.spinner.fail(chalk_1.default.red('Code generation failed'));
}
throw error;
}
}
async function generateTests(workspace, options = {}) {
try {
const monorepoRoot = await (0, monorepo_1.findMonorepoRoot)(process.cwd());
if (!monorepoRoot) {
throw new Error('Not in a Re-Shell monorepo');
}
if (options.spinner) {
options.spinner.text = 'Generating tests...';
}
const workspacePath = path.join(monorepoRoot, workspace);
if (!await fs.pathExists(workspacePath)) {
throw new Error(`Workspace not found: ${workspace}`);
}
await generateTestSuite(workspacePath, options);
if (options.spinner) {
options.spinner.succeed(chalk_1.default.green('Test suite generated successfully!'));
}
}
catch (error) {
if (options.spinner) {
options.spinner.fail(chalk_1.default.red('Test generation failed'));
}
throw error;
}
}
async function generateDocumentation(options = {}) {
try {
const monorepoRoot = await (0, monorepo_1.findMonorepoRoot)(process.cwd());
if (!monorepoRoot) {
throw new Error('Not in a Re-Shell monorepo');
}
if (options.spinner) {
options.spinner.text = 'Generating documentation...';
}
await generateProjectDocs(monorepoRoot, options);
await generateAPIDocumentation(monorepoRoot, options);
await generateReadme(monorepoRoot, options);
if (options.spinner) {
options.spinner.succeed(chalk_1.default.green('Documentation generated successfully!'));
}
}
catch (error) {
if (options.spinner) {
options.spinner.fail(chalk_1.default.red('Documentation generation failed'));
}
throw error;
}
}
function getGenerator(type) {
const generators = {
component: generateComponent,
hook: generateHook,
service: generateService,
test: generateTestFile,
config: generateConfig,
documentation: generateDocs,
backend: generateBackend
};
const generator = generators[type];
if (!generator) {
throw new Error(`Unknown generator type: ${type}`);
}
return generator;
}
async function generateComponent(monorepoRoot, name, options) {
const framework = options.framework || 'react';
const workspace = options.workspace || findBestWorkspace(monorepoRoot, 'component');
const workspacePath = path.join(monorepoRoot, workspace);
if (!await fs.pathExists(workspacePath)) {
throw new Error(`Workspace not found: ${workspace}`);
}
const template = getComponentTemplate(name, framework, options);
for (const file of template.files) {
const filePath = path.join(workspacePath, file.path);
await fs.ensureDir(path.dirname(filePath));
await fs.writeFile(filePath, file.content);
if (options.verbose) {
console.log(chalk_1.default.green(` ✓ Created ${file.path}`));
}
}
// Update exports if requested
if (options.export) {
await updateExports(workspacePath, name, template);
}
}
async function generateHook(monorepoRoot, name, options) {
const framework = options.framework || 'react';
if (framework !== 'react') {
throw new Error('Hooks are currently only supported for React');
}
const workspace = options.workspace || findBestWorkspace(monorepoRoot, 'hook');
const workspacePath = path.join(monorepoRoot, workspace);
const hookName = name.startsWith('use') ? name : `use${name.charAt(0).toUpperCase() + name.slice(1)}`;
const hookContent = `import { useState, useEffect } from 'react';
export interface ${hookName.charAt(3).toUpperCase() + hookName.slice(4)}Options {
// Add your options here
}
export function ${hookName}(options?: ${hookName.charAt(3).toUpperCase() + hookName.slice(4)}Options) {
const [state, setState] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
// Implement your hook logic here
setLoading(true);
// Example async operation
const performOperation = async () => {
try {
// Your logic here
setState(null);
} catch (err) {
setError(err instanceof Error ? err : new Error('Unknown error'));
} finally {
setLoading(false);
}
};
performOperation();
}, []);
return {
state,
loading,
error,
// Add your return values here
};
}
`;
const testContent = `import { renderHook } from '@testing-library/react';
import { ${hookName} } from './${hookName}';
describe('${hookName}', () => {
it('should initialize with default values', () => {
const { result } = renderHook(() => ${hookName}());
expect(result.current.state).toBeNull();
expect(result.current.loading).toBe(false);
expect(result.current.error).toBeNull();
});
it('should handle options', () => {
const options = { /* your options */ };
const { result } = renderHook(() => ${hookName}(options));
// Add your assertions here
});
});
`;
const hooksDir = path.join(workspacePath, 'src', 'hooks');
await fs.ensureDir(hooksDir);
await fs.writeFile(path.join(hooksDir, `${hookName}.ts`), hookContent);
await fs.writeFile(path.join(hooksDir, `${hookName}.test.ts`), testContent);
if (options.verbose) {
console.log(chalk_1.default.green(` ✓ Created src/hooks/${hookName}.ts`));
console.log(chalk_1.default.green(` ✓ Created src/hooks/${hookName}.test.ts`));
}
}
async function generateService(monorepoRoot, name, options) {
const workspace = options.workspace || findBestWorkspace(monorepoRoot, 'service');
const workspacePath = path.join(monorepoRoot, workspace);
const serviceName = `${name.charAt(0).toUpperCase() + name.slice(1)}Service`;
const serviceContent = `export interface ${serviceName}Config {
baseURL?: string;
timeout?: number;
retries?: number;
}
export class ${serviceName} {
private config: ${serviceName}Config;
constructor(config: ${serviceName}Config = {}) {
this.config = {
baseURL: process.env.API_BASE_URL || 'http://localhost:3000',
timeout: 10000,
retries: 3,
...config
};
}
async get<T>(endpoint: string): Promise<T> {
return this.request<T>('GET', endpoint);
}
async post<T>(endpoint: string, data?: any): Promise<T> {
return this.request<T>('POST', endpoint, data);
}
async put<T>(endpoint: string, data?: any): Promise<T> {
return this.request<T>('PUT', endpoint, data);
}
async delete<T>(endpoint: string): Promise<T> {
return this.request<T>('DELETE', endpoint);
}
private async request<T>(
method: string,
endpoint: string,
data?: any
): Promise<T> {
const url = \`\${this.config.baseURL}\${endpoint}\`;
const requestConfig: RequestInit = {
method,
headers: {
'Content-Type': 'application/json',
},
};
if (data) {
requestConfig.body = JSON.stringify(data);
}
let lastError: Error;
for (let attempt = 0; attempt < (this.config.retries || 1); attempt++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
const response = await fetch(url, {
...requestConfig,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(\`HTTP error! status: \${response.status}\`);
}
return await response.json();
} catch (error) {
lastError = error instanceof Error ? error : new Error('Unknown error');
if (attempt < (this.config.retries || 1) - 1) {
await new Promise(resolve => setTimeout(resolve, 1000 * (attempt + 1)));
}
}
}
throw lastError!;
}
}
// Export singleton instance
export const ${name}Service = new ${serviceName}();
`;
const testContent = `import { ${serviceName} } from './${serviceName}';
// Mock fetch
global.fetch = jest.fn();
describe('${serviceName}', () => {
let service: ${serviceName};
beforeEach(() => {
service = new ${serviceName}();
(fetch as jest.MockedFunction<typeof fetch>).mockClear();
});
it('should make GET requests', async () => {
const mockResponse = { id: 1, name: 'test' };
(fetch as jest.MockedFunction<typeof fetch>).mockResolvedValueOnce(
new Response(JSON.stringify(mockResponse), { status: 200 })
);
const result = await service.get('/api/test');
expect(result).toEqual(mockResponse);
expect(fetch).toHaveBeenCalledWith(
'http://localhost:3000/api/test',
expect.objectContaining({ method: 'GET' })
);
});
it('should make POST requests', async () => {
const mockData = { name: 'test' };
const mockResponse = { id: 1, ...mockData };
(fetch as jest.MockedFunction<typeof fetch>).mockResolvedValueOnce(
new Response(JSON.stringify(mockResponse), { status: 200 })
);
const result = await service.post('/api/test', mockData);
expect(result).toEqual(mockResponse);
expect(fetch).toHaveBeenCalledWith(
'http://localhost:3000/api/test',
expect.objectContaining({
method: 'POST',
body: JSON.stringify(mockData)
})
);
});
it('should handle errors', async () => {
(fetch as jest.MockedFunction<typeof fetch>).mockRejectedValueOnce(
new Error('Network error')
);
await expect(service.get('/api/test')).rejects.toThrow('Network error');
});
});
`;
const servicesDir = path.join(workspacePath, 'src', 'services');
await fs.ensureDir(servicesDir);
await fs.writeFile(path.join(servicesDir, `${serviceName}.ts`), serviceContent);
await fs.writeFile(path.join(servicesDir, `${serviceName}.test.ts`), testContent);
if (options.verbose) {
console.log(chalk_1.default.green(` ✓ Created src/services/${serviceName}.ts`));
console.log(chalk_1.default.green(` ✓ Created src/services/${serviceName}.test.ts`));
}
}
async function generateTestFile(monorepoRoot, name, options) {
const workspace = options.workspace || findBestWorkspace(monorepoRoot, 'test');
const workspacePath = path.join(monorepoRoot, workspace);
const testContent = `describe('${name}', () => {
beforeEach(() => {
// Setup before each test
});
afterEach(() => {
// Cleanup after each test
});
it('should work correctly', () => {
// Your test implementation
expect(true).toBe(true);
});
it('should handle edge cases', () => {
// Edge case testing
expect(true).toBe(true);
});
it('should handle errors gracefully', () => {
// Error handling tests
expect(true).toBe(true);
});
});
`;
const testsDir = path.join(workspacePath, 'src', '__tests__');
await fs.ensureDir(testsDir);
await fs.writeFile(path.join(testsDir, `${name}.test.ts`), testContent);
if (options.verbose) {
console.log(chalk_1.default.green(` ✓ Created src/__tests__/${name}.test.ts`));
}
}
async function generateConfig(monorepoRoot, name, options) {
const configContent = `export interface ${name.charAt(0).toUpperCase() + name.slice(1)}Config {
// Add your configuration properties here
environment: 'development' | 'staging' | 'production';
apiUrl: string;
features: {
[key: string]: boolean;
};
}
const config: ${name.charAt(0).toUpperCase() + name.slice(1)}Config = {
environment: (process.env.NODE_ENV as any) || 'development',
apiUrl: process.env.API_URL || 'http://localhost:3000',
features: {
// Feature flags
newFeature: process.env.ENABLE_NEW_FEATURE === 'true',
},
};
export default config;
`;
const configDir = path.join(monorepoRoot, 'config');
await fs.ensureDir(configDir);
await fs.writeFile(path.join(configDir, `${name}.config.ts`), configContent);
if (options.verbose) {
console.log(chalk_1.default.green(` ✓ Created config/${name}.config.ts`));
}
}
async function generateDocs(monorepoRoot, name, options) {
const docsContent = `# ${name.charAt(0).toUpperCase() + name.slice(1)}
## Overview
Brief description of ${name}.
## Installation
\`\`\`bash
pnpm install
\`\`\`
## Usage
\`\`\`typescript
// Example usage
import { ${name} } from './${name}';
const result = ${name}();
\`\`\`
## API Reference
### Functions
#### \`${name}()\`
Description of the function.
**Parameters:**
- \`param1\` (string): Description of parameter 1
- \`param2\` (number): Description of parameter 2
**Returns:**
- \`ReturnType\`: Description of return value
**Example:**
\`\`\`typescript
const result = ${name}('example', 42);
\`\`\`
## Examples
### Basic Example
\`\`\`typescript
// Basic usage example
\`\`\`
### Advanced Example
\`\`\`typescript
// Advanced usage example
\`\`\`
## Contributing
Please read our [Contributing Guide](../CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
## License
This project is licensed under the MIT License - see the [LICENSE](../LICENSE) file for details.
`;
const docsDir = path.join(monorepoRoot, 'docs');
await fs.ensureDir(docsDir);
await fs.writeFile(path.join(docsDir, `${name}.md`), docsContent);
if (options.verbose) {
console.log(chalk_1.default.green(` ✓ Created docs/${name}.md`));
}
}
async function generateBackend(monorepoRoot, name, options) {
const workspace = options.workspace || `services/${name}`;
const workspacePath = path.join(monorepoRoot, workspace);
if (!await fs.pathExists(path.dirname(workspacePath))) {
await fs.ensureDir(path.dirname(workspacePath));
}
// Use the new backend template registry
if (options.template) {
const template = backend_template_registry_1.default.get(options.template);
if (!template) {
throw new Error(`Backend template '${options.template}' not found. Use 're-shell list templates' to see available templates.`);
}
// Validate template has a generator
const isValid = await backend_template_registry_1.default.validateTemplate(options.template);
if (!isValid) {
throw new Error(`Backend template '${options.template}' is not yet implemented. Check back soon!`);
}
// Generate using the new template system
await backend_template_registry_1.default.generateTemplate(options.template, workspacePath, {
name,
port: options.port,
features: options.features || [],
verbose: options.verbose
});
if (options.verbose) {
console.log(chalk_1.default.green(`\n✅ Generated ${template.framework} (${template.language}) backend service`));
console.log(backend_template_registry_1.default.getTemplateInfo(options.template));
}
return;
}
// Legacy support for backward compatibility
const language = options.language || 'typescript';
const framework = options.framework || (language === 'python' ? 'fastapi' : language === 'php' ? 'laravel' : 'express');
const features = options.features || [];
// Try to find a matching template in the registry
const templates = backend_template_registry_1.default.searchTemplates(`${language}-${framework}`);
if (templates.length > 0) {
const template = templates[0];
console.log(chalk_1.default.yellow(`Using new template system: ${template.name}`));
await backend_template_registry_1.default.generateTemplate(template.name, workspacePath, {
name,
port: options.port,
features,
verbose: options.verbose
});
return;
}
// Fallback to old template system for backward compatibility
if (framework === 'laravel' || framework === 'symfony' || framework === 'slim' || framework === 'codeigniter') {
const { backendTemplates } = await Promise.resolve().then(() => __importStar(require('../templates/backend')));
const template = backendTemplates[framework];
if (template) {
// Generate files from template
for (const [filePath, content] of Object.entries(template.files)) {
const processedContent = content
.replace(/{{serviceName}}/g, name)
.replace(/{{projectName}}/g, name)
.replace(/{{PORT}}/g, options.port || '8000');
const fullPath = path.join(workspacePath, filePath);
await fs.ensureDir(path.dirname(fullPath));
await fs.writeFile(fullPath, processedContent);
if (options.verbose) {
console.log(chalk_1.default.green(` ✓ Created ${filePath}`));
}
}
return;
}
}
if (language === 'python') {
// Import Python generators
const { PythonCodeQualityGenerator } = await Promise.resolve().then(() => __importStar(require('../templates/backend/python-code-quality')));
const { CeleryTaskGenerator } = await Promise.resolve().then(() => __importStar(require('../templates/backend/python-celery-tasks')));
const { RedisIntegrationGenerator } = await Promise.resolve().then(() => __importStar(require('../templates/backend/python-redis')));
const codeQualityGen = new PythonCodeQualityGenerator();
const celeryGen = new CeleryTaskGenerator();
const redisGen = new RedisIntegrationGenerator();
// Generate code quality configuration if requested
if (features.includes('code-quality')) {
const qualityConfig = {
framework,
enableStrict: true,
enableAutofix: true,
enablePreCommit: true,
enableVSCode: true,
pythonVersion: '3.11'
};
const qualityFiles = codeQualityGen.generateCodeQualityConfig(qualityConfig);
for (const file of qualityFiles) {
const filePath = path.join(workspacePath, file.path);
await fs.ensureDir(path.dirname(filePath));
await fs.writeFile(filePath, file.content);
if (options.verbose) {
console.log(chalk_1.default.green(` ✓ Created ${file.path}`));
}
}
}
// Generate Celery configuration if requested
if (features.includes('celery')) {
const celeryConfig = {
framework,
enableScheduling: true,
enableMonitoring: true,
enableRetries: true,
enablePriority: true,
enableRouting: true,
enableResultBackend: true
};
const celeryFiles = celeryGen.generateCeleryConfig(celeryConfig);
for (const file of celeryFiles) {
const filePath = path.join(workspacePath, file.path);
await fs.ensureDir(path.dirname(filePath));
await fs.writeFile(filePath, file.content);
if (options.verbose) {
console.log(chalk_1.default.green(` ✓ Created ${file.path}`));
}
}
}
// Generate Redis integration if requested
if (features.includes('redis')) {
const redisConfig = {
projectName: name,
framework: framework,
hasTypeScript: false
};
const redisFiles = redisGen.generateRedisConfig(redisConfig);
for (const file of redisFiles) {
const filePath = path.join(workspacePath, file.path);
await fs.ensureDir(path.dirname(filePath));
await fs.writeFile(filePath, file.content);
if (options.verbose) {
console.log(chalk_1.default.green(` ✓ Created ${file.path}`));
}
}
}
// Generate basic Python project structure
await generatePythonBackend(workspacePath, name, framework, options);
}
else {
// Generate TypeScript/JavaScript backend
await generateTypeScriptBackend(workspacePath, name, framework, options);
}
console.log('\n' + chalk_1.default.bold('Backend service generated successfully!'));
console.log(chalk_1.default.gray(`Location: ${workspace}`));
console.log(chalk_1.default.gray(`Language: ${language}`));
console.log(chalk_1.default.gray(`Framework: ${framework}`));
if (features.length > 0) {
console.log(chalk_1.default.gray(`Features: ${features.join(', ')}`));
}
}
async function generatePythonBackend(workspacePath, name, framework, options) {
// Create basic Python project structure
const projectFiles = {
'README.md': `# ${name}
A ${framework} backend service.
## Installation
\`\`\`bash
pip install -r requirements.txt
\`\`\`
## Development
\`\`\`bash
# Run development server
python -m uvicorn main:app --reload
\`\`\`
## Testing
\`\`\`bash
pytest
\`\`\`
`,
'requirements.txt': getPythonRequirements(framework, options.features || []),
'.gitignore': `__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
.venv/
.env
.coverage
htmlcov/
.pytest_cache/
.mypy_cache/
.ruff_cache/
`,
'main.py': getPythonMainFile(framework, name),
'config.py': `import os
from typing import Optional
from pydantic import BaseSettings
class Settings(BaseSettings):
app_name: str = "${name}"
debug: bool = False
port: int = ${options.port || '8000'}
database_url: Optional[str] = None
redis_url: Optional[str] = None
class Config:
env_file = ".env"
settings = Settings()
`
};
for (const [filePath, content] of Object.entries(projectFiles)) {
const fullPath = path.join(workspacePath, filePath);
await fs.ensureDir(path.dirname(fullPath));
await fs.writeFile(fullPath, content);
if (options.verbose) {
console.log(chalk_1.default.green(` ✓ Created ${filePath}`));
}
}
}
async function generateTypeScriptBackend(workspacePath, name, framework, options) {
// Generate TypeScript backend (Express, NestJS, etc.)
const projectFiles = {
'package.json': `{
"name": "${name}",
"version": "1.0.0",
"description": "A ${framework} backend service",
"main": "dist/index.js",
"scripts": {
"dev": "nodemon src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "jest"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"dotenv": "^16.0.3"
},
"devDependencies": {
"@types/express": "^4.17.17",
"@types/node": "^20.0.0",
"typescript": "^5.0.0",
"nodemon": "^3.0.0",
"ts-node": "^10.9.0"
}
}`,
'tsconfig.json': `{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"lib": ["es2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}`,
'src/index.ts': `import express from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
const port = process.env.PORT || ${options.port || '8000'};
app.use(cors());
app.use(express.json());
app.get('/health', (req, res) => {
res.json({ status: 'ok', service: '${name}' });
});
app.listen(port, () => {
console.log(\`${name} service running on port \${port}\`);
});
`
};
for (const [filePath, content] of Object.entries(projectFiles)) {
const fullPath = path.join(workspacePath, filePath);
await fs.ensureDir(path.dirname(fullPath));
await fs.writeFile(fullPath, content);
if (options.verbose) {
console.log(chalk_1.default.green(` ✓ Created ${filePath}`));
}
}
}
function getPythonRequirements(framework, features = []) {
const base = `python-dotenv==1.0.0
pydantic==2.5.0
pytest==7.4.0
pytest-asyncio==0.21.0
pytest-cov==4.1.0
`;
const frameworkDeps = {
fastapi: `fastapi==0.104.0
uvicorn[standard]==0.24.0
sqlalchemy==2.0.0
alembic==1.12.0
httpx==0.25.0`,
django: `django==4.2.0
djangorestframework==3.14.0
django-cors-headers==4.3.0
gunicorn==21.2.0`,
flask: `flask==3.0.0
flask-cors==4.0.0
flask-sqlalchemy==3.1.0
flask-migrate==4.0.0
gunicorn==21.2.0`,
tornado: `tornado==6.3.0
tornado-sqlalchemy==0.8.0`,
sanic: `sanic==23.6.0
sanic-cors==2.2.0
sanic-openapi==21.12.0`
};
let requirements = base + (frameworkDeps[framework] || '');
// Add feature-specific dependencies
if (features.includes('redis')) {
requirements += `\n# Redis dependencies
redis==5.0.1
hiredis==2.2.3
`;
}
if (features.includes('celery')) {
requirements += `\n# Celery dependencies
celery==5.3.4
flower==2.0.1
redis==5.0.1
`;
}
if (features.includes('code-quality')) {
requirements += `\n# Code quality tools
black==23.11.0
isort==5.12.0
mypy==1.7.0
ruff==0.1.5
pre-commit==3.5.0
`;
}
return requirements;
}
function getPythonMainFile(framework, name) {
const templates = {
fastapi: `from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from config import settings
app = FastAPI(title=settings.app_name)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
.get("/health")
async def health_check():
return {"status": "ok", "service": settings.app_name}
.get("/")
async def root():
return {"message": f"Welcome to {settings.app_name}"}
`,
django: `"""
Django settings for ${name} project.
"""
from pathlib import Path
import os
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.getenv('SECRET_KEY', 'django-insecure-key')
DEBUG = os.getenv('DEBUG', 'False') == 'True'
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = '${name}.urls'
CORS_ALLOW_ALL_ORIGINS = True
`,
flask: `from flask import Flask, jsonify
from flask_cors import CORS
from config import settings
app = Flask(__name__)
CORS(app)
.route('/health')
def health_check():
return jsonify({"status": "ok", "service": settings.app_name})
.route('/')
def root():
return jsonify({"message": f"Welcome to {settings.app_name}"})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=settings.port, debug=settings.debug)
`
};
return templates[framework] || templates.fastapi;
}
function getComponentTemplate(name, framework, options) {
const pascalName = name.charAt(0).toUpperCase() + name.slice(1);
switch (framework) {
case 'react':
return {
name: pascalName,
files: [
{
path: `src/components/${pascalName}/${pascalName}.tsx`,
content: `import React from 'react';
import './${pascalName}.css';
export interface ${pascalName}Props {
children?: React.ReactNode;
className?: string;
}
export const ${pascalName}: React.FC<${pascalName}Props> = ({
children,
className = '',
...props
}) => {
return (
<div className={\`${name.toLowerCase()} \${className}\`} {...props}>
{children || <p>Hello from ${pascalName}!</p>}
</div>
);
};
`
},
{
path: `src/components/${pascalName}/${pascalName}.css`,
content: `.${name.toLowerCase()} {
/* Add your styles here */
padding: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
}
`
},
{
path: `src/components/${pascalName}/${pascalName}.test.tsx`,
content: `import React from 'react';
import { render, screen } from '@testing-library/react';
import { ${pascalName} } from './${pascalName}';
describe('${pascalName}', () => {
it('renders without crashing', () => {
render(<${pascalName} />);
expect(screen.getByText('Hello from ${pascalName}!')).toBeInTheDocument();
});
it('renders children when provided', () => {
render(<${pascalName}>Custom content</${pascalName}>);
expect(screen.getByText('Custom content')).toBeInTheDocument();
});
it('applies custom className', () => {
const { container } = render(<${pascalName} className="custom-class" />);
expect(container.firstChild).toHaveClass('${name.toLowerCase()}', 'custom-class');
});
});
`
},
{
path: `src/components/${pascalName}/index.ts`,
content: `export { ${pascalName} } from './${pascalName}';
export type { ${pascalName}Props } from './${pascalName}';
`
}
]
};
case 'vue':
return {
name: pascalName,
files: [
{
path: `src/components/${pascalName}.vue`,
content: `<template>
<div class="${name.toLowerCase()}">
<slot>Hello from ${pascalName}!</slot>
</div>
</template>
<script setup lang="ts">
interface Props {
modelValue?: any;
}
defineProps<Props>();
defineEmits<{
'update:modelValue': [value: any];
}>();
</script>
<style scoped>
.${name.toLowerCase()} {
/* Add your styles here */
padding: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
}
</style>
`
}
]
};
case 'svelte':
return {
name: pascalName,
files: [
{
path: `src/components/${pascalName}.svelte`,
content: `<script lang="ts">
export let className: string = '';
</script>
<div class="${name.toLowerCase()} {className}">
<slot>Hello from ${pascalName}!</slot>
</div>
<style>
.${name.toLowerCase()} {
/* Add your styles here */
padding: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
}
</style>
`
}
]
};
default:
throw new Error(`Unsupported framework: ${framework}`);
}
}
async function generateTestSuite(workspacePath, options) {
// Generate Jest configuration
const jestConfig = `module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
moduleNameMapping: {
'\\\\.(css|less|scss|sass)$': 'identity-obj-proxy',
},
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/index.ts',
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
};
`;
await fs.writeFile(path.join(workspacePath, 'jest.config.js'), jestConfig);
// Generate test setup
const setupTests = `import '@testing-library/jest-dom';
// Mock IntersectionObserver
global.IntersectionObserver = class IntersectionObserver {
constructor() {}
disconnect() {}
observe() {}
unobserve() {}
};
// Mock ResizeObserver
global.ResizeObserver = class ResizeObserver {
constructor() {}
disconnect() {}
observe() {}
unobserve() {}
};
`;
await fs.writeFile(path.join(workspacePath, 'src', 'setupTests.ts'), setupTests);
// Generate example test utilities
const testUtils = `import React from 'react';
import { render, RenderOptions } from '@testing-library/react';
// Add providers here if needed
const AllTheProviders: React.FC<{ children: React.ReactNode }> = ({ children }) => {
return <>{children}</>;
};
const customRender = (ui: React.ReactElement, options?: Omit<RenderOptions, 'wrapper'>) =>
render(ui, { wrapper: AllTheProviders, ...options });
export * from '@testing-library/react';
export { customRender as render };
`;
const testUtilsDir = path.join(workspacePath, 'src', 'test-utils');
await fs.ensureDir(testUtilsDir);
await fs.writeFile(path.join(testUtilsDir, 'index.ts'), testUtils);
}
async function generateProjectDocs(monorepoRoot, options) {
const packageJson = await fs.readJson(path.join(monorepoRoot, 'package.json'));
const readme = `# ${packageJson.name || 'Re-Shell Project'}
${packageJson.description || 'A Re-Shell monorepo project'}
## 📁 Project Structure
\`\`\`
${packageJson.name || 'project'}/
├── apps/ # Applications
├── packages/ # Shared packages
├── libs/ # Libraries
├── tools/ # Build tools and scripts
├── docs/ # Documentation
└── README.md
\`\`\`
## 🚀 Quick Start
1. **Install dependencies**
\`\`\`bash
pnpm install
\`\`\`
2. **Start development**
\`\`\`bash
pnpm run dev
\`\`\`
3. **Build all packages**
\`\`\`bash
pnpm run build
\`\`\`
## 📋 Available Commands
| Command | Description |
|---------|-------------|
| \`pnpm run dev\` | Start development servers |
| \`pnpm run build\` | Build all packages |
| \`pnpm run test\` | Run all tests |
| \`pnpm run lint\` | Lint all packages |
| \`re-shell doctor\` | Health check |
| \`re-shell analyze\` | Bundle analysis |
## 🏗️ Development
### Adding New Packages
\`\`\`bash
re-shell create my-package --type package
\`\`\`
### Running Tests
\`\`\`bash
# Run all tests
pnpm run test
# Run tests for specific package
pnpm --filter my-package test
\`\`\`
### Health Check
\`\`\`bash
re-shell doctor --verbose
\`\`\`
## 📚 Documentation
- [Architecture](./docs/architecture.md)
- [Contributing](./CONTRIBUTING.md)
- [Deployment](./docs/deployment.md)
## 🤝 Contributing
Please read our [Contributing Guide](./CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
`;
await fs.writeFile(path.join(monorepoRoot, 'README.md'), readme);
}
async function generateAPIDocumentation(monorepoRoot, options) {
// Generate TypeDoc configuration
const typeDocConfig = `{
"entryPoints": ["packages/*/src/index.ts"],
"out": "docs/api",
"exclude": ["**/*.test.ts", "**/*.spec.ts"],
"excludePrivate": true,
"excludeProtected": true,
"hideGenerator": true,
"sort": ["source-order"],
"gitRevision": "main",
"readme": "README.md"
}
`;
await fs.writeFile(path.join(monorepoRoot, 'typedoc.json'), typeDocConfig);
}
async function generateReadme(monorepoRoot, options) {
// This is handled by generateProjectDocs
}
function findBestWorkspace(monorepoRoot, type) {
// Default workspace patterns based on type
const workspacePatterns = {
component: ['apps/*', 'packages/ui', 'packages/components'],
hook: ['packages/hooks', 'packages/ui', 'apps/*'],
service: ['packages/services', 'packages/api', 'apps/*'],
test: ['apps/*', 'packages/*'],
config: ['packages/config', 'packages/core'],
documentation: ['docs']
};
const patterns = workspacePatterns[type] || ['apps/*'];
// Return the first pattern (simplified logic)
return patterns[0].replace('*', 'web');
}
async function updateExports(workspacePath, name, template) {
const indexPath = path.join(workspacePath, 'src', 'index.ts');
if (await fs.pathExists(indexPath)) {
const content = await fs.readFile(indexPath, 'utf8');
const exportLine = `export { ${template.name} } from './components/${template.name}';`;
if (!content.includes(exportLine)) {
await fs.appendFile(indexPath, `\n${exportLine}\n`);
}
}
else {
const content = `export { ${template.name} } from './components/${template.name}';\n`;
await fs.writeFile(indexPath, content);
}
}