@kubb/plugin-react-query 5.0.0-beta.22 → 5.0.0-beta.27

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 (46) hide show
  1. package/dist/{components-BTwB9V-K.js → components-CDmg-RPi.js} +82 -63
  2. package/dist/components-CDmg-RPi.js.map +1 -0
  3. package/dist/{components-D4SQmw7N.cjs → components-MPBTffPl.cjs} +82 -63
  4. package/dist/components-MPBTffPl.cjs.map +1 -0
  5. package/dist/components.cjs +1 -1
  6. package/dist/components.d.ts +1 -1
  7. package/dist/components.js +1 -1
  8. package/dist/{generators-BZ_sX2lp.js → generators-C-cwJah8.js} +97 -57
  9. package/dist/generators-C-cwJah8.js.map +1 -0
  10. package/dist/{generators-C4bV7v9Z.cjs → generators-DozEZKwy.cjs} +97 -57
  11. package/dist/generators-DozEZKwy.cjs.map +1 -0
  12. package/dist/generators.cjs +1 -1
  13. package/dist/generators.d.ts +41 -1
  14. package/dist/generators.js +1 -1
  15. package/dist/index.cjs +51 -11
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.ts +30 -1
  18. package/dist/index.js +51 -11
  19. package/dist/index.js.map +1 -1
  20. package/dist/{types-DG_OxOym.d.ts → types-DiZPLTXl.d.ts} +92 -55
  21. package/extension.yaml +904 -325
  22. package/package.json +7 -7
  23. package/src/components/InfiniteQuery.tsx +7 -6
  24. package/src/components/InfiniteQueryOptions.tsx +11 -10
  25. package/src/components/Mutation.tsx +7 -5
  26. package/src/components/MutationOptions.tsx +4 -3
  27. package/src/components/Query.tsx +6 -4
  28. package/src/components/QueryOptions.tsx +4 -3
  29. package/src/components/SuspenseInfiniteQuery.tsx +7 -6
  30. package/src/components/SuspenseInfiniteQueryOptions.tsx +11 -10
  31. package/src/components/SuspenseQuery.tsx +6 -4
  32. package/src/generators/customHookOptionsFileGenerator.tsx +7 -1
  33. package/src/generators/hookOptionsGenerator.tsx +14 -8
  34. package/src/generators/infiniteQueryGenerator.tsx +19 -10
  35. package/src/generators/mutationGenerator.tsx +17 -9
  36. package/src/generators/queryGenerator.tsx +17 -9
  37. package/src/generators/suspenseInfiniteQueryGenerator.tsx +19 -10
  38. package/src/generators/suspenseQueryGenerator.tsx +18 -9
  39. package/src/plugin.ts +34 -5
  40. package/src/resolvers/resolverReactQuery.ts +15 -4
  41. package/src/types.ts +89 -52
  42. package/src/utils.ts +8 -1
  43. package/dist/components-BTwB9V-K.js.map +0 -1
  44. package/dist/components-D4SQmw7N.cjs.map +0 -1
  45. package/dist/generators-BZ_sX2lp.js.map +0 -1
  46. package/dist/generators-C4bV7v9Z.cjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-react-query",
3
- "version": "5.0.0-beta.22",
3
+ "version": "5.0.0-beta.27",
4
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
6
  "code-generation",
@@ -68,12 +68,12 @@
68
68
  "registry": "https://registry.npmjs.org/"
69
69
  },
70
70
  "dependencies": {
71
- "@kubb/core": "5.0.0-beta.22",
72
- "@kubb/renderer-jsx": "5.0.0-beta.22",
71
+ "@kubb/core": "5.0.0-beta.27",
72
+ "@kubb/renderer-jsx": "5.0.0-beta.27",
73
73
  "remeda": "^2.34.1",
74
- "@kubb/plugin-client": "5.0.0-beta.22",
75
- "@kubb/plugin-ts": "5.0.0-beta.22",
76
- "@kubb/plugin-zod": "5.0.0-beta.22"
74
+ "@kubb/plugin-client": "5.0.0-beta.27",
75
+ "@kubb/plugin-ts": "5.0.0-beta.27",
76
+ "@kubb/plugin-zod": "5.0.0-beta.27"
77
77
  },
