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

roto-rooter

v0.10.1

Published

Static analysis and functional verifier tool for React Router applications

Readme

roto-rooter

A static analysis tool for React Router apps. It catches common bugs -- broken links, missing loaders, hydration mismatches, disconnected UI elements, and incorrect database operations -- by reading your route definitions and cross-referencing them against your components.

npm install -g roto-rooter

Running Checks

rr [OPTIONS] [FILES...]

Point rr at your project and it scans your route files for issues. With no arguments it runs the default checks (links, forms, loader, params, interactivity, hydration) against all files in the current directory.

| Option | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -c, --check <checks> | Comma-separated checks to run. Use defaults for the default set, all for everything, or pick individual checks: links, loader, params, interactivity, forms, hydration, drizzle | | -f, --format <format> | Output format: text (default) or json | | -r, --root <path> | Project root containing the app/ folder (default: cwd) | | --fix | Auto-fix issues where possible | | --dry-run | Preview fixes without writing files | | --drizzle-schema <path> | Path to Drizzle schema file (auto-discovered by default) |

Checks at a glance:

  • links (default) -- validates <Link>, redirect(), navigate(), and href props on any component exist as routes
  • loader (default) -- ensures useLoaderData() is backed by a loader; catches clientLoader importing server-only modules
  • params (default) -- ensures useParams() only accesses params defined in the route path
  • interactivity (default) -- catches "Save" buttons that don't save, "Delete" buttons that don't delete, and empty click handlers
  • hydration (default) -- detects SSR/client mismatches from new Date(), Math.random(), locale-dependent formatting, window access in render
  • forms (default) -- validates <Form> targets have actions and that field names match formData.get() calls
  • drizzle (opt-in) -- validates Drizzle ORM operations against your schema (missing columns, type mismatches, sql<number> with aggregates, etc.)

Example output:

$ rr --root my-app

rr found 5 issues:

[error] dashboard.tsx:12:7
  href="/employeees"
  x No matching route
  -> Did you mean: /employees?

[error] tasks.tsx:6:16
  useLoaderData()
  x useLoaderData() called but route has no loader
  -> Add a loader function or remove the hook

[error] employees.$id.edit.tsx:7:32
  useParams().invalidParam
  x useParams() accesses "invalidParam" but route has no :invalidParam parameter
  -> Available params: :id

[error] disconnected-dialog.tsx:27:11
  <Button onClick={...}>Save Changes</Button>
  x "Save Changes" button in Dialog only closes dialog without saving data
  -> Wrap inputs in a <Form> component or use useFetcher.submit() to persist data

[warning] disconnected-dialog.tsx:78:11
  <Button onClick={...}>Add Item</Button>
  x "Add Item" button has an empty or stub onClick handler
  -> Implement the handler or remove the button if not needed

Summary: 4 errors, 1 warning
Run with --help for options.

Extracting SQL

rr sql --drizzle [OPTIONS] [FILES...]

Reads your Drizzle ORM code and prints the equivalent SQL for every query it finds. Useful for reviewing what your app actually sends to the database.

| Option | Description | | ------------------------- | -------------------------------------------------------- | | --drizzle | Required. Specifies the ORM to analyze. | | -f, --format <format> | Output format: text (default) or json | | -r, --root <path> | Project root directory (default: cwd) | | --drizzle-schema <path> | Path to Drizzle schema file (auto-discovered by default) |

Example output:

$ rr sql --drizzle --root my-app

Found 6 SQL queries:

File: app/routes/users.tsx:13:26
  SELECT * FROM users

File: app/routes/users.tsx:16:9
  SELECT id, name, email FROM users

File: app/routes/users.tsx:24:29
  SELECT * FROM users WHERE status = 'active'

File: app/routes/users.tsx:36:9
  INSERT INTO users (name, email, status) VALUES ($1, $2, $3)
  Parameters:
    $1: name (text)
    $2: email (text)
    $3: status (enum)

File: app/routes/orders.tsx:16:9
  INSERT INTO orders (status, user_id, total) VALUES ($1, $2, $3)
  Parameters:
    $1: status (enum)
    $2: userId (integer)
    $3: total (integer)

File: app/routes/users.tsx:42:9
  DELETE FROM users WHERE id = $1
  Parameters:
    $1: Number(params.id) (serial)

Development

npm install      # install dependencies
npm test         # run tests
npm run build    # build for distribution