@openapi-qraft/eslint-plugin-query 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +131 -0
  3. package/dist/cjs/index.cjs +44 -0
  4. package/dist/cjs/index.cjs.map +1 -0
  5. package/dist/cjs/rules/no-rest-destructuring/no-rest-destructuring.rule.cjs +116 -0
  6. package/dist/cjs/rules/no-rest-destructuring/no-rest-destructuring.rule.cjs.map +1 -0
  7. package/dist/cjs/rules/no-rest-destructuring/no-rest-destructuring.utils.cjs +15 -0
  8. package/dist/cjs/rules/no-rest-destructuring/no-rest-destructuring.utils.cjs.map +1 -0
  9. package/dist/cjs/rules/no-unstable-deps/no-unstable-deps.rule.cjs +126 -0
  10. package/dist/cjs/rules/no-unstable-deps/no-unstable-deps.rule.cjs.map +1 -0
  11. package/dist/cjs/rules.cjs +12 -0
  12. package/dist/cjs/rules.cjs.map +1 -0
  13. package/dist/cjs/utils/create-client-name-regex.cjs +27 -0
  14. package/dist/cjs/utils/create-client-name-regex.cjs.map +1 -0
  15. package/dist/cjs/utils/get-docs-url.cjs +6 -0
  16. package/dist/cjs/utils/get-docs-url.cjs.map +1 -0
  17. package/dist/cjs/utils/get-root-identifier.cjs +22 -0
  18. package/dist/cjs/utils/get-root-identifier.cjs.map +1 -0
  19. package/dist/esm/index.js +37 -0
  20. package/dist/esm/index.js.map +1 -0
  21. package/dist/esm/rules/no-rest-destructuring/no-rest-destructuring.rule.js +113 -0
  22. package/dist/esm/rules/no-rest-destructuring/no-rest-destructuring.rule.js.map +1 -0
  23. package/dist/esm/rules/no-rest-destructuring/no-rest-destructuring.utils.js +13 -0
  24. package/dist/esm/rules/no-rest-destructuring/no-rest-destructuring.utils.js.map +1 -0
  25. package/dist/esm/rules/no-unstable-deps/no-unstable-deps.rule.js +121 -0
  26. package/dist/esm/rules/no-unstable-deps/no-unstable-deps.rule.js.map +1 -0
  27. package/dist/esm/rules.js +10 -0
  28. package/dist/esm/rules.js.map +1 -0
  29. package/dist/esm/utils/create-client-name-regex.js +25 -0
  30. package/dist/esm/utils/create-client-name-regex.js.map +1 -0
  31. package/dist/esm/utils/get-docs-url.js +4 -0
  32. package/dist/esm/utils/get-docs-url.js.map +1 -0
  33. package/dist/esm/utils/get-root-identifier.js +20 -0
  34. package/dist/esm/utils/get-root-identifier.js.map +1 -0
  35. package/dist/types/index.d.ts +23 -0
  36. package/dist/types/index.d.ts.map +1 -0
  37. package/dist/types/rules/no-rest-destructuring/no-rest-destructuring.rule.d.ts +5 -0
  38. package/dist/types/rules/no-rest-destructuring/no-rest-destructuring.rule.d.ts.map +1 -0
  39. package/dist/types/rules/no-rest-destructuring/no-rest-destructuring.utils.d.ts +5 -0
  40. package/dist/types/rules/no-rest-destructuring/no-rest-destructuring.utils.d.ts.map +1 -0
  41. package/dist/types/rules/no-unstable-deps/no-unstable-deps.rule.d.ts +7 -0
  42. package/dist/types/rules/no-unstable-deps/no-unstable-deps.rule.d.ts.map +1 -0
  43. package/dist/types/rules.d.ts +4 -0
  44. package/dist/types/rules.d.ts.map +1 -0
  45. package/dist/types/types.d.ts +4 -0
  46. package/dist/types/types.d.ts.map +1 -0
  47. package/dist/types/utils/create-client-name-regex.d.ts +2 -0
  48. package/dist/types/utils/create-client-name-regex.d.ts.map +1 -0
  49. package/dist/types/utils/get-docs-url.d.ts +2 -0
  50. package/dist/types/utils/get-docs-url.d.ts.map +1 -0
  51. package/dist/types/utils/get-root-identifier.d.ts +3 -0
  52. package/dist/types/utils/get-root-identifier.d.ts.map +1 -0
  53. package/package.json +67 -0
  54. package/src/index.ts +54 -0
  55. package/src/rules/no-rest-destructuring/no-rest-destructuring.rule.ts +135 -0
  56. package/src/rules/no-rest-destructuring/no-rest-destructuring.utils.ts +11 -0
  57. package/src/rules/no-unstable-deps/no-unstable-deps.rule.ts +137 -0
  58. package/src/rules.ts +17 -0
  59. package/src/types.ts +3 -0
  60. package/src/utils/create-client-name-regex.ts +32 -0
  61. package/src/utils/get-docs-url.ts +2 -0
  62. package/src/utils/get-root-identifier.ts +25 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # @openapi-qraft/eslint-plugin-query
