Skip to content

Complexity model

Complexity is counted per function. Every construct adds a base cost plus a nesting penalty unless noted otherwise.

Rules

Construct Increment
if (first in a chain) 1 + nesting
else if clause 1, flat (no nesting penalty)
else clause 1, flat (no nesting penalty)
for, for-in, for-of, while, do-while 1 + nesting
catch clause 1 + nesting
switch statement 1 + nesting, once for the whole statement
ternary ?: 1 + nesting
break label / continue label 1, flat
direct recursion 1, once per function

Boolean sequences in a condition add cost on top of the structural cost:

  • A run of && costs +1 no matter how long it is.
  • Each switch to a different operator (&&, ||, ??) costs another +1.
  • ! resets the chain (starts a fresh sequence).
  • Parentheses are transparent.

So a && b && c costs 1 (one run of &&), while a && b || c && d costs 3 (one && run, one alternation to ||, one new && run).

Nesting

Nesting increases by 1 inside the bodies of:

  • if, else if, and else branches
  • loop bodies (for, for-in, for-of, while, do-while)
  • catch blocks
  • switch case bodies
  • ternary branches (both the then and else arms)
  • nested functions, function expressions, and arrow functions

Nesting does NOT increase inside:

  • try blocks
  • finally blocks
  • bare {} blocks
  • class bodies
  • labeled statement bodies

Reported units

Scores are reported for top-level functions: function f() {}, const f = () => {}, export default function, and class members named ClassName::method. Nested functions and callbacks fold their cost into the enclosing function at a higher nesting level and are not reported separately. Module-level code contributes to the file total, and --check-script reports it as a synthetic <module> function.

Worked examples

Example 1: if / else-if / else chain

function gradeScore(score) {
  if (score >= 90) {        // +1 (if, nesting=0)
    return "A";
  } else if (score >= 80) { // +1 (else if, flat)
    return "B";
  } else if (score >= 70) { // +1 (else if, flat)
    return "C";
  } else {                  // +1 (else, flat)
    return "F";
  }
}

Total: 4

Example 2: loop with nested if and catch

function processItems(items) {
  for (const item of items) {  // +1 (for-of, nesting=0) -> nesting becomes 1
    if (item.active) {         // +2 (if, nesting=1)     -> nesting becomes 2
      try {
        save(item);            // try does not change nesting
      } catch (e) {            // +3 (catch, nesting=2)  -> nesting becomes 3
        if (e.retryable) {     // +4 (if, nesting=3)
          retry(item);
        }
      }
    }
  }
}

Total: 1 + 2 + 3 + 4 = 10

The catch clause is at nesting level 2 because it lives inside both the for body (nesting 1) and the if (item.active) body (nesting 2). The try block itself does not increase nesting, so the catch sees the nesting accumulated by its enclosing constructs.

Example 3: boolean sequences

function validate(a, b, c) {
  if (a && b || c) { // +1 (if, nesting=0) + +1 (&&) + +1 (|| alternation) = 3
    return true;
  }
  return false;
}

Total: 3

Example 4: direct recursion

function factorial(n) {
  if (n <= 1) {                 // +1 (if, nesting=0)
    return 1;
  }
  return n * factorial(n - 1); // +1 (direct recursion)
}

Total: 2