Skip to content

feat: add dead code analysis tooling#194

Merged
jaredhightower-youversion merged 6 commits intomainfrom
feat/add-dead-code-analysis-tooling
Mar 12, 2026
Merged

feat: add dead code analysis tooling#194
jaredhightower-youversion merged 6 commits intomainfrom
feat/add-dead-code-analysis-tooling

Conversation

@jaredhightower-youversion
Copy link
Collaborator

Add dead code & dependency analysis tooling

Summary

Adds automated dead code detection, dependency auditing, and package boundary enforcement to the monorepo using two complementary tools: rev-dep (per-package import graph analysis) and Knip (cross-package dead code tracing).

Motivation

As the SDK grows, it becomes easy for exports, files, and dependencies to go unused without anyone noticing. Boundary violations (e.g., core accidentally importing React) can also slip through in review. This PR introduces tooling to catch these issues automatically.

What's Included

New scripts

Command Description
pnpm analyze Full analysis across all packages — runs both rev-dep and Knip, displays a unified color-coded report
pnpm analyze:select Interactive terminal picker to scope analysis to specific packages

New files

  • .rev-dep.config.jsonc — rev-dep configuration with rules for all 5 workspace targets (core, hooks, ui, eslint-config, tsconfig). Enforces the core → hooks → ui dependency graph and prevents reverse imports.
  • scripts/analyze.mjs — Runs both tools across all packages and merges results into a single grouped report (Architecture, Dead Code, Orphan Files, Dependencies).
  • scripts/analyze-select.mjs — Interactive multi-select menu for scoped analysis. Supports selecting any combination of packages/tools.
  • docs/dead-code-analysis.md — Full documentation on usage, configuration, and how to read the report.

New dependency

  • rev-dep@^2.11.0 (devDependency) — Import graph analyzer with boundary enforcement and circular dependency detection.

Package.json changes

  • Root: added analyze, analyze:select scripts + rev-dep devDependency

What the tools detect

Category Tool Checks
Architecture rev-dep Circular imports, restricted import violations, unresolved imports, dev deps in production code
Dead Code Knip Unused files, unused exports, unused types, duplicate exports
Orphan Files rev-dep Files not reachable from entry points
Dependencies Knip Unused production deps, unused dev deps

Why two tools?

Capability rev-dep Knip
Package boundary enforcement
Circular dependency detection
Restricted imports (e.g. React in core)
Per-package orphan files
Cross-package dead code
Duplicate exports
Unused deps/devDeps
  • rev-dep guards architecture — boundary enforcement, circular import detection, restricted imports. Analyzes each package in isolation from its entry points.
  • Knip guards against waste — traces exports across workspace package boundaries to find truly unused code and dependencies.
  • Where they overlap (orphan files, unused deps), having both provides cross-validation.

Boundary enforcement

The rev-dep config enforces the monorepo's dependency direction:

  • core cannot import react, react-dom, hooks, or ui
  • hooks cannot import ui
  • ui has no restrictions (top of the stack)

Testing

  • No existing tests are affected — this is additive tooling only
  • Run pnpm analyze to verify the tools work end-to-end

@changeset-bot
Copy link

changeset-bot bot commented Mar 11, 2026

⚠️ No Changeset found

Latest commit: 2f204c6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector
Copy link

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@jaredhightower-youversion jaredhightower-youversion force-pushed the feat/add-dead-code-analysis-tooling branch from ac07ff2 to 94471d2 Compare March 11, 2026 17:29
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 11, 2026

Greptile Summary

This PR adds dead code and dependency analysis tooling to the monorepo using rev-dep (per-package import graph / boundary enforcement) and Knip (cross-package dead code tracing). Two new scripts — scripts/analyze.mjs (full sweep) and scripts/analyze-select.mjs (interactive scoped run) — run both tools and merge their output into a unified color-coded report grouped into Architecture, Dead Code, Orphan Files, and Dependencies sections. A .rev-dep.config.jsonc file enforces the core → hooks → ui dependency graph and prevents boundary violations (e.g., React in core).

Issues found:

  • revdep.unusedExports is never rendered — rev-dep's per-package unused-export results are collected by the text parser in both scripts but are never displayed in the report. Any unused exports detected by rev-dep will be silently dropped. The same bug exists in both scripts/analyze.mjs and scripts/analyze-select.mjs.
  • tools/tsconfig orphan detection misconfiguredorphanFilesDetection is enabled on the tools/tsconfig rule without prodEntryPoints. This is a JSON-only package with no ES module entry point; rev-dep cannot walk import graphs through JSON config files, so every file will appear as an orphan or the detection will silently produce noise.
  • Unix-only 2>/dev/null shell redirect — used in the knip invocations in both scripts; will silently malfunction on Windows CI runners.

Confidence Score: 3/5

  • Safe to merge as additive dev tooling, but two functional gaps in the reporting scripts should be fixed before the tools are relied upon for accuracy.
  • No production code is affected — all changes are dev-only scripts and config. However, the tools have real bugs that undermine their stated purpose: rev-dep's unused-export findings are silently discarded from the report, and the tsconfig orphan-detection config will produce misleading results. These don't block the repo from building or shipping, but they do mean the tooling won't fully deliver on its documented capabilities.
  • scripts/analyze.mjs and scripts/analyze-select.mjs (unused-export rendering gap), .rev-dep.config.jsonc (tools/tsconfig orphan detection config)

Important Files Changed

