UNPKG

pi-lens

Version:

Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo

75 lines (74 loc) • 3.59 kB
import { isTestFrameworkNoiseCall, isTestSuiteOrganizer, } from "./framework-call-noise.js"; // Default matches the historical hardcoded value so behavior is unchanged for // projects without a config. Project-specific overrides are read from the // per-dispatch context; this mutable fallback exists for tests/legacy direct // rule evaluation only. export const DEFAULT_HIGH_FAN_OUT_THRESHOLD = 20; let fanOutThreshold = DEFAULT_HIGH_FAN_OUT_THRESHOLD; /** Override fallback threshold for tests/legacy direct rule evaluation. */ export function setHighFanOutThreshold(n) { // Non-positive thresholds make every function violate the rule; treat them as // invalid config/test input rather than turning the rule into noise. if (Number.isFinite(n) && n > 0) fanOutThreshold = n; } /** Test helper: restore compile-time default. */ export function resetHighFanOutThreshold() { fanOutThreshold = DEFAULT_HIGH_FAN_OUT_THRESHOLD; } export const highFanOutRule = { id: "high-fan-out", requires: ["file.functionSummaries"], appliesTo(ctx) { return /\.tsx?$/.test(ctx.filePath); }, evaluate(ctx, store) { const fns = store.getFileFact(ctx.filePath, "file.functionSummaries") ?? []; const diagnostics = []; const configuredFanOutThreshold = ctx.projectConfig?.rules["high-fan-out"]?.threshold; const activeFanOutThreshold = configuredFanOutThreshold ?? fanOutThreshold; for (const f of fns) { // A describe()/it()/test() wrapper's own call list aggregates every call // from ALL of its nested test bodies (#577) — not real fan-out. Genuinely // complex test HELPER functions don't call it/describe/test themselves, so // they're unaffected. if (isTestSuiteOrganizer(f.outgoingCalls)) continue; // Filter out noise: utility calls, logging, type assertions, and // test-framework calls (expect/it/describe/vi.*/jest.*, #577) — assertion- // and mock-heavy test bodies naturally call many of these; that's test // structure, not a coordination smell. const meaningful = f.outgoingCalls.filter((c) => { const lower = c.toLowerCase(); return (!lower.startsWith("console.") && !lower.startsWith("math.") && !lower.startsWith("json.") && !lower.startsWith("object.") && !lower.startsWith("array.") && !lower.startsWith("string(") && !lower.startsWith("number(") && !lower.startsWith("boolean(") && !lower.startsWith("error(") && c !== "resolve" && c !== "reject" && c !== "next" && c !== "done" && !isTestFrameworkNoiseCall(c)); }); if (meaningful.length < activeFanOutThreshold) continue; diagnostics.push({ id: `high-fan-out:${ctx.filePath}:${f.line}`, tool: "fact-rules", rule: "high-fan-out", filePath: ctx.filePath, line: f.line, column: f.column, severity: "warning", semantic: "warning", message: `'${f.name}' calls ${meaningful.length} distinct functions — coordination smell, consider splitting responsibilities`, }); } return diagnostics; }, };