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, andelsebranches- loop bodies (
for,for-in,for-of,while,do-while) catchblocksswitchcase bodies- ternary branches (both the then and else arms)
- nested functions, function expressions, and arrow functions
Nesting does NOT increase inside:
tryblocksfinallyblocks- 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