UNPKG

@foxframework/core

Version:

A modern, production-ready web framework for TypeScript/Node.js with modular routing, integrated template engine, CLI tools, and enterprise features

319 lines (298 loc) • 11 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateNewProject = exports.generateView = exports.generateModel = exports.generateController = exports.formatFileName = exports.formatClassName = void 0; // src/cli/generators.ts const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const templatesDir = path_1.default.resolve(__dirname, 'templates'); // Helper function to format class names (PascalCase) const formatClassName = (name) => { // Handle camelCase, PascalCase, kebab-case, snake_case, and combinations return name // First, handle camelCase and PascalCase by inserting separators before uppercase letters .replace(/([a-z])([A-Z])/g, '$1-$2') // Then split by common separators .split(/[-_\s]+/) .filter(word => word.length > 0) .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) .join(''); }; exports.formatClassName = formatClassName; // Helper function to format file names (kebab-case) const formatFileName = (name) => { // Handle common cases like APIController -> api-controller (not a-p-i-controller) return name // Insert dash before uppercase letters that follow lowercase letters .replace(/([a-z])([A-Z])/g, '$1-$2') // Handle sequences of uppercase letters followed by lowercase letters (like API -> Api) .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2') .toLowerCase() // Replace multiple separators with single dash .replace(/[-_\s]+/g, '-') // Remove leading/trailing dashes .replace(/^-|-$/g, ''); }; exports.formatFileName = formatFileName; const createFileFromTemplate = (name, templateFile, targetDir, extension, fileBaseName) => { try { const templatePath = path_1.default.join(templatesDir, templateFile); const className = (0, exports.formatClassName)(name); const fileName = (0, exports.formatFileName)(fileBaseName || name); const targetPath = path_1.default.join(targetDir, `${fileName}.${extension}`); // Check if template exists, if not create a basic template let templateContent = ''; if (fs_1.default.existsSync(templatePath)) { templateContent = fs_1.default.readFileSync(templatePath, 'utf8'); } else { // Create basic controller template if template file doesn't exist if (extension === 'controller.ts') { templateContent = createBasicControllerTemplate(); } else { templateContent = `// Generated ${className}`; } } // Ensure templateContent is not null or undefined if (!templateContent) { templateContent = createBasicControllerTemplate(); } const content = templateContent .replace(/__NAME__/g, className) .replace(/__FILENAME__/g, fileName); fs_1.default.writeFileSync(targetPath, content); console.log(`Generated ${targetPath}`); } catch (error) { console.error(`Error generating ${name}:`, error); throw error; } }; const createBasicControllerTemplate = () => { return `import { Request, Response } from 'express'; export class __NAME__Controller { /** * GET / * Index - List all items */ public index(req: Request, res: Response): void { res.json({ message: '__NAME__ index', data: [] }); } /** * GET /:id * Show - Get specific item */ public show(req: Request, res: Response): void { const { id } = req.params; res.json({ message: '__NAME__ show', id: id, data: {} }); } /** * POST / * Create - Create new item */ public create(req: Request, res: Response): void { const data = req.body; res.status(201).json({ message: '__NAME__ created', data: data }); } /** * PUT /:id * Update - Update existing item */ public update(req: Request, res: Response): void { const { id } = req.params; const data = req.body; res.json({ message: '__NAME__ updated', id: id, data: data }); } /** * DELETE /:id * Delete - Delete item */ public delete(req: Request, res: Response): void { const { id } = req.params; res.json({ message: '__NAME__ deleted', id: id }); } } export default __NAME__Controller; `; }; const generateController = (name) => { if (!name || name.trim() === '') { throw new Error('Controller name cannot be empty'); } // Handle nested paths like 'admin/user' const parts = name.split('/'); const controllerName = parts.pop() || name; const subDir = parts.length > 0 ? parts.join('/').toLowerCase() : ''; // For nested paths, combine all parts for class name (Admin/User -> AdminUser) const fullControllerName = parts.length > 0 ? parts.join('') + controllerName : controllerName; const targetDir = subDir ? path_1.default.resolve(process.cwd(), 'src/controllers', subDir) : path_1.default.resolve(process.cwd(), 'src/controllers'); if (!fs_1.default.existsSync(targetDir)) { fs_1.default.mkdirSync(targetDir, { recursive: true }); } // Use the original controllerName for the file, not the full combined name createFileFromTemplate(fullControllerName, 'controller.ts.template', targetDir, 'controller.ts', controllerName); }; exports.generateController = generateController; const generateModel = (name) => { const targetDir = path_1.default.resolve(__dirname, '../models'); if (!fs_1.default.existsSync(targetDir)) { fs_1.default.mkdirSync(targetDir, { recursive: true }); } createFileFromTemplate(name, 'model.ts.template', targetDir, 'ts'); }; exports.generateModel = generateModel; const generateView = (name) => { const targetDir = path_1.default.resolve(__dirname, '../views'); if (!fs_1.default.existsSync(targetDir)) { fs_1.default.mkdirSync(targetDir, { recursive: true }); } createFileFromTemplate(name, 'view.html.template', targetDir, 'html'); }; exports.generateView = generateView; // Generate new Fox Framework project const generateNewProject = async (projectName, template = 'basic') => { const targetDir = path_1.default.resolve(process.cwd(), projectName); // Check if directory already exists if (fs_1.default.existsSync(targetDir)) { console.error(`āŒ Directory '${projectName}' already exists!`); process.exit(1); } console.log(`🦊 Creating new Fox Framework project: ${projectName}`); console.log(`šŸ“ Template: ${template}`); try { // Create project directory fs_1.default.mkdirSync(targetDir, { recursive: true }); // Create basic project structure const directories = [ 'src/controllers', 'src/services', 'src/routes', 'src/server', 'src/views', 'tsfox' ]; directories.forEach(dir => { fs_1.default.mkdirSync(path_1.default.join(targetDir, dir), { recursive: true }); }); // Create package.json const packageJson = { "name": projectName, "version": "1.0.0", "description": `${projectName} - A Fox Framework application`, "main": "dist/src/server/index.js", "scripts": { "start": "node dist/src/server/index.js", "dev": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/server/index.ts", "build": "tsc", "test": "jest" }, "dependencies": { "@foxframework/core": "^1.0.0", "express": "^4.18.2", "typescript": "^5.3.2" }, "devDependencies": { "@types/express": "^4.17.21", "@types/node": "^20.10.4", "nodemon": "^3.0.1", "ts-node": "^10.9.1" } }; fs_1.default.writeFileSync(path_1.default.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2)); // Create tsconfig.json const tsConfig = { "compilerOptions": { "target": "ES2020", "module": "commonjs", "lib": ["ES2020"], "outDir": "./dist", "rootDir": "./", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "declaration": true, "declarationMap": true, "sourceMap": true }, "include": ["src/**/*", "tsfox/**/*"], "exclude": ["node_modules", "dist", "**/*.test.ts"] }; fs_1.default.writeFileSync(path_1.default.join(targetDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2)); // Create basic server file const serverContent = `import { FoxFactory } from '@foxframework/core'; import express from 'express'; const app = express(); const fox = new FoxFactory(app); // Basic route fox.get('/', (req, res) => { res.json({ message: 'Welcome to ${projectName}!' }); }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(\`🦊 ${projectName} server running on port \${PORT}\`); }); `; fs_1.default.writeFileSync(path_1.default.join(targetDir, 'src/server/index.ts'), serverContent); // Create README const readmeContent = `# ${projectName} A Fox Framework application. ## Getting Started \`\`\`bash # Install dependencies npm install # Start development server npm run dev # Build for production npm run build # Start production server npm start \`\`\` ## Commands \`\`\`bash # Generate controller npx tsfox generate:controller UserController # Generate model npx tsfox generate:model User # Generate view npx tsfox generate:view user-profile \`\`\` `; fs_1.default.writeFileSync(path_1.default.join(targetDir, 'README.md'), readmeContent); console.log(`āœ… Project '${projectName}' created successfully!`); console.log(`\nšŸ“‹ Next steps:`); console.log(` cd ${projectName}`); console.log(` npm install`); console.log(` npm run dev`); console.log(`\n🦊 Happy coding with Fox Framework!`); } catch (error) { console.error(`āŒ Error creating project:`, error); process.exit(1); } }; exports.generateNewProject = generateNewProject;