UNPKG

modurize

Version:

Intelligent CLI tool to scaffold dynamic, context-aware modules for Node.js apps with smart CRUD generation and database integration

252 lines (210 loc) • 9.34 kB
#!/usr/bin/env node import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import { createModule } from '../lib/templates.js'; import { interactiveSetup } from '../lib/interactive.js'; import { loadConfig, createConfigFile, mergeConfigWithArgs } from '../lib/config.js'; import { ProjectAnalyzer } from '../lib/analyzer.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Version info const VERSION = '1.2.0'; // Help text const HELP_TEXT = ` modurize - Generate clean module folders for Node.js projects USAGE: modurize <moduleName> [options] modurize init [options] Create configuration file modurize analyze Analyze project and show recommendations OPTIONS: --class Generate class-based controllers/services --func Generate function-based files (default) --typescript Generate TypeScript files (.ts extension) --tests Include test files --output <dir> Custom output directory (default: src/modules) --interactive Use interactive prompts --dry-run Show what would be generated without creating files --dynamic Use project analysis for intelligent generation (default) --static Use static templates (ignore project analysis) --help, -h Show this help message --version, -v Show version EXAMPLES: modurize auth modurize user --class modurize product --typescript --tests modurize auth --output ./custom/modules modurize auth --interactive modurize user --dry-run modurize init --typescript --tests modurize analyze For more information, visit: https://github.com/Code-Gale/modurize `; // Get args const args = process.argv.slice(2); // Handle help and version if (args.includes('--help') || args.includes('-h')) { console.log(HELP_TEXT); process.exit(0); } if (args.includes('--version') || args.includes('-v')) { console.log(`modurize v${VERSION}`); process.exit(0); } // Handle analyze command if (args[0] === 'analyze') { (async () => { try { console.log('šŸ” Analyzing project...\n'); const analyzer = new ProjectAnalyzer(); const analysis = await analyzer.analyze(); const recommendations = analyzer.getRecommendations(); console.log('šŸ“Š Project Analysis Results:'); console.log(` Framework: ${analysis.framework}`); console.log(` Database: ${analysis.database || 'none'}`); console.log(` Code Style: ${analysis.codeStyle.style}`); console.log(` TypeScript: ${analysis.codeStyle.typescript ? 'Yes' : 'No'}`); console.log(` Existing Modules: ${analysis.existingModules.length}`); if (analysis.existingModules.length > 0) { console.log('\nšŸ“ Existing Modules:'); analysis.existingModules.forEach(module => { console.log(` - ${module.name} (${module.files.length} files)`); }); } console.log('\nšŸ’” Recommendations:'); console.log(` Use TypeScript: ${recommendations.useTypeScript ? 'Yes' : 'No'}`); console.log(` Use Classes: ${recommendations.useClass ? 'Yes' : 'No'}`); console.log(` Include Tests: ${recommendations.includeTests ? 'Yes' : 'No'}`); console.log(` Naming Convention: ${recommendations.namingConvention}`); console.log(` File Extension: .${recommendations.fileExtension}`); console.log(` Database Integration: ${recommendations.databaseIntegration ? 'Yes' : 'No'}`); if (recommendations.databaseIntegration) { console.log(` Database Type: ${analysis.database}`); } console.log('\nšŸš€ Next Steps:'); console.log(` Run: modurize <moduleName> --${recommendations.useTypeScript ? 'typescript' : 'func'} --${recommendations.useClass ? 'class' : 'func'}${recommendations.includeTests ? ' --tests' : ''}`); } catch (error) { console.error('āŒ Analysis failed:', error.message); process.exit(1); } })(); process.exit(0); } // Handle init command if (args[0] === 'init') { const initFlags = args.slice(1); const configOptions = { useTypeScript: initFlags.includes('--typescript'), includeTests: initFlags.includes('--tests'), defaultStyle: initFlags.includes('--class') ? 'class' : 'func' }; createConfigFile(configOptions); process.exit(0); } // Load configuration const config = loadConfig(); // Handle interactive mode if (args.includes('--interactive') || args.length === 0) { (async () => { try { console.log('šŸ” Analyzing project for intelligent defaults...'); const analyzer = new ProjectAnalyzer(); const analysis = await analyzer.analyze(); const recommendations = analyzer.getRecommendations(); const interactiveConfig = await interactiveSetup(); const targetDir = path.join(process.cwd(), interactiveConfig.outputDir, interactiveConfig.moduleName); if (fs.existsSync(targetDir)) { console.error(`āŒ Module '${interactiveConfig.moduleName}' already exists at ${targetDir}`); process.exit(1); } const generationType = interactiveConfig.useClass ? 'class' : 'func'; console.log('\nšŸŽÆ Using intelligent defaults based on project analysis:'); console.log(` TypeScript: ${recommendations.useTypeScript ? 'Yes' : 'No'}`); console.log(` Tests: ${recommendations.includeTests ? 'Yes' : 'No'}`); console.log(` Database: ${analysis.database || 'none'}`); createModule(interactiveConfig.moduleName, targetDir, generationType, { useTypeScript: recommendations.useTypeScript, includeTests: recommendations.includeTests, analysis: analysis }); } catch (error) { console.error('āŒ Interactive setup failed:', error.message); process.exit(1); } })(); process.exit(0); } const flags = args.filter(arg => arg.startsWith('--')); const moduleArgs = args.filter(arg => !arg.startsWith('--')); if (!moduleArgs.length) { console.error("āŒ Please specify a module name."); console.error("Use 'modurize --help' for usage information."); process.exit(1); } const moduleName = moduleArgs[0].toLowerCase(); const useClass = flags.includes('--class'); const useFunc = flags.includes('--func'); const useTypeScript = flags.includes('--typescript'); const includeTests = flags.includes('--tests'); const isDryRun = flags.includes('--dry-run'); const useStatic = flags.includes('--static'); const useDynamic = !useStatic; // Default to dynamic // Merge config with command line args const finalConfig = mergeConfigWithArgs(config, args); // Handle custom output directory let outputDir = finalConfig.defaultOutput; const outputIndex = flags.findIndex(flag => flag === '--output'); if (outputIndex !== -1 && outputIndex + 1 < flags.length) { outputDir = flags[outputIndex + 1]; } const targetDir = path.join(process.cwd(), outputDir, moduleName); // Validate module name if (!/^[a-z][a-z0-9-]*$/.test(moduleName)) { console.error("āŒ Module name must start with a letter and contain only lowercase letters, numbers, and hyphens."); process.exit(1); } if (fs.existsSync(targetDir)) { console.error(`āŒ Module '${moduleName}' already exists at ${targetDir}`); process.exit(1); } // Determine generation type const generationType = finalConfig.defaultStyle; // Project analysis for dynamic generation let analysis = {}; if (useDynamic) { try { console.log('šŸ” Analyzing project for intelligent generation...'); const analyzer = new ProjectAnalyzer(); analysis = await analyzer.analyze(); console.log(` Detected: ${analysis.framework} framework, ${analysis.database || 'no'} database`); } catch (error) { console.warn('āš ļø Project analysis failed, using static templates'); analysis = {}; } } if (isDryRun) { const fileExt = finalConfig.useTypeScript ? 'ts' : 'js'; const testExt = finalConfig.useTypeScript ? 'ts' : 'js'; console.log(`šŸ” DRY RUN: Would generate ${generationType}-based module '${moduleName}' at:`); console.log(` ${targetDir}`); console.log(` ā”œā”€ā”€ ${moduleName}.controller.${fileExt}`); console.log(` ā”œā”€ā”€ ${moduleName}.service.${fileExt}`); console.log(` ā”œā”€ā”€ ${moduleName}.routes.${fileExt}`); console.log(` ā”œā”€ā”€ ${moduleName}.model.${fileExt}`); console.log(` ā”œā”€ā”€ ${moduleName}.middleware.${fileExt}`); console.log(` └── ${moduleName}.validator.${fileExt}`); if (finalConfig.includeTests) { console.log(` ā”œā”€ā”€ ${moduleName}.controller.test.${testExt}`); console.log(` └── ${moduleName}.service.test.${testExt}`); } if (analysis.database && analysis.database !== 'none') { console.log(` šŸ“Š Database integration: ${analysis.database}`); } process.exit(0); } // Create module with options createModule(moduleName, targetDir, generationType, { useTypeScript: finalConfig.useTypeScript, includeTests: finalConfig.includeTests, analysis: analysis });