All files / services/qualimetrie lintAgent.ts

87.35% Statements 76/87
43.75% Branches 7/16
73.33% Functions 11/15
88.09% Lines 74/84

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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 1516x 6x                                                                   6x     5x 5x     5x 5x 2x 2x 2x 2x 2x   2x 2x 2x 4x 4x 4x 4x 7x 4x 4x   7x 3x 3x   7x       7x       4x 4x   2x 2x   2x 4x 1x   2x       2x     3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 5x 3x 3x   5x 2x 2x   5x       5x       3x 3x   3x 3x 3x 3x 1x   3x       3x       6x     1x     1x 1x 1x      
import { execSync } from 'child_process';
import fs from 'fs/promises';
import { Command } from 'commander';
 
export interface LintAgentOptions {}
 
export interface LintAgentResult {
  passed: boolean;
  /**
   * Statistiques globales issues de ESLint
   */
  metrics?: {
    errors: number;
    warnings: number;
    /** Nombre de fichiers analysés */
    files: number;
    /** Nombre total de problèmes (errors + warnings) */
    total: number;
    /** Nombre de problèmes fixables */
    fixable: number;
  };
  /** Chemin vers le rapport JSON détaillé */
  detailPath?: string;
  /** Contenu brut en cas d'erreur JSON */
  output?: string;
  /** Synthèse des fichiers et règles les plus problématiques */
  summary?: {
    topFiles: Array<{ filePath: string; errors: number; warnings: number; total: number; fixable: number }>;
    topRules: Array<{ ruleId: string; count: number }>;
  };
}
 
/**
 * Lance ESLint et renvoie le résultat.
 */
export async function lintAgent(
  _opts?: LintAgentOptions,
): Promise<LintAgentResult> {
  const detailPath = 'eslint-report.json';
  try {
    // Run ESLint and output JSON to stdout
    // Use npx to invoke the local ESLint binary (works with npm, pnpm, yarn)
    const cmd = 'npx eslint . --ext .ts,.js --format json';
    const stdout = execSync(cmd, { encoding: 'utf-8' });
    const results = JSON.parse(stdout);
    await fs.writeFile(detailPath, stdout, 'utf-8');
    let errors = 0;
    let warnings = 0;
    let fixable = 0;
    // Détails par fichier pour synthèse
    const fileStats: Array<{ filePath: string; errors: number; warnings: number; total: number; fixable: number }> = [];
    const ruleCounts: Record<string, number> = {};
    results.forEach((file: any) => {
      let fe = 0;
      let fw = 0;
      let ff = 0;
      file.messages.forEach((m: any) => {
        if (m.severity === 2) {
          errors++;
          fe++;
        }
        if (m.severity === 1) {
          warnings++;
          fw++;
        }
        Iif (m.fix) {
          fixable++;
          ff++;
        }
        Iif (m.ruleId) {
          ruleCounts[m.ruleId] = (ruleCounts[m.ruleId] || 0) + 1;
        }
      });
      const total = fe + fw;
      fileStats.push({ filePath: file.filePath, errors: fe, warnings: fw, total, fixable: ff });
    });
    const files = results.length;
    const total = errors + warnings;
    // Top 5 fichiers et règles
    const topFiles = fileStats
      .filter(f => f.total > 0)
      .sort((a, b) => b.total - a.total)
      .slice(0, 5);
    const topRules = Object.entries(ruleCounts)
      .map(([ruleId, count]) => ({ ruleId, count }))
      .sort((a, b) => b.count - a.count)
      .slice(0, 5);
    return { passed: errors === 0, metrics: { errors, warnings, files, total, fixable }, detailPath, summary: { topFiles, topRules } };
  } catch (err: any) {
    // On errors, attempt to parse output in stdout
    let raw = '';
    if (err.stdout) raw = err.stdout as string;
    Iif (!raw && err.stderr) raw = err.stderr as string;
    await fs.writeFile(detailPath, raw, 'utf-8');
    let results: any[] = [];
    try { results = JSON.parse(raw); } catch {}
    let errors = 0;
    let warnings = 0;
    let fixable = 0;
    const fileStats: Array<{ filePath: string; errors: number; warnings: number; total: number; fixable: number }> = [];
    const ruleCounts: Record<string, number> = {};
    results.forEach((file: any) => {
      let fe = 0;
      let fw = 0;
      let ff = 0;
      file.messages.forEach((m: any) => {
        if (m.severity === 2) {
          errors++;
          fe++;
        }
        if (m.severity === 1) {
          warnings++;
          fw++;
        }
        Iif (m.fix) {
          fixable++;
          ff++;
        }
        Iif (m.ruleId) {
          ruleCounts[m.ruleId] = (ruleCounts[m.ruleId] || 0) + 1;
        }
      });
      const totalErr = fe + fw;
      fileStats.push({ filePath: file.filePath, errors: fe, warnings: fw, total: totalErr, fixable: ff });
    });
    const files = results.length;
    const total = errors + warnings;
    const topFiles = fileStats
      .filter(f => f.total > 0)
      .sort((a, b) => b.total - a.total)
      .slice(0, 5);
    const topRules = Object.entries(ruleCounts)
      .map(([ruleId, count]) => ({ ruleId, count }))
      .sort((a, b) => b.count - a.count)
      .slice(0, 5);
    return { passed: false, metrics: { errors, warnings, files, total, fixable }, detailPath, output: raw, summary: { topFiles, topRules } };
  }
}
 
export const cli = {
  command: 'qualimetrie:lint',
  description: 'Vérifie la qualité du code avec ESLint',
  builder: (cmd: Command) => cmd,
  handler: async (_opts: unknown) => {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const { lintAgent: run } = require('./lintAgent');
    const res = await run();
    console.log(res);
  },
};