UNPKG

@sun-asterisk/sunlint

Version:

☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards

386 lines (324 loc) 11.6 kB
/** * ESLint to Heuristic Migration Converter * Automated tool for migrating ESLint rules to heuristic engine */ const fs = require('fs'); const path = require('path'); const mapping = require('./mapping.json'); class MigrationConverter { constructor() { this.mapping = mapping; this.rulesDir = path.join(__dirname, '..'); this.eslintRulesDir = path.join(__dirname, '../../integrations/eslint/plugin/rules'); } /** * Get migration info for a specific rule * @param {string} ruleId - Rule ID (e.g., 'C006', 'S001') * @returns {Object|null} Migration info */ getMigrationInfo(ruleId) { return this.mapping.migrations.find(m => m.heuristic_rule.startsWith(ruleId) ); } /** * Create heuristic rule directory structure * @param {string} category - Rule category * @param {string} ruleId - Rule ID * @returns {string} Created directory path */ createRuleStructure(category, ruleId) { const ruleDir = path.join(this.rulesDir, category, ruleId); if (!fs.existsSync(ruleDir)) { fs.mkdirSync(ruleDir, { recursive: true }); console.log(`✅ Created rule directory: ${ruleDir}`); } // Create rule files if they don't exist const files = ['analyzer.js', 'config.json', 'test.js', 'README.md']; files.forEach(file => { const filePath = path.join(ruleDir, file); if (!fs.existsSync(filePath)) { this.createRuleFile(filePath, file, ruleId, category); } }); return ruleDir; } /** * Create individual rule file with template content * @param {string} filePath - File path to create * @param {string} fileName - File name * @param {string} ruleId - Rule ID * @param {string} category - Rule category */ createRuleFile(filePath, fileName, ruleId, category) { let content = ''; switch (fileName) { case 'analyzer.js': content = this.generateAnalyzerTemplate(ruleId, category); break; case 'config.json': content = this.generateConfigTemplate(ruleId, category); break; case 'test.js': content = this.generateTestTemplate(ruleId, category); break; case 'README.md': content = this.generateReadmeTemplate(ruleId, category); break; } fs.writeFileSync(filePath, content); console.log(`✅ Created: ${filePath}`); } /** * Generate analyzer template */ generateAnalyzerTemplate(ruleId, category) { return `/** * ${ruleId} - Heuristic Rule Analyzer * Category: ${category} * * TODO: Migrate logic from ESLint rule * ESLint rule: integrations/eslint/plugin/rules/${category}/${ruleId.toLowerCase().replace('_', '-')}.js */ const { PatternMatcher } = require('../common/pattern-matchers'); const { RuleHelper } = require('../common/rule-helpers'); class ${ruleId}Analyzer { constructor(config = {}) { this.config = config; this.patternMatcher = new PatternMatcher(); this.helper = new RuleHelper(); } /** * Analyze code content for rule violations * @param {string} content - File content * @param {string} filePath - File path * @param {Object} context - Analysis context * @returns {Array} Array of violations */ analyze(content, filePath, context = {}) { const violations = []; // TODO: Implement heuristic analysis logic // This should replicate the ESLint rule behavior using pattern matching try { // Example pattern-based analysis // const patterns = this.getViolationPatterns(); // const matches = this.patternMatcher.findMatches(content, patterns); // // matches.forEach(match => { // violations.push(this.helper.createViolation({ // ruleId: '${ruleId}', // message: 'Rule violation detected', // line: match.line, // column: match.column, // severity: 'error' // })); // }); } catch (error) { console.warn(\`Error analyzing \${filePath} with ${ruleId}:\`, error.message); } return violations; } /** * Get violation patterns for this rule * @returns {Array} Array of patterns to match */ getViolationPatterns() { // TODO: Define patterns based on ESLint rule logic return []; } } module.exports = ${ruleId}Analyzer; `; } /** * Generate config template */ generateConfigTemplate(ruleId, category) { const migration = this.getMigrationInfo(ruleId); return JSON.stringify({ "id": ruleId, "name": migration ? migration.heuristic_rule : ruleId, "category": category, "description": `${ruleId} heuristic rule - migrated from ESLint`, "severity": "error", "enabled": true, "migration": { "from_eslint": migration ? migration.eslint_rule : "unknown", "compatibility": migration ? migration.compatibility : "pending", "status": migration ? migration.status : "pending" }, "patterns": { "include": ["**/*.js", "**/*.ts"], "exclude": ["**/*.test.*", "**/*.spec.*"] } }, null, 2); } /** * Generate test template */ generateTestTemplate(ruleId, category) { return `/** * ${ruleId} - Rule Tests * Tests for heuristic rule analyzer */ const ${ruleId}Analyzer = require('./analyzer'); describe('${ruleId} Heuristic Rule', () => { let analyzer; beforeEach(() => { analyzer = new ${ruleId}Analyzer(); }); describe('Valid Code', () => { test('should not report violations for valid code', () => { const code = \` // TODO: Add valid code examples \`; const violations = analyzer.analyze(code, 'test.js'); expect(violations).toHaveLength(0); }); }); describe('Invalid Code', () => { test('should report violations for invalid code', () => { const code = \` // TODO: Add invalid code examples \`; const violations = analyzer.analyze(code, 'test.js'); expect(violations.length).toBeGreaterThan(0); expect(violations[0].ruleId).toBe('${ruleId}'); }); }); describe('Edge Cases', () => { test('should handle empty code', () => { const violations = analyzer.analyze('', 'test.js'); expect(violations).toHaveLength(0); }); test('should handle syntax errors gracefully', () => { const code = 'invalid javascript syntax {{{'; const violations = analyzer.analyze(code, 'test.js'); expect(Array.isArray(violations)).toBe(true); }); }); }); `; } /** * Generate README template */ generateReadmeTemplate(ruleId, category) { const migration = this.getMigrationInfo(ruleId); return `# ${ruleId} - ${category.toUpperCase()} Rule ## 📋 Overview **Rule ID**: \`${ruleId}\` **Category**: ${category} **Severity**: Error **Status**: ${migration ? migration.status : 'Pending Migration'} ## 🎯 Description TODO: Add rule description after migration from ESLint. ${migration ? ` ## 🔄 Migration Info **ESLint Rule**: \`${migration.eslint_rule}\` **Compatibility**: ${migration.compatibility} **Priority**: ${migration.priority} ` : ''} ## ✅ Valid Code Examples \`\`\`javascript // TODO: Add valid code examples \`\`\` ## ❌ Invalid Code Examples \`\`\`javascript // TODO: Add invalid code examples that should trigger violations \`\`\` ## ⚙️ Configuration \`\`\`json { "rules": { "${ruleId}": "error" } } \`\`\` ## 🧪 Testing \`\`\`bash # Run rule-specific tests npm test -- ${ruleId.toLowerCase()} # Test with SunLint CLI sunlint --rules=${ruleId} --input=examples/ \`\`\` --- **Migration Status**: ${migration ? migration.status : 'Pending'} **Last Updated**: ${new Date().toISOString().split('T')[0]} `; } /** * Migrate a specific rule * @param {string} ruleId - Rule ID to migrate * @returns {boolean} Success status */ async migrateRule(ruleId) { const migration = this.getMigrationInfo(ruleId); if (!migration) { console.error(`❌ No migration mapping found for rule: ${ruleId}`); return false; } if (migration.status === 'completed') { console.log(`✅ Rule ${ruleId} already migrated`); return true; } console.log(`🔄 Migrating rule: ${ruleId}`); console.log(` ESLint: ${migration.eslint_rule}`); console.log(` Category: ${migration.category}`); console.log(` Compatibility: ${migration.compatibility}`); try { // Create heuristic rule structure this.createRuleStructure(migration.category, migration.heuristic_rule); console.log(`✅ Migration template created for ${ruleId}`); console.log(`📝 Next steps:`); console.log(` 1. Implement analyzer logic in rules/${migration.category}/${migration.heuristic_rule}/analyzer.js`); console.log(` 2. Add test cases in rules/${migration.category}/${migration.heuristic_rule}/test.js`); console.log(` 3. Update rule documentation`); console.log(` 4. Test against ESLint rule behavior`); return true; } catch (error) { console.error(`❌ Migration failed for ${ruleId}:`, error.message); return false; } } /** * Show migration statistics */ showStats() { const stats = this.mapping.migration_stats; console.log('📊 Migration Statistics:'); console.log(` Total Rules: ${stats.total_rules}`); console.log(` Completed: ${stats.completed}`); console.log(` Pending: ${stats.pending}`); console.log(''); console.log('📋 By Category:'); Object.entries(stats.by_category).forEach(([category, data]) => { console.log(` ${category}: ${data.completed}/${data.total} completed`); }); } } // CLI usage if (require.main === module) { const converter = new MigrationConverter(); const args = process.argv.slice(2); if (args.includes('--stats')) { converter.showStats(); } else if (args.includes('--rule')) { const ruleIndex = args.indexOf('--rule'); const ruleId = args[ruleIndex + 1]; if (ruleId) { converter.migrateRule(ruleId); } else { console.error('❌ Please specify a rule ID with --rule'); } } else { console.log('🚀 SunLint Migration Converter'); console.log(''); console.log('Usage:'); console.log(' node converter.js --stats # Show migration statistics'); console.log(' node converter.js --rule C006 # Migrate specific rule'); console.log(''); converter.showStats(); } } module.exports = MigrationConverter;