@kubb/plugin-react-query 5.0.0-beta.15 → 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.
- package/dist/{components-BZ3a2O0G.cjs → components-C1_zAoAO.cjs} +65 -84
- package/dist/components-C1_zAoAO.cjs.map +1 -0
- package/dist/{components-DJqIUiZW.js → components-C91DnOOV.js} +65 -84
- package/dist/components-C91DnOOV.js.map +1 -0
- package/dist/components.cjs +1 -1
- package/dist/components.d.ts +1 -1
- package/dist/components.js +1 -1
- package/dist/{generators-BQ_vEksc.js → generators-9srJC_zb.js} +115 -75
- package/dist/generators-9srJC_zb.js.map +1 -0
- package/dist/{generators-DSjer1xY.cjs → generators-DS3JH1hR.cjs} +115 -75
- package/dist/generators-DS3JH1hR.cjs.map +1 -0
- package/dist/generators.cjs +1 -1
- package/dist/generators.d.ts +41 -1
- package/dist/generators.js +1 -1
- package/dist/index.cjs +51 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +30 -1
- package/dist/index.js +51 -11
- package/dist/index.js.map +1 -1
- package/dist/{types-DG_OxOym.d.ts → types-DiZPLTXl.d.ts} +92 -55
- package/extension.yaml +904 -325
- package/package.json +7 -7
- package/src/components/InfiniteQuery.tsx +4 -4
- package/src/components/InfiniteQueryOptions.tsx +21 -31
- package/src/components/Mutation.tsx +2 -2
- package/src/components/MutationOptions.tsx +1 -1
- package/src/components/Query.tsx +1 -1
- package/src/components/QueryOptions.tsx +1 -1
- package/src/components/SuspenseInfiniteQuery.tsx +4 -4
- package/src/components/SuspenseInfiniteQueryOptions.tsx +21 -31
- package/src/components/SuspenseQuery.tsx +1 -1
- package/src/generators/customHookOptionsFileGenerator.tsx +7 -1
- package/src/generators/hookOptionsGenerator.tsx +17 -11
- package/src/generators/infiniteQueryGenerator.tsx +22 -13
- package/src/generators/mutationGenerator.tsx +20 -12
- package/src/generators/queryGenerator.tsx +20 -12
- package/src/generators/suspenseInfiniteQueryGenerator.tsx +22 -13
- package/src/generators/suspenseQueryGenerator.tsx +21 -12
- package/src/plugin.ts +34 -5
- package/src/resolvers/resolverReactQuery.ts +15 -4
- package/src/types.ts +89 -52
- package/dist/components-BZ3a2O0G.cjs.map +0 -1
- package/dist/components-DJqIUiZW.js.map +0 -1
- package/dist/generators-BQ_vEksc.js.map +0 -1
- package/dist/generators-DSjer1xY.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.
|
|
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.
|
|
72
|
-
"@kubb/renderer-jsx": "5.0.0-beta.
|
|
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.
|
|
75
|
-
"@kubb/plugin-ts": "5.0.0-beta.
|
|
76
|
-
"@kubb/plugin-zod": "5.0.0-beta.
|
|
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.
|
|
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) :
|
|
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 :
|
|
107
|
+
return groupName !== individualName ? groupName : null
|
|
108
108
|
})()
|
|
109
|
-
:
|
|
109
|
+
: null
|
|
110
110
|
|
|
111
|
-
const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` :
|
|
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 :
|
|
75
|
+
return groupName !== individualName ? groupName : null
|
|
76
76
|
})()
|
|
77
|
-
:
|
|
77
|
+
: null
|
|
78
78
|
|
|
79
|
-
const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` :
|
|
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,37 +90,27 @@ export function InfiniteQueryOptions({
|
|
|
90
90
|
const enabledSource = buildEnabledCheck(queryKeyParamsNode)
|
|
91
91
|
const enabledText = enabledSource ? `enabled: !!(${enabledSource}),` : ''
|
|
92
92
|
|
|
93
|
-
const hasNewParams = nextParam
|
|
93
|
+
const hasNewParams = nextParam != null || previousParam != null
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (previousParam) {
|
|
106
|
-
const accessor = getNestedAccessor(previousParam, 'firstPage')
|
|
107
|
-
if (accessor) {
|
|
108
|
-
getPreviousPageParamExpr = `getPreviousPageParam: (firstPage) => ${accessor}`
|
|
109
|
-
}
|
|
95
|
+
const [getNextPageParamExpr, getPreviousPageParamExpr] = (() => {
|
|
96
|
+
if (hasNewParams) {
|
|
97
|
+
const nextAccessor = nextParam ? getNestedAccessor(nextParam, 'lastPage') : null
|
|
98
|
+
const prevAccessor = previousParam ? getNestedAccessor(previousParam, 'firstPage') : null
|
|
99
|
+
return [
|
|
100
|
+
nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : null,
|
|
101
|
+
prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : null,
|
|
102
|
+
] as const
|
|
110
103
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
getPreviousPageParamExpr = `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`
|
|
114
|
-
} else {
|
|
115
|
-
if (dataReturnType === 'full') {
|
|
116
|
-
getNextPageParamExpr =
|
|
117
|
-
'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage.data) && lastPage.data.length === 0 ? undefined : lastPageParam + 1'
|
|
118
|
-
} else {
|
|
119
|
-
getNextPageParamExpr =
|
|
120
|
-
'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1'
|
|
104
|
+
if (cursorParam) {
|
|
105
|
+
return [`getNextPageParam: (lastPage) => lastPage['${cursorParam}']`, `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`] as const
|
|
121
106
|
}
|
|
122
|
-
|
|
123
|
-
|
|
107
|
+
return [
|
|
108
|
+
dataReturnType === 'full'
|
|
109
|
+
? 'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage.data) && lastPage.data.length === 0 ? undefined : lastPageParam + 1'
|
|
110
|
+
: 'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1',
|
|
111
|
+
'getPreviousPageParam: (_firstPage, _allPages, firstPageParam) => firstPageParam <= 1 ? undefined : firstPageParam - 1',
|
|
112
|
+
] as const
|
|
113
|
+
})()
|
|
124
114
|
|
|
125
115
|
const queryOptionsArr = [
|
|
126
116
|
`initialPageParam: ${typeof initialPageParam === 'string' ? JSON.stringify(initialPageParam) : initialPageParam}`,
|
|
@@ -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}}` : '
|
|
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}}` : '
|
|
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}}` : '
|
|
93
|
+
return mutationOptions<${TData}, ${TError}, ${TRequest ? `{${TRequest}}` : 'undefined'}, TContext>({
|
|
94
94
|
mutationKey,
|
|
95
95
|
mutationFn: async(${hasMutationParams ? `{ ${argKeysStr} }` : '_'}) => {
|
|
96
96
|
return ${clientName}(${clientCallStr})
|
package/src/components/Query.tsx
CHANGED
|
@@ -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) :
|
|
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) :
|
|
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) :
|
|
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 :
|
|
107
|
+
return groupName !== individualName ? groupName : null
|
|
108
108
|
})()
|
|
109
|
-
:
|
|
109
|
+
: null
|
|
110
110
|
|
|
111
|
-
const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` :
|
|
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 :
|
|
75
|
+
return groupName !== individualName ? groupName : null
|
|
76
76
|
})()
|
|
77
|
-
:
|
|
77
|
+
: null
|
|
78
78
|
|
|
79
|
-
const queryParamType = queryParam && queryParamsTypeName ? `${queryParamsTypeName}['${queryParam}']` :
|
|
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,37 +90,27 @@ export function SuspenseInfiniteQueryOptions({
|
|
|
90
90
|
const enabledSource = buildEnabledCheck(queryKeyParamsNode)
|
|
91
91
|
const enabledText = enabledSource ? `enabled: !!(${enabledSource}),` : ''
|
|
92
92
|
|
|
93
|
-
const hasNewParams = nextParam
|
|
93
|
+
const hasNewParams = nextParam != null || previousParam != null
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (previousParam) {
|
|
106
|
-
const accessor = getNestedAccessor(previousParam, 'firstPage')
|
|
107
|
-
if (accessor) {
|
|
108
|
-
getPreviousPageParamExpr = `getPreviousPageParam: (firstPage) => ${accessor}`
|
|
109
|
-
}
|
|
95
|
+
const [getNextPageParamExpr, getPreviousPageParamExpr] = (() => {
|
|
96
|
+
if (hasNewParams) {
|
|
97
|
+
const nextAccessor = nextParam ? getNestedAccessor(nextParam, 'lastPage') : null
|
|
98
|
+
const prevAccessor = previousParam ? getNestedAccessor(previousParam, 'firstPage') : null
|
|
99
|
+
return [
|
|
100
|
+
nextAccessor ? `getNextPageParam: (lastPage) => ${nextAccessor}` : null,
|
|
101
|
+
prevAccessor ? `getPreviousPageParam: (firstPage) => ${prevAccessor}` : null,
|
|
102
|
+
] as const
|
|
110
103
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
getPreviousPageParamExpr = `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`
|
|
114
|
-
} else {
|
|
115
|
-
if (dataReturnType === 'full') {
|
|
116
|
-
getNextPageParamExpr =
|
|
117
|
-
'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage.data) && lastPage.data.length === 0 ? undefined : lastPageParam + 1'
|
|
118
|
-
} else {
|
|
119
|
-
getNextPageParamExpr =
|
|
120
|
-
'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1'
|
|
104
|
+
if (cursorParam) {
|
|
105
|
+
return [`getNextPageParam: (lastPage) => lastPage['${cursorParam}']`, `getPreviousPageParam: (firstPage) => firstPage['${cursorParam}']`] as const
|
|
121
106
|
}
|
|
122
|
-
|
|
123
|
-
|
|
107
|
+
return [
|
|
108
|
+
dataReturnType === 'full'
|
|
109
|
+
? 'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage.data) && lastPage.data.length === 0 ? undefined : lastPageParam + 1'
|
|
110
|
+
: 'getNextPageParam: (lastPage, _allPages, lastPageParam) => Array.isArray(lastPage) && lastPage.length === 0 ? undefined : lastPageParam + 1',
|
|
111
|
+
'getPreviousPageParam: (_firstPage, _allPages, firstPageParam) => firstPageParam <= 1 ? undefined : firstPageParam - 1',
|
|
112
|
+
] as const
|
|
113
|
+
})()
|
|
124
114
|
|
|
125
115
|
const queryOptionsArr = [
|
|
126
116
|
`initialPageParam: ${typeof initialPageParam === 'string' ? JSON.stringify(initialPageParam) : initialPageParam}`,
|
|
@@ -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) :
|
|
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,24 +9,30 @@ 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,
|
|
15
21
|
operations(nodes, ctx) {
|
|
16
|
-
const { resolver, config, root
|
|
22
|
+
const { resolver, config, root } = ctx
|
|
17
23
|
const { output, customOptions, query, mutation, suspense, infinite, group, override } = ctx.options
|
|
18
24
|
|
|
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 :
|
|
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}>>`
|
|
@@ -116,8 +122,8 @@ export const hookOptionsGenerator = defineGenerator<PluginReactQuery>({
|
|
|
116
122
|
baseName={hookOptionsFile.baseName}
|
|
117
123
|
path={hookOptionsFile.path}
|
|
118
124
|
meta={hookOptionsFile.meta}
|
|
119
|
-
banner={resolver.resolveBanner(
|
|
120
|
-
footer={resolver.resolveFooter(
|
|
125
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config })}
|
|
126
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config })}
|
|
121
127
|
>
|
|
122
128
|
{imports}
|
|
123
129
|
<File.Source name={name} isExportable isIndexable isTypeOnly>
|
|
@@ -10,11 +10,17 @@ 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,
|
|
16
22
|
operation(node, ctx) {
|
|
17
|
-
const { config, driver, resolver, root
|
|
23
|
+
const { config, driver, resolver, root } = ctx
|
|
18
24
|
const { output, query, mutation, infinite, paramsCasing, paramsType, pathParamsType, parser, client: clientOptions, group, customOptions } = ctx.options
|
|
19
25
|
|
|
20
26
|
const pluginTs = driver.getPlugin(pluginTsName)
|
|
@@ -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 :
|
|
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(
|
|
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) :
|
|
65
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) :
|
|
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
|
-
:
|
|
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) :
|
|
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
|
-
:
|
|
97
|
+
: null
|
|
89
98
|
|
|
90
99
|
const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientBaseName) : clientBaseName
|
|
91
100
|
|
|
@@ -94,8 +103,8 @@ export const infiniteQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
94
103
|
baseName={meta.file.baseName}
|
|
95
104
|
path={meta.file.path}
|
|
96
105
|
meta={meta.file.meta}
|
|
97
|
-
banner={resolver.resolveBanner(
|
|
98
|
-
footer={resolver.resolveFooter(
|
|
106
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config })}
|
|
107
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config })}
|
|
99
108
|
>
|
|
100
109
|
{fileZod && zodSchemaNames.length > 0 && <File.Import name={zodSchemaNames} root={meta.file.path} path={fileZod.path} />}
|
|
101
110
|
{clientOptions.importPath ? (
|
|
@@ -10,11 +10,16 @@ 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,
|
|
16
21
|
operation(node, ctx) {
|
|
17
|
-
const { config, driver, resolver, root
|
|
22
|
+
const { config, driver, resolver, root } = ctx
|
|
18
23
|
const { output, query, mutation, paramsCasing, paramsType, pathParamsType, parser, client: clientOptions, group, customOptions } = ctx.options
|
|
19
24
|
|
|
20
25
|
const pluginTs = driver.getPlugin(pluginTsName)
|
|
@@ -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(
|
|
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) :
|
|
51
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) :
|
|
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
|
-
:
|
|
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) :
|
|
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
|
-
:
|
|
82
|
+
: null
|
|
75
83
|
|
|
76
84
|
const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientName) : clientName
|
|
77
85
|
|
|
@@ -80,8 +88,8 @@ export const mutationGenerator = defineGenerator<PluginReactQuery>({
|
|
|
80
88
|
baseName={meta.file.baseName}
|
|
81
89
|
path={meta.file.path}
|
|
82
90
|
meta={meta.file.meta}
|
|
83
|
-
banner={resolver.resolveBanner(
|
|
84
|
-
footer={resolver.resolveFooter(
|
|
91
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config })}
|
|
92
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config })}
|
|
85
93
|
>
|
|
86
94
|
{fileZod && zodSchemaNames.length > 0 && <File.Import name={zodSchemaNames} root={meta.file.path} path={fileZod.path} />}
|
|
87
95
|
{clientOptions.importPath ? (
|