@boychibor/react-tabib
v0.1.6
Published
Static analysis CLI for finding likely memory leaks and lifecycle misuse in React apps.
Maintainers
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
useEffectwithoutclearInterval,clearTimeout,cancelAnimationFrame, orcancelIdleCallback - Event listeners added in
useEffectwithout matching removal - Persistent resources and subscriptions not disposed:
WebSocketEventSource- Rx-style
.subscribe() - emitter
.on()without.off()/.removeListener()
- Observers not disconnected:
MutationObserverResizeObserverIntersectionObserver
- 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-tabibOr install locally:
npm install --save-dev @boychibor/react-tabibAfter 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-onlyIf the package is installed in the current project, you can also run:
npx react-tabibCLI 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 pointsrc/core/: project orchestration and analyzersrc/filesystem/: file discoverysrc/parser/: Babel parsingsrc/rules/: modular rule registry and rule implementationssrc/reporters/: table, compact, and JSON formatterssrc/config/: defaults and config loadersrc/types/: shared typestests/: 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
removeEventListenerwhen setup is explicit - Missing
unsubscribe,close, ordisconnectfor 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 0Limitations
- 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.
.tsconfig 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 publicBefore publishing:
- Make sure the scoped package name
@boychibor/react-tabibis available to your npm org or account permissions - Bump
versioninpackage.json - Verify the
binentry points todist/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
