// Complexité cyclomatique par fonction (méthode de McCabe : 1 + chaque branche), avec le compilateur TypeScript du projet. // node complexite.cjs -> JSON trié, plus résumé const ts = require(require.resolve("typescript", { paths: [process.cwd()] })); const fs = require("fs"), path = require("path"); const root = process.argv[2] || "src"; const files = []; (function walk(d) { for (const f of fs.readdirSync(d)) { const p = path.join(d, f); fs.statSync(p).isDirectory() ? walk(p) : /\.tsx?$/.test(f) && files.push(p); } })(root); const BR = new Set([ts.SyntaxKind.IfStatement, ts.SyntaxKind.ForStatement, ts.SyntaxKind.ForInStatement, ts.SyntaxKind.ForOfStatement, ts.SyntaxKind.WhileStatement, ts.SyntaxKind.DoStatement, ts.SyntaxKind.CaseClause, ts.SyntaxKind.CatchClause, ts.SyntaxKind.ConditionalExpression]); const LOG = new Set([ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken, ts.SyntaxKind.QuestionQuestionToken]); const isFn = n => ts.isFunctionDeclaration(n) || ts.isMethodDeclaration(n) || ts.isArrowFunction(n) || ts.isFunctionExpression(n); const out = []; for (const file of files) { const sf = ts.createSourceFile(file, fs.readFileSync(file, "utf8"), ts.ScriptTarget.Latest, true, file.endsWith("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS); (function visit(node) { if (isFn(node)) { let c = 1; (function count(n) { if (n !== node && isFn(n)) return; if (BR.has(n.kind)) c++; if (ts.isBinaryExpression(n) && LOG.has(n.operatorToken.kind)) c++; ts.forEachChild(n, count); })(node); let name = node.name ? node.name.getText(sf) : (ts.isVariableDeclaration(node.parent) ? node.parent.name.getText(sf) : (ts.isPropertyAssignment(node.parent) ? node.parent.name.getText(sf) : "(anonyme)")); const { line } = sf.getLineAndCharacterOfPosition(node.getStart(sf)); const end = sf.getLineAndCharacterOfPosition(node.getEnd()).line; out.push({ file, line: line + 1, name, complexity: c, lines: end - line + 1 }); } ts.forEachChild(node, visit); })(sf); } out.sort((a, b) => b.complexity - a.complexity); const n = out.length, over10 = out.filter(f => f.complexity > 10).length, over20 = out.filter(f => f.complexity > 20).length, long = out.filter(f => f.lines > 80); console.log(JSON.stringify({ fonctions: n, au_dessus_de_10: over10, au_dessus_de_20: over20, plus_de_80_lignes: long.length, top: out.slice(0, 12), longues: long.sort((a,b)=>b.lines-a.lines).slice(0,8) }, null, 1));