All files / src scanner.ts

86.95% Statements 20/23
60% Branches 3/5
100% Functions 2/2
90.9% Lines 20/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108                                                      12x 12x   2x     10x 10x   10x 10x 10x 10x   10x 10x               10x     10x 10x 10x     10x 10x               10x                               2x                           2x                    
/**
 * Language-agnostic scanner for AI signal clarity signals.
 *
 * Detects code patterns that empirically cause AI models to generate
 * incorrect code across all supported languages (TS, Python, Java, C#, Go).
 */
 
import { readFileSync } from 'fs';
import { getParser } from '@aiready/core';
import type {
  FileAiSignalClarityResult,
  AiSignalClarityOptions,
} from './types';
 
import { detectExportSignals } from './signals/exports';
import { detectStructuralSignals } from './signals/visitor';
import type { SignalContext, SignalResult } from './signals/types';
 
// ---------------------------------------------------------------------------
// Main scanner
// ---------------------------------------------------------------------------
 
export async function scanFile(
  filePath: string,
  options: AiSignalClarityOptions = { rootDir: '.' }
): Promise<FileAiSignalClarityResult> {
  let code: string;
  try {
    code = readFileSync(filePath, 'utf-8');
  } catch {
    return emptyResult(filePath);
  }
 
  const parser = await getParser(filePath);
  Iif (!parser) return emptyResult(filePath);
 
  try {
    await parser.initialize();
    const parseResult = parser.parse(code, filePath);
    const ast = await parser.getAST(code, filePath);
 
    const lineCount = code.split('\n').length;
    const ctx: SignalContext = {
      filePath,
      code,
      lineCount,
      options,
    };
 
    // 1. Detect Export and File-level signals
    const exportResult = detectExportSignals(ctx, parseResult);
 
    // 2. Detect Structural signals (Magic literals, Boolean traps, etc.)
    let structuralResult: SignalResult = { issues: [], signals: {} };
    Eif (ast) {
      structuralResult = detectStructuralSignals(ctx, ast);
    }
 
    const allIssues = [...exportResult.issues, ...structuralResult.issues];
    const combinedSignals = {
      ...exportResult.signals,
      ...structuralResult.signals,
      totalSymbols: parseResult.exports.length + parseResult.imports.length,
      totalExports: parseResult.exports.length,
      totalLines: lineCount,
    };
 
    return {
      fileName: filePath,
      issues: allIssues,
      signals: combinedSignals as any,
      metrics: {
        totalSymbols: parseResult.exports.length + parseResult.imports.length,
        totalExports: parseResult.exports.length,
      },
    };
  } catch (error) {
    console.error(`AI Signal Clarity: Failed to scan ${filePath}: ${error}`);
    return emptyResult(filePath);
  }
}
 
function emptyResult(filePath: string): FileAiSignalClarityResult {
  const aggregateSignals: any = {
    magicLiterals: 0,
    booleanTraps: 0,
    ambiguousNames: 0,
    undocumentedExports: 0,
    implicitSideEffects: 0,
    deepCallbacks: 0,
    overloadedSymbols: 0,
    largeFiles: 0,
    totalSymbols: 0,
    totalExports: 0,
    totalLines: 0,
  };
 
  return {
    fileName: filePath,
    issues: [],
    signals: aggregateSignals,
    metrics: {
      totalSymbols: 0,
      totalExports: 0,
    },
  };
}