Skip to content

Commit

Permalink
feat(eslint-plugin-router): add rule to ensure correct property order…
Browse files Browse the repository at this point in the history
… for createXXXRoute functions (#2362)
  • Loading branch information
schiller-manuel committed Sep 18, 2024
1 parent 9c0feb3 commit 1b22087
Show file tree
Hide file tree
Showing 23 changed files with 1,077 additions and 0 deletions.
13 changes: 13 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,19 @@
}
]
},
{
"label": "ESLint",
"children": [
{
"label": "ESLint Plugin Router",
"to": "eslint/eslint-plugin-router"
},
{
"label": "Create Route Property Order",
"to": "eslint/create-route-property-order"
}
]
},
{
"label": "Router Examples",
"children": [],
Expand Down
57 changes: 57 additions & 0 deletions docs/eslint/create-route-property-order.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
id: create-route-property-order
title: Ensure correct order of inference sensitive properties for createRoute functions
---

For the following functions, the property order of the passed in object matters due to type inference:

- `createRoute`
- `createFileRoute`
- `createRootRoute`
- `createRootRouteWithContext`

The correct property order is as follows:

- `params`
- `validateSearch`
- `context`
- `beforeLoad`
- `loaderDeps`
- `loader`

All other properties are insensitive to the order as they do not depend on type inference.

## Rule Details

Examples of **incorrect** code for this rule:

```tsx
/* eslint "@tanstack/router/create-route-property-order": "warn" */
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/foo/bar/$id')({
loader: async ({context}) => {
await context.queryClient.ensureQueryData(getQueryOptions(context.hello)),
},
beforeLoad: () => ({hello: 'world'})
})
```

Examples of **correct** code for this rule:

```tsx
/* eslint "@tanstack/router/create-route-property-order": "warn" */
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/foo/bar/$id')({
beforeLoad: () => ({hello: 'world'}),
loader: async ({context}) => {
await context.queryClient.ensureQueryData(getQueryOptions(context.hello)),
}
})
```

## Attributes

- [x] ✅ Recommended
- [x] 🔧 Fixable
96 changes: 96 additions & 0 deletions docs/eslint/eslint-plugin-router.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
id: eslint-plugin-router
title: ESLint Plugin Router
---

TanStack Router comes with its own ESLint plugin. This plugin is used to enforce best practices and to help you avoid common mistakes.

## Installation

The plugin is a separate package that you need to install:

```bash
$ npm i -D @tanstack/eslint-plugin-router
```

or

```bash
$ pnpm add -D @tanstack/eslint-plugin-router
```

or

```bash
$ yarn add -D @tanstack/eslint-plugin-router
```

or

```bash
$ bun add -D @tanstack/eslint-plugin-router
```

## Flat Config (`eslint.config.js`)

### Recommended setup

To enable all of the recommended rules for our plugin, add the following config:

```js
import pluginRouter from '@tanstack/eslint-plugin-router'

export default [
...pluginRouter.configs['flat/recommended'],
// Any other config...
]
```

### Custom setup

Alternatively, you can load the plugin and configure only the rules you want to use:

```js
import pluginRouter from '@tanstack/eslint-plugin-router'

export default [
{
plugins: {
'@tanstack/router': pluginRouter,
},
rules: {
'@tanstack/router/create-route-property-order': 'error',
},
},
// Any other config...
]
```

## Legacy Config (`.eslintrc`)

### Recommended setup

To enable all of the recommended rules for our plugin, add `plugin:@tanstack/eslint-plugin-router/recommended` in extends:

```json
{
"extends": ["plugin:@tanstack/eslint-plugin-router/recommended"]
}
```

### Custom setup

Alternatively, add `@tanstack/eslint-plugin-router` to the plugins section, and configure the rules you want to use:

```json
{
"plugins": ["@tanstack/eslint-plugin-router"],
"rules": {
"@tanstack/router/create-route-property-order": "error"
}
}
```

## Rules

- [@tanstack/router/create-route-property-order](../create-route-property-order)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"@tanstack/router-arktype-adapter": "workspace:*",
"@tanstack/start": "workspace:*",
"@tanstack/start-vite-plugin": "workspace:*",
"@tanstack/eslint-plugin-router": "workspace:*",
"temp-react": "0.0.0-experimental-035a41c4e-20230704",
"temp-react-dom": "0.0.0-experimental-035a41c4e-20230704"
}
Expand Down
3 changes: 3 additions & 0 deletions packages/eslint-plugin-router/.attw.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ignoreRules": ["false-export-default"]
}
5 changes: 5 additions & 0 deletions packages/eslint-plugin-router/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// @ts-check

import rootConfig from '../../eslint.config.js'

export default [...rootConfig]
65 changes: 65 additions & 0 deletions packages/eslint-plugin-router/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"name": "@tanstack/eslint-plugin-router",
"version": "5.53.0",
"description": "ESLint plugin for TanStack Router",
"author": "Manuel Schiller",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/TanStack/router.git",
"directory": "packages/eslint-plugin-router"
},
"homepage": "https://tanstack.com/router",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"scripts": {
"clean": "rimraf ./dist ./coverage",
"test:eslint": "eslint ./src",
"test:types": "pnpm run \"/^test:types:ts[0-9]{2}$/\"",
"test:types:ts51": "node ../../node_modules/typescript51/lib/tsc.js",
"test:types:ts52": "node ../../node_modules/typescript52/lib/tsc.js",
"test:types:ts53": "node ../../node_modules/typescript53/lib/tsc.js",
"test:types:ts54": "node ../../node_modules/typescript54/lib/tsc.js",
"test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js",
"test:types:ts56": "tsc",
"test:unit": "vitest",
"test:unit:dev": "pnpm run test:unit --watch --typecheck",
"test:build": "publint --strict && attw --pack .",
"build": "vite build"
},
"type": "module",
"types": "dist/esm/index.d.ts",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.js",
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/cjs/index.d.cts",
"default": "./dist/cjs/index.cjs"
}
},
"./package.json": "./package.json"
},
"sideEffects": false,
"files": [
"dist",
"src"
],
"dependencies": {
"@typescript-eslint/utils": "^8.3.0"
},
"devDependencies": {
"@typescript-eslint/rule-tester": "^8.3.0",
"combinate": "^1.1.11",
"eslint": "^9.9.1"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0"
}
}
Loading

0 comments on commit 1b22087

Please sign in to comment.