UNPKG

@diagramers/cli

Version:

Diagramers CLI - Command-line tools for managing Diagramers projects

409 lines (385 loc) • 15.6 kB
"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.ProjectInitializer = void 0; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const glob = __importStar(require("glob")); const chalk_1 = __importDefault(require("chalk")); const child_process_1 = require("child_process"); class ProjectInitializer { constructor() { this.templateFiles = [ 'scripts', 'src/**/*', 'scripts/**/*', 'package.json', 'tsconfig.json', 'webpack.config.js', 'main.ts', 'certs/**/*', '.env.example' ]; // No need for template path as we'll use npm package } async initialize(projectName, options) { const projectPath = path.resolve(process.cwd(), projectName); const templateType = options.template || 'api'; const targetVersion = options.version || (templateType === 'admin' ? '3.0.17' : 'latest'); // Check if project directory already exists if (await fs.pathExists(projectPath)) { throw new Error(`Project directory ${projectName} already exists`); } // Create project directory await fs.ensureDir(projectPath); if (templateType === 'admin') { // Handle admin projects differently await this.initializeAdminProject(projectPath, projectName, targetVersion, options); } else { // Handle API projects (existing logic) await this.initializeApiProject(projectPath, projectName, templateType, targetVersion, options); } console.log(chalk_1.default.green(`āœ… Project structure created successfully`)); } async initializeAdminProject(projectPath, projectName, targetVersion, options) { console.log(chalk_1.default.blue(`šŸš€ Initializing admin project: ${projectName}`)); try { // Use the admin CLI to initialize the project const adminCommand = `npx @diagramers/admin@${targetVersion} init ${projectName}`; // Add options if provided const commandOptions = []; if (options.yes) commandOptions.push('--yes'); const fullCommand = `${adminCommand} ${commandOptions.join(' ')}`; console.log(chalk_1.default.gray(`Executing: ${fullCommand}`)); (0, child_process_1.execSync)(fullCommand, { stdio: 'inherit', cwd: path.dirname(projectPath) }); console.log(chalk_1.default.green(`āœ… Admin project '${projectName}' initialized successfully!`)); } catch (error) { throw new Error(`Failed to initialize admin project: ${error.message}`); } } async initializeApiProject(projectPath, projectName, templateType, targetVersion, options) { // Install the appropriate template package await this.installTemplatePackage(projectPath, templateType, targetVersion); // Copy template files from the installed package await this.copyTemplateFiles(projectPath, templateType); // Update package.json with project name await this.updatePackageJson(projectPath, projectName, templateType); // Create .gitignore await this.createGitignore(projectPath); // Create README await this.createReadme(projectPath, projectName, templateType); // Make scripts executable await this.makeScriptsExecutable(projectPath); // Process template for project name (update database names, etc.) await this.processTemplate(projectPath, projectName); // Ensure .env.example is synced with the latest from the template package await this.syncEnvExampleFromTemplate(projectPath, projectName, templateType); // Clean up the temporary installation await this.cleanup(projectPath); } async installTemplatePackage(projectPath, templateType, targetVersion) { const packageName = `@diagramers/${templateType}`; console.log(chalk_1.default.blue(`šŸ“¦ Installing ${packageName} template...`)); try { // Create a temporary package.json to enable npm install const tempPackageJson = { name: 'temp-project', version: '1.0.0', private: true }; await fs.writeFile(path.join(projectPath, 'package.json'), JSON.stringify(tempPackageJson, null, 2)); // Install the template package temporarily (0, child_process_1.execSync)(`npm install ${packageName}@${targetVersion} --no-save`, { cwd: projectPath, stdio: 'inherit' }); } catch (error) { throw new Error(`Failed to install ${packageName} package: ${error.message}`); } } async copyTemplateFiles(projectPath, templateType) { const nodeModulesPath = path.join(projectPath, 'node_modules', '@diagramers', templateType); if (!await fs.pathExists(nodeModulesPath)) { throw new Error(`@diagramers/${templateType} package not found in node_modules`); } for (const pattern of this.templateFiles) { try { const files = glob.sync(pattern, { cwd: nodeModulesPath, ignore: ['node_modules/**', 'dist/**', '.git/**'], nodir: false }); for (const file of files) { const sourcePath = path.join(nodeModulesPath, file); const destPath = path.join(projectPath, file); if (await fs.pathExists(sourcePath)) { const stat = await fs.stat(sourcePath); if (stat.isDirectory()) { // Copy directory await fs.ensureDir(destPath); await fs.copy(sourcePath, destPath); } else { // Copy file await fs.ensureDir(path.dirname(destPath)); await fs.copy(sourcePath, destPath); } } } } catch (error) { // Log warning but continue with other files console.log(chalk_1.default.yellow(`āš ļø Warning: Could not copy pattern '${pattern}': ${error.message}`)); } } } async cleanup(projectPath) { // Remove the temporary node_modules const nodeModulesPath = path.join(projectPath, 'node_modules'); if (await fs.pathExists(nodeModulesPath)) { await fs.remove(nodeModulesPath); } // Remove package-lock.json if it exists const packageLockPath = path.join(projectPath, 'package-lock.json'); if (await fs.pathExists(packageLockPath)) { await fs.remove(packageLockPath); } } async updatePackageJson(projectPath, projectName, templateType) { const packageJsonPath = path.join(projectPath, 'package.json'); if (await fs.pathExists(packageJsonPath)) { const packageJson = await fs.readJson(packageJsonPath); packageJson.name = projectName; packageJson.description = `${templateType.toUpperCase()} project: ${projectName}`; // Remove the bin field if it exists (this is for CLI packages) if (packageJson.bin) { delete packageJson.bin; } // Ensure CLI scripts are included if (!packageJson.scripts) { packageJson.scripts = {}; } // Add CLI scripts if they don't exist if (!packageJson.scripts['generate:module']) { packageJson.scripts['generate:module'] = './scripts/generate-module.sh'; } if (!packageJson.scripts['cli']) { packageJson.scripts['cli'] = './scripts/cli-commands.sh'; } await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 }); } } async createGitignore(projectPath) { const gitignoreContent = ` # Dependencies node_modules/ npm-debug.log* yarn-debug.log* yarn-error.log* # Build outputs dist/ build/ *.tsbuildinfo # Environment variables .env .env.local .env.development.local .env.test.local .env.production.local # IDE .vscode/ .idea/ *.swp *.swo # OS .DS_Store Thumbs.db # Logs logs *.log # Runtime data pids *.pid *.seed *.pid.lock # Coverage directory used by tools like istanbul coverage/ # Firebase .firebase/ firebase-debug.log firestore-debug.log ui-debug.log # Temporary folders tmp/ temp/ `.trim(); await fs.writeFile(path.join(projectPath, '.gitignore'), gitignoreContent); } async createReadme(projectPath, projectName, templateType) { const templateTitle = templateType.toUpperCase(); const readmeContent = `# ${projectName} A Node.js ${templateType} project built with TypeScript and Firebase Functions. ## Features - Express.js server with TypeScript - Firebase Functions integration - Socket.io for real-time communication - MongoDB with Mongoose - Authentication system - Email notifications - Cron jobs - Audit logging ## Getting Started 1. Install dependencies: \`\`\`bash npm install \`\`\` 2. Set up environment variables: - Copy \`.env.example\` to \`.env.development\` - Set \`NODE_ENV=development\` in \`.env.development\` - Update the values according to your configuration 3. Start development server: \`\`\`bash npm start \`\`\` ## Available Scripts - \`npm start\` - Start development server - \`npm run build:dev\` - Build for development - \`npm run build:prod\` - Build for production - \`npm run deploy\` - Deploy to Firebase ## Project Structure \`\`\` src/ ā”œā”€ā”€ config/ # Configuration files ā”œā”€ā”€ controllers/ # Route controllers ā”œā”€ā”€ entities/ # Database entities ā”œā”€ā”€ helpers/ # Utility functions ā”œā”€ā”€ routes/ # API routes ā”œā”€ā”€ schemas/ # Validation schemas ā”œā”€ā”€ server/ # Server setup └── services/ # Business logic \`\`\` ## Contributing 1. Fork the repository 2. Create a feature branch 3. Make your changes 4. Test thoroughly 5. Submit a pull request ## License MIT `; await fs.writeFile(path.join(projectPath, 'README.md'), readmeContent); } async makeScriptsExecutable(projectPath) { const scriptsDir = path.join(projectPath, 'scripts'); if (await fs.pathExists(scriptsDir)) { try { const scripts = await fs.readdir(scriptsDir); for (const script of scripts) { if (script.endsWith('.sh')) { const scriptPath = path.join(scriptsDir, script); // Make script executable (chmod 755) await fs.chmod(scriptPath, 0o755); console.log(chalk_1.default.blue(`šŸ”§ Made executable: ${script}`)); } } } catch (error) { console.log(chalk_1.default.yellow(`āš ļø Warning: Could not make scripts executable: ${error.message}`)); } } } async processTemplate(projectPath, projectName) { try { console.log(chalk_1.default.blue(`šŸ”§ Processing template for project: ${projectName}`)); // Change to project directory const originalCwd = process.cwd(); process.chdir(projectPath); // Run the template processor (0, child_process_1.execSync)(`npm run cli process:template ${projectName}`, { stdio: 'inherit' }); // Change back to original directory process.chdir(originalCwd); console.log(chalk_1.default.green(`āœ… Template processed successfully`)); } catch (error) { console.log(chalk_1.default.yellow(`āš ļø Warning: Could not process template: ${error.message}`)); console.log(chalk_1.default.yellow(` You can manually run: npm run cli process:template ${projectName}`)); } } /** * Sync the generated project's .env.example with the latest one from the installed template package * and replace placeholders with project-specific values. */ async syncEnvExampleFromTemplate(projectPath, projectName, templateType) { try { const templatePath = path.join(projectPath, 'node_modules', '@diagramers', templateType); const sourceEnvPath = path.join(templatePath, '.env.example'); const destEnvPath = path.join(projectPath, '.env.example'); if (await fs.pathExists(sourceEnvPath)) { let envContent = await fs.readFile(sourceEnvPath, 'utf8'); const dbName = this.deriveDbName(projectName); envContent = envContent.replace(/DB_NAME_PLACEHOLDER/g, dbName); await fs.writeFile(destEnvPath, envContent); console.log(chalk_1.default.blue('šŸ”„ Synchronized .env.example from @diagramers/api template')); } else { console.log(chalk_1.default.gray('ā„¹ļø No .env.example found in template package; keeping generated one')); } } catch (error) { console.log(chalk_1.default.yellow(`āš ļø Warning: Could not sync .env.example from template: ${error.message}`)); } } deriveDbName(projectName) { return projectName .toLowerCase() .replace(/[^a-zA-Z0-9]/g, '_') .replace(/_+/g, '_') .replace(/^_|_$/g, ''); } } exports.ProjectInitializer = ProjectInitializer; //# sourceMappingURL=project-initializer.js.map