Skip to content

Commit d08c82e

Browse files
authored
Merge pull request #363 from OpenAPI-Qraft/feat/add-eslint-plugin
feat: add OpenAPI Qraft Query ESLint Plugin
2 parents 3cb137a + 1c23c9c commit d08c82e

35 files changed

Lines changed: 2109 additions & 18 deletions

.changeset/config.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"@openapi-qraft/plugin",
99
"@openapi-qraft/tanstack-query-react-plugin",
1010
"@openapi-qraft/react",
11-
"@openapi-qraft/tanstack-query-react-types"
11+
"@openapi-qraft/tanstack-query-react-types",
12+
"@openapi-qraft/eslint-plugin-query"
1213
]
1314
],
1415
"linked": [],

.changeset/wise-cats-stare.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@openapi-qraft/eslint-plugin-query': minor
3+
---
4+
5+
Added `@openapi-qraft/eslint-plugin-query`
6+
7+
An ESLint plugin to enforce best practices and prevent common mistakes with Qraft-generated hooks has been added.

.github/actions/spelling/expect.txt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ amannn
22
APITS
33
apps
44
Batalov
5-
Bcategory
65
bccc
76
bnpm
87
bnpx
98
bytesin
109
cbb
1110
cdn
11+
clientapi
1212
codegen
1313
codemod
1414
csr
@@ -24,17 +24,18 @@ expressjs
2424
fba
2525
fddbf
2626
Firefox
27+
gimuy
2728
gitlab
2829
groupnames
2930
hipchat
3031
htpasswd
3132
ianvs
3233
iat
33-
iddle
3434
idx
3535
ietf
3636
ifm
3737
INADDR
38+
Intelli
3839
iss
3940
JOPT
4041
jscodeshift
@@ -43,11 +44,12 @@ jwt
4344
kubb
4445
linkcheck
4546
linting
46-
mnot
4747
monite
4848
mozilla
4949
mryechkin
5050
mycdn
51+
myclient
52+
myqraft
5153
nbf
5254
nochange
5355
npmrc
@@ -61,8 +63,10 @@ petstore
6163
pids
6264
QMethod
6365
qraft
66+
qraftclient
6467
qraftio
6568
radist
69+
reate
6670
redocly
6771
refetched
6872
Refetches
@@ -75,6 +79,7 @@ somedomain
7579
srv
7680
ssr
7781
swcrc
82+
TAdditional
7883
tagline
7984
tarball
8085
TAsync
@@ -93,6 +98,7 @@ TRequest
9398
TResult
9499
TSchema
95100
tservice
101+
TSES
96102
TSignal
97103
TValue
98104
TVariables
@@ -113,6 +119,3 @@ workaround
113119
workflows
114120
workspaces
115121
XMonite
116-
TCallbacks
117-
reate
118-
Intelli

knip.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@
8585
],
8686
"ignore": ["src/**/__snapshots__/**", "eslint.config.js"]
8787
},
88+
"packages/eslint-plugin-query": {
89+
"project": [
90+
"src/**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}",
91+
"!src/__tests__/**"
92+
],
93+
"ignore": ["rollup.config.mjs", "eslint.config.js"]
94+
},
8895
"playground": {
8996
"ignoreDependencies": ["dotenv", "dotenv-expand", "eslint-plugin-react", "@tanstack/react-query"]
9097
}

