Advertisement

← vibesdk

worker/services/code-fixer/fixers/ts2307.ts

7.9 KB Download file
/**
 * TS2307: Cannot find module fixer
 * Handles missing module imports by either finding existing files or creating stubs
 */

import { CodeIssue } from '../../sandbox/sandboxTypes';
import { FixerContext, FixResult, FixedIssue, ImportInfo } from '../types';
import * as t from '@babel/types';
import { generateCode } from '../utils/ast';
import { findImportAtLocation, getFileAST } from '../utils/imports';
import { findModuleFile, makeRelativeImport, resolveImportToFilePath } from '../utils/paths';
import { generateStubFileContent } from '../utils/stubs';
import { createObjectLogger } from '../../../logger';
import { isExternalModule } from '../utils/modules';
import { createExternalModuleError, handleFixerError } from '../utils/helpers';

const logger = createObjectLogger({ name: 'TS2307Fixer' }, 'TS2307Fixer');

/**
 * Fix TS2307 "Cannot find module" errors
 * Preserves exact logic from working ImportExportFixer.fixModuleNotFound
 */
export function fixModuleNotFound(
    context: FixerContext,
    issues: CodeIssue[]
): FixResult {
    logger.info(`Starting TS2307 fixer with ${issues.length} issues`);
    
    const fixedIssues: FixedIssue[] = [];
    const unfixableIssues = [];
    const modifiedFiles = [];
    const newFiles = [];

    for (const issue of issues) {
        logger.info(`Processing TS2307 issue: ${issue.message} at ${issue.filePath}:${issue.line}`);
        
        try {
            logger.info(`Getting AST for file: ${issue.filePath}`);
            const ast = getFileAST(issue.filePath, context.files);
            if (!ast) {
                logger.warn(`Failed to get AST for ${issue.filePath}`);
                unfixableIssues.push({
                    issueCode: 'TS2307',
                    filePath: issue.filePath,
                    line: issue.line,
                    column: issue.column,
                    originalMessage: issue.message,
                    reason: 'Failed to parse file AST'
                });
                continue;
            }

            logger.info(`Finding import at line ${issue.line} in ${issue.filePath}`);
            const importInfo = findImportAtLocation(ast, issue.line);
            if (!importInfo) {
                logger.warn(`No import found at line ${issue.line} in ${issue.filePath}`);
                unfixableIssues.push({
                    issueCode: 'TS2307',
                    filePath: issue.filePath,
                    line: issue.line,
                    column: issue.column,
                    originalMessage: issue.message,
                    reason: 'No import found at specified location'
                });
                continue;
            }

            logger.info(`Found import: ${importInfo.moduleSpecifier}, default: ${importInfo.defaultImport}, named: [${importInfo.namedImports.join(', ')}]`);

            const moduleSpecifier = importInfo.moduleSpecifier;
            importInfo.filePath = issue.filePath; // Set the file path for usage analysis
            
            // Skip external packages - only handle local modules
            logger.info(`Checking if "${moduleSpecifier}" is external package`);
            if (isExternalModule(moduleSpecifier)) {
                logger.info(`Skipping external package: ${moduleSpecifier}`);
                unfixableIssues.push(createExternalModuleError(issue, moduleSpecifier));
                continue;
            }
            
            logger.info(`Searching for local module file: ${moduleSpecifier}`);
            // Try to find existing file with fuzzy matching
            const foundFile = findModuleFile(
                moduleSpecifier, 
                issue.filePath, 
                context.files
            );
            logger.info(`Module file search result: ${foundFile || 'NOT FOUND'}`);
            
            if (foundFile) {
                logger.info(`Found existing file, fixing import path from "${moduleSpecifier}" to "${foundFile}"`);
                // File exists, fix the import path
                const relativeImport = makeRelativeImport(issue.filePath, foundFile);
                logger.info(`Generated relative import path: "${relativeImport}"`);
                
                logger.info(`Updating AST for import path change`);
                const updatedAST = updateImportPath(ast, importInfo, relativeImport);
                const generatedCode = generateCode(updatedAST);
                logger.info(`Generated updated code (${generatedCode.code.length} characters)`);
                
                modifiedFiles.push({
                    filePath: issue.filePath,
                    fileContents: generatedCode.code,
                    filePurpose: `Fixed import path in ${issue.filePath}`,
                });
                
                fixedIssues.push({
                    issueCode: 'TS2307',
                    filePath: issue.filePath,
                    line: issue.line,
                    column: issue.column,
                    originalMessage: issue.message,
                    fixApplied: `Updated import path from "${moduleSpecifier}" to "${relativeImport}"`,
                    fixType: 'import_fix',
                });
                logger.info(`Successfully fixed import path for ${issue.filePath}`);
            } else {
                logger.info(`No existing file found, creating stub file for "${moduleSpecifier}"`);
                // File doesn't exist, create stub file
                const targetFilePath = resolveImportToFilePath(moduleSpecifier, issue.filePath);
                logger.info(`Resolved stub file path: "${targetFilePath}"`);
                
                logger.info(`Generating stub content for import: ${importInfo.defaultImport ? 'default: ' + importInfo.defaultImport + ', ' : ''}named: [${importInfo.namedImports.join(', ')}]`);
                const stubContent = generateStubFileContent(
                    importInfo, 
                    context.files
                );
                logger.info(`Generated stub content (${stubContent.length} characters)`);
                
                newFiles.push({
                    filePath: targetFilePath,
                    fileContents: stubContent,
                    filePurpose: `Generated stub file for ${moduleSpecifier}`,
                });
                
                fixedIssues.push({
                    issueCode: 'TS2307',
                    filePath: issue.filePath,
                    line: issue.line,
                    column: issue.column,
                    originalMessage: issue.message,
                    fixApplied: `Created stub file "${targetFilePath}" with required exports`,
                    fixType: 'stub_creation',
                });
                logger.info(`Successfully created stub file: "${targetFilePath}"`);
            }
        } catch (error) {
            logger.error(`Failed to fix TS2307 issue at ${issue.filePath}:${issue.line}: ${error instanceof Error ? error.message : 'Unknown error'}`, error);
            unfixableIssues.push(handleFixerError(issue, error as Error, 'TS2307Fixer'));
        }
    }

    logger.info(`TS2307 fixer completed: ${fixedIssues.length} fixed, ${unfixableIssues.length} unfixable, ${modifiedFiles.length} modified files, ${newFiles.length} new files`);
    
    return {
        fixedIssues,
        unfixableIssues,
        modifiedFiles,
        newFiles
    };
}

/**
 * Update import path in AST by modifying the source value
 * Helper function to modify import statements
 */
function updateImportPath(ast: t.File, importInfo: ImportInfo, newPath: string): t.File {
    const body = ast.program?.body ?? [];
    for (const node of body) {
        if (t.isImportDeclaration(node) && t.isStringLiteral(node.source) && node.source.value === importInfo.specifier) {
            node.source.value = newPath;
        }
    }
    return ast;
}