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

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 (45) hide show
  1. package/dist/{components-D4SQmw7N.cjs → components-C1_zAoAO.cjs} +51 -51
  2. package/dist/components-C1_zAoAO.cjs.map +1 -0
  3. package/dist/{components-BTwB9V-K.js → components-C91DnOOV.js} +51 -51
  4. package/dist/components-C91DnOOV.js.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-9srJC_zb.js} +97 -57
  9. package/dist/generators-9srJC_zb.js.map +1 -0
  10. package/dist/{generators-C4bV7v9Z.cjs → generators-DS3JH1hR.cjs} +97 -57
  11. package/dist/generators-DS3JH1hR.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 +4 -4
  24. package/src/components/InfiniteQueryOptions.tsx +8 -8
  25. package/src/components/Mutation.tsx +2 -2
  26. package/src/components/MutationOptions.tsx +1 -1
  27. package/src/components/Query.tsx +1 -1
  28. package/src/components/QueryOptions.tsx +1 -1
  29. package/src/components/SuspenseInfiniteQuery.tsx +4 -4
  30. package/src/components/SuspenseInfiniteQueryOptions.tsx +8 -8
  31. package/src/components/SuspenseQuery.tsx +1 -1
  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/dist/components-BTwB9V-K.js.map +0 -1
  43. package/dist/components-D4SQmw7N.cjs.map +0 -1
  44. package/dist/generators-BZ_sX2lp.js.map +0 -1
  45. 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.25",
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.25",
72
+ "@kubb/renderer-jsx": "5.0.0-beta.25",
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.25",
75
+ "@kubb/plugin-ts": "5.0.0-beta.25",
76
+ "@kubb/plugin-zod": "5.0.0-beta.25"
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.25"
85
85
  },
