Created
November 15, 2025 14:01
-
-
Save scytacki/1c814dc3845f4cc6049e76ad75acd24c to your computer and use it in GitHub Desktop.
A script to count the MST action and view blocks in each MST model in a repository.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env node | |
| import fs from 'fs'; | |
| import path from 'path'; | |
| import { fileURLToPath } from 'url'; | |
| const __filename = fileURLToPath(import.meta.url); | |
| const __dirname = path.dirname(__filename); | |
| /** | |
| * Recursively find all files in a directory | |
| */ | |
| function findFiles(dir, fileList = []) { | |
| const files = fs.readdirSync(dir); | |
| files.forEach(file => { | |
| const filePath = path.join(dir, file); | |
| const stat = fs.statSync(filePath); | |
| if (stat.isDirectory()) { | |
| findFiles(filePath, fileList); | |
| } else if (file.endsWith('.ts') || file.endsWith('.tsx')) { | |
| fileList.push(filePath); | |
| } | |
| }); | |
| return fileList; | |
| } | |
| /** | |
| * Extract model name from a model definition line | |
| */ | |
| function extractModelName(line) { | |
| // Match types.model("ModelName", ...) or types.model('ModelName', ...) | |
| const typesModelMatch = line.match(/types\.model\(\s*["']([^"']+)["']/); | |
| if (typesModelMatch) { | |
| return typesModelMatch[1]; | |
| } | |
| // Match .named("ModelName") or .named('ModelName') | |
| const namedMatch = line.match(/\.named\(\s*["']([^"']+)["']/); | |
| if (namedMatch) { | |
| return namedMatch[1]; | |
| } | |
| // Try to extract from variable name like: export const SomeModel = types.model( | |
| const varMatch = line.match(/(?:export\s+)?const\s+(\w+)\s*=/); | |
| if (varMatch) { | |
| return varMatch[1]; | |
| } | |
| return null; | |
| } | |
| /** | |
| * Count MST .actions() and .views() patterns per model in a file | |
| */ | |
| function countPatternsInFile(filePath) { | |
| const content = fs.readFileSync(filePath, 'utf8'); | |
| const lines = content.split('\n'); | |
| const models = []; | |
| // Find all model definitions (lines with types.model( or .named() | |
| const modelStarts = []; | |
| for (let i = 0; i < lines.length; i++) { | |
| const line = lines[i]; | |
| if (/types\.model\(|\.named\(/.test(line)) { | |
| const modelName = extractModelName(line); | |
| if (modelName) { | |
| modelStarts.push({ line: i, name: modelName }); | |
| } | |
| } | |
| } | |
| // For each model, count patterns until the next model or end of file | |
| for (let i = 0; i < modelStarts.length; i++) { | |
| const start = modelStarts[i]; | |
| const endLine = i < modelStarts.length - 1 ? modelStarts[i + 1].line : lines.length; | |
| let count = 0; | |
| const patterns = []; | |
| // Count patterns in this model's section | |
| for (let j = start.line; j < endLine; j++) { | |
| const matches = lines[j].match(/\.actions\(|\.views\(/g); | |
| if (matches) { | |
| count += matches.length; | |
| patterns.push(...matches); | |
| } | |
| } | |
| if (count > 0) { | |
| models.push({ | |
| name: start.name, | |
| startLine: start.line + 1, | |
| endLine, | |
| count, | |
| patterns | |
| }); | |
| } | |
| } | |
| const totalCount = models.reduce((sum, model) => sum + model.count, 0); | |
| return { | |
| count: totalCount, | |
| models, | |
| hasMultipleModels: models.length > 1 | |
| }; | |
| } | |
| /** | |
| * Main function | |
| */ | |
| function main() { | |
| const modelsDir = path.join(__dirname, '..', 'src', 'models'); | |
| if (!fs.existsSync(modelsDir)) { | |
| console.error(`Error: Directory not found: ${modelsDir}`); | |
| process.exit(1); | |
| } | |
| console.log(`Searching for .actions() and .views() patterns in: ${modelsDir}\n`); | |
| const files = findFiles(modelsDir); | |
| const results = []; | |
| let totalCount = 0; | |
| let totalModels = 0; | |
| files.forEach(file => { | |
| const result = countPatternsInFile(file); | |
| if (result.count > 0) { | |
| const relativePath = path.relative(process.cwd(), file); | |
| results.push({ | |
| file: relativePath, | |
| count: result.count, | |
| models: result.models, | |
| hasMultipleModels: result.hasMultipleModels | |
| }); | |
| totalCount += result.count; | |
| totalModels += result.models.length; | |
| } | |
| }); | |
| // Sort by count (descending) | |
| results.sort((a, b) => b.count - a.count); | |
| // Print results | |
| console.log('Files with .actions() or .views() patterns:\n'); | |
| console.log('Count | File'); | |
| console.log('------|-----'); | |
| results.forEach(result => { | |
| console.log(` ${result.count.toString().padStart(2)} | ${result.file}`); | |
| // Show per-model breakdown for files with multiple models | |
| if (result.hasMultipleModels && result.models.length > 1) { | |
| result.models.forEach(model => { | |
| if (model.count > 0) { | |
| console.log(` | └─ ${model.name}: ${model.count}`); | |
| } | |
| }); | |
| } | |
| }); | |
| console.log('\n' + '='.repeat(60)); | |
| console.log(`Total files scanned: ${files.length}`); | |
| console.log(`Files with patterns: ${results.length}`); | |
| console.log(`Total models found: ${totalModels}`); | |
| console.log(`Total pattern occurrences: ${totalCount}`); | |
| const highCountLimit = 7; | |
| // Warn about models (not just files) with many patterns | |
| const modelsWithHighCount = []; | |
| results.forEach(result => { | |
| result.models.forEach(model => { | |
| if (model.count > highCountLimit) { | |
| modelsWithHighCount.push({ | |
| file: result.file, | |
| model: model.name, | |
| count: model.count | |
| }); | |
| } | |
| }); | |
| }); | |
| if (modelsWithHighCount.length > 0) { | |
| console.log(`\n⚠️ Warning: Models with more than ${highCountLimit} .actions()/.views() blocks:`); | |
| modelsWithHighCount | |
| .sort((a, b) => b.count - a.count) | |
| .forEach(item => { | |
| console.log(` - ${item.model} in ${item.file} (${item.count} occurrences)`); | |
| }); | |
| console.log('\nThese models may cause TypeScript compilation issues.'); | |
| } | |
| } | |
| main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment