Jest's runtime and mocking layers lead a 5-function risk list to fix first

In jestjs/jest, ESM graph loading, iterable equality, and mock construction show the highest activity-weighted risk, all in the fire quadrant with active recent commits.

Stephen Collins ·
Generated by hotspots · free & open source
pip
$ pip install hotspots-cli
Activity Risk20.51Low
Hottest Function_tryLoadEsmGraphSync

Antipatterns Detected

complex_branching5exit_heavy5deeply_nested4god_function3long_function3hub_function2neighbor_risk1

Run this on your own codebase

See if your own repo has a _tryLoadEsmGraphSync-style hotspot — run this in any local git repo:

pip
$ pip install hotspots-cli
npm
$ npm install -g @stephencollinstech/hotspots
Run in any repo
$ hotspots analyze .
★ Star on GitHub

Key Points

What is deep nesting and why does it matter in jest?

Deep nesting means the maximum number of nested control structures — if statements, loops, try/catch blocks — stacked inside a single function. A nesting depth of 4 or more makes code hard to reason about because a reader has to track multiple simultaneous conditions to know which branch they're in; depth 8 or higher, as seen in `iterableEquality` and `_makeComponent` in this analysis, means a reviewer is holding eight layers of context in their head at once. In jest, four of the top five hotspots carry this deeply-nested pattern, a direct signal that extracting inner blocks into named helper functions would meaningfully improve readability.

How do I reduce cyclomatic complexity in typescript?

The standard technique is decompose-conditional: pull long if-else or switch chains into small, named functions that each handle one classification or one branch, then compose them. A cyclomatic complexity above 15 warrants a look, and above 30 — like `_tryLoadEsmGraphSync` at 43 and `iterableEquality` at 46 in this analysis — warrants immediate attention, since each additional path is a required test case. A concrete first step: take `getType` in `packages/jest-mock/src/index.ts`, currently an eight-branch if-else chain at complexity 20, and convert it to a lookup table keyed by the `typeName` string, which would cut its branching to a single dictionary access plus fallback.

Is jest actively maintained?

Yes — every function in the top five hotspots sits in the fire quadrant, meaning they're structurally complex and under active edit right now, not abandoned complexity. `_tryLoadEsmGraphSync` shows 6 commits in the last 30 days and was last changed today, and `resolve` in `cjsRequire.ts` also changed today with 2 recent touches. Active development and high structural complexity clearly coexist here — the runtime and mocking internals are getting real engineering attention, which is exactly why they're worth reviewing now rather than treating as settled infrastructure.

How do I reproduce this analysis?

The hotspots CLI is available on GitHub, and I ran this analysis against commit `6abdcf1`. To reproduce it: run `git checkout 6abdcf1`, then `hotspots analyze . --mode snapshot --explain-patterns --force`. The same command works on any local git repository without additional configuration.

What does activity-weighted risk mean?

Activity-weighted risk multiplies structural complexity — cyclomatic complexity times nesting depth times fan-out — by recent commit frequency, so functions that are both hard to understand and actively changing score highest. A function with cyclomatic complexity 80 that hasn't been touched in two years scores much lower than one with complexity 20 touched every week, because the dormant function carries lower near-term regression risk even though it looks worse structurally. In this jest snapshot, `_tryLoadEsmGraphSync` tops the list at an activity-weighted risk of 20.51 precisely because it combines a complexity of 43 with 6 touches in the last 30 days — the prioritization is about where a bug is most likely to land next, not just where the code is most tangled in the abstract.

Every one of Jest’s top five hotspots sits in the fire quadrant — complex code that is also under active edit right now, not dormant debt waiting for someone to circle back. _tryLoadEsmGraphSync in packages/jest-runtime/src/index.ts leads with an activity-weighted risk of 20.51, a cyclomatic complexity of 43, and 6 commits touching it in the last 30 days — that combination alone is reason enough to review it this week, not next quarter. Jest itself spans 2,552 functions, with 172 flagged critical and 494 sitting in the fire quadrant, which tells me the risk here isn’t isolated to one file — it’s concentrated in the runtime and mocking internals that most test runs pass through. I’d start with _tryLoadEsmGraphSync because it’s the highest-scoring function in the dataset and it was changed earlier today.

