npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@boychibor/react-tabib

v0.1.6

Published

Static analysis CLI for finding likely memory leaks and lifecycle misuse in React apps.

Readme

react-tabib

react-tabib is a production-oriented static analysis CLI for React codebases. It scans JavaScript and TypeScript React files, looks for likely memory leaks and lifecycle misuse, and reports both high-confidence issues and heuristic risks before they reach production.

The default terminal output uses the pretty reporter. You only need --format when you want table, compact, or json.

What it checks

  • Timers created in useEffect without clearInterval, clearTimeout, cancelAnimationFrame, or cancelIdleCallback
  • Event listeners added in useEffect without matching removal
  • Persistent resources and subscriptions not disposed:
    • WebSocket
    • EventSource
    • Rx-style .subscribe()
    • emitter .on() without .off() / .removeListener()
  • Observers not disconnected:
    • MutationObserver
    • ResizeObserver
    • IntersectionObserver
  • Async effects that may update stale state after unmount
  • Repeated side effects caused by missing dependency arrays

The current implementation intentionally separates high-confidence checks from heuristic ones. It does not claim to prove runtime leaks; it highlights suspicious patterns with explicit confidence levels.

Installation

Run directly with npx after publishing:

npx @boychibor/react-tabib

Or install locally:

npm install --save-dev @boychibor/react-tabib

After local installation, the executable is still react-tabib because the package keeps that bin name.

Usage

npx @boychibor/react-tabib
npx @boychibor/react-tabib --path ./src
npx @boychibor/react-tabib --format compact
npx @boychibor/react-tabib --json
npx @boychibor/react-tabib --severity high
npx @boychibor/react-tabib --max-warnings 0
npx @boychibor/react-tabib --top 10
npx @boychibor/react-tabib --files-with-issues
npx @boychibor/react-tabib --explain event-listener-cleanup
npx @boychibor/react-tabib --ignore "storybook/**"
npx @boychibor/react-tabib --config react-tabib.config.ts
npx @boychibor/react-tabib --summary-only

If the package is installed in the current project, you can also run:

npx react-tabib

CLI flags

  • --path <dir>: scan a custom directory
  • --json: emit JSON instead of terminal text
  • --format <table|pretty|compact|json>: choose a non-default reporter format
  • --severity <level>: set the minimum displayed severity and fail threshold
  • --max-warnings <count>: fail if findings exceed the limit
  • --top <count>: optionally limit output to the highest-risk findings; by default all findings are shown
  • --files-with-issues: print only file paths with findings
  • --explain <rule-id>: explain what a rule checks and how to fix it
  • --ignore <glob>: add an ignore pattern
  • --config <path>: load a config file
  • --no-color: disable ANSI colors
  • --no-banner: remove the pretty report header
  • --debug: print resolved metadata to stderr
  • --summary-only: only print summary information in table or pretty format

Config file

Create react-tabib.config.ts or react-tabib.config.js:

import type { ReactTabibConfig } from '@boychibor/react-tabib';

const config: ReactTabibConfig = {
  ignore: ['storybook/**'],
  disabledRules: ['repeated-side-effect'],
  severityOverrides: {
    'async-unmount-update': 'high',
  },
  reporter: 'table',
  failOnSeverity: 'high',
  maxWarnings: 0,
};

export default config;

Inline suppressions

  • // react-tabib-ignore-file
  • // react-tabib-ignore-next-line rule-id

Use suppressions sparingly and only after reviewing the finding.

Example terminal output

pretty mode includes a banner, elapsed time, visible vs total findings, and a risk score:

react-tabib
Scan target: /repo
Files: 12  Findings: 4/4  Risk Score: 72/100  Time: 38ms

src/App.tsx
  ! [CRITICAL] 24:3 event-listener-cleanup [high]
    window adds an event listener in useEffect without a matching removal.
    Why it matters: The listener can accumulate on every render and keep component closures alive.
    Suggested fix: Return a cleanup function that calls removeEventListener with the same target and handler.