Filename Overview
scripts/analyze.mjs Full-repo analysis runner: runs knip per-workspace and rev-dep across all packages, then renders a unified report. revdep.unusedExports is populated by the parser but never rendered, silently dropping all per-package unused-export findings from rev-dep.
scripts/analyze-select.mjs Interactive scoped analysis script with raw-terminal multi-select UI. SIGINT handler correctly added. Same revdep.unusedExports silent-drop bug as analyze.mjs. Unix-only 2>/dev/null shell redirect used in knip invocation.
.rev-dep.config.jsonc Rev-dep config covering 5 workspace targets with correct boundary enforcement (core → hooks → ui). tools/tsconfig rule enables orphan detection without prodEntryPoints on a JSON-only package, which will likely flag all files as orphans.
package.json Adds analyze and analyze:select scripts plus rev-dep ^2.11.0 as a devDependency. Straightforward, no issues.
pnpm-lock.yaml Lockfile updated to add rev-dep 2.11.0 and its optional platform-specific binaries (darwin-arm64, linux-x64, win32-x64). Looks correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[pnpm analyze / pnpm analyze:select] --> B[Run rev-dep config run]
    A --> C[Run knip per workspace]

    B --> D[Parse rev-dep text output]
    D --> D1[circularDeps]
    D --> D2[orphanFiles]
    D --> D3[restrictedImports]
    D --> D4[unresolvedImports]
    D --> D5[unusedExports ⚠️ never rendered]
    D --> D6[restrictedDevDeps]

    C --> E[Parse knip JSON output]
    E --> E1[unusedFiles]
    E --> E2[unusedDeps / unusedDevDeps]
    E --> E3[unusedExports / unusedTypes]
    E --> E4[duplicateExports]

    D1 & D3 & D4 & D6 --> F[Section 1: Architecture]
    E1 & E3 & E4 --> G[Section 2: Dead Code]
    D2 --> H[Section 3: Orphan Files]
    E2 --> I[Section 4: Dependencies]

    F & G & H & I --> J[Unified Color-coded Report + Issue Count]

    style D5 fill:#ffcccc,stroke:#cc0000
Loading

Comments Outside Diff (2)

  1. scripts/analyze.mjs, line 1006-1088 (link)

    revdep.unusedExports silently dropped from report

    revdep.unusedExports is populated by the text parser (via the 'unusedExports' branch in the switch) but is never referenced anywhere in the display sections below. The four rendered sections cover Architecture (circular, restricted, unresolved, restrictedDev), Dead Code (Knip), Orphan Files (rev-dep), and Dependencies (Knip) — but rev-dep's per-package unused-export results are silently swallowed.

    This means any Unused Exports Issues reported by rev-dep will never appear to the developer running pnpm analyze. Given that the PR description explicitly lists "Unused exports" as a rev-dep capability, this is a functional gap in the report.

    The same problem exists in scripts/analyze-select.mjs (the revdep.unusedExports array is populated around line 674 but is never rendered in the output sections either).

    A display block similar to the other rev-dep sections should be added to both scripts:

    if (revdep.unusedExports.length > 0) {
      console.log(`  ${red('●')} ${bold('Unused Exports (rev-dep)')} (${revdep.unusedExports.length})`);
      for (const item of revdep.unusedExports) {
        console.log(`    ${dim(item.rule + '/')}${item.detail}`);
      }
      totalIssues += revdep.unusedExports.length;
    } else {
      console.log(`  ${green('✓')} No unused exports (rev-dep)`);
    }
  2. .rev-dep.config.jsonc, line 192-221 (link)

    Orphan detection enabled on a JSON-only package without prodEntryPoints

    The tools/tsconfig rule enables orphanFilesDetection but defines no prodEntryPoints. Unlike the other packages (which have src/index.ts or index.js as a graph root), this package contains only JSON tsconfig files, which are not ES modules and cannot be traversed as an import graph.

    Without a prodEntryPoints root, rev-dep has no start node from which to walk the graph, so every file in the directory will appear as an orphan — the same class of misconfiguration raised about tools/eslint-config in a previous comment, but more severe here because there is no valid JS entry point to add.

    The practical options are:

    1. Disable orphan detection for this rule since tsconfig files are consumed by reference (extends), not by JS import, making per-package graph analysis meaningless here:
    "orphanFilesDetection": { "enabled": false }
    1. Keep it enabled only if rev-dep is ever expected to detect orphaned helper JS/TS files added to the package in the future, and accept the noise until then.

Last reviewed commit: e2172b7

@jaredhightower-youversion
Copy link
Collaborator Author

@greptile-apps

@mic-mart
Copy link
Collaborator

@claude review

cameronapak
cameronapak previously approved these changes Mar 12, 2026
Copy link
Collaborator

@cameronapak cameronapak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like what you've done and have seen your demo. This combo of these tools seems like it'll be really helpful. Please await Claude's review to see what it says. Thank you for this, Jared!

@mic-mart
Copy link
Collaborator

@claude review

@cameronapak
Copy link
Collaborator

I'm not sure if Claude's going to review this one haha. If another hour goes by and Claude hasn't done anything, then please feel free to merge this one in.

@jaredhightower-youversion jaredhightower-youversion merged commit 8d36b68 into main Mar 12, 2026
5 checks passed
@jaredhightower-youversion jaredhightower-youversion deleted the feat/add-dead-code-analysis-tooling branch March 12, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants