@kubb/plugin-react-query 5.0.0-beta.4 → 5.0.0-beta.56
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.
- package/README.md +38 -91
- package/dist/{components-DTGLu4UV.js → components-DL0Cai7l.js} +570 -514
- package/dist/components-DL0Cai7l.js.map +1 -0
- package/dist/{components-dAKJEn9b.cjs → components-yMQOuFmI.cjs} +600 -514
- package/dist/components-yMQOuFmI.cjs.map +1 -0
- package/dist/components.cjs +1 -1
- package/dist/components.d.ts +5 -77
- package/dist/components.js +1 -1
- package/dist/{generators-C_fbcjpG.js → generators-BG-Vcvfg.js} +444 -597
- package/dist/generators-BG-Vcvfg.js.map +1 -0
- package/dist/{generators-CWEQsdO9.cjs → generators-zGKP8yII.cjs} +442 -595
- package/dist/generators-zGKP8yII.cjs.map +1 -0
- package/dist/generators.cjs +1 -1
- package/dist/generators.d.ts +49 -10
- package/dist/generators.js +1 -1
- package/dist/index.cjs +201 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +32 -4
- package/dist/index.js +203 -30
- package/dist/index.js.map +1 -1
- package/dist/types-X7D0NSvJ.d.ts +396 -0
- package/package.json +18 -27
- package/src/components/InfiniteQuery.tsx +27 -17
- package/src/components/InfiniteQueryOptions.tsx +60 -81
- package/src/components/Mutation.tsx +39 -20
- package/src/components/MutationOptions.tsx +15 -14
- package/src/components/Query.tsx +18 -15
- package/src/components/QueryOptions.tsx +20 -56
- package/src/components/SuspenseInfiniteQuery.tsx +22 -17
- package/src/components/SuspenseInfiniteQueryOptions.tsx +51 -76
- package/src/components/SuspenseQuery.tsx +13 -15
- package/src/generators/customHookOptionsFileGenerator.tsx +16 -12
- package/src/generators/hookOptionsGenerator.tsx +42 -49
- package/src/generators/infiniteQueryGenerator.tsx +55 -80
- package/src/generators/mutationGenerator.tsx +54 -66
- package/src/generators/queryGenerator.tsx +52 -65
- package/src/generators/suspenseInfiniteQueryGenerator.tsx +50 -67
- package/src/generators/suspenseQueryGenerator.tsx +54 -78
- package/src/plugin.ts +47 -33
- package/src/resolvers/resolverReactQuery.ts +104 -8
- package/src/types.ts +202 -68
- package/src/utils.ts +11 -33
- package/dist/components-DTGLu4UV.js.map +0 -1
- package/dist/components-dAKJEn9b.cjs.map +0 -1
- package/dist/generators-CWEQsdO9.cjs.map +0 -1
- package/dist/generators-C_fbcjpG.js.map +0 -1
- package/dist/types-DfaFRSBf.d.ts +0 -284
- package/extension.yaml +0 -938
- /package/dist/{chunk--u3MIqq1.js → chunk-C0LytTxp.js} +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
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 { buildQueryKeyParams, buildStatusUnionType, getComments, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
|
|
9
9
|
import { getQueryOptionsParams } from './QueryOptions.tsx'
|
|
10
10
|
|
|
11
11
|
type Props = {
|
|
@@ -27,7 +27,7 @@ type Props = {
|
|
|
27
27
|
const declarationPrinter = functionPrinter({ mode: 'declaration' })
|
|
28
28
|
const callPrinter = functionPrinter({ mode: 'call' })
|
|
29
29
|
|
|
30
|
-
function
|
|
30
|
+
function buildSuspenseInfiniteQueryParamsNode(
|
|
31
31
|
node: ast.OperationNode,
|
|
32
32
|
options: {
|
|
33
33
|
paramsType: PluginReactQuery['resolvedOptions']['paramsType']
|
|
@@ -39,7 +39,7 @@ function getParams(
|
|
|
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) :
|
|
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,10 +77,11 @@ export function SuspenseInfiniteQuery({
|
|
|
77
77
|
initialPageParam,
|
|
78
78
|
queryParam,
|
|
79
79
|
}: Props): KubbReactNode {
|
|
80
|
-
const
|
|
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
|
-
const responseType = dataReturnType === 'data' ? responseName :
|
|
84
|
+
const responseType = dataReturnType === 'data' ? responseName : buildStatusUnionType(node, tsResolver)
|
|
84
85
|
const errorType = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
|
|
85
86
|
|
|
86
87
|
const isInitialPageParamDefined = initialPageParam !== undefined && initialPageParam !== null
|
|
@@ -98,17 +99,17 @@ export function SuspenseInfiniteQuery({
|
|
|
98
99
|
? 'boolean'
|
|
99
100
|
: 'unknown'
|
|
100
101
|
|
|
101
|
-
const rawQueryParams = node
|
|
102
|
+
const rawQueryParams = getOperationParameters(node).query
|
|
102
103
|
const queryParamsTypeName =
|
|
103
104
|
rawQueryParams.length > 0
|
|
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 :
|
|
108
|
+
return groupName !== individualName ? groupName : null
|
|
108
109
|
})()
|
|
109
|
-
:
|
|
110
|
+
: null
|
|
110
111
|
|
|
111
|
-
const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` :
|
|
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 }'
|
|
@@ -120,13 +121,20 @@ export function SuspenseInfiniteQuery({
|
|
|
120
121
|
`TPageParam = ${pageParamType}`,
|
|
121
122
|
]
|
|
122
123
|
|
|
123
|
-
const queryKeyParamsNode =
|
|
124
|
+
const queryKeyParamsNode = buildQueryKeyParams(node, { pathParamsType, paramsCasing, resolver: tsResolver })
|
|
124
125
|
const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? ''
|
|
125
126
|
|
|
126
127
|
const queryOptionsParamsNode = getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver })
|
|
127
128
|
const queryOptionsParamsCall = callPrinter.print(queryOptionsParamsNode) ?? ''
|
|
128
129
|
|
|
129
|
-
const paramsNode =
|
|
130
|
+
const paramsNode = buildSuspenseInfiniteQueryParamsNode(node, {
|
|
131
|
+
paramsType,
|
|
132
|
+
paramsCasing,
|
|
133
|
+
pathParamsType,
|
|
134
|
+
dataReturnType,
|
|
135
|
+
resolver: tsResolver,
|
|
136
|
+
pageParamGeneric: 'TPageParam',
|
|
137
|
+
})
|
|
130
138
|
const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
|
|
131
139
|
|
|
132
140
|
return (
|
|
@@ -135,11 +143,10 @@ export function SuspenseInfiniteQuery({
|
|
|
135
143
|
{`
|
|
136
144
|
const { query: queryConfig = {}, client: config = {} } = options ?? {}
|
|
137
145
|
const { client: queryClient, ...resolvedOptions } = queryConfig
|
|
138
|
-
const queryKey = resolvedOptions?.queryKey ?? ${queryKeyName}(${queryKeyParamsCall})
|
|
139
|
-
${customOptions ? `const customOptions = ${customOptions.name}({ hookName: '${name}', operationId: '${node.operationId}' })` : ''}
|
|
146
|
+
const queryKey = resolvedOptions?.queryKey ?? ${queryKeyName}(${queryKeyParamsCall})${customOptions ? `\n const customOptions = ${customOptions.name}({ hookName: '${name}', operationId: '${node.operationId}' })` : ''}
|
|
140
147
|
|
|
141
148
|
const query = useSuspenseInfiniteQuery({
|
|
142
|
-
...${queryOptionsName}(${queryOptionsParamsCall}),${customOptions ? '\n...customOptions,' : ''}
|
|
149
|
+
...${queryOptionsName}(${queryOptionsParamsCall}),${customOptions ? '\n ...customOptions,' : ''}
|
|
143
150
|
...resolvedOptions,
|
|
144
151
|
queryKey,
|
|
145
152
|
} as unknown as UseSuspenseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>, queryClient) as ${returnType}
|
|
@@ -152,5 +159,3 @@ export function SuspenseInfiniteQuery({
|
|
|
152
159
|
</File.Source>
|
|
153
160
|
)
|
|
154
161
|
}
|
|
155
|
-
|
|
156
|
-
SuspenseInfiniteQuery.getParams = getParams
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getOperationParameters } from '@internals/shared'
|
|
2
|
+
import { getNestedAccessor } from '@kubb/ast/utils'
|
|
2
3
|
import type { ast } from '@kubb/core'
|
|
3
4
|
import type { ResolverTs } from '@kubb/plugin-ts'
|
|
4
5
|
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 {
|
|
10
|
-
import { buildEnabledCheck, getQueryOptionsParams } from './QueryOptions.tsx'
|
|
9
|
+
import { buildQueryKeyParams, buildStatusUnionType, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
|
|
10
|
+
import { getQueryOptionsParams } from './QueryOptions.tsx'
|
|
11
11
|
|
|
12
12
|
type Props = {
|
|
13
13
|
name: string
|
|
@@ -45,8 +45,9 @@ export function SuspenseInfiniteQueryOptions({
|
|
|
45
45
|
queryParam,
|
|
46
46
|
queryKeyName,
|
|
47
47
|
}: Props): KubbReactNode {
|
|
48
|
-
const
|
|
49
|
-
const
|
|
48
|
+
const successNames = resolveSuccessNames(node, tsResolver)
|
|
49
|
+
const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
|
|
50
|
+
const queryFnDataType = dataReturnType === 'data' ? responseName : buildStatusUnionType(node, tsResolver)
|
|
50
51
|
const errorNames = resolveErrorNames(node, tsResolver)
|
|
51
52
|
const errorType = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
|
|
52
53
|
|
|
@@ -65,17 +66,17 @@ export function SuspenseInfiniteQueryOptions({
|
|
|
65
66
|
? 'boolean'
|
|
66
67
|
: 'unknown'
|
|
67
68
|
|
|
68
|
-
const rawQueryParams = node
|
|
69
|
+
const rawQueryParams = getOperationParameters(node).query
|
|
69
70
|
const queryParamsTypeName =
|
|
70
71
|
rawQueryParams.length > 0
|
|
71
72
|
? (() => {
|
|
72
73
|
const groupName = tsResolver.resolveQueryParamsName(node, rawQueryParams[0]!)
|
|
73
74
|
const individualName = tsResolver.resolveParamName(node, rawQueryParams[0]!)
|
|
74
|
-
return groupName !== individualName ? groupName :
|
|
75
|
+
return groupName !== individualName ? groupName : null
|
|
75
76
|
})()
|
|
76
|
-
:
|
|
77
|
+
: null
|
|
77
78
|
|
|
78
|
-
const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` :
|
|
79
|
+
const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` : null
|
|
79
80
|
const pageParamType = queryParamType ? (isInitialPageParamDefined ? `NonNullable<${queryParamType}>` : queryParamType) : fallbackPageParamType
|
|
80
81
|
|
|
81
82
|
const paramsNode = getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver })
|
|
@@ -83,43 +84,30 @@ export function SuspenseInfiniteQueryOptions({
|
|
|
83
84
|
const rawParamsCall = callPrinter.print(paramsNode) ?? ''
|
|
84
85
|
const clientCallStr = rawParamsCall.replace(/\bconfig\b(?=[^,]*$)/, '{ ...config, signal: config.signal ?? signal }')
|
|
85
86
|
|
|
86
|
-
const queryKeyParamsNode =
|
|
87
|
+
const queryKeyParamsNode = buildQueryKeyParams(node, { pathParamsType, paramsCasing, resolver: tsResolver })
|
|
87
88
|
const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? ''
|
|
88
89
|
|
|
89
|
-
const
|
|
90
|
-
const enabledText = enabledSource ? `enabled: !!(${enabledSource}),` : ''
|
|
90
|
+
const hasNewParams = nextParam != null || previousParam != null
|
|
91
91
|
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (accessor) {
|
|
101
|
-
getNextPageParamExpr = `getNextPageParam: (lastPage) => ${accessor}`
|
|
102
|
-
}
|
|
92
|
+
const [getNextPageParamExpr, getPreviousPageParamExpr] = (() => {
|
|
93
|
+
if (hasNewParams) {
|
|
94
|
+
const nextAccessor = nextParam ? getNestedAccessor(nextParam, 'lastPage') : null
|
|
95
|
+
const prevAccessor = previousParam ? getNestedAccessor(previousParam, 'firstPage') : null
|
|
96
|
+
return [
|
|
97
|
+
nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : null,
|
|
98
|
+
prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : null,
|
|
99
|
+
] as const
|
|
103
100
|
}
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
if (accessor) {
|
|
107
|
-
getPreviousPageParamExpr = `getPreviousPageParam: (firstPage) => ${accessor}`
|
|
108
|
-
}
|
|
101
|
+
if (cursorParam) {
|
|
102
|
+
return [`getNextPageParam: (lastPage) => lastPage['${cursorParam}']`, `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`] as const
|
|
109
103
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
} else {
|
|
118
|
-
getNextPageParamExpr =
|
|
119
|
-
'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1'
|
|
120
|
-
}
|
|
121
|
-
getPreviousPageParamExpr = 'getPreviousPageParam: (_firstPage, _allPages, firstPageParam) => firstPageParam <= 1 ? undefined : firstPageParam - 1'
|
|
122
|
-
}
|
|
104
|
+
return [
|
|
105
|
+
dataReturnType === 'full'
|
|
106
|
+
? 'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage.data) && lastPage.data.length === 0 ? undefined : lastPageParam + 1'
|
|
107
|
+
: 'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1',
|
|
108
|
+
'getPreviousPageParam: (_firstPage, _allPages, firstPageParam) => firstPageParam <= 1 ? undefined : firstPageParam - 1',
|
|
109
|
+
] as const
|
|
110
|
+
})()
|
|
123
111
|
|
|
124
112
|
const queryOptionsArr = [
|
|
125
113
|
`initialPageParam: ${typeof initialPageParam === 'string' ? JSON.stringify(initialPageParam) : initialPageParam}`,
|
|
@@ -129,11 +117,10 @@ export function SuspenseInfiniteQueryOptions({
|
|
|
129
117
|
|
|
130
118
|
const infiniteOverrideParams =
|
|
131
119
|
queryParam && queryParamsTypeName
|
|
132
|
-
? `
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
} as ${queryParamsTypeName}`
|
|
120
|
+
? `params = {
|
|
121
|
+
...(params ?? {}),
|
|
122
|
+
['${queryParam}']: pageParam as unknown as ${queryParamsTypeName}['${queryParam}'],
|
|
123
|
+
} as ${queryParamsTypeName}`
|
|
137
124
|
: ''
|
|
138
125
|
|
|
139
126
|
if (infiniteOverrideParams) {
|
|
@@ -141,16 +128,15 @@ export function SuspenseInfiniteQueryOptions({
|
|
|
141
128
|
<File.Source name={name} isExportable isIndexable>
|
|
142
129
|
<Function name={name} export params={paramsSignature}>
|
|
143
130
|
{`
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
})
|
|
131
|
+
const queryKey = ${queryKeyName}(${queryKeyParamsCall})
|
|
132
|
+
return infiniteQueryOptions<${queryFnDataType}, ${errorType}, InfiniteData<${queryFnDataType}>, typeof queryKey, ${pageParamType}>({
|
|
133
|
+
queryKey,
|
|
134
|
+
queryFn: async ({ signal, pageParam }) => {
|
|
135
|
+
${infiniteOverrideParams}
|
|
136
|
+
return ${clientName}(${clientCallStr})
|
|
137
|
+
},
|
|
138
|
+
${queryOptionsArr.join(',\n ')}
|
|
139
|
+
})
|
|
154
140
|
`}
|
|
155
141
|
</Function>
|
|
156
142
|
</File.Source>
|
|
@@ -161,27 +147,16 @@ export function SuspenseInfiniteQueryOptions({
|
|
|
161
147
|
<File.Source name={name} isExportable isIndexable>
|
|
162
148
|
<Function name={name} export params={paramsSignature}>
|
|
163
149
|
{`
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
})
|
|
150
|
+
const queryKey = ${queryKeyName}(${queryKeyParamsCall})
|
|
151
|
+
return infiniteQueryOptions<${queryFnDataType}, ${errorType}, InfiniteData<${queryFnDataType}>, typeof queryKey, ${pageParamType}>({
|
|
152
|
+
queryKey,
|
|
153
|
+
queryFn: async ({ signal }) => {
|
|
154
|
+
return ${clientName}(${clientCallStr})
|
|
155
|
+
},
|
|
156
|
+
${queryOptionsArr.join(',\n ')}
|
|
157
|
+
})
|
|
173
158
|
`}
|
|
174
159
|
</Function>
|
|
175
160
|
</File.Source>
|
|
176
161
|
)
|
|
177
162
|
}
|
|
178
|
-
|
|
179
|
-
SuspenseInfiniteQueryOptions.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,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 { getComments, resolveErrorNames } from '../utils.ts'
|
|
8
|
-
import { QueryKey } from './QueryKey.tsx'
|
|
7
|
+
import { buildQueryKeyParams, buildStatusUnionType, getComments, resolveErrorNames, resolveSuccessNames } from '../utils.ts'
|
|
9
8
|
import { getQueryOptionsParams } from './QueryOptions.tsx'
|
|
10
9
|
|
|
11
10
|
type Props = {
|
|
@@ -25,7 +24,7 @@ type Props = {
|
|
|
25
24
|
const declarationPrinter = functionPrinter({ mode: 'declaration' })
|
|
26
25
|
const callPrinter = functionPrinter({ mode: 'call' })
|
|
27
26
|
|
|
28
|
-
function
|
|
27
|
+
function buildSuspenseQueryParamsNode(
|
|
29
28
|
node: ast.OperationNode,
|
|
30
29
|
options: {
|
|
31
30
|
paramsType: PluginReactQuery['resolvedOptions']['paramsType']
|
|
@@ -36,11 +35,12 @@ function getParams(
|
|
|
36
35
|
},
|
|
37
36
|
): ast.FunctionParametersNode {
|
|
38
37
|
const { paramsType, paramsCasing, pathParamsType, dataReturnType, resolver } = options
|
|
39
|
-
const
|
|
40
|
-
const
|
|
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
|
|
41
41
|
const errorNames = resolveErrorNames(node, resolver)
|
|
42
42
|
|
|
43
|
-
const TData = dataReturnType === 'data' ? responseName :
|
|
43
|
+
const TData = dataReturnType === 'data' ? responseName : buildStatusUnionType(node, resolver)
|
|
44
44
|
const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
|
|
45
45
|
|
|
46
46
|
const optionsParam = ast.createFunctionParameter({
|
|
@@ -77,21 +77,22 @@ export function SuspenseQuery({
|
|
|
77
77
|
tsResolver,
|
|
78
78
|
customOptions,
|
|
79
79
|
}: Props): KubbReactNode {
|
|
80
|
-
const
|
|
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
|
-
const TData = dataReturnType === 'data' ? responseName :
|
|
84
|
+
const TData = dataReturnType === 'data' ? responseName : buildStatusUnionType(node, tsResolver)
|
|
84
85
|
const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
|
|
85
86
|
const returnType = `UseSuspenseQueryResult<${'TData'}, ${TError}> & { queryKey: TQueryKey }`
|
|
86
87
|
const generics = [`TData = ${TData}`, `TQueryKey extends QueryKey = ${queryKeyTypeName}`]
|
|
87
88
|
|
|
88
|
-
const queryKeyParamsNode =
|
|
89
|
+
const queryKeyParamsNode = buildQueryKeyParams(node, { pathParamsType, paramsCasing, resolver: tsResolver })
|
|
89
90
|
const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? ''
|
|
90
91
|
|
|
91
92
|
const queryOptionsParamsNode = getQueryOptionsParams(node, { paramsType, paramsCasing, pathParamsType, resolver: tsResolver })
|
|
92
93
|
const queryOptionsParamsCall = callPrinter.print(queryOptionsParamsNode) ?? ''
|
|
93
94
|
|
|
94
|
-
const paramsNode =
|
|
95
|
+
const paramsNode = buildSuspenseQueryParamsNode(node, { paramsType, paramsCasing, pathParamsType, dataReturnType, resolver: tsResolver })
|
|
95
96
|
const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
|
|
96
97
|
|
|
97
98
|
return (
|
|
@@ -100,11 +101,10 @@ export function SuspenseQuery({
|
|
|
100
101
|
{`
|
|
101
102
|
const { query: queryConfig = {}, client: config = {} } = options ?? {}
|
|
102
103
|
const { client: queryClient, ...resolvedOptions } = queryConfig
|
|
103
|
-
const queryKey = resolvedOptions?.queryKey ?? ${queryKeyName}(${queryKeyParamsCall})
|
|
104
|
-
${customOptions ? `const customOptions = ${customOptions.name}({ hookName: '${name}', operationId: '${node.operationId}' })` : ''}
|
|
104
|
+
const queryKey = resolvedOptions?.queryKey ?? ${queryKeyName}(${queryKeyParamsCall})${customOptions ? `\n const customOptions = ${customOptions.name}({ hookName: '${name}', operationId: '${node.operationId}' })` : ''}
|
|
105
105
|
|
|
106
106
|
const query = useSuspenseQuery({
|
|
107
|
-
...${queryOptionsName}(${queryOptionsParamsCall}),${customOptions ? '\n...customOptions,' : ''}
|
|
107
|
+
...${queryOptionsName}(${queryOptionsParamsCall}),${customOptions ? '\n ...customOptions,' : ''}
|
|
108
108
|
...resolvedOptions,
|
|
109
109
|
queryKey,
|
|
110
110
|
} as unknown as UseSuspenseQueryOptions, queryClient) as ${returnType}
|
|
@@ -117,5 +117,3 @@ export function SuspenseQuery({
|
|
|
117
117
|
</File.Source>
|
|
118
118
|
)
|
|
119
119
|
}
|
|
120
|
-
|
|
121
|
-
SuspenseQuery.getParams = getParams
|
|
@@ -4,32 +4,36 @@ import path from 'node:path'
|
|
|
4
4
|
import { defineGenerator } from '@kubb/core'
|
|
5
5
|
import { File, Function, jsxRenderer } from '@kubb/renderer-jsx'
|
|
6
6
|
import type { PluginReactQuery } from '../types'
|
|
7
|
-
import { transformName } from '../utils.ts'
|
|
8
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
|
+
*/
|
|
9
14
|
export const customHookOptionsFileGenerator = defineGenerator<PluginReactQuery>({
|
|
10
15
|
name: 'react-query-custom-hook-options-file',
|
|
11
16
|
renderer: jsxRenderer,
|
|
12
17
|
operations(nodes, ctx) {
|
|
13
18
|
const { resolver, config, root } = ctx
|
|
14
|
-
const { output, customOptions, query, group
|
|
19
|
+
const { output, customOptions, query, group } = ctx.options
|
|
15
20
|
|
|
16
21
|
if (!customOptions) return null
|
|
17
22
|
|
|
18
23
|
const override = output.override ?? config.output.override ?? false
|
|
19
24
|
const { importPath, name } = customOptions
|
|
25
|
+
const hookOptionsName = resolver.resolveHookOptionsName()
|
|
26
|
+
const customHookOptionsName = resolver.resolveCustomHookOptionsName()
|
|
20
27
|
|
|
21
28
|
const reactQueryImportPath = query ? query.importPath : '@tanstack/react-query'
|
|
22
29
|
|
|
23
|
-
const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1)
|
|
24
|
-
|
|
25
30
|
let hookFilePath: string
|
|
26
31
|
const firstNode = nodes[0]
|
|
27
32
|
if (firstNode) {
|
|
28
|
-
const
|
|
29
|
-
const hookName = transformName(`use${capitalize(baseName)}`, 'function', transformers)
|
|
33
|
+
const hookName = resolver.resolveQueryName(firstNode)
|
|
30
34
|
const hookFile = resolver.resolveFile(
|
|
31
35
|
{ name: hookName, extname: '.ts', tag: firstNode.tags[0] ?? 'default', path: firstNode.path },
|
|
32
|
-
{ root, output, group },
|
|
36
|
+
{ root, output, group: group ?? undefined },
|
|
33
37
|
)
|
|
34
38
|
hookFilePath = hookFile.path
|
|
35
39
|
} else {
|
|
@@ -55,9 +59,9 @@ export const customHookOptionsFileGenerator = defineGenerator<PluginReactQuery>(
|
|
|
55
59
|
<File baseName={file.baseName} path={file.path}>
|
|
56
60
|
<File.Import name={['QueryClient']} path={reactQueryImportPath} isTypeOnly />
|
|
57
61
|
<File.Import name={['useQueryClient']} path={reactQueryImportPath} />
|
|
58
|
-
<File.Import name={[
|
|
62
|
+
<File.Import name={[hookOptionsName]} root={file.path} path={path.resolve(root, './index.ts')} />
|
|
59
63
|
<File.Source name={file.name} isExportable isIndexable>
|
|
60
|
-
<Function name=
|
|
64
|
+
<Function name={customHookOptionsName} params="{ queryClient }: { queryClient: QueryClient }" returnType={`Partial<${hookOptionsName}>`}>
|
|
61
65
|
{`return {
|
|
62
66
|
// TODO: Define custom hook options here
|
|
63
67
|
// Example:
|
|
@@ -70,13 +74,13 @@ export const customHookOptionsFileGenerator = defineGenerator<PluginReactQuery>(
|
|
|
70
74
|
</Function>
|
|
71
75
|
<Function
|
|
72
76
|
name={name}
|
|
73
|
-
generics=
|
|
77
|
+
generics={`T extends keyof ${hookOptionsName}`}
|
|
74
78
|
params="{ hookName, operationId }: { hookName: T, operationId: string }"
|
|
75
|
-
returnType=
|
|
79
|
+
returnType={`${hookOptionsName}[T]`}
|
|
76
80
|
export
|
|
77
81
|
>
|
|
78
82
|
{`const queryClient = useQueryClient()
|
|
79
|
-
const customOptions =
|
|
83
|
+
const customOptions = ${customHookOptionsName}({ queryClient })
|
|
80
84
|
return customOptions[hookName] ?? {}`}
|
|
81
85
|
</Function>
|
|
82
86
|
</File.Source>
|
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getOperationParameters, operationFileEntry } from '@internals/shared'
|
|
2
|
+
import { ast, defineGenerator } from '@kubb/core'
|
|
2
3
|
import { File, jsxRenderer, Type } from '@kubb/renderer-jsx'
|
|
3
4
|
import type { KubbReactNode } from '@kubb/renderer-jsx/types'
|
|
4
|
-
import { difference } from 'remeda'
|
|
5
5
|
import type { PluginReactQuery } from '../types'
|
|
6
|
-
import { resolveOperationOverrides
|
|
6
|
+
import { resolveOperationOverrides } from '../utils.ts'
|
|
7
7
|
|
|
8
8
|
type QueryOption = PluginReactQuery['resolvedOptions']['query']
|
|
9
9
|
type MutationOption = PluginReactQuery['resolvedOptions']['mutation']
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Emits the `HookOptions` type used by `customOptions`. Enabled when
|
|
13
|
+
* `pluginReactQuery({ customOptions: { ... } })`. The generated type lists
|
|
14
|
+
* every hook keyed by name so user-supplied options stay in sync with the
|
|
15
|
+
* generated hooks at compile time.
|
|
16
|
+
*/
|
|
11
17
|
export const hookOptionsGenerator = defineGenerator<PluginReactQuery>({
|
|
12
18
|
name: 'react-query-hook-options',
|
|
13
19
|
renderer: jsxRenderer,
|
|
14
20
|
operations(nodes, ctx) {
|
|
15
|
-
const { resolver, config, root
|
|
16
|
-
const { output, customOptions, query, mutation, suspense, infinite, group,
|
|
21
|
+
const { resolver, config, root } = ctx
|
|
22
|
+
const { output, customOptions, query, mutation, suspense, infinite, group, override } = ctx.options
|
|
17
23
|
|
|
18
24
|
if (!customOptions) return null
|
|
19
25
|
|
|
20
|
-
const
|
|
26
|
+
const name = resolver.resolveHookOptionsName()
|
|
27
|
+
const resolvedFile = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group: group ?? undefined })
|
|
21
28
|
const hookOptionsFile = {
|
|
22
29
|
...resolvedFile,
|
|
23
|
-
baseName:
|
|
24
|
-
path: resolvedFile.path.replace(/
|
|
30
|
+
baseName: `${name}.ts` as const,
|
|
31
|
+
path: resolvedFile.path.replace(/[^/\\]+\.ts$/, `${name}.ts`),
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const imports: KubbReactNode[] = []
|
|
34
|
+
const imports: Array<KubbReactNode> = []
|
|
30
35
|
const hookOptions: Record<string, string> = {}
|
|
31
36
|
|
|
32
37
|
for (const node of nodes) {
|
|
33
|
-
|
|
38
|
+
if (!ast.isHttpOperationNode(node)) continue
|
|
34
39
|
const opOverrides = resolveOperationOverrides(node, override)
|
|
35
40
|
const nodeQuery: QueryOption = 'query' in opOverrides ? (opOverrides.query as QueryOption) : query
|
|
36
41
|
const nodeMutation: MutationOption = 'mutation' in opOverrides ? (opOverrides.mutation as MutationOption) : mutation
|
|
37
42
|
const nodeInfinite = 'infinite' in opOverrides ? opOverrides.infinite : infinite
|
|
38
|
-
const nodeInfiniteOptions = nodeInfinite && typeof nodeInfinite === 'object' ? nodeInfinite :
|
|
43
|
+
const nodeInfiniteOptions = nodeInfinite && typeof nodeInfinite === 'object' ? nodeInfinite : null
|
|
39
44
|
|
|
40
45
|
// query: false means "still a query but skip the useQuery hook"
|
|
41
46
|
const isQueryOp =
|
|
42
47
|
nodeQuery === false
|
|
43
48
|
? !!query && query.methods.some((m) => node.method.toLowerCase() === m.toLowerCase())
|
|
44
49
|
: !!nodeQuery && nodeQuery.methods.some((m) => node.method.toLowerCase() === m.toLowerCase())
|
|
50
|
+
const nodeQueryMethods = new Set(nodeQuery ? nodeQuery.methods : [])
|
|
45
51
|
const isMutationOp =
|
|
46
52
|
nodeMutation !== false &&
|
|
47
53
|
!isQueryOp &&
|
|
48
|
-
|
|
54
|
+
(nodeMutation ? nodeMutation.methods : []).some((m) => !nodeQueryMethods.has(m) && node.method.toLowerCase() === m.toLowerCase())
|
|
49
55
|
const isSuspenseOp = !!suspense
|
|
50
56
|
const isInfiniteOp = !!nodeInfiniteOptions
|
|
51
57
|
|
|
52
58
|
if (isQueryOp) {
|
|
53
|
-
const queryOptionsName =
|
|
54
|
-
const queryHookName =
|
|
55
|
-
const queryHookFile = resolver.resolveFile(
|
|
56
|
-
{ name: queryHookName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
57
|
-
{ root, output, group },
|
|
58
|
-
)
|
|
59
|
+
const queryOptionsName = resolver.resolveQueryOptionsName(node)
|
|
60
|
+
const queryHookName = resolver.resolveQueryName(node)
|
|
61
|
+
const queryHookFile = resolver.resolveFile(operationFileEntry(node, queryHookName), { root, output, group: group ?? undefined })
|
|
59
62
|
imports.push(<File.Import name={[queryOptionsName]} root={hookOptionsFile.path} path={queryHookFile.path} />)
|
|
60
63
|
hookOptions[queryHookName] = `Partial<ReturnType<typeof ${queryOptionsName}>>`
|
|
61
64
|
|
|
62
65
|
if (isSuspenseOp) {
|
|
63
|
-
const suspenseOptionsName =
|
|
64
|
-
const suspenseHookName =
|
|
65
|
-
const suspenseHookFile = resolver.resolveFile(
|
|
66
|
-
{ name: suspenseHookName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
67
|
-
{ root, output, group },
|
|
68
|
-
)
|
|
66
|
+
const suspenseOptionsName = resolver.resolveSuspenseQueryOptionsName(node)
|
|
67
|
+
const suspenseHookName = resolver.resolveSuspenseQueryName(node)
|
|
68
|
+
const suspenseHookFile = resolver.resolveFile(operationFileEntry(node, suspenseHookName), { root, output, group: group ?? undefined })
|
|
69
69
|
imports.push(<File.Import name={[suspenseOptionsName]} root={hookOptionsFile.path} path={suspenseHookFile.path} />)
|
|
70
70
|
hookOptions[suspenseHookName] = `Partial<ReturnType<typeof ${suspenseOptionsName}>>`
|
|
71
71
|
}
|
|
@@ -73,26 +73,24 @@ export const hookOptionsGenerator = defineGenerator<PluginReactQuery>({
|
|
|
73
73
|
if (isInfiniteOp) {
|
|
74
74
|
// Validate queryParam
|
|
75
75
|
const normalizeKey = (key: string) => key.replace(/\?$/, '')
|
|
76
|
-
const queryParamKeys = node
|
|
76
|
+
const queryParamKeys = getOperationParameters(node).query.map((p) => p.name)
|
|
77
77
|
const hasQueryParam = nodeInfiniteOptions!.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === nodeInfiniteOptions!.queryParam) : false
|
|
78
78
|
|
|
79
79
|
if (hasQueryParam) {
|
|
80
|
-
const infiniteOptionsName =
|
|
81
|
-
const infiniteHookName =
|
|
82
|
-
const infiniteHookFile = resolver.resolveFile(
|
|
83
|
-
{ name: infiniteHookName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
84
|
-
{ root, output, group },
|
|
85
|
-
)
|
|
80
|
+
const infiniteOptionsName = resolver.resolveInfiniteQueryOptionsName(node)
|
|
81
|
+
const infiniteHookName = resolver.resolveInfiniteQueryName(node)
|
|
82
|
+
const infiniteHookFile = resolver.resolveFile(operationFileEntry(node, infiniteHookName), { root, output, group: group ?? undefined })
|
|
86
83
|
imports.push(<File.Import name={[infiniteOptionsName]} root={hookOptionsFile.path} path={infiniteHookFile.path} />)
|
|
87
84
|
hookOptions[infiniteHookName] = `Partial<ReturnType<typeof ${infiniteOptionsName}>>`
|
|
88
85
|
|
|
89
86
|
if (isSuspenseOp) {
|
|
90
|
-
const suspenseInfiniteOptionsName =
|
|
91
|
-
const suspenseInfiniteHookName =
|
|
92
|
-
const suspenseInfiniteHookFile = resolver.resolveFile(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
87
|
+
const suspenseInfiniteOptionsName = resolver.resolveSuspenseInfiniteQueryOptionsName(node)
|
|
88
|
+
const suspenseInfiniteHookName = resolver.resolveSuspenseInfiniteQueryName(node)
|
|
89
|
+
const suspenseInfiniteHookFile = resolver.resolveFile(operationFileEntry(node, suspenseInfiniteHookName), {
|
|
90
|
+
root,
|
|
91
|
+
output,
|
|
92
|
+
group: group ?? undefined,
|
|
93
|
+
})
|
|
96
94
|
imports.push(<File.Import name={[suspenseInfiniteOptionsName]} root={hookOptionsFile.path} path={suspenseInfiniteHookFile.path} />)
|
|
97
95
|
hookOptions[suspenseInfiniteHookName] = `Partial<ReturnType<typeof ${suspenseInfiniteOptionsName}>>`
|
|
98
96
|
}
|
|
@@ -101,26 +99,21 @@ export const hookOptionsGenerator = defineGenerator<PluginReactQuery>({
|
|
|
101
99
|
}
|
|
102
100
|
|
|
103
101
|
if (isMutationOp) {
|
|
104
|
-
const mutationOptionsName =
|
|
105
|
-
const mutationHookName =
|
|
106
|
-
const mutationHookFile = resolver.resolveFile(
|
|
107
|
-
{ name: mutationHookName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
108
|
-
{ root, output, group },
|
|
109
|
-
)
|
|
102
|
+
const mutationOptionsName = resolver.resolveMutationOptionsName(node)
|
|
103
|
+
const mutationHookName = resolver.resolveMutationName(node)
|
|
104
|
+
const mutationHookFile = resolver.resolveFile(operationFileEntry(node, mutationHookName), { root, output, group: group ?? undefined })
|
|
110
105
|
imports.push(<File.Import name={[mutationOptionsName]} root={hookOptionsFile.path} path={mutationHookFile.path} />)
|
|
111
106
|
hookOptions[mutationHookName] = `Partial<ReturnType<typeof ${mutationOptionsName}>>`
|
|
112
107
|
}
|
|
113
108
|
}
|
|
114
109
|
|
|
115
|
-
const name = 'HookOptions'
|
|
116
|
-
|
|
117
110
|
return (
|
|
118
111
|
<File
|
|
119
112
|
baseName={hookOptionsFile.baseName}
|
|
120
113
|
path={hookOptionsFile.path}
|
|
121
114
|
meta={hookOptionsFile.meta}
|
|
122
|
-
banner={resolver.resolveBanner(
|
|
123
|
-
footer={resolver.resolveFooter(
|
|
115
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: hookOptionsFile.path, baseName: hookOptionsFile.baseName } })}
|
|
116
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: hookOptionsFile.path, baseName: hookOptionsFile.baseName } })}
|
|
124
117
|
>
|
|
125
118
|
{imports}
|
|
126
119
|
<File.Source name={name} isExportable isIndexable isTypeOnly>
|