At commit c01afad, tw93/Pake — a Tauri-based tool for packaging web apps into desktop binaries — has 425 analyzed functions, 41 of which score in the critical band. Every one of the top 5 hotspots lands in the ‘fire’ quadrant: high structural complexity and active recent commits, making them live regression risks rather than backlog cleanup items. The top-ranked function, findBuildOutputFiles, carries a risk score of 16.81 with a cyclomatic complexity of 25 and a nesting depth of 9; detectAnchorElementClick sits just behind it and has been touched 4 times in the last 30 days. I’d treat both as priorities for anyone shipping code against this repo right now.
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
| Function | File | Risk | CC | ND | FO |
|---|---|---|---|---|---|
findBuildOutputFiles | tests/index.js | 16.8 | 25 | 9 | 23 |
detectAnchorElementClick | src-tauri/src/inject/event.js | 15.5 | 25 | 4 | 16 |
getFilenameFromUrl | src-tauri/src/inject/event.js | 15.2 | 17 | 8 | 9 |
build_window | src-tauri/src/app/window.rs | 14.9 | 24 | 3 | 17 |
loadConfigFile | bin/helpers/config-file.ts | 14.4 | 26 | 3 | 11 |
Large Repo Analysis
Pake is a large repository. To stay within memory constraints, this analysis used hybrid touch mode: structural complexity — CC, ND, FO — is measured precisely for every function. Git activity is tracked at the function level (via git log -L) only for files with 5 or more commits in the last 30 days; other files use a file-level approximation. Rankings therefore surface functions that are both structurally complex and in the most actively-changing parts of the codebase. Dormant code with high structural complexity will rank lower than it would under a full per-function analysis — to surface it, run hotspots analyze . --per-function-touches on a machine with sufficient memory.
Codemod / Tooling Files in Results
The function findBuildOutputFiles in tests/index.js is part of the project’s own test infrastructure rather than a vendored third-party library, so it warrants the same review attention as production code. There are no vendored, node_modules, dist, or bundled library files in the provided data. No exclusion pattern is needed.
Quadrant and pattern overview
425 functions analyzed
Every function in this repository sits in either the ‘fire’ or ‘watch’ quadrant — no dormant structural debt is waiting quietly in the background. The 128 fire-quadrant functions are all complex enough to warrant attention and all actively changing. The 297 watch-quadrant functions are active but structurally simpler; runSearch in src-tauri/src/inject/find.js and run_app in src-tauri/src/lib.rs are worth keeping an eye on as the codebase grows, but neither demands immediate refactoring.
Calls an unusually large number of distinct functions (high fan-out), making it the structural centre of gravity for a subsystem.Long Function×5Long Function
Function body is too long to review in a single pass; likely contains multiple distinct responsibilities.Complex Branching×3Complex Branching
High cyclomatic complexity — many independent execution paths, each a potential bug surface and required test case.Exit Heavy×3Exit Heavy
Multiple return or throw paths dispersed through the body — each exit needs separate test coverage.Deeply Nested×2Deeply Nested
Control structures nested 4+ levels deep, making it hard to reason about the full execution state at inner branches.
The pattern picture is consistent across all five hotspots: every one is flagged as a god function and a long function. Three also carry complex branching, three are exit-heavy, and two are deeply nested. That’s not a coincidence — it points to a structural habit of accumulating logic inside single functions rather than distributing it across smaller, testable units.
findBuildOutputFiles — tests/index.js
findBuildOutputFiles is responsible for locating platform-specific build artifacts — .msi, .deb, .dmg, .app, and others — across a set of candidate directories derived from the target platform string. The source excerpt shows it building a searchLocations array through a chain of spread conditionals, one branch per platform (linux, win32, darwin), then walking each directory with fs.readdirSync and filtering results against per-platform extension patterns.
A cyclomatic complexity of 25 means 25 independent execution paths — each one a required test case for guaranteed coverage. The nesting depth of 9 is the most striking number: at that depth, the innermost logic is buried inside a for loop, a try block, a directory-existence guard, a filter callback, a stats.isDirectory() branch, and an Array.includes check. That’s very hard to reason about in isolation. The fan-out of 23 reflects the function’s direct dependence on fs.existsSync, fs.readdirSync, fs.statSync, path.join, path.relative, and a set of config accessors — a change to any of those call sites can affect behavior here.
External signals for this file show that its single commit in history was tagged as a bug fix, with a single author in the last 90 days. That’s too thin a commit history to draw defect conclusions from, but combined with the structural metrics it means this function has grown complex without the review feedback loop that would ordinarily surface simplification opportunities.
The most actionable step is to extract the platform-to-directory mapping into a pure data structure or a dedicated helper, and the per-directory file-scanning logic into its own function. That would bring the CC of the residual orchestration function below 10 and make the directory-walking path independently testable.
detectAnchorElementClick — src-tauri/src/inject/event.js
This function is the central click-event handler injected into the Tauri webview — every anchor click inside the packaged web app passes through here. The source excerpt shows it sequentially deciding: is the target a valid element? is the href a Pake-bypassed link? is it an OAuth/auth link? is the target _blank and internal? is the target _blank and external? is the target _new? — with early returns scattered throughout each branch.
With 4 commits in the last 30 days and last changed 6 days ago, this is the most actively changing function in the link-handling layer. A cyclomatic complexity of 25 in a function touched roughly once a week is a live regression risk. Each new navigation case — OAuth flows, forceInternalNavigation, SPA _blank quirks, download links — adds another branch rather than being dispatched through a strategy or routing table. The exit-heavy pattern is visible in the excerpt: nearly every conditional block ends with an explicit return, which means test coverage requires exercising all 25 paths individually.
The fan-out of 16 means this single handler calls shouldBypassPakeLinkHandling, isAuthLink, openAuthNavigation, isInternalUrl, handleExternalLink, getFilenameFromUrl, and others — a change to any of those helpers has this function as a direct blast-radius surface.
Every one of the 4 commits to this file has been tagged as a bug fix. That’s consistent with the branching structure — each edge case discovered in the field requires a new conditional here. I’d extract each link-handling concern (auth links, blank-target internal links, blank-target external links, download links) into its own named function, then reduce detectAnchorElementClick to a dispatcher that calls each handler in sequence. That would cut the CC roughly in half and make each case independently testable.
getFilenameFromUrl — src-tauri/src/inject/event.js
getFilenameFromUrl lives in the same file as detectAnchorElementClick and is called by it. Its job is to derive a local filename from a URL — either by reading the URL path’s trailing segment, or by generating a timestamped fallback filename keyed to the detected image type.
The nesting depth of 8 is what stands out. The source excerpt shows the deepest branch handling data URI parsing: the function enters a try block, checks whether the URL starts with data:image/, locates the MIME subtype boundaries by scanning for semicolons and commas, splits on +, then maps jpeg to jpg. All of that is nested inside the fallback branch of the filename-extraction logic. The cyclomatic complexity of 17 comes from the chain of url.includes("jpg"), url.includes("png"), url.includes("gif"), and so on that follows the data URI branch.
Because this function shares the same file commit history as detectAnchorElementClick (4 touches in 30 days, all tagged as bug fixes, 2 authors in 90 days), both are co-evolving. The data URI MIME parsing logic and the extension-sniffing-from-URL-string logic are distinct concerns that belong in separate helpers. Extracting them would bring the nesting depth below 4 and the CC below 10, and would make it possible to unit-test the MIME parsing path without constructing a full event.
build_window — src-tauri/src/app/window.rs
This is the only Rust function in the top 5, and also the most recently changed: it was modified as recently as the analysis snapshot. build_window takes an AppHandle, a PakeConfig, a Tauri Config, and a WindowBuildOptions struct and assembles the WebviewWindow that wraps the packaged web app.
The structural driver is platform-conditional compilation: #[cfg(target_os = "macos")] and #[cfg(target_os = "windows")] blocks each introduce separate logic branches — certificate bypass, blank-page navigation on macOS, DPI-scaled window sizing on Windows. A cyclomatic complexity of 24 in Rust carries extra weight because the compiler’s ownership and lifetime rules are layered on top: each branch that conditionally constructs or moves a value (like the url rebinding under the macOS cert-bypass path) requires the reader to track ownership across the branch boundary, a cognitive cost that CC alone doesn’t fully capture.
The fan-out of 17 reflects calls into get_data_dir, serde_json::to_string, Url::parse, WebviewWindowBuilder::new, and a chain of builder methods — changes to any of those APIs require coordinated updates here. The god_function and exit_heavy patterns are consistent with a function that has absorbed every platform-specific window-configuration concern over time.
External signals show this function has attracted more code review discussion than any other in the top 5, suggesting reviewers have already flagged concerns here. With 4 touches in 30 days and last changed on the day of this snapshot, it is being actively modified right now. I’d extract each #[cfg(...)] block into a dedicated platform configuration helper — a function returning the appropriate WebviewUrl for macOS cert-bypass, another applying Windows DPI scaling — and leave build_window as a thin orchestrator. That decomposition would also make each platform path independently testable.
loadConfigFile — bin/helpers/config-file.ts
loadConfigFile is the CLI entry point for reading and validating a user-supplied JSON configuration file against the pake.schema.json schema. The source excerpt shows it doing several distinct jobs in sequence: verifying the file exists, parsing the JSON, asserting the top-level shape is an object, then iterating over every key and applying type validation, range validation, and rejection rules.
A cyclomatic complexity of 26 is the highest in the top 5, driven by the per-key validation loop: each key can hit the $schema skip, the url special case, the REJECTED_KEYS check, the validKeys check, the type-match check, or the numeric range check — each an independent path. The exit-heavy pattern — throw new PakeError(...) at each validation failure — means test coverage requires exercising all 26 paths. The nesting depth stays manageable at 3 because the exit-heavy style keeps the happy-path logic relatively flat, but the number of throws scattered through the function makes reasoning about the full validation sequence non-trivial.
With only 1 touch in 30 days and a single author in 90 days, this is the least actively changing of the five hotspots — but a CC of 26 means it carries the highest structural risk per change whenever it is next modified. I’d extract the per-key validation logic — type checking, range checking, rejection rules — into a dedicated validateConfigEntry(key, value) helper. That would reduce loadConfigFile to a parsing-and-iteration shell with a CC closer to 6, and give the validation rules a home where they can be tested against individual key-value pairs without needing a full config file on disk.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
god_function | 5 |
long_function | 5 |
complex_branching | 3 |
exit_heavy | 3 |
deeply_nested | 2 |
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/tw93/Pake
cd Pake
git checkout c01afad4e258de6a27fa483908dfdeb6df3914ad
hotspots analyze . --mode snapshot --explain-patterns --force --hybrid-touches 5
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 →