@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,50 +1,45 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
+
import { getOperationParameters, operationFileEntry, resolveOperationTypeNames } from '@internals/shared'
|
|
3
|
+
import { resolveZodSchemaNames } from '@internals/tanstack-query'
|
|
2
4
|
import { ast, defineGenerator } from '@kubb/core'
|
|
3
|
-
import { Client, pluginClientName } from '@kubb/plugin-client'
|
|
5
|
+
import { Client, isParserEnabled, pluginClientName } from '@kubb/plugin-client'
|
|
4
6
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
5
7
|
import { pluginZodName } from '@kubb/plugin-zod'
|
|
6
8
|
import { File, jsxRenderer } from '@kubb/renderer-jsx'
|
|
7
|
-
import { difference } from 'remeda'
|
|
8
9
|
import { InfiniteQuery, InfiniteQueryOptions, QueryKey } from '../components'
|
|
9
10
|
import type { PluginReactQuery } from '../types'
|
|
10
|
-
import { transformName } from '../utils.ts'
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Built-in generator for `useInfiniteQuery` hooks. Enabled when
|
|
14
|
+
* `pluginReactQuery({ infinite: { ... } })`. Emits one `useFooInfiniteQuery`
|
|
15
|
+
* hook per query operation, wiring the configured `nextParam` /
|
|
16
|
+
* `previousParam` paths into TanStack Query's cursor-based pagination.
|
|
17
|
+
*/
|
|
12
18
|
export const infiniteQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
13
19
|
name: 'react-infinite-query',
|
|
14
20
|
renderer: jsxRenderer,
|
|
15
21
|
operation(node, ctx) {
|
|
16
|
-
|
|
17
|
-
const {
|
|
18
|
-
|
|
19
|
-
query,
|
|
20
|
-
mutation,
|
|
21
|
-
infinite,
|
|
22
|
-
paramsCasing,
|
|
23
|
-
paramsType,
|
|
24
|
-
pathParamsType,
|
|
25
|
-
parser,
|
|
26
|
-
client: clientOptions,
|
|
27
|
-
group,
|
|
28
|
-
transformers,
|
|
29
|
-
customOptions,
|
|
30
|
-
} = ctx.options
|
|
22
|
+
if (!ast.isHttpOperationNode(node)) return null
|
|
23
|
+
const { config, driver, resolver, root } = ctx
|
|
24
|
+
const { output, query, mutation, infinite, paramsCasing, paramsType, pathParamsType, parser, client: clientOptions, group, customOptions } = ctx.options
|
|
31
25
|
|
|
32
26
|
const pluginTs = driver.getPlugin(pluginTsName)
|
|
33
27
|
if (!pluginTs) return null
|
|
34
28
|
const tsResolver = driver.getResolver(pluginTsName)
|
|
35
29
|
|
|
36
30
|
const isQuery = query === false || (!!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase()))
|
|
31
|
+
const queryMethods = new Set(query ? query.methods : [])
|
|
37
32
|
const isMutation =
|
|
38
33
|
mutation !== false &&
|
|
39
34
|
!isQuery &&
|
|
40
|
-
|
|
41
|
-
const infiniteOptions = infinite && typeof infinite === 'object' ? infinite :
|
|
35
|
+
(mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
|
|
36
|
+
const infiniteOptions = infinite && typeof infinite === 'object' ? infinite : null
|
|
42
37
|
|
|
43
38
|
if (!isQuery || isMutation || !infiniteOptions) return null
|
|
44
39
|
|
|
45
40
|
// Validate queryParam exists in operation's query parameters
|
|
46
41
|
const normalizeKey = (key: string) => key.replace(/\?$/, '')
|
|
47
|
-
const queryParamKeys = node
|
|
42
|
+
const queryParamKeys = getOperationParameters(node).query.map((p) => p.name)
|
|
48
43
|
const hasQueryParam = infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false
|
|
49
44
|
// cursorParam validation against response schema keys is skipped in v5 (complex schema inspection)
|
|
50
45
|
const hasCursorParam = !infiniteOptions.cursorParam || true
|
|
@@ -53,64 +48,50 @@ export const infiniteQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
53
48
|
|
|
54
49
|
const importPath = query ? query.importPath : '@tanstack/react-query'
|
|
55
50
|
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const queryKeyTypeName = transformName(`${capitalize(baseName)}InfiniteQueryKey`, 'type', transformers)
|
|
62
|
-
const clientBaseName = transformName(`${baseName}Infinite`, 'function', transformers)
|
|
51
|
+
const queryName = resolver.resolveInfiniteQueryName(node)
|
|
52
|
+
const queryOptionsName = resolver.resolveInfiniteQueryOptionsName(node)
|
|
53
|
+
const queryKeyName = resolver.resolveInfiniteQueryKeyName(node)
|
|
54
|
+
const queryKeyTypeName = resolver.resolveInfiniteQueryKeyTypeName(node)
|
|
55
|
+
const clientBaseName = resolver.resolveInfiniteClientName(node)
|
|
63
56
|
|
|
64
57
|
const meta = {
|
|
65
|
-
file: resolver.resolveFile(
|
|
66
|
-
fileTs: tsResolver.resolveFile(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
file: resolver.resolveFile(operationFileEntry(node, queryName), { root, output, group: group ?? undefined }),
|
|
59
|
+
fileTs: tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
60
|
+
root,
|
|
61
|
+
output: pluginTs.options?.output ?? output,
|
|
62
|
+
group: pluginTs.options?.group ?? undefined,
|
|
63
|
+
}),
|
|
70
64
|
}
|
|
71
65
|
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
81
|
-
...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
82
|
-
...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
83
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode)),
|
|
84
|
-
].filter((name): name is string => !!name && name !== queryKeyTypeName)
|
|
85
|
-
|
|
86
|
-
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
|
|
87
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : undefined
|
|
66
|
+
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, {
|
|
67
|
+
paramsCasing,
|
|
68
|
+
exclude: [queryKeyTypeName],
|
|
69
|
+
order: 'body-response-first',
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null
|
|
73
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
|
|
88
74
|
const fileZod = zodResolver
|
|
89
|
-
? zodResolver.resolveFile(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : undefined].filter(Boolean)
|
|
97
|
-
: []
|
|
75
|
+
? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
76
|
+
root,
|
|
77
|
+
output: pluginZod?.options?.output ?? output,
|
|
78
|
+
group: pluginZod?.options?.group ?? undefined,
|
|
79
|
+
})
|
|
80
|
+
: null
|
|
81
|
+
const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, parser)
|
|
98
82
|
|
|
99
83
|
const clientPlugin = driver.getPlugin(pluginClientName)
|
|
100
84
|
const hasClientPlugin = clientPlugin?.name === pluginClientName
|
|
101
85
|
const shouldUseClientPlugin = hasClientPlugin && clientOptions.clientType !== 'class'
|
|
102
|
-
const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) :
|
|
86
|
+
const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : null
|
|
103
87
|
|
|
104
88
|
const clientFile = shouldUseClientPlugin
|
|
105
|
-
? clientResolver?.resolveFile(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
},
|
|
112
|
-
)
|
|
113
|
-
: undefined
|
|
89
|
+
? clientResolver?.resolveFile(operationFileEntry(node, node.operationId), {
|
|
90
|
+
root,
|
|
91
|
+
output: clientPlugin?.options?.output ?? output,
|
|
92
|
+
group: clientPlugin?.options?.group ?? undefined,
|
|
93
|
+
})
|
|
94
|
+
: null
|
|
114
95
|
|
|
115
96
|
const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientBaseName) : clientBaseName
|
|
116
97
|
|
|
@@ -119,30 +100,24 @@ export const infiniteQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
119
100
|
baseName={meta.file.baseName}
|
|
120
101
|
path={meta.file.path}
|
|
121
102
|
meta={meta.file.meta}
|
|
122
|
-
banner={resolver.resolveBanner(
|
|
123
|
-
footer={resolver.resolveFooter(
|
|
103
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
104
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
124
105
|
>
|
|
125
|
-
{
|
|
126
|
-
<File.Import name={zodSchemaNames as string[]} root={meta.file.path} path={fileZod.path} />
|
|
127
|
-
)}
|
|
106
|
+
{fileZod && zodSchemaNames.length > 0 && <File.Import name={zodSchemaNames} root={meta.file.path} path={fileZod.path} />}
|
|
128
107
|
{clientOptions.importPath ? (
|
|
129
108
|
<>
|
|
130
|
-
{!shouldUseClientPlugin && <File.Import name={'
|
|
109
|
+
{!shouldUseClientPlugin && <File.Import name={'client'} path={clientOptions.importPath} />}
|
|
131
110
|
<File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={clientOptions.importPath} isTypeOnly />
|
|
132
|
-
{clientOptions.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={clientOptions.importPath} isTypeOnly />}
|
|
133
111
|
</>
|
|
134
112
|
) : (
|
|
135
113
|
<>
|
|
136
|
-
{!shouldUseClientPlugin && <File.Import name={['
|
|
114
|
+
{!shouldUseClientPlugin && <File.Import name={['client']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} />}
|
|
137
115
|
<File.Import
|
|
138
116
|
name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
|
|
139
117
|
root={meta.file.path}
|
|
140
|
-
path={path.resolve(root, '.kubb/
|
|
118
|
+
path={path.resolve(root, '.kubb/client.ts')}
|
|
141
119
|
isTypeOnly
|
|
142
120
|
/>
|
|
143
|
-
{clientOptions.dataReturnType === 'full' && (
|
|
144
|
-
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/fetch.ts')} isTypeOnly />
|
|
145
|
-
)}
|
|
146
121
|
</>
|
|
147
122
|
)}
|
|
148
123
|
{shouldUseClientPlugin && clientFile && <File.Import name={[resolvedClientName]} root={meta.file.path} path={clientFile.path} />}
|
|
@@ -1,93 +1,82 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
+
import { operationFileEntry, resolveOperationTypeNames } from '@internals/shared'
|
|
3
|
+
import { resolveZodSchemaNames } from '@internals/tanstack-query'
|
|
2
4
|
import { ast, defineGenerator } from '@kubb/core'
|
|
3
|
-
import { Client, pluginClientName } from '@kubb/plugin-client'
|
|
5
|
+
import { Client, isParserEnabled, pluginClientName } from '@kubb/plugin-client'
|
|
4
6
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
5
7
|
import { pluginZodName } from '@kubb/plugin-zod'
|
|
6
8
|
import { File, jsxRenderer } from '@kubb/renderer-jsx'
|
|
7
|
-
import { difference } from 'remeda'
|
|
8
9
|
import { Mutation, MutationKey, MutationOptions } from '../components'
|
|
9
10
|
import type { PluginReactQuery } from '../types'
|
|
10
|
-
import { transformName } from '../utils.ts'
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Built-in generator for `useMutation` hooks. Emits one `useFooMutation` hook
|
|
14
|
+
* per POST/PUT/DELETE operation (configurable via `mutation.methods`) plus
|
|
15
|
+
* the matching `fooMutationKey` / `fooMutationOptions` helpers.
|
|
16
|
+
*/
|
|
12
17
|
export const mutationGenerator = defineGenerator<PluginReactQuery>({
|
|
13
18
|
name: 'react-query-mutation',
|
|
14
19
|
renderer: jsxRenderer,
|
|
15
20
|
operation(node, ctx) {
|
|
16
|
-
|
|
17
|
-
const {
|
|
21
|
+
if (!ast.isHttpOperationNode(node)) return null
|
|
22
|
+
const { config, driver, resolver, root } = ctx
|
|
23
|
+
const { output, query, mutation, paramsCasing, paramsType, pathParamsType, parser, client: clientOptions, group, customOptions } = ctx.options
|
|
18
24
|
|
|
19
25
|
const pluginTs = driver.getPlugin(pluginTsName)
|
|
20
26
|
if (!pluginTs) return null
|
|
21
27
|
const tsResolver = driver.getResolver(pluginTsName)
|
|
22
28
|
|
|
23
29
|
const isQuery = query === false || (!!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase()))
|
|
30
|
+
const queryMethods = new Set(query ? query.methods : [])
|
|
24
31
|
const isMutation =
|
|
25
32
|
mutation !== false &&
|
|
26
33
|
!isQuery &&
|
|
27
|
-
|
|
34
|
+
(mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
|
|
28
35
|
|
|
29
36
|
if (!isMutation) return null
|
|
30
37
|
|
|
31
38
|
const importPath = mutation ? mutation.importPath : '@tanstack/react-query'
|
|
32
39
|
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const mutationKeyName = transformName(`${baseName}MutationKey`, 'const', transformers)
|
|
39
|
-
const clientName = transformName(baseName, 'function', transformers)
|
|
40
|
+
const mutationHookName = resolver.resolveMutationName(node)
|
|
41
|
+
const mutationTypeName = resolver.resolveMutationTypeName(node)
|
|
42
|
+
const mutationOptionsName = resolver.resolveMutationOptionsName(node)
|
|
43
|
+
const mutationKeyName = resolver.resolveMutationKeyName(node)
|
|
44
|
+
const clientName = resolver.resolveClientName(node)
|
|
40
45
|
|
|
41
46
|
const meta = {
|
|
42
|
-
file: resolver.resolveFile(
|
|
43
|
-
fileTs: tsResolver.resolveFile(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
file: resolver.resolveFile(operationFileEntry(node, mutationHookName), { root, output, group: group ?? undefined }),
|
|
48
|
+
fileTs: tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
49
|
+
root,
|
|
50
|
+
output: pluginTs.options?.output ?? output,
|
|
51
|
+
group: pluginTs.options?.group ?? undefined,
|
|
52
|
+
}),
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
const importedTypeNames = [
|
|
55
|
-
node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : undefined,
|
|
56
|
-
tsResolver.resolveResponseName(node),
|
|
57
|
-
...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
58
|
-
...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
59
|
-
...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
60
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode)),
|
|
61
|
-
].filter((name): name is string => !!name)
|
|
62
|
-
|
|
63
|
-
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
|
|
64
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : undefined
|
|
55
|
+
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing, order: 'body-response-first' })
|
|
56
|
+
|
|
57
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null
|
|
58
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
|
|
65
59
|
const fileZod = zodResolver
|
|
66
|
-
? zodResolver.resolveFile(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : undefined].filter(Boolean)
|
|
74
|
-
: []
|
|
60
|
+
? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
61
|
+
root,
|
|
62
|
+
output: pluginZod?.options?.output ?? output,
|
|
63
|
+
group: pluginZod?.options?.group ?? undefined,
|
|
64
|
+
})
|
|
65
|
+
: null
|
|
66
|
+
const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, parser)
|
|
75
67
|
|
|
76
68
|
const clientPlugin = driver.getPlugin(pluginClientName)
|
|
77
69
|
const hasClientPlugin = clientPlugin?.name === pluginClientName
|
|
78
70
|
const shouldUseClientPlugin = hasClientPlugin && clientOptions.clientType !== 'class'
|
|
79
|
-
const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) :
|
|
71
|
+
const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : null
|
|
80
72
|
|
|
81
73
|
const clientFile = shouldUseClientPlugin
|
|
82
|
-
? clientResolver?.resolveFile(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
},
|
|
89
|
-
)
|
|
90
|
-
: undefined
|
|
74
|
+
? clientResolver?.resolveFile(operationFileEntry(node, node.operationId), {
|
|
75
|
+
root,
|
|
76
|
+
output: clientPlugin?.options?.output ?? output,
|
|
77
|
+
group: clientPlugin?.options?.group ?? undefined,
|
|
78
|
+
})
|
|
79
|
+
: null
|
|
91
80
|
|
|
92
81
|
const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientName) : clientName
|
|
93
82
|
|
|
@@ -96,34 +85,33 @@ export const mutationGenerator = defineGenerator<PluginReactQuery>({
|
|
|
96
85
|
baseName={meta.file.baseName}
|
|
97
86
|
path={meta.file.path}
|
|
98
87
|
meta={meta.file.meta}
|
|
99
|
-
banner={resolver.resolveBanner(
|
|
100
|
-
footer={resolver.resolveFooter(
|
|
88
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
89
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
101
90
|
>
|
|
102
|
-
{
|
|
103
|
-
<File.Import name={zodSchemaNames as string[]} root={meta.file.path} path={fileZod.path} />
|
|
104
|
-
)}
|
|
91
|
+
{fileZod && zodSchemaNames.length > 0 && <File.Import name={zodSchemaNames} root={meta.file.path} path={fileZod.path} />}
|
|
105
92
|
{clientOptions.importPath ? (
|
|
106
93
|
<>
|
|
107
|
-
{!shouldUseClientPlugin && <File.Import name={'
|
|
94
|
+
{!shouldUseClientPlugin && <File.Import name={'client'} path={clientOptions.importPath} />}
|
|
108
95
|
<File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={clientOptions.importPath} isTypeOnly />
|
|
109
|
-
{clientOptions.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={clientOptions.importPath} isTypeOnly />}
|
|
110
96
|
</>
|
|
111
97
|
) : (
|
|
112
98
|
<>
|
|
113
|
-
{!shouldUseClientPlugin && <File.Import name={['
|
|
99
|
+
{!shouldUseClientPlugin && <File.Import name={['client']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} />}
|
|
114
100
|
<File.Import
|
|
115
101
|
name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
|
|
116
102
|
root={meta.file.path}
|
|
117
|
-
path={path.resolve(root, '.kubb/
|
|
103
|
+
path={path.resolve(root, '.kubb/client.ts')}
|
|
118
104
|
isTypeOnly
|
|
119
105
|
/>
|
|
120
|
-
{clientOptions.dataReturnType === 'full' && (
|
|
121
|
-
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/fetch.ts')} isTypeOnly />
|
|
122
|
-
)}
|
|
123
106
|
</>
|
|
124
107
|
)}
|
|
125
108
|
{shouldUseClientPlugin && clientFile && <File.Import name={[resolvedClientName]} root={meta.file.path} path={clientFile.path} />}
|
|
126
|
-
{!shouldUseClientPlugin &&
|
|
109
|
+
{!shouldUseClientPlugin && node.requestBody?.content?.some((e) => e.contentType === 'multipart/form-data') && (
|
|
110
|
+
<File.Import name={['buildFormData']} root={meta.file.path} path={path.resolve(root, '.kubb/config.ts')} />
|
|
111
|
+
)}
|
|
112
|
+
{!shouldUseClientPlugin && parser === 'zod' && zodResolver && node.requestBody?.content?.[0]?.schema && (
|
|
113
|
+
<File.Import name={['z']} path="zod" isTypeOnly />
|
|
114
|
+
)}
|
|
127
115
|
{customOptions && <File.Import name={[customOptions.name]} path={customOptions.importPath} />}
|
|
128
116
|
{meta.fileTs && importedTypeNames.length > 0 && (
|
|
129
117
|
<File.Import name={Array.from(new Set(importedTypeNames))} root={meta.file.path} path={meta.fileTs.path} isTypeOnly />
|
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
+
import { operationFileEntry, resolveOperationTypeNames } from '@internals/shared'
|
|
3
|
+
import { resolveZodSchemaNames } from '@internals/tanstack-query'
|
|
2
4
|
import { ast, defineGenerator } from '@kubb/core'
|
|
3
|
-
import { Client, pluginClientName } from '@kubb/plugin-client'
|
|
5
|
+
import { Client, isParserEnabled, pluginClientName } from '@kubb/plugin-client'
|
|
4
6
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
5
7
|
import { pluginZodName } from '@kubb/plugin-zod'
|
|
6
8
|
import { File, jsxRenderer } from '@kubb/renderer-jsx'
|
|
7
|
-
import { difference } from 'remeda'
|
|
8
9
|
import { Query, QueryKey, QueryOptions } from '../components'
|
|
9
10
|
import type { PluginReactQuery } from '../types'
|
|
10
|
-
import { transformName } from '../utils.ts'
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Built-in generator for `useQuery` hooks. Emits one `useFooQuery` hook per
|
|
14
|
+
* GET operation (configurable via `query.methods`) plus the matching
|
|
15
|
+
* `fooQueryKey` / `fooQueryOptions` helpers.
|
|
16
|
+
*/
|
|
12
17
|
export const queryGenerator = defineGenerator<PluginReactQuery>({
|
|
13
18
|
name: 'react-query',
|
|
14
19
|
renderer: jsxRenderer,
|
|
15
20
|
operation(node, ctx) {
|
|
16
|
-
|
|
17
|
-
const {
|
|
21
|
+
if (!ast.isHttpOperationNode(node)) return null
|
|
22
|
+
const { config, driver, resolver, root } = ctx
|
|
23
|
+
const { output, query, mutation, paramsCasing, paramsType, pathParamsType, parser, client: clientOptions, group, customOptions } = ctx.options
|
|
18
24
|
|
|
19
25
|
const pluginTs = driver.getPlugin(pluginTsName)
|
|
20
26
|
if (!pluginTs) return null
|
|
@@ -22,73 +28,60 @@ export const queryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
22
28
|
|
|
23
29
|
// query: false means "this IS a query op, but skip the useQuery hook"
|
|
24
30
|
const isQuery = query === false || (!!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase()))
|
|
31
|
+
const queryMethods = new Set(query ? query.methods : [])
|
|
25
32
|
const isMutation =
|
|
26
33
|
mutation !== false &&
|
|
27
34
|
!isQuery &&
|
|
28
|
-
|
|
35
|
+
(mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
|
|
29
36
|
|
|
30
37
|
if (!isQuery || isMutation) return null
|
|
31
38
|
|
|
32
39
|
const importPath = query ? query.importPath : '@tanstack/react-query'
|
|
33
40
|
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const queryKeyTypeName = transformName(`${capitalize(baseName)}QueryKey`, 'type', transformers)
|
|
40
|
-
const clientName = transformName(baseName, 'function', transformers)
|
|
41
|
+
const queryName = resolver.resolveQueryName(node)
|
|
42
|
+
const queryOptionsName = resolver.resolveQueryOptionsName(node)
|
|
43
|
+
const queryKeyName = resolver.resolveQueryKeyName(node)
|
|
44
|
+
const queryKeyTypeName = resolver.resolveQueryKeyTypeName(node)
|
|
45
|
+
const clientName = resolver.resolveClientName(node)
|
|
41
46
|
|
|
42
47
|
const meta = {
|
|
43
|
-
file: resolver.resolveFile(
|
|
44
|
-
fileTs: tsResolver.resolveFile(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
file: resolver.resolveFile(operationFileEntry(node, queryName), { root, output, group: group ?? undefined }),
|
|
49
|
+
fileTs: tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
50
|
+
root,
|
|
51
|
+
output: pluginTs.options?.output ?? output,
|
|
52
|
+
group: pluginTs.options?.group ?? undefined,
|
|
53
|
+
}),
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
59
|
-
...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
60
|
-
...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
61
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode)),
|
|
62
|
-
].filter((name): name is string => !!name && name !== queryKeyTypeName)
|
|
63
|
-
|
|
64
|
-
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
|
|
65
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : undefined
|
|
56
|
+
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, {
|
|
57
|
+
paramsCasing,
|
|
58
|
+
exclude: [queryKeyTypeName],
|
|
59
|
+
order: 'body-response-first',
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null
|
|
63
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
|
|
66
64
|
const fileZod = zodResolver
|
|
67
|
-
? zodResolver.resolveFile(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : undefined].filter(Boolean)
|
|
75
|
-
: []
|
|
65
|
+
? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
66
|
+
root,
|
|
67
|
+
output: pluginZod?.options?.output ?? output,
|
|
68
|
+
group: pluginZod?.options?.group ?? undefined,
|
|
69
|
+
})
|
|
70
|
+
: null
|
|
71
|
+
const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, parser)
|
|
76
72
|
|
|
77
73
|
const clientPlugin = driver.getPlugin(pluginClientName)
|
|
78
74
|
const hasClientPlugin = clientPlugin?.name === pluginClientName
|
|
79
75
|
const shouldUseClientPlugin = hasClientPlugin && clientOptions.clientType !== 'class'
|
|
80
|
-
const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) :
|
|
76
|
+
const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : null
|
|
81
77
|
|
|
82
78
|
const clientFile = shouldUseClientPlugin
|
|
83
|
-
? clientResolver?.resolveFile(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
},
|
|
90
|
-
)
|
|
91
|
-
: undefined
|
|
79
|
+
? clientResolver?.resolveFile(operationFileEntry(node, node.operationId), {
|
|
80
|
+
root,
|
|
81
|
+
output: clientPlugin?.options?.output ?? output,
|
|
82
|
+
group: clientPlugin?.options?.group ?? undefined,
|
|
83
|
+
})
|
|
84
|
+
: null
|
|
92
85
|
|
|
93
86
|
const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientName) : clientName
|
|
94
87
|
|
|
@@ -97,30 +90,24 @@ export const queryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
97
90
|
baseName={meta.file.baseName}
|
|
98
91
|
path={meta.file.path}
|
|
99
92
|
meta={meta.file.meta}
|
|
100
|
-
banner={resolver.resolveBanner(
|
|
101
|
-
footer={resolver.resolveFooter(
|
|
93
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
94
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
102
95
|
>
|
|
103
|
-
{
|
|
104
|
-
<File.Import name={zodSchemaNames as string[]} root={meta.file.path} path={fileZod.path} />
|
|
105
|
-
)}
|
|
96
|
+
{fileZod && zodSchemaNames.length > 0 && <File.Import name={zodSchemaNames} root={meta.file.path} path={fileZod.path} />}
|
|
106
97
|
{clientOptions.importPath ? (
|
|
107
98
|
<>
|
|
108
|
-
{!shouldUseClientPlugin && <File.Import name={'
|
|
99
|
+
{!shouldUseClientPlugin && <File.Import name={'client'} path={clientOptions.importPath} />}
|
|
109
100
|
<File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={clientOptions.importPath} isTypeOnly />
|
|
110
|
-
{clientOptions.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={clientOptions.importPath} isTypeOnly />}
|
|
111
101
|
</>
|
|
112
102
|
) : (
|
|
113
103
|
<>
|
|
114
|
-
{!shouldUseClientPlugin && <File.Import name={['
|
|
104
|
+
{!shouldUseClientPlugin && <File.Import name={['client']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} />}
|
|
115
105
|
<File.Import
|
|
116
106
|
name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
|
|
117
107
|
root={meta.file.path}
|
|
118
|
-
path={path.resolve(root, '.kubb/
|
|
108
|
+
path={path.resolve(root, '.kubb/client.ts')}
|
|
119
109
|
isTypeOnly
|
|
120
110
|
/>
|
|
121
|
-
{clientOptions.dataReturnType === 'full' && (
|
|
122
|
-
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/fetch.ts')} isTypeOnly />
|
|
123
|
-
)}
|
|
124
111
|
</>
|
|
125
112
|
)}
|
|
126
113
|
{shouldUseClientPlugin && clientFile && <File.Import name={[resolvedClientName]} root={meta.file.path} path={clientFile.path} />}
|