| 62 | } |
| 63 | |
| 64 | export class ReactAnalyzer implements FrameworkAnalyzer { |
| 65 | readonly name = 'react'; |
| 66 | readonly version = '1.0.0'; |
| 67 | readonly supportedExtensions = ['.tsx', '.jsx', '.ts', '.js', '.mjs', '.cjs', '.mts', '.cts']; |
| 68 | readonly priority = 80; |
| 69 | |
| 70 | canAnalyze(filePath: string, content?: string): boolean { |
| 71 | const extension = path.extname(filePath).toLowerCase(); |
| 72 | if (!this.supportedExtensions.includes(extension)) { |
| 73 | return false; |
| 74 | } |
| 75 | |
| 76 | if (extension === '.tsx' || extension === '.jsx') { |
| 77 | return true; |
| 78 | } |
| 79 | |
| 80 | if (!content) { |
| 81 | return false; |
| 82 | } |
| 83 | |
| 84 | return ( |
| 85 | /\bfrom\s+['"]react['"]/.test(content) || |
| 86 | /\brequire\(\s*['"]react['"]\s*\)/.test(content) || |
| 87 | /\bReact\.createElement\b/.test(content) || |
| 88 | /<[A-Za-z][^>]*>/.test(content) |
| 89 | ); |
| 90 | } |
| 91 | |
| 92 | async analyze(filePath: string, content: string): Promise<AnalysisResult> { |
| 93 | const extension = path.extname(filePath).toLowerCase(); |
| 94 | const language = |
| 95 | extension === '.ts' || extension === '.tsx' || extension === '.mts' || extension === '.cts' |
| 96 | ? 'typescript' |
| 97 | : 'javascript'; |
| 98 | const relativePath = path.relative(process.cwd(), filePath); |
| 99 | |
| 100 | const imports: ImportStatement[] = []; |
| 101 | const exports: ExportStatement[] = []; |
| 102 | const dependencyNames = new Set<string>(); |
| 103 | const importSources = new Set<string>(); |
| 104 | const detectedPatterns: DetectedPattern[] = []; |
| 105 | let components: CodeComponent[] = []; |
| 106 | |
| 107 | try { |
| 108 | const program = parse(content, { |
| 109 | loc: true, |
| 110 | range: true, |
| 111 | comment: true, |
| 112 | jsx: extension.includes('x'), |
| 113 | sourceType: 'module' |
| 114 | }); |
| 115 | |
| 116 | for (const statement of program.body) { |
| 117 | if (statement.type === 'ImportDeclaration' && typeof statement.source.value === 'string') { |
| 118 | const source = statement.source.value; |
| 119 | importSources.add(getPackageName(source)); |
| 120 | imports.push({ |
| 121 | source, |
nothing calls this directly
no outgoing calls
no test coverage detected