packages/eslint-config/eslint.vanilla.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export default [
3030
files: [
3131
'**/**.spec.{ts,tsx}',
3232
'**/**.test.{ts,tsx}',
33+
'**/__tests__/**',
3334
'**/setupTests.ts',
3435
'**/vitestFsMock.ts',
3536
],
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# @openapi-qraft/eslint-plugin-query
2+
3+
ESLint plugin for projects using `@openapi-qraft/react`. It helps enforce best practices around Qraft-generated hooks (e.g. `qraft.service.operation.useQuery()`, `qraft.useInfiniteQuery()`) and prevents mistakes that cause unnecessary re-renders or unstable dependencies.
4+
5+
- Homepage: `https://openapi-qraft.github.io/openapi-qraft/docs/eslint/eslint-plugin-query`
6+
- Repository: `https://github.com/OpenAPI-Qraft/openapi-qraft`
7+
8+
## Requirements
9+
10+
- Node.js >= 18
11+
- ESLint ^8.57.0 or ^9.0.0
12+
13+
## Installation
14+
15+
```bash
16+
npm i -D @openapi-qraft/eslint-plugin-query
17+
# or
18+
pnpm add -D @openapi-qraft/eslint-plugin-query
19+
# or
20+
yarn add -D @openapi-qraft/eslint-plugin-query
21+
# or
22+
bun add -D @openapi-qraft/eslint-plugin-query
23+
```
24+
25+
## Usage (ESLint Flat Config)
26+
27+
Create or update `eslint.config.js`:
28+
29+
```js
30+
import pluginQraftQuery from '@openapi-qraft/eslint-plugin-query'
31+
32+
export default [
33+
// Enable the recommended rules for this plugin
34+
...pluginQraftQuery.configs['flat/recommended'],
35+
// Any other config...
36+
]
37+
```
38+
39+
### Custom setup (Flat Config)
40+
41+
```js
42+
import pluginQraftQuery from '@openapi-qraft/eslint-plugin-query'
43+
44+
export default [
45+
{
46+
plugins: {
47+
'@openapi-qraft/query': pluginQraftQuery,
48+
},
49+
rules: {
50+
'@openapi-qraft/query/no-rest-destructuring': 'warn',
51+
'@openapi-qraft/query/no-unstable-deps': 'error',
52+
},
53+
},
54+
]
55+
```
56+
57+
## Usage (Legacy config)
58+
59+
Add to your `.eslintrc`:
60+
61+
```json
62+
{
63+
"extends": [
64+
"plugin:@openapi-qraft/query/recommended"
65+
]
66+
}
67+
```
68+
69+
### Custom setup (Legacy)
70+
71+
```json
72+
{
73+
"plugins": ["@openapi-qraft/query"],
74+
"rules": {
75+
"@openapi-qraft/query/no-rest-destructuring": "warn",
76+
"@openapi-qraft/query/no-unstable-deps": "error"
77+
}
78+
}
79+
```
80+
81+
## Options
82+
83+
Some rules detect Qraft-generated hooks only when they are called as methods on your client instance (e.g. `qraft.service.operation.useQuery()`).
84+
To support custom client names, each rule accepts an optional `clientNamePattern` (string or regex literal) option. By default, it matches the case-insensitive regex `/qraft|api/i`.
85+
86+
Example (Flat Config):
87+
88+
```js
89+
import pluginQraftQuery from '@openapi-qraft/eslint-plugin-query'
90+
91+
export default [
92+
{
93+
plugins: { '@openapi-qraft/query': pluginQraftQuery },
94+
rules: {
95+
'@openapi-qraft/query/no-rest-destructuring': [
96+
'warn',
97+
{ clientNamePattern: '/myQraftClient/i' },
98+
],
99+
'@openapi-qraft/query/no-unstable-deps': [
100+
'error',
101+
{ clientNamePattern: '/myQraftClient/i' },
102+
],
103+
},
104+
},
105+
]
106+
```
107+
108+
## Configurations
109+
110+
This plugin ships with the following shareable configurations:
111+
112+
- `plugin:@openapi-qraft/query/recommended` (Legacy)
113+
- `plugin.configs['flat/recommended']` (Flat)
114+
115+
They enable the rules as follows:
116+
117+
- `@openapi-qraft/query/no-rest-destructuring`: warn
118+
- `@openapi-qraft/query/no-unstable-deps`: error
119+
120+
## Rules
121+
122+
- `@openapi-qraft/query/no-rest-destructuring` — Disallow object rest destructuring on query results. Recommended: warn. Docs: `https://openapi-qraft.github.io/openapi-qraft/docs/eslint/no-rest-destructuring`
123+
- `@openapi-qraft/query/no-unstable-deps` — Disallow putting the result of query hooks directly in a React hook dependency array. Recommended: error. Docs: `https://openapi-qraft.github.io/openapi-qraft/docs/eslint/no-unstable-deps`
124+
125+
## TypeScript
126+
127+
This plugin is implemented with `@typescript-eslint/utils` and works in both JavaScript and TypeScript projects. No special TypeScript configuration is required beyond your project's usual setup.
128+
129+
## License
130+
131+
MIT © OpenAPI Qraft
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import openAPIQraftConfig from '@openapi-qraft/eslint-config/eslint.vanilla.config';
2+
import globals from 'globals';
3+
4+
export default [
5+
{ languageOptions: { globals: globals.node } },
6+
...openAPIQraftConfig,
7+
];
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"name": "@openapi-qraft/eslint-plugin-query",
3+
"version": "2.11.0",
4+
"description": "ESLint plugin for OpenAPI Qraft",
5+
"homepage": "https://openapi-qraft.github.io/openapi-qraft/docs/eslint/eslint-plugin-query",
6+
"packageManager": "yarn@4.0.2",
7+
"license": "MIT",
8+
"publishConfig": {
9+
"access": "public"
10+
},
11+
"repository": {
12+
"type": "git",
13+
"url": "git+https://github.com/OpenAPI-Qraft/openapi-qraft.git",
14+
"directory": "packages/eslint-plugin-query"
15+
},
16+
"scripts": {
17+
"build": "yarn clean && NODE_ENV=production rollup --config rollup.config.mjs && tsc --project tsconfig.build.json --emitDeclarationOnly",
18+
"typecheck": "tsc --noEmit",
19+
"lint": "eslint",
20+
"test": "vitest run --coverage",
21+
"clean": "rimraf dist/"
22+
},
23+
"type": "module",
24+
"types": "dist/types/index.d.ts",
25+
"module": "dist/esm/index.js",
26+
"main": "dist/cjs/index.cjs",
27+
"exports": {
28+
".": {
29+
"types": "./dist/types/index.d.ts",
30+
"import": "./dist/esm/index.js",
31+
"require": "./dist/cjs/index.cjs"
32+
},
33+
"./package.json": "./package.json"
34+
},
35+
"typesVersions": {
36+
"*": {
37+
"*": [
38+
"dist/types/*"
39+
]
40+
}
41+
},
42+
"sideEffects": false,
43+
"files": [
44+
"dist",
45+
"src",
46+
"!src/__tests__"
47+
],
48+
"dependencies": {
49+
"@typescript-eslint/utils": "^8.37.0"
50+
},
51+
"devDependencies": {
52+
"@openapi-qraft/eslint-config": "workspace:*",
53+
"@openapi-qraft/rollup-config": "workspace:*",
54+
"@types/node": "^20.16.5",
55+
"@typescript-eslint/rule-tester": "^8.37.0",
56+
"@typescript-eslint/typescript-estree": "^8.39.1",
57+
"@vitest/coverage-v8": "^3.0.5",
58+
"eslint": "^9.24.0",
59+
"rimraf": "^5.0.10",
60+
"rollup": "~4.22.4",
61+
"typescript": "^5.6.2",
62+
"vitest": "^3.0.5"
63+
},
64+
"peerDependencies": {
65+
"eslint": "^8.57.0 || ^9.0.0"
66+
}
67+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { rollupConfig } from '@openapi-qraft/rollup-config';
2+
import packageJson from './package.json' with { type: 'json' };
3+
4+
const moduleDist = {
5+
import: packageJson['exports']['.']['import'],
6+
require: packageJson['exports']['.']['require'],
7+
};
8+
9+
const config = [rollupConfig(moduleDist, { input: 'src/index.ts' })];
10+
11+
export default config;

0 commit comments

Comments
 (0)