78
78
  "devDependencies": {
79
79
  "@internals/shared": "0.0.0",
@@ -81,7 +81,7 @@
81
81
  "@internals/utils": "0.0.0"
82
82
  },
83
83
  "peerDependencies": {
84
- "@kubb/renderer-jsx": "5.0.0-beta.22"
84
+ "@kubb/renderer-jsx": "5.0.0-beta.27"
85
85
  },
86
86
  "size-limit": [
87
87
  {
@@ -5,7 +5,7 @@ import { functionPrinter } from '@kubb/plugin-ts'
5
5
  import { File, Function } from '@kubb/renderer-jsx'
6
6
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
7
7
  import type { Infinite, PluginReactQuery } from '../types.ts'
8
- import { buildQueryKeyParams, getComments, resolveErrorNames } from '../utils.ts'
8
+ import { buildQueryKeyParams, getComments, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
9
9
  import { getQueryOptionsParams } from './QueryOptions.tsx'
10
10
 
11
11
  type Props = {
@@ -39,7 +39,7 @@ function buildInfiniteQueryParamsNode(
39
39
  },
40
40
  ): ast.FunctionParametersNode {
41
41
  const { paramsType, paramsCasing, pathParamsType, resolver, pageParamGeneric } = options
42
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
42
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null
43
43
 
44
44
  const optionsParam = ast.createFunctionParameter({
45
45
  name: 'options',
@@ -77,7 +77,8 @@ export function InfiniteQuery({
77
77
  queryParam,
78
78
  customOptions,
79
79
  }: Props): KubbReactNode {
80
- const responseName = tsResolver.resolveResponseName(node)
80
+ const successNames = resolveSuccessNames(node, tsResolver)
81
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
81
82
  const errorNames = resolveErrorNames(node, tsResolver)
82
83
 
83
84
  const responseType = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -104,11 +105,11 @@ export function InfiniteQuery({
104
105
  ? (() => {
105
106
  const groupName = tsResolver.resolveQueryParamsName(node, rawQueryParams[0]!)
106
107
  const individualName = tsResolver.resolveParamName(node, rawQueryParams[0]!)
107
- return groupName !== individualName ? groupName : undefined
108
+ return groupName !== individualName ? groupName : null
108
109
  })()
109
- : undefined
110
+ : null
110
111
 
111
- const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : undefined
112
+ const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null
112
113
  const pageParamType = queryParamType ? (isInitialPageParamDefined ? `NonNullable<${queryParamType}>` : queryParamType) : fallbackPageParamType
113
114
 
114
115
  const returnType = 'UseInfiniteQueryResult<TData, TError> & { queryKey: TQueryKey }'
@@ -6,7 +6,7 @@ import { functionPrinter } from '@kubb/plugin-ts'
6
6
  import { File, Function } from '@kubb/renderer-jsx'
7
7
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
8
8
  import type { Infinite, PluginReactQuery } from '../types.ts'
9
- import { buildQueryKeyParams, resolveErrorNames } from '../utils.ts'
9
+ import { buildQueryKeyParams, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
10
10
  import { buildEnabledCheck } from '@internals/tanstack-query'
11
11
  import { getQueryOptionsParams } from './QueryOptions.tsx'
12
12
 
@@ -46,7 +46,8 @@ export function InfiniteQueryOptions({
46
46
  queryParam,
47
47
  queryKeyName,
48
48
  }: Props): KubbReactNode {
49
- const responseName = tsResolver.resolveResponseName(node)
49
+ const successNames = resolveSuccessNames(node, tsResolver)
50
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
50
51
  const queryFnDataType = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
51
52
  const errorNames = resolveErrorNames(node, tsResolver)
52
53
  const errorType = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
@@ -72,11 +73,11 @@ export function InfiniteQueryOptions({
72
73
  ? (() => {
73
74
  const groupName = tsResolver.resolveQueryParamsName(node, rawQueryParams[0]!)
74
75
  const individualName = tsResolver.resolveParamName(node, rawQueryParams[0]!)
75
- return groupName !== individualName ? groupName : undefined
76
+ return groupName !== individualName ? groupName : null
76
77
  })()
77
- : undefined
78
+ : null
78
79
 
79
- const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : undefined
80
+ const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null
80
81
  const pageParamType = queryParamType ? (isInitialPageParamDefined ? `NonNullable<${queryParamType}>` : queryParamType) : fallbackPageParamType
81
82
 
82
83
  const paramsNode = getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver })
@@ -90,15 +91,15 @@ export function InfiniteQueryOptions({
90
91
  const enabledSource = buildEnabledCheck(queryKeyParamsNode)
91
92
  const enabledText = enabledSource ? `enabled: !!(${enabledSource}),` : ''
92
93
 
93
- const hasNewParams = nextParam !== undefined || previousParam !== undefined
94
+ const hasNewParams = nextParam != null || previousParam != null
94
95
 
95
96
  const [getNextPageParamExpr, getPreviousPageParamExpr] = (() => {
96
97
  if (hasNewParams) {
97
- const nextAccessor = nextParam ? getNestedAccessor(nextParam, 'lastPage') : undefined
98
- const prevAccessor = previousParam ? getNestedAccessor(previousParam, 'firstPage') : undefined
98
+ const nextAccessor = nextParam ? getNestedAccessor(nextParam, 'lastPage') : null
99
+ const prevAccessor = previousParam ? getNestedAccessor(previousParam, 'firstPage') : null
99
100
  return [
100
- nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : undefined,
101
- prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : undefined,
101
+ nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : null,
102
+ prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : null,
102
103
  ] as const
103
104
  }
104
105
  if (cursorParam) {
@@ -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 { buildRequestConfigType, getComments, resolveErrorNames } from '../utils.ts'
7
+ import { buildRequestConfigType, getComments, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
8
8
  import { buildMutationConfigParamsNode } from './MutationOptions.tsx'
9
9
 
10
10
  type Props = {
@@ -47,7 +47,8 @@ function buildMutationParamsNode(
47
47
  },
48
48
  ): ast.FunctionParametersNode {
49
49
  const { paramsCasing, dataReturnType, resolver } = options
50
- const responseName = resolver.resolveResponseName(node)
50
+ const successNames = resolveSuccessNames(node, resolver)
51
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : resolver.resolveResponseName(node)
51
52
  const errorNames = resolveErrorNames(node, resolver)
52
53
 
53
54
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -58,7 +59,7 @@ function buildMutationParamsNode(
58
59
  resolver,
59
60
  })
60
61
  const TRequest = mutationArgParamsNode.params.length > 0 ? (declarationPrinter.print(mutationArgParamsNode) ?? '') : ''
61
- const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'void', 'TContext'].join(', ')
62
+ const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'undefined', 'TContext'].join(', ')
62
63
 
63
64
  return ast.createFunctionParameters({
64
65
  params: [
@@ -78,7 +79,8 @@ function buildMutationParamsNode(
78
79
  }
79
80
 
80
81
  export function Mutation({ name, mutationOptionsName, paramsCasing, dataReturnType, node, tsResolver, mutationKeyName, customOptions }: Props): KubbReactNode {
81
- const responseName = tsResolver.resolveResponseName(node)
82
+ const successNames = resolveSuccessNames(node, tsResolver)
83
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
82
84
  const errorNames = resolveErrorNames(node, tsResolver)
83
85
 
84
86
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -89,7 +91,7 @@ export function Mutation({ name, mutationOptionsName, paramsCasing, dataReturnTy
89
91
  resolver: tsResolver,
90
92
  })
91
93
  const TRequest = mutationArgParamsNode.params.length > 0 ? (declarationPrinter.print(mutationArgParamsNode) ?? '') : ''
92
- const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'void', 'TContext'].join(', ')
94
+ const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'undefined', 'TContext'].join(', ')
93
95
  const returnType = `UseMutationResult<${generics}>`
94
96
 
95
97
  const mutationOptionsConfigNode = buildMutationConfigParamsNode(node, tsResolver)
@@ -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 { buildRequestConfigType, resolveErrorNames } from '../utils.ts'
7
+ import { buildRequestConfigType, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
8
8
 
9
9
  type Props = {
10
10
  name: string
@@ -48,7 +48,8 @@ export function MutationOptions({
48
48
  pathParamsType,
49
49
  mutationKeyName,
50
50
  }: Props): KubbReactNode {
51
- const responseName = tsResolver.resolveResponseName(node)
51
+ const successNames = resolveSuccessNames(node, tsResolver)
52
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
52
53
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
53
54
  const errorNames = resolveErrorNames(node, tsResolver)
54
55
  const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
@@ -90,7 +91,7 @@ export function MutationOptions({
90
91
  <Function name={name} export params={paramsSignature} generics={['TContext = unknown']}>
91
92
  {`
92
93
  const mutationKey = ${mutationKeyName}()
93
- return mutationOptions<${TData}, ${TError}, ${TRequest ? `{${TRequest}}` : 'void'}, TContext>({
94
+ return mutationOptions<${TData}, ${TError}, ${TRequest ? `{${TRequest}}` : 'undefined'}, TContext>({
94
95
  mutationKey,
95
96
  mutationFn: async(${hasMutationParams ? `{ ${argKeysStr} }` : '_'}) => {
96
97
  return ${clientName}(${clientCallStr})
@@ -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 { buildQueryKeyParams, getComments, resolveErrorNames } from '../utils.ts'
7
+ import { buildQueryKeyParams, getComments, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
8
8
  import { getQueryOptionsParams } from './QueryOptions.tsx'
9
9
 
10
10
  type Props = {
@@ -35,8 +35,9 @@ function buildQueryParamsNode(
35
35
  },
36
36
  ): ast.FunctionParametersNode {
37
37
  const { paramsType, paramsCasing, pathParamsType, dataReturnType, resolver } = options
38
- const responseName = resolver.resolveResponseName(node)
39
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
38
+ const successNames = resolveSuccessNames(node, resolver)
39
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : resolver.resolveResponseName(node)
40
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null
40
41
  const errorNames = resolveErrorNames(node, resolver)
41
42
 
42
43
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -76,7 +77,8 @@ export function Query({
76
77
  tsResolver,
77
78
  customOptions,
78
79
  }: Props): KubbReactNode {
79
- const responseName = tsResolver.resolveResponseName(node)
80
+ const successNames = resolveSuccessNames(node, tsResolver)
81
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
80
82
  const errorNames = resolveErrorNames(node, tsResolver)
81
83
 
82
84
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -5,7 +5,7 @@ import { File, Function } from '@kubb/renderer-jsx'
5
5
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
6
6
  import { buildEnabledCheck } from '@internals/tanstack-query'
7
7
  import type { PluginReactQuery } from '../types.ts'
8
- import { buildQueryKeyParams, resolveErrorNames } from '../utils.ts'
8
+ import { buildQueryKeyParams, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
9
9
 
10
10
  type Props = {
11
11
  name: string
@@ -32,7 +32,7 @@ export function getQueryOptionsParams(
32
32
  },
33
33
  ): ast.FunctionParametersNode {
34
34
  const { paramsType, paramsCasing, pathParamsType, resolver } = options
35
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
35
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null
36
36
 
37
37
  return ast.createOperationParams(node, {
38
38
  paramsType,
@@ -63,7 +63,8 @@ export function QueryOptions({
63
63
  pathParamsType,
64
64
  queryKeyName,
65
65
  }: Props): KubbReactNode {
66
- const responseName = tsResolver.resolveResponseName(node)
66
+ const successNames = resolveSuccessNames(node, tsResolver)
67
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
67
68
  const errorNames = resolveErrorNames(node, tsResolver)
68
69
 
69
70
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -5,7 +5,7 @@ import { functionPrinter } from '@kubb/plugin-ts'
5
5
  import { File, Function } from '@kubb/renderer-jsx'
6
6
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
7
7
  import type { Infinite, PluginReactQuery } from '../types.ts'
8
- import { buildQueryKeyParams, getComments, resolveErrorNames } from '../utils.ts'
8
+ import { buildQueryKeyParams, getComments, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
9
9
  import { getQueryOptionsParams } from './QueryOptions.tsx'
10
10
 
11
11
  type Props = {
@@ -39,7 +39,7 @@ function buildSuspenseInfiniteQueryParamsNode(
39
39
  },
40
40
  ): ast.FunctionParametersNode {
41
41
  const { paramsType, paramsCasing, pathParamsType, resolver, pageParamGeneric } = options
42
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
42
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null
43
43
 
44
44
  const optionsParam = ast.createFunctionParameter({
45
45
  name: 'options',
@@ -77,7 +77,8 @@ export function SuspenseInfiniteQuery({
77
77
  initialPageParam,
78
78
  queryParam,
79
79
  }: Props): KubbReactNode {
80
- const responseName = tsResolver.resolveResponseName(node)
80
+ const successNames = resolveSuccessNames(node, tsResolver)
81
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
81
82
  const errorNames = resolveErrorNames(node, tsResolver)
82
83
 
83
84
  const responseType = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -104,11 +105,11 @@ export function SuspenseInfiniteQuery({
104
105
  ? (() => {
105
106
  const groupName = tsResolver.resolveQueryParamsName(node, rawQueryParams[0]!)
106
107
  const individualName = tsResolver.resolveParamName(node, rawQueryParams[0]!)
107
- return groupName !== individualName ? groupName : undefined
108
+ return groupName !== individualName ? groupName : null
108
109
  })()
109
- : undefined
110
+ : null
110
111
 
111
- const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : undefined
112
+ const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null
112
113
  const pageParamType = queryParamType ? (isInitialPageParamDefined ? `NonNullable<${queryParamType}>` : queryParamType) : fallbackPageParamType
113
114
 
114
115
  const returnType = 'UseSuspenseInfiniteQueryResult<TData, TError> & { queryKey: TQueryKey }'
@@ -6,7 +6,7 @@ import { functionPrinter } from '@kubb/plugin-ts'
6
6
  import { File, Function } from '@kubb/renderer-jsx'
7
7
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
8
8
  import type { Infinite, PluginReactQuery } from '../types.ts'
9
- import { buildQueryKeyParams, resolveErrorNames } from '../utils.ts'
9
+ import { buildQueryKeyParams, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
10
10
  import { buildEnabledCheck } from '@internals/tanstack-query'
11
11
  import { getQueryOptionsParams } from './QueryOptions.tsx'
12
12
 
@@ -46,7 +46,8 @@ export function SuspenseInfiniteQueryOptions({
46
46
  queryParam,
47
47
  queryKeyName,
48
48
  }: Props): KubbReactNode {
49
- const responseName = tsResolver.resolveResponseName(node)
49
+ const successNames = resolveSuccessNames(node, tsResolver)
50
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
50
51
  const queryFnDataType = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
51
52
  const errorNames = resolveErrorNames(node, tsResolver)
52
53
  const errorType = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
@@ -72,11 +73,11 @@ export function SuspenseInfiniteQueryOptions({
72
73
  ? (() => {
73
74
  const groupName = tsResolver.resolveQueryParamsName(node, rawQueryParams[0]!)
74
75
  const individualName = tsResolver.resolveParamName(node, rawQueryParams[0]!)
75
- return groupName !== individualName ? groupName : undefined
76
+ return groupName !== individualName ? groupName : null
76
77
  })()
77
- : undefined
78
+ : null
78
79
 
79
- const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : undefined
80
+ const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null
80
81
  const pageParamType = queryParamType ? (isInitialPageParamDefined ? `NonNullable<${queryParamType}>` : queryParamType) : fallbackPageParamType
81
82
 
82
83
  const paramsNode = getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver })
@@ -90,15 +91,15 @@ export function SuspenseInfiniteQueryOptions({
90
91
  const enabledSource = buildEnabledCheck(queryKeyParamsNode)
91
92
  const enabledText = enabledSource ? `enabled: !!(${enabledSource}),` : ''
92
93
 
93
- const hasNewParams = nextParam !== undefined || previousParam !== undefined
94
+ const hasNewParams = nextParam != null || previousParam != null
94
95
 
95
96
  const [getNextPageParamExpr, getPreviousPageParamExpr] = (() => {
96
97
  if (hasNewParams) {
97
- const nextAccessor = nextParam ? getNestedAccessor(nextParam, 'lastPage') : undefined
98
- const prevAccessor = previousParam ? getNestedAccessor(previousParam, 'firstPage') : undefined
98
+ const nextAccessor = nextParam ? getNestedAccessor(nextParam, 'lastPage') : null
99
+ const prevAccessor = previousParam ? getNestedAccessor(previousParam, 'firstPage') : null
99
100
  return [
100
- nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : undefined,
101
- prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : undefined,
101
+ nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : null,
102
+ prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : null,
102
103
  ] as const
103
104
  }
104
105
  if (cursorParam) {
@@ -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 { buildQueryKeyParams, getComments, resolveErrorNames } from '../utils.ts'
7
+ import { buildQueryKeyParams, getComments, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
8
8
  import { getQueryOptionsParams } from './QueryOptions.tsx'
9
9
 
10
10
  type Props = {
@@ -35,8 +35,9 @@ function buildSuspenseQueryParamsNode(
35
35
  },
36
36
  ): ast.FunctionParametersNode {
37
37
  const { paramsType, paramsCasing, pathParamsType, dataReturnType, resolver } = options
38
- const responseName = resolver.resolveResponseName(node)
39
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
38
+ const successNames = resolveSuccessNames(node, resolver)
39
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : resolver.resolveResponseName(node)
40
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null
40
41
  const errorNames = resolveErrorNames(node, resolver)
41
42
 
42
43
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -76,7 +77,8 @@ export function SuspenseQuery({
76
77
  tsResolver,
77
78
  customOptions,
78
79
  }: Props): KubbReactNode {
79
- const responseName = tsResolver.resolveResponseName(node)
80
+ const successNames = resolveSuccessNames(node, tsResolver)
81
+ const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
80
82
  const errorNames = resolveErrorNames(node, tsResolver)
81
83
 
82
84
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -5,6 +5,12 @@ import { defineGenerator } from '@kubb/core'
5
5
  import { File, Function, jsxRendererSync } from '@kubb/renderer-jsx'
6
6
  import type { PluginReactQuery } from '../types'
7
7
 
8
+ /**
9
+ * Scaffolds the user-editable `useCustomHookOptions` file when
10
+ * `pluginReactQuery({ customOptions: { ... } })` is configured. The file is
11
+ * only created when it does not already exist, so user edits persist across
12
+ * regeneration.
13
+ */
8
14
  export const customHookOptionsFileGenerator = defineGenerator<PluginReactQuery>({
9
15
  name: 'react-query-custom-hook-options-file',
10
16
  renderer: jsxRendererSync,
@@ -27,7 +33,7 @@ export const customHookOptionsFileGenerator = defineGenerator<PluginReactQuery>(
27
33
  const hookName = resolver.resolveQueryName(firstNode)
28
34
  const hookFile = resolver.resolveFile(
29
35
  { name: hookName, extname: '.ts', tag: firstNode.tags[0] ?? 'default', path: firstNode.path },
30
- { root, output, group },
36
+ { root, output, group: group ?? undefined },
31
37
  )
32
38
  hookFilePath = hookFile.path
33
39
  } else {
@@ -9,6 +9,12 @@ import { resolveOperationOverrides } from '../utils.ts'
9
9
  type QueryOption = PluginReactQuery['resolvedOptions']['query']
10
10
  type MutationOption = PluginReactQuery['resolvedOptions']['mutation']
11
11
 
12
+ /**
13
+ * Emits the `HookOptions` type used by `customOptions`. Enabled when
14
+ * `pluginReactQuery({ customOptions: { ... } })`. The generated type lists
15
+ * every hook keyed by name so user-supplied options stay in sync with the
16
+ * generated hooks at compile time.
17
+ */
12
18
  export const hookOptionsGenerator = defineGenerator<PluginReactQuery>({
13
19
  name: 'react-query-hook-options',
14
20
  renderer: jsxRendererSync,
@@ -19,14 +25,14 @@ export const hookOptionsGenerator = defineGenerator<PluginReactQuery>({
19
25
  if (!customOptions) return null
20
26
 
21
27
  const name = resolver.resolveHookOptionsName()
22
- const resolvedFile = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group })
28
+ const resolvedFile = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group: group ?? undefined })
23
29
  const hookOptionsFile = {
24
30
  ...resolvedFile,
25
31
  baseName: `${name}.ts` as const,
26
32
  path: resolvedFile.path.replace(/[^/\\]+\.ts$/, `${name}.ts`),
27
33
  }
28
34
 
29
- const imports: KubbReactNode[] = []
35
+ const imports: Array<KubbReactNode> = []
30
36
  const hookOptions: Record<string, string> = {}
31
37
 
32
38
  for (const node of nodes) {
@@ -34,7 +40,7 @@ export const hookOptionsGenerator = defineGenerator<PluginReactQuery>({
34
40
  const nodeQuery: QueryOption = 'query' in opOverrides ? (opOverrides.query as QueryOption) : query
35
41
  const nodeMutation: MutationOption = 'mutation' in opOverrides ? (opOverrides.mutation as MutationOption) : mutation
36
42
  const nodeInfinite = 'infinite' in opOverrides ? opOverrides.infinite : infinite
37
- const nodeInfiniteOptions = nodeInfinite && typeof nodeInfinite === 'object' ? nodeInfinite : undefined
43
+ const nodeInfiniteOptions = nodeInfinite && typeof nodeInfinite === 'object' ? nodeInfinite : null
38
44
 
39
45
  // query: false means "still a query but skip the useQuery hook"
40
46
  const isQueryOp =
@@ -53,7 +59,7 @@ export const hookOptionsGenerator = defineGenerator<PluginReactQuery>({
53
59
  const queryHookName = resolver.resolveQueryName(node)
54
60
  const queryHookFile = resolver.resolveFile(
55
61
  { name: queryHookName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
56
- { root, output, group },
62
+ { root, output, group: group ?? undefined },
57
63
  )
58
64
  imports.push(<File.Import name={[queryOptionsName]} root={hookOptionsFile.path} path={queryHookFile.path} />)
59
65
  hookOptions[queryHookName] = `Partial<ReturnType<typeof ${queryOptionsName}>>`
@@ -63,7 +69,7 @@ export const hookOptionsGenerator = defineGenerator<PluginReactQuery>({
63
69
  const suspenseHookName = resolver.resolveSuspenseQueryName(node)
64
70
  const suspenseHookFile = resolver.resolveFile(
65
71
  { name: suspenseHookName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
66
- { root, output, group },
72
+ { root, output, group: group ?? undefined },
67
73
  )
68
74
  imports.push(<File.Import name={[suspenseOptionsName]} root={hookOptionsFile.path} path={suspenseHookFile.path} />)
69
75
  hookOptions[suspenseHookName] = `Partial<ReturnType<typeof ${suspenseOptionsName}>>`
@@ -80,7 +86,7 @@ export const hookOptionsGenerator = defineGenerator<PluginReactQuery>({
80
86
  const infiniteHookName = resolver.resolveInfiniteQueryName(node)
81
87
  const infiniteHookFile = resolver.resolveFile(
82
88
  { name: infiniteHookName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
83
- { root, output, group },
89
+ { root, output, group: group ?? undefined },
84
90
  )
85
91
  imports.push(<File.Import name={[infiniteOptionsName]} root={hookOptionsFile.path} path={infiniteHookFile.path} />)
86
92
  hookOptions[infiniteHookName] = `Partial<ReturnType<typeof ${infiniteOptionsName}>>`
@@ -90,7 +96,7 @@ export const hookOptionsGenerator = defineGenerator<PluginReactQuery>({
90
96
  const suspenseInfiniteHookName = resolver.resolveSuspenseInfiniteQueryName(node)
91
97
  const suspenseInfiniteHookFile = resolver.resolveFile(
92
98
  { name: suspenseInfiniteHookName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
93
- { root, output, group },
99
+ { root, output, group: group ?? undefined },
94
100
  )
95
101
  imports.push(<File.Import name={[suspenseInfiniteOptionsName]} root={hookOptionsFile.path} path={suspenseInfiniteHookFile.path} />)
96
102
  hookOptions[suspenseInfiniteHookName] = `Partial<ReturnType<typeof ${suspenseInfiniteOptionsName}>>`
@@ -104,7 +110,7 @@ export const hookOptionsGenerator = defineGenerator<PluginReactQuery>({
104
110
  const mutationHookName = resolver.resolveMutationName(node)
105
111
  const mutationHookFile = resolver.resolveFile(
106
112
  { name: mutationHookName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
107
- { root, output, group },
113
+ { root, output, group: group ?? undefined },
108
114
  )
109
115
  imports.push(<File.Import name={[mutationOptionsName]} root={hookOptionsFile.path} path={mutationHookFile.path} />)
110
116
  hookOptions[mutationHookName] = `Partial<ReturnType<typeof ${mutationOptionsName}>>`
@@ -10,6 +10,12 @@ import { difference } from 'remeda'
10
10
  import { InfiniteQuery, InfiniteQueryOptions, QueryKey } from '../components'
11
11
  import type { PluginReactQuery } from '../types'
12
12
 
13
+ /**
14
+ * Built-in generator for `useInfiniteQuery` hooks. Enabled when
15
+ * `pluginReactQuery({ infinite: { ... } })`. Emits one `useFooInfiniteQuery`
16
+ * hook per query operation, wiring the configured `nextParam` /
17
+ * `previousParam` paths into TanStack Query's cursor-based pagination.
18
+ */
13
19
  export const infiniteQueryGenerator = defineGenerator<PluginReactQuery>({
14
20
  name: 'react-infinite-query',
15
21
  renderer: jsxRendererSync,
@@ -26,7 +32,7 @@ export const infiniteQueryGenerator = defineGenerator<PluginReactQuery>({
26
32
  mutation !== false &&
27
33
  !isQuery &&
28
34
  difference(mutation ? mutation.methods : [], query ? query.methods : []).some((method) => node.method.toLowerCase() === method.toLowerCase())
29
- const infiniteOptions = infinite && typeof infinite === 'object' ? infinite : undefined
35
+ const infiniteOptions = infinite && typeof infinite === 'object' ? infinite : null
30
36
 
31
37
  if (!isQuery || isMutation || !infiniteOptions) return null
32
38
 
@@ -48,10 +54,13 @@ export const infiniteQueryGenerator = defineGenerator<PluginReactQuery>({
48
54
  const clientBaseName = resolver.resolveInfiniteClientName(node)
49
55
 
50
56
  const meta = {
51
- file: resolver.resolveFile({ name: queryName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
57
+ file: resolver.resolveFile(
58
+ { name: queryName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
59
+ { root, output, group: group ?? undefined },
60
+ ),
52
61
  fileTs: tsResolver.resolveFile(
53
62
  { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
54
- { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group },
63
+ { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group ?? undefined },
55
64
  ),
56
65
  }
57
66
 
@@ -61,20 +70,20 @@ export const infiniteQueryGenerator = defineGenerator<PluginReactQuery>({
61
70
  order: 'body-response-first',
62
71
  })
63
72
 
64
- const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
65
- const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : undefined
73
+ const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : null
74
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
66
75
  const fileZod = zodResolver
67
76
  ? zodResolver.resolveFile(
68
77
  { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
69
- { root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group },
78
+ { root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group ?? undefined },
70
79
  )
71
- : undefined
80
+ : null
72
81
  const zodSchemaNames = resolveZodSchemaNames(node, zodResolver)
73
82
 
74
83
  const clientPlugin = driver.getPlugin(pluginClientName)
75
84
  const hasClientPlugin = clientPlugin?.name === pluginClientName
76
85
  const shouldUseClientPlugin = hasClientPlugin && clientOptions.clientType !== 'class'
77
- const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : undefined
86
+ const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : null
78
87
 
79
88
  const clientFile = shouldUseClientPlugin
80
89
  ? clientResolver?.resolveFile(
@@ -82,10 +91,10 @@ export const infiniteQueryGenerator = defineGenerator<PluginReactQuery>({
82
91
  {
83
92
  root,
84
93
  output: clientPlugin?.options?.output ?? output,
85
- group: clientPlugin?.options?.group,
94
+ group: clientPlugin?.options?.group ?? undefined,
86
95
  },
87
96
  )
88
- : undefined
97
+ : null
89
98
 
90
99
  const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientBaseName) : clientBaseName
91
100