UNPKG

@simplyhomes/sos-sdk

Version:

TypeScript SDK for Simply Homes SoS API v4

206 lines (205 loc) • 7.7 kB
import { readFileSync, writeFileSync, readdirSync, statSync } from 'fs'; import { join } from 'path'; import { parseConfig } from './config-parser'; /** * Recursively get all .ts files in a directory */ function getAllTsFiles(dir) { const files = []; const entries = readdirSync(dir); for (const entry of entries) { const fullPath = join(dir, entry); const stat = statSync(fullPath); if (stat.isDirectory()) { files.push(...getAllTsFiles(fullPath)); } else if (entry.endsWith('.ts')) { files.push(fullPath); } } return files; } /** * Extract all enums matching a pattern from a file */ function extractEnums(content, pattern) { const enums = []; // Match: export enum EnumName { ... } // Handles multi-line enums with various formatting const enumRegex = /export enum (\w+) \{([^}]+)\}/g; let match; while ((match = enumRegex.exec(content)) !== null) { const enumName = match[1]; const enumBody = match[2]; // Check if enum name matches the pattern if (pattern.test(enumName)) { enums.push({ name: enumName, fullText: match[0], values: enumBody.trim() }); } } return enums; } /** * Check if two enums have identical values */ function haveIdenticalValues(enum1, enum2) { // Normalize whitespace and compare const normalize = (str) => str.replace(/\s+/g, ' ').trim(); return normalize(enum1.values) === normalize(enum2.values); } /** * Process a single file for enum consolidation */ function consolidateEnumsInFile(filePath, rule, canonicalEnum) { let content = readFileSync(filePath, 'utf-8'); const originalContent = content; const pattern = new RegExp(rule.pattern); const enums = extractEnums(content, pattern); if (enums.length === 0) { return { modified: false, canonicalEnum, aliasesCreated: 0 }; } let aliasesCreated = 0; let newCanonicalEnum = canonicalEnum; // If we don't have a canonical enum yet, use the first one found if (!canonicalEnum && enums.length > 0) { newCanonicalEnum = enums[0]; const originalName = newCanonicalEnum.name; // Rename it to the canonical name const enumDeclaration = `export enum ${originalName}`; const renamedDeclaration = `export enum ${rule.canonicalName}`; content = content.replace(enumDeclaration, renamedDeclaration); // Create a type alias for the original name to maintain backward compatibility const typeAlias = `export type ${originalName} = ${rule.canonicalName};`; // Find the closing brace of the enum and insert the type alias after it const enumPattern = new RegExp(`export enum ${rule.canonicalName} \\{[^}]+\\}`, 's'); const enumMatch = content.match(enumPattern); if (enumMatch) { content = content.replace(enumMatch[0], `${enumMatch[0]}\n${typeAlias}`); aliasesCreated++; console.log(` šŸ“Œ Using '${originalName}' as base, renamed to '${rule.canonicalName}' (type alias created)`); } else { console.log(` šŸ“Œ Using '${originalName}' as base, renamed to '${rule.canonicalName}'`); } } // Process all enums for (const enumMatch of enums) { // Skip the canonical enum itself if (canonicalEnum && enumMatch.name === canonicalEnum.name) { continue; } // Skip if this is the new canonical enum (first occurrence) if (!canonicalEnum && enumMatch.name === enums[0].name) { continue; } // Verify values match if (newCanonicalEnum && !haveIdenticalValues(enumMatch, newCanonicalEnum)) { console.warn(` āš ļø Enum '${enumMatch.name}' has different values, skipping`); continue; } // Replace enum with type alias const typeAlias = `export type ${enumMatch.name} = ${rule.canonicalName};`; content = content.replace(enumMatch.fullText, typeAlias); aliasesCreated++; console.log(` āœ“ ${enumMatch.name} → type alias`); } // Write back if modified const modified = content !== originalContent; if (modified) { writeFileSync(filePath, content, 'utf-8'); } return { modified, canonicalEnum: newCanonicalEnum, aliasesCreated }; } /** * Process all files for a single consolidation rule */ async function processRule(rule, baseDir) { console.log(`\nšŸ”„ Processing rule: ${rule.pattern} → ${rule.canonicalName}`); if (rule.description) { console.log(` ${rule.description}`); } const stats = { filesProcessed: 0, filesModified: 0, enumsConsolidated: 0, aliasesCreated: 0 }; // Determine directories to process based on scope const dirs = []; if (rule.scope === 'apis' || rule.scope === 'both') { dirs.push(join(baseDir, 'src', 'generated', 'src', 'apis')); } if (rule.scope === 'models' || rule.scope === 'both') { dirs.push(join(baseDir, 'src', 'generated', 'src', 'models')); } let canonicalEnum = null; for (const dir of dirs) { const files = getAllTsFiles(dir); for (const file of files) { stats.filesProcessed++; const result = consolidateEnumsInFile(file, rule, canonicalEnum); if (result.modified) { stats.filesModified++; } if (result.canonicalEnum && !canonicalEnum) { canonicalEnum = result.canonicalEnum; stats.enumsConsolidated++; } stats.aliasesCreated += result.aliasesCreated; stats.enumsConsolidated += result.aliasesCreated; } } return stats; } /** * Main consolidation function */ async function main() { console.log('šŸ”„ Starting enum consolidation...\n'); // Parse configuration const config = await parseConfig('sdk.config.yml'); const rules = config.postProcessing?.enumConsolidation?.rules || []; if (rules.length === 0) { console.log('āš ļø No enum consolidation rules found in sdk.config.yml'); console.log(' Add rules to postProcessing.enumConsolidation.rules\n'); process.exit(0); } console.log(`šŸ“‹ Found ${rules.length} consolidation rule(s)\n`); const baseDir = process.cwd(); const allStats = { filesProcessed: 0, filesModified: 0, enumsConsolidated: 0, aliasesCreated: 0 }; // Process each rule for (const rule of rules) { const stats = await processRule(rule, baseDir); allStats.filesProcessed += stats.filesProcessed; allStats.filesModified += stats.filesModified; allStats.enumsConsolidated += stats.enumsConsolidated; allStats.aliasesCreated += stats.aliasesCreated; } console.log('\n✨ Enum consolidation complete!\n'); console.log('šŸ“Š Summary:'); console.log(` • Files processed: ${allStats.filesProcessed}`); console.log(` • Files modified: ${allStats.filesModified}`); console.log(` • Enums consolidated: ${allStats.enumsConsolidated}`); console.log(` • Type aliases created: ${allStats.aliasesCreated}\n`); if (allStats.filesModified === 0) { console.log('āš ļø No files were modified. The enums may already be consolidated.\n'); } process.exit(0); } main().catch((error) => { console.error('\nāŒ Enum consolidation failed:\n'); console.error(error); process.exit(1); });