@kubb/plugin-react-query 5.0.0-beta.3 → 5.0.0-beta.31

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 (48) hide show
  1. package/README.md +34 -83
  2. package/dist/{components-DTGLu4UV.js → components-BVmVgpLX.js} +379 -279
  3. package/dist/components-BVmVgpLX.js.map +1 -0
  4. package/dist/{components-dAKJEn9b.cjs → components-DLUeLMsz.cjs} +409 -279
  5. package/dist/components-DLUeLMsz.cjs.map +1 -0
  6. package/dist/components.cjs +1 -1
  7. package/dist/components.d.ts +4 -76
  8. package/dist/components.js +1 -1
  9. package/dist/{generators-CWEQsdO9.cjs → generators--AcF4Y4n.cjs} +332 -410
  10. package/dist/generators--AcF4Y4n.cjs.map +1 -0
  11. package/dist/{generators-C_fbcjpG.js → generators-BFn9CLBS.js} +333 -411
  12. package/dist/generators-BFn9CLBS.js.map +1 -0
  13. package/dist/generators.cjs +1 -1
  14. package/dist/generators.d.ts +41 -1
  15. package/dist/generators.js +1 -1
  16. package/dist/index.cjs +179 -26
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.ts +30 -1
  19. package/dist/index.js +179 -26
  20. package/dist/index.js.map +1 -1
  21. package/dist/types-DiZPLTXl.d.ts +400 -0
  22. package/extension.yaml +1507 -0
  23. package/package.json +16 -18
  24. package/src/components/InfiniteQuery.tsx +24 -13
  25. package/src/components/InfiniteQueryOptions.tsx +37 -55
  26. package/src/components/Mutation.tsx +35 -15
  27. package/src/components/MutationOptions.tsx +14 -13
  28. package/src/components/Query.tsx +14 -10
  29. package/src/components/QueryOptions.tsx +17 -34
  30. package/src/components/SuspenseInfiniteQuery.tsx +19 -13
  31. package/src/components/SuspenseInfiniteQueryOptions.tsx +28 -52
  32. package/src/components/SuspenseQuery.tsx +9 -10
  33. package/src/generators/customHookOptionsFileGenerator.tsx +18 -14
  34. package/src/generators/hookOptionsGenerator.tsx +42 -49
  35. package/src/generators/infiniteQueryGenerator.tsx +55 -76
  36. package/src/generators/mutationGenerator.tsx +51 -62
  37. package/src/generators/queryGenerator.tsx +52 -61
  38. package/src/generators/suspenseInfiniteQueryGenerator.tsx +50 -63
  39. package/src/generators/suspenseQueryGenerator.tsx +54 -74
  40. package/src/plugin.ts +45 -31
  41. package/src/resolvers/resolverReactQuery.ts +102 -6
  42. package/src/types.ts +199 -61
  43. package/src/utils.ts +10 -33
  44. package/dist/components-DTGLu4UV.js.map +0 -1
  45. package/dist/components-dAKJEn9b.cjs.map +0 -1
  46. package/dist/generators-CWEQsdO9.cjs.map +0 -1
  47. package/dist/generators-C_fbcjpG.js.map +0 -1
  48. package/dist/types-DfaFRSBf.d.ts +0 -284