Overview
  Severity: critical=1, high=2, medium=1, low=0
  Categories: events=1, timers=1, subscriptions=1, async=1
  Rules triggered: event-listener-cleanup, use-effect-timer-cleanup, subscription-cleanup, async-unmount-update
Next actions
  Fix critical/high findings first.
  Re-run with --top to focus on the highest-risk items.
  Use --files-with-issues for quick targeted cleanup.

Example JSON output

{
  "rootPath": "/repo",
  "findings": [
    {
      "ruleId": "use-effect-timer-cleanup",
      "category": "timers",
      "severity": "high",
      "confidence": "high",
      "filePath": "/repo/src/App.tsx",
      "line": 12,
      "column": 11,
      "snippet": "const timer = setInterval(doWork, 1000);",
      "explanation": "setInterval is created inside useEffect but is not cancelled in the cleanup function.",
      "impact": "The callback can keep running after the component unmounts and retain component state or closures.",
      "suggestion": "Store the handle and call clearInterval in the function returned from useEffect."
    }
  ],
  "filesScanned": 12,
  "filesWithErrors": [],
  "skippedFiles": 0,
  "rulesTriggered": ["use-effect-timer-cleanup"],
  "summary": {
    "bySeverity": {
      "low": 0,
      "medium": 0,
      "high": 1,
      "critical": 0
    },
    "byCategory": {
      "timers": 1
    }
  }
}

Architecture

  • src/cli/: command-line entry point
  • src/core/: project orchestration and analyzer
  • src/filesystem/: file discovery
  • src/parser/: Babel parsing
  • src/rules/: modular rule registry and rule implementations
  • src/reporters/: table, compact, and JSON formatters
  • src/config/: defaults and config loader
  • src/types/: shared types
  • tests/: fixture-driven tests and reporter snapshots

Design decisions and tradeoffs

  • Babel-based AST parsing is fast and broadly compatible with mixed JS/TS React codebases.
  • Rules are modular and metadata-driven so the same engine can later back an ESLint rule set or editor diagnostics.
  • Some checks are inherently heuristic, especially async state updates and repeated side effects. Those are marked with medium confidence rather than overstating certainty.
  • The analyzer currently uses limited local identifier tracking inside useEffect, which keeps the MVP reliable and understandable without pretending to be a full interprocedural dataflow engine.

High-confidence vs heuristic checks

High-confidence:

  • Missing timer cleanup when a handle is clearly assigned
  • Missing removeEventListener when setup is explicit
  • Missing unsubscribe, close, or disconnect for obvious resources

Heuristic:

  • Async work that may resolve after unmount
  • Effects with missing dependency arrays that may repeatedly attach side effects
  • .on() style event emitters where exact cleanup pairing may be ambiguous

CI usage

Use JSON output for machine parsing and fail on high-severity issues:

npx @boychibor/react-tabib --json --severity high --max-warnings 0

Limitations

  • Static analysis cannot prove actual heap retention or runtime leak severity.
  • The current analyzer focuses on useEffect-driven resource lifecycles.
  • Deep dataflow across helper functions, third-party abstractions, and custom hooks is intentionally conservative in this MVP.
  • .ts config loading is supported through on-the-fly transpilation, but configs that depend on complex ESM-only runtime behavior may need .mjs.

Build and publish

npm run build
npm test
npm run lint
npm run typecheck
npm publish --access public

Before publishing:

  • Make sure the scoped package name @boychibor/react-tabib is available to your npm org or account permissions
  • Bump version in package.json
  • Verify the bin entry points to dist/cli.js

Future evolution

To evolve this into an ESLint plugin:

  • Reuse the rule metadata and analysis logic as rule adapters
  • Convert findings into ESLint diagnostics with node-level reporting
  • Add optional safe autofixes for simple patterns

To evolve this into a VS Code extension:

  • Run the analyzer on save or in a language server
  • Surface findings as diagnostics with code actions
  • Add workspace-level config and suppression management