Files
tsmorph/scripts/refactor-tsx-pass2.ts
2026-01-17 23:34:09 +00:00

183 lines
5.5 KiB
TypeScript

#!/usr/bin/env ts-node
import { Project, SyntaxKind, SourceFile, VariableDeclaration } from 'ts-morph';
import * as path from 'path';
/**
* Second pass: Extract utility functions from inside the component
* This extracts helper functions like validateForm, getRoleBadgeColor, etc.
*/
class TSXRefactorer2 {
private project: Project;
private sourceFile: SourceFile;
constructor(filePath: string) {
this.project = new Project({
tsConfigFilePath: path.join(__dirname, '..', 'tsconfig.json'),
});
this.sourceFile = this.project.addSourceFileAtPath(filePath);
}
/**
* Extract helper functions that don't use hooks or state
*/
extractHelperFunctions(): void {
console.log('\n🔄 Extracting helper functions (2nd pass)...');
const utilsFilePath = this.sourceFile.getFilePath().replace('.tsx', '.utils.ts');
// Find the main component
const componentDecl = this.sourceFile.getVariableDeclaration('UserManagementDashboard');
if (!componentDecl) {
console.log(' ⚠️ Could not find component');
return;
}
const arrowFunc = componentDecl.getInitializerIfKind(SyntaxKind.ArrowFunction);
if (!arrowFunc) {
console.log(' ⚠️ Component is not an arrow function');
return;
}
const body = arrowFunc.getBody();
if (!body || !body.isKind(SyntaxKind.Block)) {
console.log(' ⚠️ Component body not found');
return;
}
// Find helper functions inside the component
const helperFunctions: Array<{ name: string; text: string }> = [];
// Look for const declarations with arrow functions
const block = body.asKind(SyntaxKind.Block);
if (!block) return;
const statements = block.getStatements();
statements.forEach(stmt => {
if (stmt.isKind(SyntaxKind.VariableStatement)) {
const declarations = stmt.getDeclarations();
declarations.forEach(decl => {
const name = decl.getName();
const initializer = decl.getInitializer();
// Check if it's a helper function (arrow function that doesn't use hooks)
if (initializer && initializer.isKind(SyntaxKind.ArrowFunction)) {
const text = stmt.getText();
// Extract these specific helper functions
if (name.match(/^(validate|getRoleBadgeColor|getStatusBadgeColor|formatDate)/)) {
helperFunctions.push({ name, text });
}
}
});
}
});
if (helperFunctions.length === 0) {
console.log(' ⏭️ No helper functions to extract');
return;
}
// Read existing utils file or create new content
let utilsContent = '';
try {
const existingUtils = this.project.getSourceFile(utilsFilePath);
if (existingUtils) {
utilsContent = existingUtils.getFullText();
}
} catch (e) {
// File doesn't exist yet
utilsContent = [
'/**',
' * Extracted utility functions',
' * Auto-generated by ts-morph refactoring script',
' */',
'',
"import type { FormData, ValidationErrors, User } from './UserManagementDashboard.types';",
'',
].join('\n');
}
// Add the helper functions
const exportedFunctions = helperFunctions.map(func => {
// Make it exported
const exportedText = func.text.replace(/^(\s*)(const|let|var)/, '$1export const');
console.log(` ✓ Extracted: ${func.name}`);
return exportedText;
});
utilsContent += '\n' + exportedFunctions.join('\n\n');
// Write the utils file
const utilsFile = this.project.createSourceFile(utilsFilePath, utilsContent, { overwrite: true });
utilsFile.saveSync();
// Remove helper functions from component and add imports
const functionNames = helperFunctions.map(f => f.name);
statements.forEach(stmt => {
if (stmt.isKind(SyntaxKind.VariableStatement)) {
const declarations = stmt.getDeclarations();
declarations.forEach(decl => {
const name = decl.getName();
if (functionNames.includes(name)) {
stmt.remove();
}
});
}
});
// Add import
const existingImport = this.sourceFile.getImportDeclaration('./UserManagementDashboard.utils');
if (existingImport) {
// Remove existing and recreate with combined imports
const namedImports = existingImport.getNamedImports().map(ni => ni.getName());
const newImports = [...new Set([...namedImports, ...functionNames])];
existingImport.remove();
this.sourceFile.addImportDeclaration({
moduleSpecifier: './UserManagementDashboard.utils',
namedImports: newImports,
});
} else {
// Create new import
this.sourceFile.addImportDeclaration({
moduleSpecifier: './UserManagementDashboard.utils',
namedImports: functionNames,
});
}
this.sourceFile.saveSync();
console.log(` 💾 Saved: ${path.basename(utilsFilePath)}`);
}
save(): void {
this.sourceFile.saveSync();
console.log('\n✅ Second pass refactoring complete!');
}
}
async function main() {
console.log('🚀 TSX Refactoring Tool - Second Pass\n');
const targetFile = path.join(
__dirname,
'..',
'src',
'components',
'UserManagementDashboard.tsx'
);
const refactorer = new TSXRefactorer2(targetFile);
refactorer.extractHelperFunctions();
refactorer.save();
console.log('\n💡 Helper functions extracted!');
console.log(' - Run npm run type-check to verify');
}
main().catch(console.error);