An open API service indexing awesome lists of open source software.

https://github.com/glideapps/roto-rooter

Static analysis and functional verifier tool for React Router applications
https://github.com/glideapps/roto-rooter

Last synced: 4 months ago
JSON representation

Static analysis and functional verifier tool for React Router applications

Awesome Lists containing this project

README

          

# roto-rooter

A static analysis tool for [React Router](https://reactrouter.com/) 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 ` | 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 ` | Output format: `text` (default) or `json` |
| `-r, --root ` | Project root containing the `app/` folder (default: cwd) |
| `--fix` | Auto-fix issues where possible |
| `--dry-run` | Preview fixes without writing files |
| `--drizzle-schema ` | Path to Drizzle schema file (auto-discovered by default) |

**Checks at a glance:**

- **links** (default) -- validates ``, `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 `` 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, 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
Save Changes
x "Save Changes" button in Dialog only closes dialog without saving data
-> Wrap inputs in a component or use useFetcher.submit() to persist data

[warning] disconnected-dialog.tsx:78:11
Add Item
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 ` | Output format: `text` (default) or `json` |
| `-r, --root ` | Project root directory (default: cwd) |
| `--drizzle-schema ` | 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
```