@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
JavaScript
"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;