86
86
  "size-limit": [
87
87
  {
@@ -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',
@@ -104,11 +104,11 @@ export function InfiniteQuery({
104
104
  ? (() => {
105
105
  const groupName = tsResolver.resolveQueryParamsName(node, rawQueryParams[0]!)
106
106
  const individualName = tsResolver.resolveParamName(node, rawQueryParams[0]!)
107
- return groupName !== individualName ? groupName : undefined
107
+ return groupName !== individualName ? groupName : null
108
108
  })()
109
- : undefined
109
+ : null
110
110
 
111
- const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : undefined
111
+ const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null
112
112
  const pageParamType = queryParamType ? (isInitialPageParamDefined ? `NonNullable<${queryParamType}>` : queryParamType) : fallbackPageParamType
113
113
 
114
114
  const returnType = 'UseInfiniteQueryResult<TData, TError> & { queryKey: TQueryKey }'
@@ -72,11 +72,11 @@ export function InfiniteQueryOptions({
72
72
  ? (() => {
73
73
  const groupName = tsResolver.resolveQueryParamsName(node, rawQueryParams[0]!)
74
74
  const individualName = tsResolver.resolveParamName(node, rawQueryParams[0]!)
75
- return groupName !== individualName ? groupName : undefined
75
+ return groupName !== individualName ? groupName : null
76
76
  })()
77
- : undefined
77
+ : null
78
78
 
79
- const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : undefined
79
+ const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null
80
80
  const pageParamType = queryParamType ? (isInitialPageParamDefined ? `NonNullable<${queryParamType}>` : queryParamType) : fallbackPageParamType
81
81
 
82
82
  const paramsNode = getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver })
@@ -90,15 +90,15 @@ export function InfiniteQueryOptions({
90
90
  const enabledSource = buildEnabledCheck(queryKeyParamsNode)
91
91
  const enabledText = enabledSource ? `enabled: !!(${enabledSource}),` : ''
92
92
 
93
- const hasNewParams = nextParam !== undefined || previousParam !== undefined
93
+ const hasNewParams = nextParam != null || previousParam != null
94
94
 
95
95
  const [getNextPageParamExpr, getPreviousPageParamExpr] = (() => {
96
96
  if (hasNewParams) {
97
- const nextAccessor = nextParam ? getNestedAccessor(nextParam, 'lastPage') : undefined
98
- const prevAccessor = previousParam ? getNestedAccessor(previousParam, 'firstPage') : undefined
97
+ const nextAccessor = nextParam ? getNestedAccessor(nextParam, 'lastPage') : null
98
+ const prevAccessor = previousParam ? getNestedAccessor(previousParam, 'firstPage') : null
99
99
  return [
100
- nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : undefined,
101
- prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : undefined,
100
+ nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : null,
101
+ prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : null,
102
102
  ] as const
103
103
  }
104
104
  if (cursorParam) {
@@ -58,7 +58,7 @@ function buildMutationParamsNode(
58
58
  resolver,
59
59
  })
60
60
  const TRequest = mutationArgParamsNode.params.length > 0 ? (declarationPrinter.print(mutationArgParamsNode) ?? '') : ''
61
- const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'void', 'TContext'].join(', ')
61
+ const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'undefined', 'TContext'].join(', ')
62
62
 
63
63
  return ast.createFunctionParameters({
64
64
  params: [
@@ -89,7 +89,7 @@ export function Mutation({ name, mutationOptionsName, paramsCasing, dataReturnTy
89
89
  resolver: tsResolver,
90
90
  })
91
91
  const TRequest = mutationArgParamsNode.params.length > 0 ? (declarationPrinter.print(mutationArgParamsNode) ?? '') : ''
92
- const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'void', 'TContext'].join(', ')
92
+ const generics = [TData, TError, TRequest ? `{${TRequest}}` : 'undefined', 'TContext'].join(', ')
93
93
  const returnType = `UseMutationResult<${generics}>`
94
94
 
95
95
  const mutationOptionsConfigNode = buildMutationConfigParamsNode(node, tsResolver)
@@ -90,7 +90,7 @@ export function MutationOptions({
90
90
  <Function name={name} export params={paramsSignature} generics={['TContext = unknown']}>
91
91
  {`
92
92
  const mutationKey = ${mutationKeyName}()
93
- return mutationOptions<${TData}, ${TError}, ${TRequest ? `{${TRequest}}` : 'void'}, TContext>({
93
+ return mutationOptions<${TData}, ${TError}, ${TRequest ? `{${TRequest}}` : 'undefined'}, TContext>({
94
94
  mutationKey,
95
95
  mutationFn: async(${hasMutationParams ? `{ ${argKeysStr} }` : '_'}) => {
96
96
  return ${clientName}(${clientCallStr})
@@ -36,7 +36,7 @@ function buildQueryParamsNode(
36
36
  ): ast.FunctionParametersNode {
37
37
  const { paramsType, paramsCasing, pathParamsType, dataReturnType, resolver } = options
38
38
  const responseName = resolver.resolveResponseName(node)
39
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
39
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null
40
40
  const errorNames = resolveErrorNames(node, resolver)
41
41
 
42
42
  const TData = dataReturnType === 'data' ? responseName : `ResponseConfig<${responseName}>`
@@ -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,
@@ -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',
@@ -104,11 +104,11 @@ export function SuspenseInfiniteQuery({
104
104
  ? (() => {
105
105
  const groupName = tsResolver.resolveQueryParamsName(node, rawQueryParams[0]!)
106
106
  const individualName = tsResolver.resolveParamName(node, rawQueryParams[0]!)
107
- return groupName !== individualName ? groupName : undefined
107
+ return groupName !== individualName ? groupName : null
108
108
  })()
109
- : undefined
109
+ : null
110
110
 
111
- const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : undefined
111
+ const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null
112
112
  const pageParamType = queryParamType ? (isInitialPageParamDefined ? `NonNullable<${queryParamType}>` : queryParamType) : fallbackPageParamType
113
113
 
114
114
  const returnType = 'UseSuspenseInfiniteQueryResult<TData, TError> & { queryKey: TQueryKey }'
@@ -72,11 +72,11 @@ export function SuspenseInfiniteQueryOptions({
72
72
  ? (() => {
73
73
  const groupName = tsResolver.resolveQueryParamsName(node, rawQueryParams[0]!)
74
74
  const individualName = tsResolver.resolveParamName(node, rawQueryParams[0]!)
75
- return groupName !== individualName ? groupName : undefined
75
+ return groupName !== individualName ? groupName : null
76
76
  })()
77
- : undefined
77
+ : null
78
78
 
79
- const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : undefined
79
+ const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null
80
80
  const pageParamType = queryParamType ? (isInitialPageParamDefined ? `NonNullable<${queryParamType}>` : queryParamType) : fallbackPageParamType
81
81
 
82
82
  const paramsNode = getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver })
@@ -90,15 +90,15 @@ export function SuspenseInfiniteQueryOptions({
90
90
  const enabledSource = buildEnabledCheck(queryKeyParamsNode)
91
91
  const enabledText = enabledSource ? `enabled: !!(${enabledSource}),` : ''
92
92
 
93
- const hasNewParams = nextParam !== undefined || previousParam !== undefined
93
+ const hasNewParams = nextParam != null || previousParam != null
94
94
 
95
95
  const [getNextPageParamExpr, getPreviousPageParamExpr] = (() => {
96
96
  if (hasNewParams) {
97
- const nextAccessor = nextParam ? getNestedAccessor(nextParam, 'lastPage') : undefined
98
- const prevAccessor = previousParam ? getNestedAccessor(previousParam, 'firstPage') : undefined
97
+ const nextAccessor = nextParam ? getNestedAccessor(nextParam, 'lastPage') : null
98
+ const prevAccessor = previousParam ? getNestedAccessor(previousParam, 'firstPage') : null
99
99
  return [
100
- nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : undefined,
101
- prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : undefined,
100
+ nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : null,
101
+ prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : null,
102
102
  ] as const
103
103
  }
104
104
  if (cursorParam) {
@@ -36,7 +36,7 @@ function buildSuspenseQueryParamsNode(
36
36
  ): ast.FunctionParametersNode {
37
37
  const { paramsType, paramsCasing, pathParamsType, dataReturnType, resolver } = options
38
38
  const responseName = resolver.resolveResponseName(node)
39
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined
39
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null
40
40
  const errorNames = resolveErrorNames(node, resolver)
41
41
 
42
42
  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
 
@@ -10,6 +10,11 @@ import { difference } from 'remeda'
10
10
  import { Mutation, MutationKey, MutationOptions } from '../components'
11
11
  import type { PluginReactQuery } from '../types'
12
12
 
13
+ /**
14
+ * Built-in generator for `useMutation` hooks. Emits one `useFooMutation` hook
15
+ * per POST/PUT/DELETE operation (configurable via `mutation.methods`) plus
16
+ * the matching `fooMutationKey` / `fooMutationOptions` helpers.
17
+ */
13
18
  export const mutationGenerator = defineGenerator<PluginReactQuery>({
14
19
  name: 'react-query-mutation',
15
20
  renderer: jsxRendererSync,
@@ -38,29 +43,32 @@ export const mutationGenerator = defineGenerator<PluginReactQuery>({
38
43
  const clientName = resolver.resolveClientName(node)
39
44
 
40
45
  const meta = {
41
- file: resolver.resolveFile({ name: mutationHookName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
46
+ file: resolver.resolveFile(
47
+ { name: mutationHookName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
48
+ { root, output, group: group ?? undefined },
49
+ ),
42
50
  fileTs: tsResolver.resolveFile(
43
51
  { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
44
- { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group },
52
+ { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group ?? undefined },
45
53
  ),
46
54
  }
47
55
 
48
56
  const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing, order: 'body-response-first' })
49
57
 
50
- const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
51
- const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : undefined
58
+ const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : null
59
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
52
60
  const fileZod = zodResolver
53
61
  ? zodResolver.resolveFile(
54
62
  { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
55
- { root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group },
63
+ { root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group ?? undefined },
56
64
  )
57
- : undefined
65
+ : null
58
66
  const zodSchemaNames = resolveZodSchemaNames(node, zodResolver)
59
67
 
60
68
  const clientPlugin = driver.getPlugin(pluginClientName)
61
69
  const hasClientPlugin = clientPlugin?.name === pluginClientName
62
70
  const shouldUseClientPlugin = hasClientPlugin && clientOptions.clientType !== 'class'
63
- const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : undefined
71
+ const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : null
64
72
 
65
73
  const clientFile = shouldUseClientPlugin
66
74
  ? clientResolver?.resolveFile(
@@ -68,10 +76,10 @@ export const mutationGenerator = defineGenerator<PluginReactQuery>({
68
76
  {
69
77
  root,
70
78
  output: clientPlugin?.options?.output ?? output,
71
- group: clientPlugin?.options?.group,
79
+ group: clientPlugin?.options?.group ?? undefined,
72
80
  },
73
81
  )
74
- : undefined
82
+ : null
75
83
 
76
84
  const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientName) : clientName
77
85
 
@@ -10,6 +10,11 @@ import { difference } from 'remeda'
10
10
  import { Query, QueryKey, QueryOptions } from '../components'
11
11
  import type { PluginReactQuery } from '../types'
12
12
 
13
+ /**
14
+ * Built-in generator for `useQuery` hooks. Emits one `useFooQuery` hook per
15
+ * GET operation (configurable via `query.methods`) plus the matching
16
+ * `fooQueryKey` / `fooQueryOptions` helpers.
17
+ */
13
18
  export const queryGenerator = defineGenerator<PluginReactQuery>({
14
19
  name: 'react-query',
15
20
  renderer: jsxRendererSync,
@@ -39,10 +44,13 @@ export const queryGenerator = defineGenerator<PluginReactQuery>({
39
44
  const clientName = resolver.resolveClientName(node)
40
45
 
41
46
  const meta = {
42
- file: resolver.resolveFile({ name: queryName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
47
+ file: resolver.resolveFile(
48
+ { name: queryName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
49
+ { root, output, group: group ?? undefined },
50
+ ),
43
51
  fileTs: tsResolver.resolveFile(
44
52
  { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
45
- { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group },
53
+ { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group ?? undefined },
46
54
  ),
47
55
  }
48
56
 
@@ -52,20 +60,20 @@ export const queryGenerator = defineGenerator<PluginReactQuery>({
52
60
  order: 'body-response-first',
53
61
  })
54
62
 
55
- const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
56
- const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : undefined
63
+ const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : null
64
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
57
65
  const fileZod = zodResolver
58
66
  ? zodResolver.resolveFile(
59
67
  { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
60
- { root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group },
68
+ { root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group ?? undefined },
61
69
  )
62
- : undefined
70
+ : null
63
71
  const zodSchemaNames = resolveZodSchemaNames(node, zodResolver)
64
72
 
65
73
  const clientPlugin = driver.getPlugin(pluginClientName)
66
74
  const hasClientPlugin = clientPlugin?.name === pluginClientName
67
75
  const shouldUseClientPlugin = hasClientPlugin && clientOptions.clientType !== 'class'
68
- const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : undefined
76
+ const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : null
69
77
 
70
78
  const clientFile = shouldUseClientPlugin
71
79
  ? clientResolver?.resolveFile(
@@ -73,10 +81,10 @@ export const queryGenerator = defineGenerator<PluginReactQuery>({
73
81
  {
74
82
  root,
75
83
  output: clientPlugin?.options?.output ?? output,
76
- group: clientPlugin?.options?.group,
84
+ group: clientPlugin?.options?.group ?? undefined,
77
85
  },
78
86
  )
79
- : undefined
87
+ : null
80
88
 
81
89
  const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientName) : clientName
82
90
 
@@ -10,6 +10,12 @@ import { difference } from 'remeda'
10
10
  import { QueryKey, SuspenseInfiniteQuery, SuspenseInfiniteQueryOptions } from '../components'
11
11
  import type { PluginReactQuery } from '../types'
12
12
 
13
+ /**
14
+ * Built-in generator for `useSuspenseInfiniteQuery` hooks. Enabled when both
15
+ * `suspense` and `infinite` are configured. Combines suspense semantics with
16
+ * cursor-based pagination — handlers throw promises while loading and pull
17
+ * additional pages on demand.
18
+ */
13
19
  export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>({
14
20
  name: 'react-suspense-infinite-query',
15
21
  renderer: jsxRendererSync,
@@ -40,7 +46,7 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
40
46
  !isQuery &&
41
47
  difference(mutation ? mutation.methods : [], query ? query.methods : []).some((method) => node.method.toLowerCase() === method.toLowerCase())
42
48
  const isSuspense = !!suspense
43
- const infiniteOptions = infinite && typeof infinite === 'object' ? infinite : undefined
49
+ const infiniteOptions = infinite && typeof infinite === 'object' ? infinite : null
44
50
 
45
51
  if (!isQuery || isMutation || !isSuspense || !infiniteOptions) return null
46
52
 
@@ -61,29 +67,32 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
61
67
  const clientBaseName = resolver.resolveSuspenseInfiniteClientName(node)
62
68
 
63
69
  const meta = {
64
- file: resolver.resolveFile({ name: queryName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
70
+ file: resolver.resolveFile(
71
+ { name: queryName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
72
+ { root, output, group: group ?? undefined },
73
+ ),
65
74
  fileTs: tsResolver.resolveFile(
66
75
  { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
67
- { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group },
76
+ { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group ?? undefined },
68
77
  ),
69
78
  }
70
79
 
71
80
  const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing, order: 'body-response-first' })
72
81
 
73
- const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
74
- const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : undefined
82
+ const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : null
83
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
75
84
  const fileZod = zodResolver
76
85
  ? zodResolver.resolveFile(
77
86
  { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
78
- { root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group },
87
+ { root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group ?? undefined },
79
88
  )
80
- : undefined
89
+ : null
81
90
  const zodSchemaNames = resolveZodSchemaNames(node, zodResolver)
82
91
 
83
92
  const clientPlugin = driver.getPlugin(pluginClientName)
84
93
  const hasClientPlugin = clientPlugin?.name === pluginClientName
85
94
  const shouldUseClientPlugin = hasClientPlugin && clientOptions.clientType !== 'class'
86
- const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : undefined
95
+ const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : null
87
96
 
88
97
  const clientFile = shouldUseClientPlugin
89
98
  ? clientResolver?.resolveFile(
@@ -91,10 +100,10 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
91
100
  {
92
101
  root,
93
102
  output: clientPlugin?.options?.output ?? output,
94
- group: clientPlugin?.options?.group,
103
+ group: clientPlugin?.options?.group ?? undefined,
95
104
  },
96
105
  )
97
- : undefined
106
+ : null
98
107
 
99
108
  const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientBaseName) : clientBaseName
100
109