package/package.json CHANGED
@@ -1,25 +1,22 @@
1
1
  {
2
2
  "name": "@kubb/plugin-react-query",
3
- "version": "5.0.0-beta.3",
4
- "description": "React Query hooks generator plugin for Kubb, creating type-safe API client hooks from OpenAPI specifications for React applications.",
3
+ "version": "5.0.0-beta.31",
4
+ "description": "Generate type-safe TanStack Query (React Query) hooks from your OpenAPI specification. Covers useQuery, useMutation, useInfiniteQuery, and queryOptions with full TypeScript support.",
5
5
  "keywords": [
6
- "api-client",
7
- "code-generator",
6
+ "code-generation",
8
7
  "codegen",
9
8
  "data-fetching",
10
9
  "hooks",
11
10
  "kubb",
12
- "oas",
13
11
  "openapi",
14
- "plugins",
15
12
  "react",
16
- "react-hooks",
17
13
  "react-query",
18
- "sdk-generator",
19
14
  "swagger",
15
+ "tanstack",
20
16
  "tanstack-query",
21
- "type-safe",
22
- "typescript"
17
+ "typescript",
18
+ "use-mutation",
19
+ "use-query"
23
20
  ],
24
21
  "license": "MIT",
25
22
  "author": "stijnvanhulle",
@@ -31,7 +28,7 @@
31
28
  "files": [
32
29
  "src",
33
30
  "dist",
34
- "plugin.json",
31
+ "extension.yaml",
35
32
  "!/**/**.test.**",
36
33
  "!/**/__tests__/**",
37
34
  "!/**/__snapshots__/**"
@@ -71,19 +68,20 @@
71
68
  "registry": "https://registry.npmjs.org/"
72
69
  },
73
70
  "dependencies": {
74
- "@kubb/core": "5.0.0-beta.3",
75
- "@kubb/renderer-jsx": "5.0.0-beta.3",
76
- "remeda": "^2.34.0",
77
- "@kubb/plugin-client": "5.0.0-beta.3",
78
- "@kubb/plugin-ts": "5.0.0-beta.3",
79
- "@kubb/plugin-zod": "5.0.0-beta.3"
71
+ "@kubb/core": "5.0.0-beta.31",
72
+ "@kubb/renderer-jsx": "5.0.0-beta.31",
73
+ "remeda": "^2.34.1",
74
+ "@kubb/plugin-client": "5.0.0-beta.31",
75
+ "@kubb/plugin-ts": "5.0.0-beta.31",
76
+ "@kubb/plugin-zod": "5.0.0-beta.31"
80
77
  },
81
78
  "devDependencies": {
79
+ "@internals/shared": "0.0.0",
82
80
  "@internals/tanstack-query": "0.0.0",
83
81
  "@internals/utils": "0.0.0"
84
82
  },
85
83
  "peerDependencies": {
86
- "@kubb/renderer-jsx": "5.0.0-beta.3"
84
+ "@kubb/renderer-jsx": "5.0.0-beta.31"
87
85
  },
88
86
  "size-limit": [
89
87
  {
@@ -1,11 +1,12 @@
1
+ import { getOperationParameters } from '@internals/shared'
1
2
  import { ast } from '@kubb/core'
2
3
  import type { ResolverTs } from '@kubb/plugin-ts'
3
4
  import { functionPrinter } from '@kubb/plugin-ts'
4
5
  import { File, Function } from '@kubb/renderer-jsx'
5
6
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
6
7
  import type { Infinite, PluginReactQuery } from '../types.ts'
7
- import { getComments, resolveErrorNames } from '../utils.ts'
8
- import { QueryKey } from './QueryKey.tsx'
8
+ import { getEnabledParamNames, markParamsOptional } from '@internals/tanstack-query'
9
+ import { buildQueryKeyParams, getComments, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
9
10
  import { getQueryOptionsParams } from './QueryOptions.tsx'
10
11
 
11
12
  type Props = {
@@ -27,7 +28,7 @@ type Props = {
27
28
  const declarationPrinter = functionPrinter({ mode: 'declaration' })
28
29
  const callPrinter = functionPrinter({ mode: 'call' })
29
30
 
30
- function getParams(
31
+ function buildInfiniteQueryParamsNode(
31
32
  node: ast.OperationNode,
32
33
  options: {
33
34
  paramsType: PluginReactQuery['resolvedOptions']['paramsType']
@@ -39,7 +40,7 @@ function getParams(
39
40
  },
40
41
  ): ast.FunctionParametersNode {
41
42
  const { paramsType, paramsCasing, pathParamsType, resolver, pageParamGeneric } = options
42
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
43
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null
43
44
 
44
45
  const optionsParam = ast.createFunctionParameter({
45
46
  name: 'options',
@@ -77,7 +78,8 @@ export function InfiniteQuery({
77
78
  queryParam,
78
79
  customOptions,
79
80
  }: Props): KubbReactNode {
80
- const responseName = tsResolver.resolveResponseName(node)
81
+ const successNames = resolveSuccessNames(node, tsResolver)
82
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
81
83
  const errorNames = resolveErrorNames(node, tsResolver)
82
84
 
83
85
  const responseType = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -98,17 +100,17 @@ export function InfiniteQuery({
98
100
  ? 'boolean'
99
101
  : 'unknown'
100
102
 
101
- const rawQueryParams = node.parameters.filter((p) => p.in === 'query')
103
+ const rawQueryParams = getOperationParameters(node).query
102
104
  const queryParamsTypeName =
103
105
  rawQueryParams.length > 0
104
106
  ? (() => {
105
107
  const groupName = tsResolver.resolveQueryParamsName(node, rawQueryParams[0]!)
106
108
  const individualName = tsResolver.resolveParamName(node, rawQueryParams[0]!)
107
- return groupName !== individualName ? groupName : undefined
109
+ return groupName !== individualName ? groupName : null
108
110
  })()
109
- : undefined
111
+ : null
110
112
 
111
- const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : undefined
113
+ const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null
112
114
  const pageParamType = queryParamType ? (isInitialPageParamDefined ? `NonNullable<${queryParamType}>` : queryParamType) : fallbackPageParamType
113
115
 
114
116
  const returnType = 'UseInfiniteQueryResult<TData, TError> & { queryKey: TQueryKey }'
@@ -120,13 +122,24 @@ export function InfiniteQuery({
120
122
  `TPageParam = ${pageParamType}`,
121
123
  ]
122
124
 
123
- const queryKeyParamsNode = QueryKey.getParams(node, { pathParamsType, paramsCasing, resolver: tsResolver })
125
+ const queryKeyParamsNode = buildQueryKeyParams(node, { pathParamsType, paramsCasing, resolver: tsResolver })
124
126
  const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? ''
127
+ const enabledNames = getEnabledParamNames(queryKeyParamsNode)
125
128
 
126
129
  const queryOptionsParamsNode = getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver })
127
130
  const queryOptionsParamsCall = callPrinter.print(queryOptionsParamsNode) ?? ''
128
131
 
129
- const paramsNode = getParams(node, { paramsType, paramsCasing, pathParamsType, dataReturnType, resolver: tsResolver, pageParamGeneric: 'TPageParam' })
132
+ const paramsNode = markParamsOptional(
133
+ buildInfiniteQueryParamsNode(node, {
134
+ paramsType,
135
+ paramsCasing,
136
+ pathParamsType,
137
+ dataReturnType,
138
+ resolver: tsResolver,
139
+ pageParamGeneric: 'TPageParam',
140
+ }),
141
+ enabledNames,
142
+ )
130
143
  const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
131
144
 
132
145
  return (
@@ -152,5 +165,3 @@ export function InfiniteQuery({
152
165
  </File.Source>
153
166
  )
154
167
  }
155
-
156
- InfiniteQuery.getParams = getParams
@@ -1,3 +1,4 @@
1
+ import { getOperationParameters } from '@internals/shared'
1
2
  import { getNestedAccessor } from '@internals/utils'
2
3
  import type { ast } from '@kubb/core'
3
4
  import type { ResolverTs } from '@kubb/plugin-ts'
@@ -5,9 +6,9 @@ import { functionPrinter } from '@kubb/plugin-ts'
5
6
  import { File, Function } from '@kubb/renderer-jsx'
6
7
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
7
8
  import type { Infinite, PluginReactQuery } from '../types.ts'
8
- import { resolveErrorNames } from '../utils.ts'
9
- import { QueryKey } from './QueryKey.tsx'
10
- import { buildEnabledCheck, getQueryOptionsParams } from './QueryOptions.tsx'
9
+ import { buildQueryKeyParams, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
10
+ import { getEnabledParamNames, injectNonNullAssertions, markParamsOptional } from '@internals/tanstack-query'
11
+ import { getQueryOptionsParams } from './QueryOptions.tsx'
11
12
 
12
13
  type Props = {
13
14
  name: string
@@ -45,7 +46,8 @@ export function InfiniteQueryOptions({
45
46
  queryParam,
46
47
  queryKeyName,
47
48
  }: Props): KubbReactNode {
48
- const responseName = tsResolver.resolveResponseName(node)
49
+ const successNames = resolveSuccessNames(node, tsResolver)
50
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
49
51
  const queryFnDataType = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
50
52
  const errorNames = resolveErrorNames(node, tsResolver)
51
53
  const errorType = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
@@ -65,61 +67,51 @@ export function InfiniteQueryOptions({
65
67
  ? 'boolean'
66
68
  : 'unknown'
67
69
 
68
- const rawQueryParams = node.parameters.filter((p) => p.in === 'query')
70
+ const rawQueryParams = getOperationParameters(node).query
69
71
  const queryParamsTypeName =
70
72
  rawQueryParams.length > 0
71
73
  ? (() => {
72
74
  const groupName = tsResolver.resolveQueryParamsName(node, rawQueryParams[0]!)
73
75
  const individualName = tsResolver.resolveParamName(node, rawQueryParams[0]!)
74
- return groupName !== individualName ? groupName : undefined
76
+ return groupName !== individualName ? groupName : null
75
77
  })()
76
- : undefined
78
+ : null
77
79
 
78
- const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : undefined
80
+ const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null
79
81
  const pageParamType = queryParamType ? (isInitialPageParamDefined ? `NonNullable<${queryParamType}>` : queryParamType) : fallbackPageParamType
80
82
 
81
- const paramsNode = getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver })
82
- const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
83
- const rawParamsCall = callPrinter.print(paramsNode) ?? ''
84
- const clientCallStr = rawParamsCall.replace(/\bconfig\b(?=[^,]*$)/, '{ ...config, signal: config.signal ?? signal }')
85
-
86
- const queryKeyParamsNode = QueryKey.getParams(node, { pathParamsType, paramsCasing, resolver: tsResolver })
83
+ const queryKeyParamsNode = buildQueryKeyParams(node, { pathParamsType, paramsCasing, resolver: tsResolver })
87
84
  const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? ''
88
85
 
89
- const enabledSource = buildEnabledCheck(queryKeyParamsNode)
90
- const enabledText = enabledSource ? `enabled: !!(${enabledSource}),` : ''
91
-
92
- const hasNewParams = nextParam !== undefined || previousParam !== undefined
93
-
94
- let getNextPageParamExpr: string | undefined
95
- let getPreviousPageParamExpr: string | undefined
86
+ const enabledNames = getEnabledParamNames(queryKeyParamsNode)
87
+ const enabledText = enabledNames.length ? `enabled: !!(${enabledNames.join(' && ')}),` : ''
96
88
 
97
- if (hasNewParams) {
98
- if (nextParam) {
99
- const accessor = getNestedAccessor(nextParam, 'lastPage')
100
- if (accessor) {
101
- getNextPageParamExpr = `getNextPageParam: (lastPage) => ${accessor}`
102
- }
103
- }
104
- if (previousParam) {
105
- const accessor = getNestedAccessor(previousParam, 'firstPage')
106
- if (accessor) {
107
- getPreviousPageParamExpr = `getPreviousPageParam: (firstPage) => ${accessor}`
108
- }
89
+ const paramsNode = markParamsOptional(getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver }), enabledNames)
90
+ const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
91
+ const rawParamsCall = callPrinter.print(paramsNode) ?? ''
92
+ const clientCallStr = injectNonNullAssertions(rawParamsCall.replace(/\bconfig\b(?=[^,]*$)/, '{ ...config, signal: config.signal ?? signal }'), enabledNames)
93
+
94
+ const hasNewParams = nextParam != null || previousParam != null
95
+
96
+ const [getNextPageParamExpr, getPreviousPageParamExpr] = (() => {
97
+ if (hasNewParams) {
98
+ const nextAccessor = nextParam ? getNestedAccessor(nextParam, 'lastPage') : null
99
+ const prevAccessor = previousParam ? getNestedAccessor(previousParam, 'firstPage') : null
100
+ return [
101
+ nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : null,
102
+ prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : null,
103
+ ] as const
109
104
  }
110
- } else if (cursorParam) {
111
- getNextPageParamExpr = `getNextPageParam: (lastPage) => lastPage['${cursorParam}']`
112
- getPreviousPageParamExpr = `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`
113
- } else {
114
- if (dataReturnType === 'full') {
115
- getNextPageParamExpr =
116
- 'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage.data) && lastPage.data.length === 0 ? undefined : lastPageParam + 1'
117
- } else {
118
- getNextPageParamExpr =
119
- 'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1'
105
+ if (cursorParam) {
106
+ return [`getNextPageParam: (lastPage) => lastPage['${cursorParam}']`, `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`] as const
120
107
  }
121
- getPreviousPageParamExpr = 'getPreviousPageParam: (_firstPage, _allPages, firstPageParam) => firstPageParam <= 1 ? undefined : firstPageParam - 1'
122
- }
108
+ return [
109
+ dataReturnType === 'full'
110
+ ? 'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage.data) && lastPage.data.length === 0 ? undefined : lastPageParam + 1'
111
+ : 'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1',
112
+ 'getPreviousPageParam: (_firstPage, _allPages, firstPageParam) => firstPageParam <= 1 ? undefined : firstPageParam - 1',
113
+ ] as const
114
+ })()
123
115
 
124
116
  const queryOptionsArr = [
125
117
  `initialPageParam: ${typeof initialPageParam === 'string' ? JSON.stringify(initialPageParam) : initialPageParam}`,
@@ -175,13 +167,3 @@ export function InfiniteQueryOptions({
175
167
  </File.Source>
176
168
  )
177
169
  }
178
-
179
- InfiniteQueryOptions.getParams = (
180
- node: ast.OperationNode,
181
- options: {
182
- paramsType: PluginReactQuery['resolvedOptions']['paramsType']
183
- paramsCasing: PluginReactQuery['resolvedOptions']['paramsCasing']
184
- pathParamsType: PluginReactQuery['resolvedOptions']['pathParamsType']
185
- resolver: ResolverTs
186
- },
187
- ) => getQueryOptionsParams(node, options)
@@ -4,8 +4,8 @@ import { functionPrinter } from '@kubb/plugin-ts'
4
4
  import { File, Function } from '@kubb/renderer-jsx'
5
5
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
6
6
  import type { PluginReactQuery } from '../types.ts'
7
- import { buildMutationArgParams, getComments, resolveErrorNames } from '../utils.ts'
8
- import { MutationOptions } from './MutationOptions.tsx'
7
+ import { buildRequestConfigType, getComments, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
8
+ import { buildMutationConfigParamsNode } from './MutationOptions.tsx'
9
9
 
10
10
  type Props = {
11
11
  name: string
@@ -23,7 +23,22 @@ type Props = {
23
23
  const declarationPrinter = functionPrinter({ mode: 'declaration' })
24
24
  const callPrinter = functionPrinter({ mode: 'call' })
25
25
 
26
- function getParams(
26
+ function createMutationArgParams(
27
+ node: ast.OperationNode,
28
+ options: {
29
+ paramsCasing: PluginReactQuery['resolvedOptions']['paramsCasing']
30
+ resolver: ResolverTs
31
+ },
32
+ ): ast.FunctionParametersNode {
33
+ return ast.createOperationParams(node, {
34
+ paramsType: 'inline',
35
+ pathParamsType: 'inline',
36
+ paramsCasing: options.paramsCasing,
37
+ resolver: options.resolver,
38
+ })
39
+ }
40
+
41
+ function buildMutationParamsNode(
27
42
  node: ast.OperationNode,
28
43
  options: {
29
44
  paramsCasing: PluginReactQuery['resolvedOptions']['paramsCasing']
@@ -32,16 +47,19 @@ function getParams(
32
47
  },
33
48
  ): ast.FunctionParametersNode {
34
49
  const { paramsCasing, dataReturnType, resolver } = options
35
- const responseName = resolver.resolveResponseName(node)
36
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
50
+ const successNames = resolveSuccessNames(node, resolver)
51
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : resolver.resolveResponseName(node)
37
52
  const errorNames = resolveErrorNames(node, resolver)
38
53
 
39
54
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
40
55
  const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
41
56
 
42
- const mutationArgParamsNode = buildMutationArgParams(node, { paramsCasing, resolver })
57
+ const mutationArgParamsNode = createMutationArgParams(node, {
58
+ paramsCasing,
59
+ resolver,
60
+ })
43
61
  const TRequest = mutationArgParamsNode.params.length > 0 ? (declarationPrinter.print(mutationArgParamsNode) ?? '') : ''
44
- const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'void', 'TContext'].join(', ')
62
+ const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'undefined', 'TContext'].join(', ')
45
63
 
46
64
  return ast.createFunctionParameters({
47
65
  params: [
@@ -51,7 +69,7 @@ function getParams(
51
69
  variant: 'reference',
52
70
  name: `{
53
71
  mutation?: UseMutationOptions<${generics}> & { client?: QueryClient },
54
- client?: ${requestName ? `Partial<RequestConfig<${requestName}>> & { client?: Client }` : 'Partial<RequestConfig> & { client?: Client }'},
72
+ client?: ${buildRequestConfigType(node, resolver)},
55
73
  }`,
56
74
  }),
57
75
  default: '{}',
@@ -61,21 +79,25 @@ function getParams(
61
79
  }
62
80
 
63
81
  export function Mutation({ name, mutationOptionsName, paramsCasing, dataReturnType, node, tsResolver, mutationKeyName, customOptions }: Props): KubbReactNode {
64
- const responseName = tsResolver.resolveResponseName(node)
82
+ const successNames = resolveSuccessNames(node, tsResolver)
83
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
65
84
  const errorNames = resolveErrorNames(node, tsResolver)
66
85
 
67
86
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
68
87
  const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
69
88
 
70
- const mutationArgParamsNode = buildMutationArgParams(node, { paramsCasing, resolver: tsResolver })
89
+ const mutationArgParamsNode = createMutationArgParams(node, {
90
+ paramsCasing,
91
+ resolver: tsResolver,
92
+ })
71
93
  const TRequest = mutationArgParamsNode.params.length > 0 ? (declarationPrinter.print(mutationArgParamsNode) ?? '') : ''
72
- const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'void', 'TContext'].join(', ')
94
+ const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'undefined', 'TContext'].join(', ')
73
95
  const returnType = `UseMutationResult<${generics}>`
74
96
 
75
- const mutationOptionsConfigNode = MutationOptions.getParams(node, tsResolver)
97
+ const mutationOptionsConfigNode = buildMutationConfigParamsNode(node, tsResolver)
76
98
  const mutationOptionsParamsCall = callPrinter.print(mutationOptionsConfigNode) ?? ''
77
99
 
78
- const paramsNode = getParams(node, { paramsCasing, dataReturnType, resolver: tsResolver })
100
+ const paramsNode = buildMutationParamsNode(node, { paramsCasing, dataReturnType, resolver: tsResolver })
79
101
  const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
80
102
 
81
103
  return (
@@ -99,5 +121,3 @@ export function Mutation({ name, mutationOptionsName, paramsCasing, dataReturnTy
99
121
  </File.Source>
100
122
  )
101
123
  }
102
-
103
- Mutation.getParams = getParams
@@ -4,7 +4,7 @@ import { functionPrinter } from '@kubb/plugin-ts'
4
4
  import { File, Function } from '@kubb/renderer-jsx'
5
5
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
6
6
  import type { PluginReactQuery } from '../types.ts'
7
- import { buildMutationArgParams, resolveErrorNames } from '../utils.ts'
7
+ import { buildRequestConfigType, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
8
8
 
9
9
  type Props = {
10
10
  name: string
@@ -22,15 +22,14 @@ const declarationPrinter = functionPrinter({ mode: 'declaration' })
22
22
  const callPrinter = functionPrinter({ mode: 'call' })
23
23
  const keysPrinter = functionPrinter({ mode: 'keys' })
24
24
 
25
- function getConfigParam(node: ast.OperationNode, resolver: ResolverTs): ast.FunctionParametersNode {
26
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
25
+ export function buildMutationConfigParamsNode(node: ast.OperationNode, resolver: ResolverTs): ast.FunctionParametersNode {
27
26
  return ast.createFunctionParameters({
28
27
  params: [
29
28
  ast.createFunctionParameter({
30
29
  name: 'config',
31
30
  type: ast.createParamsType({
32
31
  variant: 'reference',
33
- name: requestName ? `Partial<RequestConfig<${requestName}>> & { client?: Client }` : 'Partial<RequestConfig> & { client?: Client }',
32
+ name: buildRequestConfigType(node, resolver),
34
33
  }),
35
34
  default: '{}',
36
35
  }),
@@ -49,15 +48,21 @@ export function MutationOptions({
49
48
  pathParamsType,
50
49
  mutationKeyName,
51
50
  }: Props): KubbReactNode {
52
- const responseName = tsResolver.resolveResponseName(node)
51
+ const successNames = resolveSuccessNames(node, tsResolver)
52
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
53
53
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
54
54
  const errorNames = resolveErrorNames(node, tsResolver)
55
55
  const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
56
56
 
57
- const configParamsNode = getConfigParam(node, tsResolver)
57
+ const configParamsNode = buildMutationConfigParamsNode(node, tsResolver)
58
58
  const paramsSignature = declarationPrinter.print(configParamsNode) ?? ''
59
59
 
60
- const mutationArgParamsNode = buildMutationArgParams(node, { paramsCasing, resolver: tsResolver })
60
+ const mutationArgParamsNode = ast.createOperationParams(node, {
61
+ paramsType: 'inline',
62
+ pathParamsType: 'inline',
63
+ paramsCasing,
64
+ resolver: tsResolver,
65
+ })
61
66
  const hasMutationParams = mutationArgParamsNode.params.length > 0
62
67
 
63
68
  const TRequest = hasMutationParams ? (declarationPrinter.print(mutationArgParamsNode) ?? '') : ''
@@ -73,9 +78,7 @@ export function MutationOptions({
73
78
  name: 'config',
74
79
  type: ast.createParamsType({
75
80
  variant: 'reference',
76
- name: node.requestBody?.content?.[0]?.schema
77
- ? `Partial<RequestConfig<${tsResolver.resolveDataName(node)}>> & { client?: Client }`
78
- : 'Partial<RequestConfig> & { client?: Client }',
81
+ name: buildRequestConfigType(node, tsResolver),
79
82
  }),
80
83
  default: '{}',
81
84
  }),
@@ -88,7 +91,7 @@ export function MutationOptions({
88
91
  <Function name={name} export params={paramsSignature} generics={['TContext = unknown']}>
89
92
  {`
90
93
  const mutationKey = ${mutationKeyName}()
91
- return mutationOptions<${TData}, ${TError}, ${TRequest ? `{${TRequest}}` : 'void'}, TContext>({
94
+ return mutationOptions<${TData}, ${TError}, ${TRequest ? `{${TRequest}}` : 'undefined'}, TContext>({
92
95
  mutationKey,
93
96
  mutationFn: async(${hasMutationParams ? `{ ${argKeysStr} }` : '_'}) => {
94
97
  return ${clientName}(${clientCallStr})
@@ -99,5 +102,3 @@ export function MutationOptions({
99
102
  </File.Source>
100
103
  )
101
104
  }
102
-
103
- MutationOptions.getParams = getConfigParam
@@ -4,8 +4,8 @@ import { functionPrinter } from '@kubb/plugin-ts'
4
4
  import { File, Function } from '@kubb/renderer-jsx'
5
5
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
6
6
  import type { PluginReactQuery } from '../types.ts'
7
- import { getComments, resolveErrorNames } from '../utils.ts'
8
- import { QueryKey } from './QueryKey.tsx'
7
+ import { getEnabledParamNames, markParamsOptional } from '@internals/tanstack-query'
8
+ import { buildQueryKeyParams, getComments, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
9
9
  import { getQueryOptionsParams } from './QueryOptions.tsx'
10
10
 
11
11
  type Props = {
@@ -25,7 +25,7 @@ type Props = {
25
25
  const declarationPrinter = functionPrinter({ mode: 'declaration' })
26
26
  const callPrinter = functionPrinter({ mode: 'call' })
27
27
 
28
- function getParams(
28
+ function buildQueryParamsNode(
29
29
  node: ast.OperationNode,
30
30
  options: {
31
31
  paramsType: PluginReactQuery['resolvedOptions']['paramsType']
@@ -36,8 +36,9 @@ function getParams(
36
36
  },
37
37
  ): ast.FunctionParametersNode {
38
38
  const { paramsType, paramsCasing, pathParamsType, dataReturnType, resolver } = options
39
- const responseName = resolver.resolveResponseName(node)
40
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
39
+ const successNames = resolveSuccessNames(node, resolver)
40
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : resolver.resolveResponseName(node)
41
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null
41
42
  const errorNames = resolveErrorNames(node, resolver)
42
43
 
43
44
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -77,7 +78,8 @@ export function Query({
77
78
  tsResolver,
78
79
  customOptions,
79
80
  }: Props): KubbReactNode {
80
- const responseName = tsResolver.resolveResponseName(node)
81
+ const successNames = resolveSuccessNames(node, tsResolver)
82
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
81
83
  const errorNames = resolveErrorNames(node, tsResolver)
82
84
 
83
85
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -85,13 +87,17 @@ export function Query({
85
87
  const returnType = `UseQueryResult<${'TData'}, ${TError}> & { queryKey: TQueryKey }`
86
88
  const generics = [`TData = ${TData}`, `TQueryData = ${TData}`, `TQueryKey extends QueryKey = ${queryKeyTypeName}`]
87
89
 
88
- const queryKeyParamsNode = QueryKey.getParams(node, { pathParamsType, paramsCasing, resolver: tsResolver })
90
+ const queryKeyParamsNode = buildQueryKeyParams(node, { pathParamsType, paramsCasing, resolver: tsResolver })
89
91
  const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? ''
92
+ const enabledNames = getEnabledParamNames(queryKeyParamsNode)
90
93
 
91
94
  const queryOptionsParamsNode = getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver })
92
95
  const queryOptionsParamsCall = callPrinter.print(queryOptionsParamsNode) ?? ''
93
96
 
94
- const paramsNode = getParams(node, { paramsType, paramsCasing, pathParamsType, dataReturnType, resolver: tsResolver })
97
+ const paramsNode = markParamsOptional(
98
+ buildQueryParamsNode(node, { paramsType, paramsCasing, pathParamsType, dataReturnType, resolver: tsResolver }),
99
+ enabledNames,
100
+ )
95
101
  const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
96
102
 
97
103
  return (
@@ -117,5 +123,3 @@ export function Query({
117
123
  </File.Source>
118
124
  )
119
125
  }
120
-
121
- Query.getParams = getParams
@@ -3,9 +3,9 @@ import type { ResolverTs } from '@kubb/plugin-ts'
3
3
  import { functionPrinter } from '@kubb/plugin-ts'
4
4
  import { File, Function } from '@kubb/renderer-jsx'
5
5
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
6
+ import { getEnabledParamNames, injectNonNullAssertions, markParamsOptional } from '@internals/tanstack-query'
6
7
  import type { PluginReactQuery } from '../types.ts'
7
- import { resolveErrorNames } from '../utils.ts'
8
- import { QueryKey } from './QueryKey.tsx'
8
+ import { buildQueryKeyParams, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
9
9
 
10
10
  type Props = {
11
11
  name: string
@@ -17,6 +17,7 @@ type Props = {
17
17
  paramsType: PluginReactQuery['resolvedOptions']['paramsType']
18
18
  pathParamsType: PluginReactQuery['resolvedOptions']['pathParamsType']
19
19
  dataReturnType: PluginReactQuery['resolvedOptions']['client']['dataReturnType']
20
+ suspense?: boolean
20
21
  }
21
22
 
22
23
  const declarationPrinter = functionPrinter({ mode: 'declaration' })
@@ -32,7 +33,7 @@ export function getQueryOptionsParams(
32
33
  },
33
34
  ): ast.FunctionParametersNode {
34
35
  const { paramsType, paramsCasing, pathParamsType, resolver } = options
35
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
36
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null
36
37
 
37
38
  return ast.createOperationParams(node, {
38
39
  paramsType,
@@ -52,26 +53,6 @@ export function getQueryOptionsParams(
52
53
  })
53
54
  }
54
55
 
55
- export function buildEnabledCheck(paramsNode: ast.FunctionParametersNode): string {
56
- const required: string[] = []
57
- for (const param of paramsNode.params) {
58
- if ('kind' in param && (param as ast.ParameterGroupNode).kind === 'ParameterGroup') {
59
- const group = param as ast.ParameterGroupNode
60
- for (const child of group.properties) {
61
- if (!child.optional && child.default === undefined) {
62
- required.push(child.name)
63
- }
64
- }
65
- } else {
66
- const fp = param as ast.FunctionParameterNode
67
- if (!fp.optional && fp.default === undefined) {
68
- required.push(fp.name)
69
- }
70
- }
71
- }
72
- return required.join(' && ')
73
- }
74
-
75
56
  export function QueryOptions({
76
57
  name,
77
58
  clientName,
@@ -82,23 +63,27 @@ export function QueryOptions({
82
63
  paramsType,
83
64
  pathParamsType,
84
65
  queryKeyName,
66
+ suspense,
85
67
  }: Props): KubbReactNode {
86
- const responseName = tsResolver.resolveResponseName(node)
68
+ const successNames = resolveSuccessNames(node, tsResolver)
69
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
87
70
  const errorNames = resolveErrorNames(node, tsResolver)
88
71
 
89
72
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
90
73
  const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
91
74
 
92
- const paramsNode = getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver })
93
- const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
94
- const rawParamsCall = callPrinter.print(paramsNode) ?? ''
95
- const clientCallStr = rawParamsCall.replace(/\bconfig\b(?=[^,]*$)/, '{ ...config, signal: config.signal ?? signal }')
96
-
97
- const queryKeyParamsNode = QueryKey.getParams(node, { pathParamsType, paramsCasing, resolver: tsResolver })
75
+ const queryKeyParamsNode = buildQueryKeyParams(node, { pathParamsType, paramsCasing, resolver: tsResolver })
98
76
  const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? ''
99
77
 
100
- const enabledSource = buildEnabledCheck(queryKeyParamsNode)
101
- const enabledText = enabledSource ? `enabled: !!(${enabledSource}),` : ''
78
+ const enabledNames = getEnabledParamNames(queryKeyParamsNode)
79
+ // Suspense queries can't be disabled, so their params stay required.
80
+ const optionalNames = suspense ? [] : enabledNames
81
+ const enabledText = suspense ? '' : enabledNames.length ? `enabled: !!(${enabledNames.join(' && ')}),` : ''
82
+
83
+ const paramsNode = markParamsOptional(getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver }), optionalNames)
84
+ const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
85
+ const rawParamsCall = callPrinter.print(paramsNode) ?? ''
86
+ const clientCallStr = injectNonNullAssertions(rawParamsCall.replace(/\bconfig\b(?=[^,]*$)/, '{ ...config, signal: config.signal ?? signal }'), optionalNames)
102
87
 
103
88
  return (
104
89
  <File.Source name={name} isExportable isIndexable>
@@ -117,5 +102,3 @@ export function QueryOptions({
117
102
  </File.Source>
118
103
  )
119
104
  }
120
-
121
- QueryOptions.getParams = getQueryOptionsParams