The table below ranks functions by activity-weighted risk — a score that multiplies structural complexity by recent commit frequency. A function that is both hard to understand (high cyclomatic complexity) and actively changing is a higher priority than one that is complex but untouched. CC = cyclomatic complexity (independent execution paths); ND = max nesting depth; FO = fan-out (distinct callees).

Top 5 Hotspots

FunctionFileRiskCCNDFO
_tryLoadEsmGraphSyncpackages/jest-runtime/src/index.ts20.543527
iterableEqualitypackages/expect-utils/src/utils.ts20.046822
getTypepackages/jest-mock/src/index.ts18.72082
resolvepackages/jest-runtime/src/internals/cjsRequire.ts18.61246
_makeComponentpackages/jest-mock/src/index.ts18.619920
Risk quadrant distribution across 2,552 functions
Fire494Watch2058

2,552 functions analyzed

Zero functions landed in the debt quadrant for this snapshot — every complex function that scored critical or high is also showing recent commit activity. That’s a specific signal: nothing here is quietly rotting untouched, but it also means every one of the 172 critical functions is a live editing target where structural complexity and change frequency are compounding at the same time.

Detected Antipatterns
Complex Branching×5Complex Branching
High cyclomatic complexity — many independent execution paths, each a potential bug surface and required test case.
Exit Heavy×5Exit Heavy
Multiple return or throw paths dispersed through the body — each exit needs separate test coverage.
Deeply Nested×4Deeply Nested
Control structures nested 4+ levels deep, making it hard to reason about the full execution state at inner branches.
God Function×3God Function
Calls an unusually large number of distinct functions (high fan-out), making it the structural centre of gravity for a subsystem.
Long Function×3Long Function
Function body is too long to review in a single pass; likely contains multiple distinct responsibilities.
Hub Function×2Hub Function
Many other functions call this one — a change here ripples widely through callers.
Neighbor Risk×1Neighbor Risk
Co-located with other high-risk functions in the same file, compounding the blast radius of any change to that file.

Across the top five, the antipattern mix is consistent: every function has complex branching, four are exit-heavy, four are deeply nested, and three qualify as god functions handling multiple responsibilities in one body. That’s not a coincidence of naming — these are the functions where Jest’s core loading, mocking, and equality logic converge, and TypeScript’s async/await chains and conditional types only add implicit branches that raw cyclomatic complexity numbers understate.

_tryLoadEsmGraphSync — packages/jest-runtime/src/index.ts

_tryLoadEsmGraphSync
packages/jest-runtime/src/index.ts
20.51
critical
CC 43
ND 5
FO 27
touches/30d 6

This is the top hotspot in the repository, and the source excerpt shows why. It walks an ES module dependency graph with an explicit worklist and a scratch map, checking a transform-cache mutex at both the root and per-dependency level to defer to an in-flight legacy async load rather than race it. A cyclomatic complexity of 43 with a fan-out of 27 means this function branches constantly and calls into more than two dozen other functions — registry lookups, core-module resolution, synthetic module construction — while also being one of the few functions tagged neighbor_risk, meaning changes here likely require coordinated edits elsewhere in the ESM loading path. With 6 commits in the last 30 days and a change made earlier today, this is being actively reshaped right now, not a stable piece of infrastructure. My first move would be to extract the worklist-walking loop into its own named function, separate from the early-exit guard clauses at the top — that alone would cut the branching surface reviewers have to hold in their heads at once.

iterableEquality — packages/expect-utils/src/utils.ts

iterableEquality
packages/expect-utils/src/utils.ts
19.98
critical
CC 46
ND 8
FO 22
touches/30d 2