2
+
3
+ ## 2.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8dcc7c1: Added `@openapi-qraft/eslint-plugin-query`
8
+
9
+ An ESLint plugin to enforce best practices and prevent common mistakes with Qraft-generated hooks has been added.
package/README.md ADDED
@@ -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
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var rules = require('./rules.cjs');
6
+
7
+ const plugin = {
8
+ meta: {
9
+ name: '@openapi-qraft/eslint-plugin-query'
10
+ },
11
+ configs: {},
12
+ rules: rules.rules
13
+ };
14
+ // Assign configs here so we can reference `plugin`
15
+ Object.assign(plugin.configs, {
16
+ recommended: {
17
+ plugins: [
18
+ '@openapi-qraft/query'
19
+ ],
20
+ rules: {
21
+ '@openapi-qraft/query/no-rest-destructuring': 'warn',
22
+ '@openapi-qraft/query/no-unstable-deps': 'error'
23
+ }
24
+ },
25
+ 'flat/recommended': [
26
+ {
27
+ name: 'openapi-qraft/query/flat/recommended',
28
+ plugins: {
29
+ '@openapi-qraft/query': plugin
30
+ },
31
+ rules: {
32
+ '@openapi-qraft/query/no-rest-destructuring': 'warn',
33
+ '@openapi-qraft/query/no-unstable-deps': 'error'
34
+ }
35
+ }
36
+ ]
37
+ });
38
+ const configs = plugin.configs;
39
+
40
+ exports.rules = rules.rules;
41
+ exports.configs = configs;
42
+ exports.default = plugin;
43
+ exports.plugin = plugin;
44
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["import type { RuleModule } from '@typescript-eslint/utils/ts-eslint';\nimport type { ESLint, Linter } from 'eslint';\nimport { rules } from './rules';\n\ntype RuleKey = keyof typeof rules;\n\nexport interface Plugin extends Omit<ESLint.Plugin, 'rules'> {\n rules: Record<RuleKey, RuleModule<any, any, any>>;\n configs: {\n recommended: ESLint.ConfigData;\n 'flat/recommended': Array<Linter.Config>;\n };\n}\n\nexport const plugin: Plugin = {\n meta: {\n name: '@openapi-qraft/eslint-plugin-query',\n },\n configs: {} as Plugin['configs'],\n rules,\n};\n\n// Assign configs here so we can reference `plugin`\nObject.assign(plugin.configs, {\n recommended: {\n plugins: ['@openapi-qraft/query'],\n rules: {\n '@openapi-qraft/query/no-rest-destructuring': 'warn',\n '@openapi-qraft/query/no-unstable-deps': 'error',\n },\n },\n 'flat/recommended': [\n {\n name: 'openapi-qraft/query/flat/recommended',\n plugins: {\n '@openapi-qraft/query': plugin,\n },\n rules: {\n '@openapi-qraft/query/no-rest-destructuring': 'warn',\n '@openapi-qraft/query/no-unstable-deps': 'error',\n },\n },\n ],\n});\n\n// Reexport rules & configs for the Legacy config\nexport { rules };\nexport const configs = plugin.configs;\n\n/**\n * @alias\n * @type {Plugin}\n **/\nexport default plugin;\n"],"names":["plugin","meta","name","configs","rules","Object","assign","recommended","plugins"],"mappings":";;;;;;MAcaA,MAAiB,GAAA;IAC5BC,IAAM,EAAA;QACJC,IAAM,EAAA,oCAAA;AACR,KAAA;AACAC,IAAAA,OAAAA,EAAS,EAAC;AACVC,WAAAA,WAAAA;AACF,EAAE;AAEF;AACAC,MAAAA,CAAOC,MAAM,CAACN,MAAOG,CAAAA,OAAO,EAAE;IAC5BI,WAAa,EAAA;QACXC,OAAS,EAAA;AAAC,YAAA,sBAAA;AAAuB,SAAA;QACjCJ,KAAO,EAAA;YACL,4CAA8C,EAAA,MAAA;YAC9C,uCAAyC,EAAA,OAAA;AAC3C,SAAA;AACF,KAAA;IACA,kBAAoB,EAAA;AAClB,QAAA;YACEF,IAAM,EAAA,sCAAA;YACNM,OAAS,EAAA;gBACP,sBAAwBR,EAAAA,MAAAA;AAC1B,aAAA;YACAI,KAAO,EAAA;gBACL,4CAA8C,EAAA,MAAA;gBAC9C,uCAAyC,EAAA,OAAA;AAC3C,aAAA;AACF,SAAA;AACD,KAAA;AACH,CAAA,CAAA,CAAA;AAIaD,MAAAA,OAAAA,GAAUH,MAAOG,CAAAA;;;;;;;"}
@@ -0,0 +1,116 @@
1
+ 'use strict';
2
+
3
+ var utils = require('@typescript-eslint/utils');
4
+ var createClientNameRegex = require('../../utils/create-client-name-regex.cjs');
5
+ var getDocsUrl = require('../../utils/get-docs-url.cjs');
6
+ var getRootIdentifier = require('../../utils/get-root-identifier.cjs');
7
+ var noRestDestructuring_utils = require('./no-rest-destructuring.utils.cjs');
8
+
9
+ const name = 'no-rest-destructuring';
10
+ const createRule = utils.ESLintUtils.RuleCreator(getDocsUrl.getDocsUrl);
11
+ const queryHooks = [
12
+ 'useQuery',
13
+ 'useQueries',
14
+ 'useInfiniteQuery',
15
+ 'useSuspenseQuery',
16
+ 'useSuspenseQueries',
17
+ 'useSuspenseInfiniteQuery'
18
+ ];
19
+ const rule = createRule({
20
+ name,
21
+ meta: {
22
+ type: 'problem',
23
+ docs: {
24
+ description: 'Disallows rest destructuring in queries',
25
+ recommended: 'warn'
26
+ },
27
+ messages: {
28
+ objectRestDestructure: `Object rest destructuring on a query will observe all changes to the query, leading to excessive re-renders.`
29
+ },
30
+ schema: [
31
+ {
32
+ type: 'object',
33
+ properties: {
34
+ clientNamePattern: {
35
+ type: 'string'
36
+ }
37
+ },
38
+ additionalProperties: false
39
+ }
40
+ ]
41
+ },
42
+ defaultOptions: [
43
+ {}
44
+ ],
45
+ create: (context)=>{
46
+ const option = context.options[0] ?? {};
47
+ const clientRegex = createClientNameRegex.createClientNameRegex(option.clientNamePattern);
48
+ const queryResultVariables = new Set();
49
+ return {
50
+ CallExpression: (node)=>{
51
+ if (node.parent.type !== utils.AST_NODE_TYPES.VariableDeclarator) return;
52
+ const callee = node.callee;
53
+ if (callee.type !== utils.AST_NODE_TYPES.MemberExpression) return;
54
+ const hookName = getHookNameIfTargetFromClient(clientRegex, callee);
55
+ if (!hookName) return;
56
+ const returnValue = node.parent.id;
57
+ if (hookName !== 'useQueries' && hookName !== 'useSuspenseQueries') {
58
+ if (noRestDestructuring_utils.NoRestDestructuringUtils.isObjectRestDestructuring(returnValue)) {
59
+ return context.report({
60
+ node: node.parent,
61
+ messageId: 'objectRestDestructure'
62
+ });
63
+ }
64
+ if (returnValue.type === utils.AST_NODE_TYPES.Identifier) {
65
+ queryResultVariables.add(returnValue.name);
66
+ }
67
+ return;
68
+ }
69
+ if (returnValue.type !== utils.AST_NODE_TYPES.ArrayPattern) {
70
+ if (returnValue.type === utils.AST_NODE_TYPES.Identifier) {
71
+ queryResultVariables.add(returnValue.name);
72
+ }
73
+ return;
74
+ }
75
+ returnValue.elements.forEach((queryResult)=>{
76
+ if (queryResult === null) {
77
+ return;
78
+ }
79
+ if (noRestDestructuring_utils.NoRestDestructuringUtils.isObjectRestDestructuring(queryResult)) {
80
+ context.report({
81
+ node: queryResult,
82
+ messageId: 'objectRestDestructure'
83
+ });
84
+ }
85
+ });
86
+ },
87
+ VariableDeclarator: (node)=>{
88
+ if (node.init?.type === utils.AST_NODE_TYPES.Identifier && queryResultVariables.has(node.init.name) && noRestDestructuring_utils.NoRestDestructuringUtils.isObjectRestDestructuring(node.id)) {
89
+ context.report({
90
+ node,
91
+ messageId: 'objectRestDestructure'
92
+ });
93
+ }
94
+ },
95
+ SpreadElement: (node)=>{
96
+ if (node.argument.type === utils.AST_NODE_TYPES.Identifier && queryResultVariables.has(node.argument.name)) {
97
+ context.report({
98
+ node,
99
+ messageId: 'objectRestDestructure'
100
+ });
101
+ }
102
+ }
103
+ };
104
+ }
105
+ });
106
+ function getHookNameIfTargetFromClient(clientRegex, callee) {
107
+ if (callee.property.type !== utils.AST_NODE_TYPES.Identifier) return;
108
+ const name = callee.property.name;
109
+ if (!queryHooks.includes(name)) return;
110
+ const root = getRootIdentifier.getRootIdentifier(callee.object);
111
+ return root && clientRegex.test(root.name) ? name : undefined;
112
+ }
113
+
114
+ exports.name = name;
115
+ exports.rule = rule;
116
+ //# sourceMappingURL=no-rest-destructuring.rule.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-rest-destructuring.rule.cjs","sources":["../../../../src/rules/no-rest-destructuring/no-rest-destructuring.rule.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport type { ExtraRuleDocs } from '../../types';\nimport { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';\nimport { createClientNameRegex } from '../../utils/create-client-name-regex';\nimport { getDocsUrl } from '../../utils/get-docs-url';\nimport { getRootIdentifier } from '../../utils/get-root-identifier';\nimport { NoRestDestructuringUtils } from './no-rest-destructuring.utils';\n\nexport const name = 'no-rest-destructuring';\n\nconst createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl);\n\nconst queryHooks = [\n 'useQuery',\n 'useQueries',\n 'useInfiniteQuery',\n 'useSuspenseQuery',\n 'useSuspenseQueries',\n 'useSuspenseInfiniteQuery',\n];\n\nexport const rule = createRule({\n name,\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallows rest destructuring in queries',\n recommended: 'warn',\n },\n messages: {\n objectRestDestructure: `Object rest destructuring on a query will observe all changes to the query, leading to excessive re-renders.`,\n },\n schema: [\n {\n type: 'object',\n properties: {\n clientNamePattern: { type: 'string' },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [{}],\n\n create: (context) => {\n const option =\n (context.options[0] as { clientNamePattern?: string } | undefined) ?? {};\n\n const clientRegex = createClientNameRegex(option.clientNamePattern);\n\n const queryResultVariables = new Set<string>();\n\n return {\n CallExpression: (node) => {\n if (node.parent.type !== AST_NODE_TYPES.VariableDeclarator) return;\n\n const callee = node.callee;\n if (callee.type !== AST_NODE_TYPES.MemberExpression) return;\n const hookName = getHookNameIfTargetFromClient(clientRegex, callee);\n if (!hookName) return;\n const returnValue = node.parent.id;\n\n if (hookName !== 'useQueries' && hookName !== 'useSuspenseQueries') {\n if (NoRestDestructuringUtils.isObjectRestDestructuring(returnValue)) {\n return context.report({\n node: node.parent,\n messageId: 'objectRestDestructure',\n });\n }\n\n if (returnValue.type === AST_NODE_TYPES.Identifier) {\n queryResultVariables.add(returnValue.name);\n }\n\n return;\n }\n\n if (returnValue.type !== AST_NODE_TYPES.ArrayPattern) {\n if (returnValue.type === AST_NODE_TYPES.Identifier) {\n queryResultVariables.add(returnValue.name);\n }\n return;\n }\n\n returnValue.elements.forEach((queryResult) => {\n if (queryResult === null) {\n return;\n }\n if (NoRestDestructuringUtils.isObjectRestDestructuring(queryResult)) {\n context.report({\n node: queryResult,\n messageId: 'objectRestDestructure',\n });\n }\n });\n },\n\n VariableDeclarator: (node) => {\n if (\n node.init?.type === AST_NODE_TYPES.Identifier &&\n queryResultVariables.has(node.init.name) &&\n NoRestDestructuringUtils.isObjectRestDestructuring(node.id)\n ) {\n context.report({\n node,\n messageId: 'objectRestDestructure',\n });\n }\n },\n\n SpreadElement: (node) => {\n if (\n node.argument.type === AST_NODE_TYPES.Identifier &&\n queryResultVariables.has(node.argument.name)\n ) {\n context.report({\n node,\n messageId: 'objectRestDestructure',\n });\n }\n },\n };\n },\n});\n\nfunction getHookNameIfTargetFromClient(\n clientRegex: RegExp,\n callee: TSESTree.MemberExpression\n): string | undefined {\n if (callee.property.type !== AST_NODE_TYPES.Identifier) return;\n const name = callee.property.name;\n if (!queryHooks.includes(name)) return;\n const root = getRootIdentifier(callee.object);\n return root && clientRegex.test(root.name) ? name : undefined;\n}\n"],"names":["name","createRule","ESLintUtils","RuleCreator","getDocsUrl","queryHooks","rule","meta","type","docs","description","recommended","messages","objectRestDestructure","schema","properties","clientNamePattern","additionalProperties","defaultOptions","create","context","option","options","clientRegex","createClientNameRegex","queryResultVariables","Set","CallExpression","node","parent","AST_NODE_TYPES","VariableDeclarator","callee","MemberExpression","hookName","getHookNameIfTargetFromClient","returnValue","id","NoRestDestructuringUtils","isObjectRestDestructuring","report","messageId","Identifier","add","ArrayPattern","elements","forEach","queryResult","init","has","SpreadElement","argument","property","includes","root","getRootIdentifier","object","test","undefined"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ var utils = require('@typescript-eslint/utils');
4
+
5
+ const NoRestDestructuringUtils = {
6
+ isObjectRestDestructuring (node) {
7
+ if (node.type !== utils.AST_NODE_TYPES.ObjectPattern) {
8
+ return false;
9
+ }
10
+ return node.properties.some((p)=>p.type === utils.AST_NODE_TYPES.RestElement);
11
+ }
12
+ };
13
+
14
+ exports.NoRestDestructuringUtils = NoRestDestructuringUtils;
15
+ //# sourceMappingURL=no-rest-destructuring.utils.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-rest-destructuring.utils.cjs","sources":["../../../../src/rules/no-rest-destructuring/no-rest-destructuring.utils.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { AST_NODE_TYPES } from '@typescript-eslint/utils';\n\nexport const NoRestDestructuringUtils = {\n isObjectRestDestructuring(node: TSESTree.Node): boolean {\n if (node.type !== AST_NODE_TYPES.ObjectPattern) {\n return false;\n }\n return node.properties.some((p) => p.type === AST_NODE_TYPES.RestElement);\n },\n};\n"],"names":["NoRestDestructuringUtils","isObjectRestDestructuring","node","type","AST_NODE_TYPES","ObjectPattern","properties","some","p","RestElement"],"mappings":";;;;MAGaA,wBAA2B,GAAA;AACtCC,IAAAA,yBAAAA,CAAAA,CAA0BC,IAAmB,EAAA;AAC3C,QAAA,IAAIA,IAAKC,CAAAA,IAAI,KAAKC,oBAAAA,CAAeC,aAAa,EAAE;YAC9C,OAAO,KAAA,CAAA;AACT,SAAA;QACA,OAAOH,IAAAA,CAAKI,UAAU,CAACC,IAAI,CAAC,CAACC,CAAAA,GAAMA,CAAEL,CAAAA,IAAI,KAAKC,oBAAAA,CAAeK,WAAW,CAAA,CAAA;AAC1E,KAAA;AACF;;;;"}
@@ -0,0 +1,126 @@
1
+ 'use strict';
2
+
3
+ var utils = require('@typescript-eslint/utils');
4
+ var createClientNameRegex = require('../../utils/create-client-name-regex.cjs');
5
+ var getDocsUrl = require('../../utils/get-docs-url.cjs');
6
+ var getRootIdentifier = require('../../utils/get-root-identifier.cjs');
7
+
8
+ const name = 'no-unstable-deps';
9
+ const reactHookNames = [
10
+ 'useEffect',
11
+ 'useCallback',
12
+ 'useMemo'
13
+ ];
14
+ const useQueryHookNames = [
15
+ 'useQuery',
16
+ 'useSuspenseQuery',
17
+ 'useQueries',
18
+ 'useSuspenseQueries',
19
+ 'useInfiniteQuery',
20
+ 'useSuspenseInfiniteQuery'
21
+ ];
22
+ const allHookNames = [
23
+ 'useMutation',
24
+ ...useQueryHookNames
25
+ ];
26
+ const createRule = utils.ESLintUtils.RuleCreator(getDocsUrl.getDocsUrl);
27
+ const rule = createRule({
28
+ name,
29
+ meta: {
30
+ type: 'problem',
31
+ docs: {
32
+ description: 'Disallow putting the result of query hooks directly in a React hook dependency array',
33
+ recommended: 'error'
34
+ },
35
+ messages: {
36
+ noUnstableDeps: `The result of {{queryHook}} is not referentially stable, so don't pass it directly into the dependencies array of {{reactHook}}. Instead, destructure the return value of {{queryHook}} and pass the destructured values into the dependency array of {{reactHook}}.`
37
+ },
38
+ schema: [
39
+ {
40
+ type: 'object',
41
+ properties: {
42
+ clientNamePattern: {
43
+ type: 'string'
44
+ }
45
+ },
46
+ additionalProperties: false
47
+ }
48
+ ]
49
+ },
50
+ defaultOptions: [
51
+ {}
52
+ ],
53
+ create: (context)=>{
54
+ const option = context.options[0] ?? {};
55
+ const clientRegex = createClientNameRegex.createClientNameRegex(option.clientNamePattern);
56
+ const trackedVariables = {};
57
+ const hookAliasMap = {};
58
+ function getReactHook(node) {
59
+ if (node.callee.type === 'Identifier') {
60
+ const calleeName = node.callee.name;
61
+ if (reactHookNames.includes(calleeName) || calleeName in hookAliasMap) {
62
+ // Return the actually used identifier (could be alias)
63
+ return calleeName;
64
+ }
65
+ } else if (node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier' && node.callee.object.name === 'React' && node.callee.property.type === 'Identifier' && reactHookNames.includes(node.callee.property.name)) {
66
+ return node.callee.property.name;
67
+ }
68
+ return undefined;
69
+ }
70
+ return {
71
+ ImportDeclaration (node) {
72
+ if (node.specifiers.length > 0 && node.importKind === 'value' && node.source.value === 'React') {
73
+ node.specifiers.forEach((specifier)=>{
74
+ if (specifier.type === utils.AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === utils.AST_NODE_TYPES.Identifier && reactHookNames.includes(specifier.imported.name)) {
75
+ hookAliasMap[specifier.local.name] = specifier.imported.name;
76
+ }
77
+ });
78
+ }
79
+ },
80
+ VariableDeclarator (node) {
81
+ if (node.init?.type !== utils.AST_NODE_TYPES.CallExpression) return;
82
+ const hook = isQueryHookFromClient(clientRegex, node.init);
83
+ if (!hook) return;
84
+ if (node.id.type === utils.AST_NODE_TYPES.Identifier) {
85
+ trackedVariables[node.id.name] = hook;
86
+ }
87
+ },
88
+ CallExpression: (node)=>{
89
+ const reactHook = getReactHook(node);
90
+ if (!reactHook) return;
91
+ if (node.arguments.length <= 1) return;
92
+ if (node.arguments[1]?.type !== utils.AST_NODE_TYPES.ArrayExpression) return;
93
+ const depsArray = node.arguments[1].elements;
94
+ depsArray.forEach((dep)=>{
95
+ if (dep?.type !== utils.AST_NODE_TYPES.Identifier) return;
96
+ const queryHook = trackedVariables[dep.name];
97
+ if (!queryHook) return;
98
+ context.report({
99
+ node: dep,
100
+ messageId: 'noUnstableDeps',
101
+ data: {
102
+ queryHook,
103
+ reactHook
104
+ }
105
+ });
106
+ });
107
+ }
108
+ };
109
+ }
110
+ });
111
+ function isQueryHookFromClient(clientRegex, call) {
112
+ if (call.callee.type !== utils.AST_NODE_TYPES.MemberExpression) return null;
113
+ const member = call.callee;
114
+ if (member.property.type !== utils.AST_NODE_TYPES.Identifier) return null;
115
+ const hook = member.property.name;
116
+ if (!allHookNames.includes(hook)) return null;
117
+ const root = getRootIdentifier.getRootIdentifier(member.object);
118
+ if (!root) return null;
119
+ return clientRegex.test(root.name) ? hook : null;
120
+ }
121
+
122
+ exports.name = name;
123
+ exports.reactHookNames = reactHookNames;
124
+ exports.rule = rule;
125
+ exports.useQueryHookNames = useQueryHookNames;
126
+ //# sourceMappingURL=no-unstable-deps.rule.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-unstable-deps.rule.cjs","sources":["../../../../src/rules/no-unstable-deps/no-unstable-deps.rule.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport type { ExtraRuleDocs } from '../../types';\nimport { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';\nimport { createClientNameRegex } from '../../utils/create-client-name-regex';\nimport { getDocsUrl } from '../../utils/get-docs-url';\nimport { getRootIdentifier } from '../../utils/get-root-identifier';\n\nexport const name = 'no-unstable-deps';\n\nexport const reactHookNames = ['useEffect', 'useCallback', 'useMemo'];\nexport const useQueryHookNames = [\n 'useQuery',\n 'useSuspenseQuery',\n 'useQueries',\n 'useSuspenseQueries',\n 'useInfiniteQuery',\n 'useSuspenseInfiniteQuery',\n];\nconst allHookNames = ['useMutation', ...useQueryHookNames];\nconst createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl);\n\nexport const rule = createRule({\n name,\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow putting the result of query hooks directly in a React hook dependency array',\n recommended: 'error',\n },\n messages: {\n noUnstableDeps: `The result of {{queryHook}} is not referentially stable, so don't pass it directly into the dependencies array of {{reactHook}}. Instead, destructure the return value of {{queryHook}} and pass the destructured values into the dependency array of {{reactHook}}.`,\n },\n schema: [\n {\n type: 'object',\n properties: {\n clientNamePattern: { type: 'string' },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [{}],\n\n create: (context) => {\n const option =\n (context.options[0] as { clientNamePattern?: string } | undefined) ?? {};\n const clientRegex = createClientNameRegex(option.clientNamePattern);\n\n const trackedVariables: Record<string, string> = {};\n const hookAliasMap: Record<string, string> = {};\n\n function getReactHook(node: TSESTree.CallExpression): string | undefined {\n if (node.callee.type === 'Identifier') {\n const calleeName = node.callee.name;\n if (reactHookNames.includes(calleeName) || calleeName in hookAliasMap) {\n // Return the actually used identifier (could be alias)\n return calleeName;\n }\n } else if (\n node.callee.type === 'MemberExpression' &&\n node.callee.object.type === 'Identifier' &&\n node.callee.object.name === 'React' &&\n node.callee.property.type === 'Identifier' &&\n reactHookNames.includes(node.callee.property.name)\n ) {\n return node.callee.property.name;\n }\n return undefined;\n }\n\n return {\n ImportDeclaration(node: TSESTree.ImportDeclaration) {\n if (\n node.specifiers.length > 0 &&\n node.importKind === 'value' &&\n node.source.value === 'React'\n ) {\n node.specifiers.forEach((specifier) => {\n if (\n specifier.type === AST_NODE_TYPES.ImportSpecifier &&\n specifier.imported.type === AST_NODE_TYPES.Identifier &&\n reactHookNames.includes(specifier.imported.name)\n ) {\n hookAliasMap[specifier.local.name] = specifier.imported.name;\n }\n });\n }\n },\n\n VariableDeclarator(node) {\n if (node.init?.type !== AST_NODE_TYPES.CallExpression) return;\n const hook = isQueryHookFromClient(clientRegex, node.init);\n if (!hook) return;\n if (node.id.type === AST_NODE_TYPES.Identifier) {\n trackedVariables[node.id.name] = hook;\n }\n },\n CallExpression: (node) => {\n const reactHook = getReactHook(node);\n if (!reactHook) return;\n if (node.arguments.length <= 1) return;\n if (node.arguments[1]?.type !== AST_NODE_TYPES.ArrayExpression) return;\n\n const depsArray = node.arguments[1].elements;\n depsArray.forEach((dep) => {\n if (dep?.type !== AST_NODE_TYPES.Identifier) return;\n const queryHook = trackedVariables[dep.name];\n if (!queryHook) return;\n context.report({\n node: dep,\n messageId: 'noUnstableDeps',\n data: {\n queryHook,\n reactHook,\n },\n });\n });\n },\n };\n },\n});\n\nfunction isQueryHookFromClient(\n clientRegex: RegExp,\n call: TSESTree.CallExpression\n): string | null {\n if (call.callee.type !== AST_NODE_TYPES.MemberExpression) return null;\n const member = call.callee;\n if (member.property.type !== AST_NODE_TYPES.Identifier) return null;\n const hook = member.property.name;\n if (!allHookNames.includes(hook)) return null;\n const root = getRootIdentifier(member.object);\n if (!root) return null;\n return clientRegex.test(root.name) ? hook : null;\n}\n"],"names":["name","reactHookNames","useQueryHookNames","allHookNames","createRule","ESLintUtils","RuleCreator","getDocsUrl","rule","meta","type","docs","description","recommended","messages","noUnstableDeps","schema","properties","clientNamePattern","additionalProperties","defaultOptions","create","context","option","options","clientRegex","createClientNameRegex","trackedVariables","hookAliasMap","getReactHook","node","callee","calleeName","includes","object","property","undefined","ImportDeclaration","specifiers","length","importKind","source","value","forEach","specifier","AST_NODE_TYPES","ImportSpecifier","imported","Identifier","local","VariableDeclarator","init","CallExpression","hook","isQueryHookFromClient","id","reactHook","arguments","ArrayExpression","depsArray","elements","dep","queryHook","report","messageId","data","call","MemberExpression","member","root","getRootIdentifier","test"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ var noRestDestructuring_rule = require('./rules/no-rest-destructuring/no-rest-destructuring.rule.cjs');
4
+ var noUnstableDeps_rule = require('./rules/no-unstable-deps/no-unstable-deps.rule.cjs');
5
+
6
+ const rules = {
7
+ [noRestDestructuring_rule.name]: noRestDestructuring_rule.rule,
8
+ [noUnstableDeps_rule.name]: noUnstableDeps_rule.rule
9
+ };
10
+
11
+ exports.rules = rules;
12
+ //# sourceMappingURL=rules.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.cjs","sources":["../../src/rules.ts"],"sourcesContent":["import type { ESLintUtils } from '@typescript-eslint/utils';\nimport type { ExtraRuleDocs } from './types';\nimport * as noRestDestructuring from './rules/no-rest-destructuring/no-rest-destructuring.rule';\nimport * as noUnstableDeps from './rules/no-unstable-deps/no-unstable-deps.rule';\n\nexport const rules: Record<\n string,\n ESLintUtils.RuleModule<\n string,\n ReadonlyArray<unknown>,\n ExtraRuleDocs,\n ESLintUtils.RuleListener\n >\n> = {\n [noRestDestructuring.name]: noRestDestructuring.rule,\n [noUnstableDeps.name]: noUnstableDeps.rule,\n};\n"],"names":["rules","noRestDestructuring","noUnstableDeps"],"mappings":";;;;;MAKaA,KAQT,GAAA;AACF,IAAA,CAACC,6BAAwB,GAAGA,6BAAwB;AACpD,IAAA,CAACC,wBAAmB,GAAGA,wBAAmB;AAC5C;;;;"}
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ function createClientNameRegex(clientNamePattern) {
4
+ clientNamePattern = clientNamePattern?.trim();
5
+ if (clientNamePattern) {
6
+ // Check if pattern is in regex format: /pattern/flags
7
+ const regexMatch = clientNamePattern.match(/^\/(.*)\/([gimuy]*)$/);
8
+ if (regexMatch) {
9
+ const regexPattern = regexMatch[1].trim();
10
+ const flags = regexMatch[2];
11
+ // Validate that regex pattern is not empty or whitespace-only
12
+ if (regexPattern === '') {
13
+ throw new Error('clientNamePattern cannot be empty or contain only whitespace');
14
+ }
15
+ return new RegExp(regexPattern, flags);
16
+ }
17
+ // Fallback to treating as plain string pattern
18
+ return new RegExp(clientNamePattern);
19
+ } else if (clientNamePattern === '') {
20
+ throw new Error('clientNamePattern cannot be empty or contain only whitespace');
21
+ }
22
+ // Default if no pattern provided
23
+ return /qraft|api/i;
24
+ }
25
+
26
+ exports.createClientNameRegex = createClientNameRegex;
27
+ //# sourceMappingURL=create-client-name-regex.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-client-name-regex.cjs","sources":["../../../src/utils/create-client-name-regex.ts"],"sourcesContent":["export function createClientNameRegex(clientNamePattern: string | undefined) {\n clientNamePattern = clientNamePattern?.trim();\n\n if (clientNamePattern) {\n // Check if pattern is in regex format: /pattern/flags\n const regexMatch = clientNamePattern.match(/^\\/(.*)\\/([gimuy]*)$/);\n\n if (regexMatch) {\n const regexPattern = regexMatch[1].trim();\n const flags = regexMatch[2];\n\n // Validate that regex pattern is not empty or whitespace-only\n if (regexPattern === '') {\n throw new Error(\n 'clientNamePattern cannot be empty or contain only whitespace'\n );\n }\n\n return new RegExp(regexPattern, flags);\n }\n\n // Fallback to treating as plain string pattern\n return new RegExp(clientNamePattern);\n } else if (clientNamePattern === '') {\n throw new Error(\n 'clientNamePattern cannot be empty or contain only whitespace'\n );\n }\n\n // Default if no pattern provided\n return /qraft|api/i;\n}\n"],"names":["createClientNameRegex","clientNamePattern","trim","regexMatch","match","regexPattern","flags","Error","RegExp"],"mappings":";;AAAO,SAASA,sBAAsBC,iBAAqC,EAAA;AACzEA,IAAAA,iBAAAA,GAAoBA,iBAAmBC,EAAAA,IAAAA,EAAAA,CAAAA;AAEvC,IAAA,IAAID,iBAAmB,EAAA;;QAErB,MAAME,UAAAA,GAAaF,iBAAkBG,CAAAA,KAAK,CAAC,sBAAA,CAAA,CAAA;AAE3C,QAAA,IAAID,UAAY,EAAA;AACd,YAAA,MAAME,YAAeF,GAAAA,UAAU,CAAC,CAAA,CAAE,CAACD,IAAI,EAAA,CAAA;YACvC,MAAMI,KAAAA,GAAQH,UAAU,CAAC,CAAE,CAAA,CAAA;;AAG3B,YAAA,IAAIE,iBAAiB,EAAI,EAAA;AACvB,gBAAA,MAAM,IAAIE,KACR,CAAA,8DAAA,CAAA,CAAA;AAEJ,aAAA;YAEA,OAAO,IAAIC,OAAOH,YAAcC,EAAAA,KAAAA,CAAAA,CAAAA;AAClC,SAAA;;AAGA,QAAA,OAAO,IAAIE,MAAOP,CAAAA,iBAAAA,CAAAA,CAAAA;KACb,MAAA,IAAIA,sBAAsB,EAAI,EAAA;AACnC,QAAA,MAAM,IAAIM,KACR,CAAA,8DAAA,CAAA,CAAA;AAEJ,KAAA;;IAGA,OAAO,YAAA,CAAA;AACT;;;;"}
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ const getDocsUrl = (ruleName)=>`https://openapi-qraft.github.io/openapi-qraft/docs/eslint/${ruleName}`;
4
+
5
+ exports.getDocsUrl = getDocsUrl;
6
+ //# sourceMappingURL=get-docs-url.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-docs-url.cjs","sources":["../../../src/utils/get-docs-url.ts"],"sourcesContent":["export const getDocsUrl = (ruleName: string): string =>\n `https://openapi-qraft.github.io/openapi-qraft/docs/eslint/${ruleName}`;\n"],"names":["getDocsUrl","ruleName"],"mappings":";;AAAO,MAAMA,aAAa,CAACC,QAAAA,GACzB,CAAC,0DAA0D,EAAEA,QAAS,CAAA;;;;"}
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ var utils = require('@typescript-eslint/utils');
4
+
5
+ function getRootIdentifier(node) {
6
+ let current = node;
7
+ while(current.type === utils.AST_NODE_TYPES.MemberExpression || current.type === utils.AST_NODE_TYPES.TSNonNullExpression || current.type === utils.AST_NODE_TYPES.ChainExpression){
8
+ if (current.type === utils.AST_NODE_TYPES.MemberExpression) {
9
+ current = current.object;
10
+ continue;
11
+ }
12
+ if (current.type === utils.AST_NODE_TYPES.TSNonNullExpression) {
13
+ current = current.expression;
14
+ continue;
15
+ }
16
+ current = current.expression;
17
+ }
18
+ return current.type === utils.AST_NODE_TYPES.Identifier ? current : undefined;
19
+ }
20
+
21
+ exports.getRootIdentifier = getRootIdentifier;
22
+ //# sourceMappingURL=get-root-identifier.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-root-identifier.cjs","sources":["../../../src/utils/get-root-identifier.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { AST_NODE_TYPES } from '@typescript-eslint/utils';\n\nexport function getRootIdentifier(\n node: TSESTree.Node\n): TSESTree.Identifier | undefined {\n let current: TSESTree.Node | undefined = node;\n while (\n current.type === AST_NODE_TYPES.MemberExpression ||\n current.type === AST_NODE_TYPES.TSNonNullExpression ||\n current.type === AST_NODE_TYPES.ChainExpression\n ) {\n if (current.type === AST_NODE_TYPES.MemberExpression) {\n current = current.object;\n continue;\n }\n if (current.type === AST_NODE_TYPES.TSNonNullExpression) {\n current = current.expression;\n continue;\n }\n\n current = current.expression;\n }\n return current.type === AST_NODE_TYPES.Identifier ? current : undefined;\n}\n"],"names":["getRootIdentifier","node","current","type","AST_NODE_TYPES","MemberExpression","TSNonNullExpression","ChainExpression","object","expression","Identifier","undefined"],"mappings":";;;;AAGO,SAASA,kBACdC,IAAmB,EAAA;AAEnB,IAAA,IAAIC,OAAqCD,GAAAA,IAAAA,CAAAA;AACzC,IAAA,MACEC,QAAQC,IAAI,KAAKC,oBAAeC,CAAAA,gBAAgB,IAChDH,OAAQC,CAAAA,IAAI,KAAKC,oBAAAA,CAAeE,mBAAmB,IACnDJ,OAAAA,CAAQC,IAAI,KAAKC,oBAAAA,CAAeG,eAAe,CAC/C;AACA,QAAA,IAAIL,OAAQC,CAAAA,IAAI,KAAKC,oBAAAA,CAAeC,gBAAgB,EAAE;AACpDH,YAAAA,OAAAA,GAAUA,QAAQM,MAAM,CAAA;AACxB,YAAA,SAAA;AACF,SAAA;AACA,QAAA,IAAIN,OAAQC,CAAAA,IAAI,KAAKC,oBAAAA,CAAeE,mBAAmB,EAAE;AACvDJ,YAAAA,OAAAA,GAAUA,QAAQO,UAAU,CAAA;AAC5B,YAAA,SAAA;AACF,SAAA;AAEAP,QAAAA,OAAAA,GAAUA,QAAQO,UAAU,CAAA;AAC9B,KAAA;AACA,IAAA,OAAOP,QAAQC,IAAI,KAAKC,oBAAeM,CAAAA,UAAU,GAAGR,OAAUS,GAAAA,SAAAA,CAAAA;AAChE;;;;"}
@@ -0,0 +1,37 @@
1
+ import { rules } from './rules.js';
2
+
3
+ const plugin = {
4
+ meta: {
5
+ name: '@openapi-qraft/eslint-plugin-query'
6
+ },
7
+ configs: {},
8
+ rules
9
+ };
10
+ // Assign configs here so we can reference `plugin`
11
+ Object.assign(plugin.configs, {
12
+ recommended: {
13
+ plugins: [
14
+ '@openapi-qraft/query'
15
+ ],
16
+ rules: {
17
+ '@openapi-qraft/query/no-rest-destructuring': 'warn',
18
+ '@openapi-qraft/query/no-unstable-deps': 'error'
19
+ }
20
+ },
21
+ 'flat/recommended': [
22
+ {
23
+ name: 'openapi-qraft/query/flat/recommended',
24
+ plugins: {
25
+ '@openapi-qraft/query': plugin
26
+ },
27
+ rules: {
28
+ '@openapi-qraft/query/no-rest-destructuring': 'warn',
29
+ '@openapi-qraft/query/no-unstable-deps': 'error'
30
+ }
31
+ }
32
+ ]
33
+ });
34
+ const configs = plugin.configs;
35
+
36
+ export { configs, plugin as default, plugin, rules };
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["import type { RuleModule } from '@typescript-eslint/utils/ts-eslint';\nimport type { ESLint, Linter } from 'eslint';\nimport { rules } from './rules';\n\ntype RuleKey = keyof typeof rules;\n\nexport interface Plugin extends Omit<ESLint.Plugin, 'rules'> {\n rules: Record<RuleKey, RuleModule<any, any, any>>;\n configs: {\n recommended: ESLint.ConfigData;\n 'flat/recommended': Array<Linter.Config>;\n };\n}\n\nexport const plugin: Plugin = {\n meta: {\n name: '@openapi-qraft/eslint-plugin-query',\n },\n configs: {} as Plugin['configs'],\n rules,\n};\n\n// Assign configs here so we can reference `plugin`\nObject.assign(plugin.configs, {\n recommended: {\n plugins: ['@openapi-qraft/query'],\n rules: {\n '@openapi-qraft/query/no-rest-destructuring': 'warn',\n '@openapi-qraft/query/no-unstable-deps': 'error',\n },\n },\n 'flat/recommended': [\n {\n name: 'openapi-qraft/query/flat/recommended',\n plugins: {\n '@openapi-qraft/query': plugin,\n },\n rules: {\n '@openapi-qraft/query/no-rest-destructuring': 'warn',\n '@openapi-qraft/query/no-unstable-deps': 'error',\n },\n },\n ],\n});\n\n// Reexport rules & configs for the Legacy config\nexport { rules };\nexport const configs = plugin.configs;\n\n/**\n * @alias\n * @type {Plugin}\n **/\nexport default plugin;\n"],"names":["plugin","meta","name","configs","rules","Object","assign","recommended","plugins"],"mappings":";;MAcaA,MAAiB,GAAA;IAC5BC,IAAM,EAAA;QACJC,IAAM,EAAA,oCAAA;AACR,KAAA;AACAC,IAAAA,OAAAA,EAAS,EAAC;AACVC,IAAAA,KAAAA;AACF,EAAE;AAEF;AACAC,MAAAA,CAAOC,MAAM,CAACN,MAAOG,CAAAA,OAAO,EAAE;IAC5BI,WAAa,EAAA;QACXC,OAAS,EAAA;AAAC,YAAA,sBAAA;AAAuB,SAAA;QACjCJ,KAAO,EAAA;YACL,4CAA8C,EAAA,MAAA;YAC9C,uCAAyC,EAAA,OAAA;AAC3C,SAAA;AACF,KAAA;IACA,kBAAoB,EAAA;AAClB,QAAA;YACEF,IAAM,EAAA,sCAAA;YACNM,OAAS,EAAA;gBACP,sBAAwBR,EAAAA,MAAAA;AAC1B,aAAA;YACAI,KAAO,EAAA;gBACL,4CAA8C,EAAA,MAAA;gBAC9C,uCAAyC,EAAA,OAAA;AAC3C,aAAA;AACF,SAAA;AACD,KAAA;AACH,CAAA,CAAA,CAAA;AAIaD,MAAAA,OAAAA,GAAUH,MAAOG,CAAAA;;;;"}