UNPKG

@zerospacegg/iolin

Version:

Pure TypeScript implementation of ZeroSpace game data processing (PKL-free)

125 lines • 4.12 kB
/** * Test that ensures all TypeScript files in src/zerospace/ export * proper entity subclasses as their default export */ import { promises as fs } from 'fs'; import path from 'path'; // Base entity classes that are valid exports const VALID_BASE_CLASSES = [ 'GameEntity', 'GameMechanic', 'GameFactionEntity', 'GameUnit', 'GameBuilding', 'GameUpgrade', 'GameAbility', 'GameAttack', 'GamePassive', 'GameSpell', 'GameTurret', // Co-op specific classes 'CoopCommanderUnit', 'CoopBuilding', 'CoopUnit', // Mercenary classes 'MercenaryFaction', 'MercenaryUnit', 'MercenaryBuilding', 'MercenaryHero', // Game modifier classes 'Boon', 'Mutator', // Nonplayer classes 'NonPlayerFaction', 'NonPlayerUnit', 'NonPlayerBuilding' ]; /** * Recursively find all .ts files in a directory */ async function findTsFiles(dir) { const files = []; const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { const subFiles = await findTsFiles(fullPath); files.push(...subFiles); } else if (entry.isFile() && entry.name.endsWith('.ts')) { files.push(fullPath); } } return files; } /** * Check if a file exports a valid entity class */ async function isValidEntityFile(filePath) { try { const content = await fs.readFile(filePath, 'utf-8'); // Skip test files if (filePath.includes('.test.') || filePath.includes('/test/')) { return { valid: true, reason: 'test file - skipped' }; } // Look for class declarations that extend valid base classes const classRegex = /export\s+(?:default\s+)?class\s+\w+\s+extends\s+(\w+)/g; const matches = [...content.matchAll(classRegex)]; if (matches.length === 0) { return { valid: false, reason: 'no class extending entity base class found' }; } // Check if any class extends a valid base class for (const match of matches) { const baseClass = match[1]; if (VALID_BASE_CLASSES.includes(baseClass)) { return { valid: true, reason: `extends ${baseClass}` }; } } return { valid: false, reason: `classes found but none extend valid base classes: ${matches.map(m => m[1]).join(', ')}` }; } catch (error) { return { valid: false, reason: `error reading file: ${error instanceof Error ? error.message : String(error)}` }; } } /** * Main test function */ async function testEntityExports() { const zerostpaceSrcDir = path.resolve(process.cwd(), 'src/zerospace'); console.log('šŸ” Finding all TypeScript files in src/zerospace/...'); const tsFiles = await findTsFiles(zerostpaceSrcDir); console.log(`šŸ“ Found ${tsFiles.length} TypeScript files`); const results = []; for (const file of tsFiles) { const relativePath = path.relative(zerostpaceSrcDir, file); const result = await isValidEntityFile(file); results.push({ file: relativePath, valid: result.valid, reason: result.reason || 'unknown' }); if (!result.valid) { console.log(`āŒ ${relativePath}: ${result.reason}`); } else { console.log(`āœ… ${relativePath}: ${result.reason}`); } } const invalidFiles = results.filter(r => !r.valid); if (invalidFiles.length > 0) { console.log(`\nšŸ’„ ${invalidFiles.length} files do not export valid entity classes:`); invalidFiles.forEach(f => { console.log(` - ${f.file}: ${f.reason}`); }); process.exit(1); } else { console.log(`\nšŸŽ‰ All ${results.length} files export valid entity classes!`); } } // Run the test testEntityExports().catch(error => { console.error('Test failed:', error); process.exit(1); }); //# sourceMappingURL=entity-exports.test.js.map