This has the highest cyclomatic complexity in the top five at 46, and the deepest nesting at 8 — well past the level-4 threshold where reasoning about control flow gets genuinely hard. The excerpt shows why: it’s doing circular-reference detection via a manual stack walk, cross-realm constructor comparisons with a comment referencing a specific past issue (#14011), and then branches out into Set, Map, and Immutable-collection comparison logic, recursively rebuilding its own custom-tester list on each call for cycle safety. That’s a lot of responsibility for one comparator, and it’s the kind of function where a subtle change to the constructor-equality branch or the Set/Map dispatch logic can silently alter equality semantics used across every test file in every consumer’s suite. With only 2 touches in the last 30 days but a change as recent as yesterday, this isn’t stale — it’s being edited on a slower but still current cadence. Given a fan-out of 22, I’d look at splitting the Set/Map/collection dispatch into separate named comparator functions before the next change lands.

getType — packages/jest-mock/src/index.ts

getType
packages/jest-mock/src/index.ts
18.65
critical
CC 20
ND 8
FO 2
touches/30d 1

The excerpt shows a long if/else-if chain classifying a value into one of eight mock metadata types — function variants, array, object, numeric/string/boolean/symbol constants, collections, regexp, undefined, null. Cyclomatic complexity of 20 and nesting depth of 8 for what is fundamentally a type-classification dispatch is a strong decompose-conditional candidate: this reads like exactly the kind of chain that maps cleanly onto a lookup table or a switch on typeName rather than a stacked if-else. Fan-out is low at 2, so the coupling risk is minimal — the risk here is purely in how much branching a reader has to trace to confirm classification is correct for a given value. Only 1 touch in the last 30 days, but it’s a foundational function for ModuleMocker, so any future change to the type-classification logic deserves a table-driven rewrite rather than another appended else if.

resolve — packages/jest-runtime/src/internals/cjsRequire.ts

resolve
packages/jest-runtime/src/internals/cjsRequire.ts
18.63
critical
CC 12
ND 4
FO 6
touches/30d 2

This one has the lowest cyclomatic complexity of the top five at 12, but it’s tagged hub_function and exit_heavy — the excerpt shows four distinct return/throw exit points across absolute-path resolution, explicit-paths resolution, and a try/catch fallback into mock-module resolution. It’s the entry point for require.resolve semantics inside Jest’s CommonJS runtime, so every module resolution failure or mock fallback path runs through here. With 2 touches in 30 days and a change made earlier today, it’s under active edit alongside _tryLoadEsmGraphSync in the same runtime package — worth reviewing together since both handle module loading and both changed today. The fix here is smaller than for the other functions: name each exit condition explicitly (absolute path hit, paths-option hit, cjs resolve success, mock fallback) so the four exit points map onto named cases a reviewer can check off one at a time.

_makeComponent — packages/jest-mock/src/index.ts

_makeComponent
packages/jest-mock/src/index.ts
18.56
critical
CC 19
ND 9
FO 20
touches/30d 1

This has the deepest nesting in the entire top five at 9, and the excerpt shows why — it’s constructing mock components (objects, arrays, regexps, functions) and, for the function case, building a full mock constructor inline that tracks call state, instances, contexts, and results, with a nested immediately-invoked arrow function to capture the return value across multiple internal branches, plus constructor-vs-call detection that copies prototype methods onto instances. Fan-out of 20 combined with the god_function tag means this single function is responsible for mock object creation, call-tracking bookkeeping, and constructor-semantics emulation all at once. Only 1 touch in 30 days, but given the nesting depth, I’d treat any future change here as high-risk by default — my recommendation is to pull the inline mock-constructor body out into its own named factory function so the constructor-vs-call branching isn’t buried nine levels deep inside _makeComponent itself.

A few other functions worth noting for context — things like _importWasmModule and _getFakeTimers, both in jest-runtime/src/index.ts — sit in the watch quadrant with cyclomatic complexity no higher than 7. They’re active (2–4 touches in 30 days) but structurally simple, so they’re worth watching if their complexity climbs, not a refactoring priority today.

Patterns Found

Antipatterns detected across the top functions in this snapshot:

PatternOccurrences
complex_branching5
exit_heavy5
deeply_nested4
god_function3
long_function3
hub_function2
neighbor_risk1

These labels belong to two tiers — Tier 1 (structural): complex_branching, deeply_nested, exit_heavy, long_function, god_function. Tier 2 (relational/temporal): hub_function, cyclic_hub, middle_man, neighbor_risk, stale_complex, churn_magnet, shotgun_target, volatile_god.

See more analyses with these patterns: complex_branching, deeply_nested, exit_heavy, god_function, long_function.

Reproduce This Analysis

git clone https://github.com/jestjs/jest
cd jest
git checkout 6abdcf1627d7e150111b6ffc03ce01fdbba28270
hotspots analyze . --mode snapshot --explain-patterns --force

To run the same analysis on your own codebase, run hotspots analyze . --mode snapshot in any local git repo — no configuration required.

I use Hotspots to highlight structural and activity risk — not “bad code.” I treat these findings as a prioritization aid, not a bug predictor. Editorial policy →

Was this useful? Let me know →

Related Analyses