tina4-nodejs 3.13.89 → 3.13.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -646,7 +646,12 @@ function extractFunctions(source: string, filePath: string, root: string = "."):
646
646
 
647
647
  // Extract function body by brace matching (on cleaned lines).
648
648
  const funcBody = extractFunctionBody(lines, i);
649
- const funcLoc = funcBody.split("\n").length;
649
+ // Code lines, by the exact same counter the file level uses. This was
650
+ // `funcBody.split("\n").length` - a raw line span - while file LOC excluded
651
+ // blanks and comments, so `loc` meant two different things in one payload
652
+ // and the dashboard sized bubbles in one unit while printing the function
653
+ // table in the other. Floor of 1: a one-line body must never report 0.
654
+ const funcLoc = Math.max(1, countLines(funcBody).loc);
650
655
  const complexity = cycloMaticComplexity(funcBody);
651
656
 
652
657
  // Parse args
@@ -675,6 +680,53 @@ function extractFunctions(source: string, filePath: string, root: string = "."):
675
680
  }
676
681
  }
677
682
 
683
+ return chargeNestedComplexityToTheNestedFunction(functions);
684
+ }
685
+
686
+ /**
687
+ * Stop a function being charged for the complexity of the functions nested
688
+ * inside it.
689
+ *
690
+ * Each function's raw score is measured over its whole span, so a branch inside
691
+ * a nested function landed on BOTH that function and every function enclosing
692
+ * it. The over-count compounded with depth: an IIFE wrapper or a registrar
693
+ * defining twenty inner handlers absorbed the entire file's complexity and
694
+ * topped the offenders list, hiding the genuine hot spots.
695
+ *
696
+ * The correction is exact. A raw score is 1 + every decision in the span, so
697
+ * (raw - 1) is the total decision count of a function's whole subtree.
698
+ * Subtracting that for each DIRECT child leaves the function's own branches:
699
+ *
700
+ * own(F) = raw(F) - sum over direct children C of (raw(C) - 1)
701
+ *
702
+ * Anything the extractor does NOT list is deliberately unaffected: nothing
703
+ * subtracts it, so its decisions stay with the function that contains it -
704
+ * moved, never lost.
705
+ */
706
+ export function chargeNestedComplexityToTheNestedFunction(
707
+ functions: FunctionInfo[],
708
+ ): FunctionInfo[] {
709
+ if (functions.length < 2) return functions;
710
+
711
+ const lastLine = (f: FunctionInfo) => f.line + Math.max(1, f.loc) - 1;
712
+ const contains = (outer: FunctionInfo, inner: FunctionInfo) =>
713
+ inner.line > outer.line && lastLine(inner) <= lastLine(outer);
714
+
715
+ const raw = functions.map((f) => f.complexity);
716
+ functions.forEach((outer, i) => {
717
+ let subtract = 0;
718
+ functions.forEach((inner, j) => {
719
+ if (i === j || !contains(outer, inner)) return;
720
+ // Direct child only: skip it if another function sits between the two,
721
+ // or its complexity would be subtracted twice.
722
+ const nestedDeeper = functions.some(
723
+ (mid, k) => k !== i && k !== j && contains(outer, mid) && contains(mid, inner),
724
+ );
725
+ if (!nestedDeeper) subtract += raw[j] - 1;
726
+ });
727
+ outer.complexity = Math.max(1, raw[i] - subtract);
728
+ });
729
+
678
730
  return functions;
679
731
  }
680
732
 
@@ -8573,7 +8573,7 @@ function extractFunctions(source, filePath, root = ".") {
8573
8573
  if (funcName !== null && !NON_FUNCTION_WORDS.has(funcName)) {
8574
8574
  const displayName = funcName === "constructor" && currentClass ? `${currentClass}.constructor` : currentClass !== null && !isTopLevelDecl ? `${currentClass}.${funcName}` : funcName;
8575
8575
  const funcBody = extractFunctionBody(lines, i);
8576
- const funcLoc = funcBody.split("\n").length;
8576
+ const funcLoc = Math.max(1, countLines(funcBody).loc);
8577
8577
  const complexity = cycloMaticComplexity(funcBody);
8578
8578
  const args = argsStr.split(",").map((a) => a.trim().split(":")[0].split("=")[0].replace("?", "").trim()).filter((a) => a && a !== "this");
8579
8579
  functions.push({
@@ -8589,6 +8589,24 @@ function extractFunctions(source, filePath, root = ".") {
8589
8589
  currentClass = null;
8590
8590
  }
8591
8591
  }
8592
+ return chargeNestedComplexityToTheNestedFunction(functions);
8593
+ }
8594
+ function chargeNestedComplexityToTheNestedFunction(functions) {
8595
+ if (functions.length < 2) return functions;
8596
+ const lastLine = (f) => f.line + Math.max(1, f.loc) - 1;
8597
+ const contains = (outer, inner) => inner.line > outer.line && lastLine(inner) <= lastLine(outer);
8598
+ const raw = functions.map((f) => f.complexity);
8599
+ functions.forEach((outer, i) => {
8600
+ let subtract = 0;
8601
+ functions.forEach((inner, j) => {
8602
+ if (i === j || !contains(outer, inner)) return;
8603
+ const nestedDeeper = functions.some(
8604
+ (mid, k) => k !== i && k !== j && contains(outer, mid) && contains(mid, inner)
8605
+ );
8606
+ if (!nestedDeeper) subtract += raw[j] - 1;
8607
+ });
8608
+ outer.complexity = Math.max(1, raw[i] - subtract);
8609
+ });
8592
8610
  return functions;
8593
8611
  }
8594
8612
  function extractFunctionBody(lines, startLine) {