@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,19 +1,26 @@
|
|
|
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 { QueryKey, SuspenseInfiniteQuery, SuspenseInfiniteQueryOptions } from '../components'
|
|
9
10
|
import type { PluginReactQuery } from '../types'
|
|
10
|
-
import { transformName } from '../utils.ts'
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Built-in generator for `useSuspenseInfiniteQuery` hooks. Enabled when both
|
|
14
|
+
* `suspense` and `infinite` are configured. Combines suspense semantics with
|
|
15
|
+
* cursor-based pagination — handlers throw promises while loading and pull
|
|
16
|
+
* additional pages on demand.
|
|
17
|
+
*/
|
|
12
18
|
export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
13
19
|
name: 'react-suspense-infinite-query',
|
|
14
20
|
renderer: jsxRenderer,
|
|
15
21
|
operation(node, ctx) {
|
|
16
|
-
|
|
22
|
+
if (!ast.isHttpOperationNode(node)) return null
|
|
23
|
+
const { config, driver, resolver, root } = ctx
|
|
17
24
|
const {
|
|
18
25
|
output,
|
|
19
26
|
query,
|
|
@@ -26,7 +33,6 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
|
|
|
26
33
|
parser,
|
|
27
34
|
client: clientOptions,
|
|
28
35
|
group,
|
|
29
|
-
transformers,
|
|
30
36
|
customOptions,
|
|
31
37
|
} = ctx.options
|
|
32
38
|
|
|
@@ -35,18 +41,19 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
|
|
|
35
41
|
const tsResolver = driver.getResolver(pluginTsName)
|
|
36
42
|
|
|
37
43
|
const isQuery = query === false || (!!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase()))
|
|
44
|
+
const queryMethods = new Set(query ? query.methods : [])
|
|
38
45
|
const isMutation =
|
|
39
46
|
mutation !== false &&
|
|
40
47
|
!isQuery &&
|
|
41
|
-
|
|
48
|
+
(mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
|
|
42
49
|
const isSuspense = !!suspense
|
|
43
|
-
const infiniteOptions = infinite && typeof infinite === 'object' ? infinite :
|
|
50
|
+
const infiniteOptions = infinite && typeof infinite === 'object' ? infinite : null
|
|
44
51
|
|
|
45
52
|
if (!isQuery || isMutation || !isSuspense || !infiniteOptions) return null
|
|
46
53
|
|
|
47
54
|
// Validate queryParam exists in operation's query parameters
|
|
48
55
|
const normalizeKey = (key: string) => key.replace(/\?$/, '')
|
|
49
|
-
const queryParamKeys = node
|
|
56
|
+
const queryParamKeys = getOperationParameters(node).query.map((p) => p.name)
|
|
50
57
|
const hasQueryParam = infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false
|
|
51
58
|
const hasCursorParam = !infiniteOptions.cursorParam || true
|
|
52
59
|
|
|
@@ -54,64 +61,46 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
|
|
|
54
61
|
|
|
55
62
|
const importPath = query ? query.importPath : '@tanstack/react-query'
|
|
56
63
|
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
const queryKeyTypeName = transformName(`${capitalize(baseName)}SuspenseInfiniteQueryKey`, 'type', transformers)
|
|
63
|
-
const clientBaseName = transformName(`${baseName}SuspenseInfinite`, 'function', transformers)
|
|
64
|
+
const queryName = resolver.resolveSuspenseInfiniteQueryName(node)
|
|
65
|
+
const queryOptionsName = resolver.resolveSuspenseInfiniteQueryOptionsName(node)
|
|
66
|
+
const queryKeyName = resolver.resolveSuspenseInfiniteQueryKeyName(node)
|
|
67
|
+
const queryKeyTypeName = resolver.resolveSuspenseInfiniteQueryKeyTypeName(node)
|
|
68
|
+
const clientBaseName = resolver.resolveSuspenseInfiniteClientName(node)
|
|
64
69
|
|
|
65
70
|
const meta = {
|
|
66
|
-
file: resolver.resolveFile(
|
|
67
|
-
fileTs: tsResolver.resolveFile(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
file: resolver.resolveFile(operationFileEntry(node, queryName), { root, output, group: group ?? undefined }),
|
|
72
|
+
fileTs: tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
73
|
+
root,
|
|
74
|
+
output: pluginTs.options?.output ?? output,
|
|
75
|
+
group: pluginTs.options?.group ?? undefined,
|
|
76
|
+
}),
|
|
71
77
|
}
|
|
72
78
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
const importedTypeNames = [
|
|
79
|
-
node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : undefined,
|
|
80
|
-
tsResolver.resolveResponseName(node),
|
|
81
|
-
...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
82
|
-
...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
83
|
-
...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
84
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode)),
|
|
85
|
-
].filter(Boolean)
|
|
86
|
-
|
|
87
|
-
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
|
|
88
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : undefined
|
|
79
|
+
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing, order: 'body-response-first' })
|
|
80
|
+
|
|
81
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null
|
|
82
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
|
|
89
83
|
const fileZod = zodResolver
|
|
90
|
-
? zodResolver.resolveFile(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : undefined].filter(Boolean)
|
|
98
|
-
: []
|
|
84
|
+
? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
85
|
+
root,
|
|
86
|
+
output: pluginZod?.options?.output ?? output,
|
|
87
|
+
group: pluginZod?.options?.group ?? undefined,
|
|
88
|
+
})
|
|
89
|
+
: null
|
|
90
|
+
const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, parser)
|
|
99
91
|
|
|
100
92
|
const clientPlugin = driver.getPlugin(pluginClientName)
|
|
101
93
|
const hasClientPlugin = clientPlugin?.name === pluginClientName
|
|
102
94
|
const shouldUseClientPlugin = hasClientPlugin && clientOptions.clientType !== 'class'
|
|
103
|
-
const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) :
|
|
95
|
+
const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : null
|
|
104
96
|
|
|
105
97
|
const clientFile = shouldUseClientPlugin
|
|
106
|
-
? clientResolver?.resolveFile(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
},
|
|
113
|
-
)
|
|
114
|
-
: undefined
|
|
98
|
+
? clientResolver?.resolveFile(operationFileEntry(node, node.operationId), {
|
|
99
|
+
root,
|
|
100
|
+
output: clientPlugin?.options?.output ?? output,
|
|
101
|
+
group: clientPlugin?.options?.group ?? undefined,
|
|
102
|
+
})
|
|
103
|
+
: null
|
|
115
104
|
|
|
116
105
|
const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientBaseName) : clientBaseName
|
|
117
106
|
|
|
@@ -120,30 +109,24 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
|
|
|
120
109
|
baseName={meta.file.baseName}
|
|
121
110
|
path={meta.file.path}
|
|
122
111
|
meta={meta.file.meta}
|
|
123
|
-
banner={resolver.resolveBanner(
|
|
124
|
-
footer={resolver.resolveFooter(
|
|
112
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
113
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
125
114
|
>
|
|
126
|
-
{
|
|
127
|
-
<File.Import name={zodSchemaNames as string[]} root={meta.file.path} path={fileZod.path} />
|
|
128
|
-
)}
|
|
115
|
+
{fileZod && zodSchemaNames.length > 0 && <File.Import name={zodSchemaNames} root={meta.file.path} path={fileZod.path} />}
|
|
129
116
|
{clientOptions.importPath ? (
|
|
130
117
|
<>
|
|
131
|
-
{!shouldUseClientPlugin && <File.Import name={'
|
|
118
|
+
{!shouldUseClientPlugin && <File.Import name={'client'} path={clientOptions.importPath} />}
|
|
132
119
|
<File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={clientOptions.importPath} isTypeOnly />
|
|
133
|
-
{clientOptions.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={clientOptions.importPath} isTypeOnly />}
|
|
134
120
|
</>
|
|
135
121
|
) : (
|
|
136
122
|
<>
|
|
137
|
-
{!shouldUseClientPlugin && <File.Import name={['
|
|
123
|
+
{!shouldUseClientPlugin && <File.Import name={['client']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} />}
|
|
138
124
|
<File.Import
|
|
139
125
|
name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
|
|
140
126
|
root={meta.file.path}
|
|
141
|
-
path={path.resolve(root, '.kubb/
|
|
127
|
+
path={path.resolve(root, '.kubb/client.ts')}
|
|
142
128
|
isTypeOnly
|
|
143
129
|
/>
|
|
144
|
-
{clientOptions.dataReturnType === 'full' && (
|
|
145
|
-
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/fetch.ts')} isTypeOnly />
|
|
146
|
-
)}
|
|
147
130
|
</>
|
|
148
131
|
)}
|
|
149
132
|
{shouldUseClientPlugin && clientFile && <File.Import name={[resolvedClientName]} root={meta.file.path} path={clientFile.path} />}
|
|
@@ -1,33 +1,27 @@
|
|
|
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 { QueryKey, QueryOptions, SuspenseQuery } from '../components'
|
|
9
10
|
import type { PluginReactQuery } from '../types'
|
|
10
|
-
import { transformName } from '../utils.ts'
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Built-in generator for `useSuspenseQuery` hooks. Enabled when
|
|
14
|
+
* `pluginReactQuery({ suspense: {} })`. Emits one `useFooSuspenseQuery` hook
|
|
15
|
+
* per query operation. Suspense queries throw promises while loading and
|
|
16
|
+
* require a `<Suspense>` boundary in the React tree. TanStack Query v5+ only.
|
|
17
|
+
*/
|
|
12
18
|
export const suspenseQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
13
19
|
name: 'react-suspense-query',
|
|
14
20
|
renderer: jsxRenderer,
|
|
15
21
|
operation(node, ctx) {
|
|
16
|
-
|
|
17
|
-
const {
|
|
18
|
-
|
|
19
|
-
query,
|
|
20
|
-
mutation,
|
|
21
|
-
suspense,
|
|
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, suspense, 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
|
|
@@ -35,74 +29,61 @@ export const suspenseQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
35
29
|
|
|
36
30
|
// query: false means "this IS a query op" (suspense hooks still generate)
|
|
37
31
|
const isQuery = query === false || (!!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase()))
|
|
32
|
+
const queryMethods = new Set(query ? query.methods : [])
|
|
38
33
|
const isMutation =
|
|
39
34
|
mutation !== false &&
|
|
40
35
|
!isQuery &&
|
|
41
|
-
|
|
36
|
+
(mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
|
|
42
37
|
const isSuspense = !!suspense
|
|
43
38
|
|
|
44
39
|
if (!isQuery || isMutation || !isSuspense) return null
|
|
45
40
|
|
|
46
41
|
const importPath = query ? query.importPath : '@tanstack/react-query'
|
|
47
42
|
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const queryKeyTypeName = transformName(`${capitalize(baseName)}SuspenseQueryKey`, 'type', transformers)
|
|
54
|
-
const clientName = transformName(`${baseName}Suspense`, 'function', transformers)
|
|
43
|
+
const queryName = resolver.resolveSuspenseQueryName(node)
|
|
44
|
+
const queryOptionsName = resolver.resolveSuspenseQueryOptionsName(node)
|
|
45
|
+
const queryKeyName = resolver.resolveSuspenseQueryKeyName(node)
|
|
46
|
+
const queryKeyTypeName = resolver.resolveSuspenseQueryKeyTypeName(node)
|
|
47
|
+
const clientName = resolver.resolveSuspenseClientName(node)
|
|
55
48
|
|
|
56
49
|
const meta = {
|
|
57
|
-
file: resolver.resolveFile(
|
|
58
|
-
fileTs: tsResolver.resolveFile(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
50
|
+
file: resolver.resolveFile(operationFileEntry(node, queryName), { root, output, group: group ?? undefined }),
|
|
51
|
+
fileTs: tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
52
|
+
root,
|
|
53
|
+
output: pluginTs.options?.output ?? output,
|
|
54
|
+
group: pluginTs.options?.group ?? undefined,
|
|
55
|
+
}),
|
|
62
56
|
}
|
|
63
57
|
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
73
|
-
...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
74
|
-
...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
75
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode)),
|
|
76
|
-
].filter((name): name is string => !!name && name !== queryKeyTypeName)
|
|
77
|
-
|
|
78
|
-
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
|
|
79
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : undefined
|
|
58
|
+
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, {
|
|
59
|
+
paramsCasing,
|
|
60
|
+
exclude: [queryKeyTypeName],
|
|
61
|
+
order: 'body-response-first',
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null
|
|
65
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
|
|
80
66
|
const fileZod = zodResolver
|
|
81
|
-
? zodResolver.resolveFile(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : undefined].filter(Boolean)
|
|
89
|
-
: []
|
|
67
|
+
? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
68
|
+
root,
|
|
69
|
+
output: pluginZod?.options?.output ?? output,
|
|
70
|
+
group: pluginZod?.options?.group ?? undefined,
|
|
71
|
+
})
|
|
72
|
+
: null
|
|
73
|
+
const zodSchemaNames = resolveZodSchemaNames(node, zodResolver, parser)
|
|
90
74
|
|
|
91
75
|
const clientPlugin = driver.getPlugin(pluginClientName)
|
|
92
76
|
const hasClientPlugin = clientPlugin?.name === pluginClientName
|
|
93
77
|
const shouldUseClientPlugin = hasClientPlugin && clientOptions.clientType !== 'class'
|
|
94
|
-
const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) :
|
|
78
|
+
const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : null
|
|
95
79
|
|
|
96
80
|
const clientFile = shouldUseClientPlugin
|
|
97
|
-
? clientResolver?.resolveFile(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
},
|
|
104
|
-
)
|
|
105
|
-
: undefined
|
|
81
|
+
? clientResolver?.resolveFile(operationFileEntry(node, node.operationId), {
|
|
82
|
+
root,
|
|
83
|
+
output: clientPlugin?.options?.output ?? output,
|
|
84
|
+
group: clientPlugin?.options?.group ?? undefined,
|
|
85
|
+
})
|
|
86
|
+
: null
|
|
106
87
|
|
|
107
88
|
const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientName) : clientName
|
|
108
89
|
|
|
@@ -111,30 +92,24 @@ export const suspenseQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
111
92
|
baseName={meta.file.baseName}
|
|
112
93
|
path={meta.file.path}
|
|
113
94
|
meta={meta.file.meta}
|
|
114
|
-
banner={resolver.resolveBanner(
|
|
115
|
-
footer={resolver.resolveFooter(
|
|
95
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
96
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
116
97
|
>
|
|
117
|
-
{
|
|
118
|
-
<File.Import name={zodSchemaNames as string[]} root={meta.file.path} path={fileZod.path} />
|
|
119
|
-
)}
|
|
98
|
+
{fileZod && zodSchemaNames.length > 0 && <File.Import name={zodSchemaNames} root={meta.file.path} path={fileZod.path} />}
|
|
120
99
|
{clientOptions.importPath ? (
|
|
121
100
|
<>
|
|
122
|
-
{!shouldUseClientPlugin && <File.Import name={'
|
|
101
|
+
{!shouldUseClientPlugin && <File.Import name={'client'} path={clientOptions.importPath} />}
|
|
123
102
|
<File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={clientOptions.importPath} isTypeOnly />
|
|
124
|
-
{clientOptions.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={clientOptions.importPath} isTypeOnly />}
|
|
125
103
|
</>
|
|
126
104
|
) : (
|
|
127
105
|
<>
|
|
128
|
-
{!shouldUseClientPlugin && <File.Import name={['
|
|
106
|
+
{!shouldUseClientPlugin && <File.Import name={['client']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} />}
|
|
129
107
|
<File.Import
|
|
130
108
|
name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
|
|
131
109
|
root={meta.file.path}
|
|
132
|
-
path={path.resolve(root, '.kubb/
|
|
110
|
+
path={path.resolve(root, '.kubb/client.ts')}
|
|
133
111
|
isTypeOnly
|
|
134
112
|
/>
|
|
135
|
-
{clientOptions.dataReturnType === 'full' && (
|
|
136
|
-
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/fetch.ts')} isTypeOnly />
|
|
137
|
-
)}
|
|
138
113
|
</>
|
|
139
114
|
)}
|
|
140
115
|
{shouldUseClientPlugin && clientFile && <File.Import name={[resolvedClientName]} root={meta.file.path} path={clientFile.path} />}
|
|
@@ -181,6 +156,7 @@ export const suspenseQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
181
156
|
paramsType={paramsType}
|
|
182
157
|
pathParamsType={pathParamsType}
|
|
183
158
|
dataReturnType={clientOptions.dataReturnType || 'data'}
|
|
159
|
+
suspense
|
|
184
160
|
/>
|
|
185
161
|
|
|
186
162
|
{suspense && (
|
package/src/plugin.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import {
|
|
3
|
-
import { ast, definePlugin
|
|
4
|
-
import { pluginClientName } from '@kubb/plugin-client'
|
|
2
|
+
import { createGroupConfig } from '@internals/shared'
|
|
3
|
+
import { ast, definePlugin } from '@kubb/core'
|
|
4
|
+
import { isParserEnabled, pluginClientName } from '@kubb/plugin-client'
|
|
5
5
|
import { source as axiosClientSource } from '@kubb/plugin-client/templates/clients/axios.source'
|
|
6
6
|
import { source as fetchClientSource } from '@kubb/plugin-client/templates/clients/fetch.source'
|
|
7
7
|
import { source as configSource } from '@kubb/plugin-client/templates/config.source'
|
|
8
8
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
9
9
|
import { pluginZodName } from '@kubb/plugin-zod'
|
|
10
|
-
import {
|
|
11
|
-
import { QueryKey } from './components/QueryKey.tsx'
|
|
10
|
+
import { mutationKeyTransformer, queryKeyTransformer } from '@internals/tanstack-query'
|
|
12
11
|
import {
|
|
13
12
|
customHookOptionsFileGenerator,
|
|
14
13
|
hookOptionsGenerator,
|
|
@@ -21,25 +20,53 @@ import {
|
|
|
21
20
|
import { resolverReactQuery } from './resolvers/resolverReactQuery.ts'
|
|
22
21
|
import type { PluginReactQuery } from './types.ts'
|
|
23
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Canonical plugin name for `@kubb/plugin-react-query`. Used for driver lookups
|
|
25
|
+
* and cross-plugin dependency references.
|
|
26
|
+
*/
|
|
24
27
|
export const pluginReactQueryName = 'plugin-react-query' satisfies PluginReactQuery['name']
|
|
25
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Generates one TanStack Query hook per OpenAPI operation for React. Queries
|
|
31
|
+
* become `useFooQuery`/`useFooSuspenseQuery`/`useFooInfiniteQuery`; mutations
|
|
32
|
+
* become `useFooMutation`. Each hook is fully typed: query keys, input
|
|
33
|
+
* variables, response data, and error shape all come from the spec.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { defineConfig } from 'kubb'
|
|
38
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
39
|
+
* import { pluginReactQuery } from '@kubb/plugin-react-query'
|
|
40
|
+
*
|
|
41
|
+
* export default defineConfig({
|
|
42
|
+
* input: { path: './petStore.yaml' },
|
|
43
|
+
* output: { path: './src/gen' },
|
|
44
|
+
* plugins: [
|
|
45
|
+
* pluginTs(),
|
|
46
|
+
* pluginReactQuery({
|
|
47
|
+
* output: { path: './hooks' },
|
|
48
|
+
* suspense: {},
|
|
49
|
+
* }),
|
|
50
|
+
* ],
|
|
51
|
+
* })
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
26
54
|
export const pluginReactQuery = definePlugin<PluginReactQuery>((options) => {
|
|
27
55
|
const {
|
|
28
|
-
output = { path: 'hooks',
|
|
56
|
+
output = { path: 'hooks', barrel: { type: 'named' } },
|
|
29
57
|
group,
|
|
30
58
|
exclude = [],
|
|
31
59
|
include,
|
|
32
60
|
override = [],
|
|
33
|
-
parser =
|
|
61
|
+
parser = false,
|
|
34
62
|
suspense = {},
|
|
35
63
|
infinite = false,
|
|
36
|
-
transformers = {},
|
|
37
64
|
paramsType = 'inline',
|
|
38
65
|
pathParamsType = paramsType === 'object' ? 'object' : options.pathParamsType || 'inline',
|
|
39
66
|
mutation = {},
|
|
40
67
|
query = {},
|
|
41
|
-
mutationKey =
|
|
42
|
-
queryKey =
|
|
68
|
+
mutationKey = mutationKeyTransformer,
|
|
69
|
+
queryKey = queryKeyTransformer,
|
|
43
70
|
customOptions,
|
|
44
71
|
paramsCasing,
|
|
45
72
|
client,
|
|
@@ -61,33 +88,20 @@ export const pluginReactQuery = definePlugin<PluginReactQuery>((options) => {
|
|
|
61
88
|
mutationGenerator,
|
|
62
89
|
hookOptionsGenerator,
|
|
63
90
|
customHookOptionsFileGenerator,
|
|
64
|
-
].filter(Boolean)
|
|
91
|
+
].filter((generator): generator is NonNullable<typeof generator> => Boolean(generator))
|
|
65
92
|
|
|
66
|
-
const groupConfig = group
|
|
67
|
-
? ({
|
|
68
|
-
...group,
|
|
69
|
-
name: group.name
|
|
70
|
-
? group.name
|
|
71
|
-
: (ctx: { group: string }) => {
|
|
72
|
-
if (group.type === 'path') {
|
|
73
|
-
return `${ctx.group.split('/')[1]}`
|
|
74
|
-
}
|
|
75
|
-
return `${camelCase(ctx.group)}Controller`
|
|
76
|
-
},
|
|
77
|
-
} satisfies Group)
|
|
78
|
-
: undefined
|
|
93
|
+
const groupConfig = createGroupConfig(group)
|
|
79
94
|
|
|
80
95
|
return {
|
|
81
96
|
name: pluginReactQueryName,
|
|
82
97
|
options,
|
|
83
|
-
dependencies: [pluginTsName, parser
|
|
98
|
+
dependencies: [pluginTsName, isParserEnabled(parser) ? pluginZodName : undefined].filter((dependency): dependency is string => Boolean(dependency)),
|
|
84
99
|
hooks: {
|
|
85
100
|
'kubb:plugin:setup'(ctx) {
|
|
86
101
|
const resolver = userResolver ? { ...resolverReactQuery, ...userResolver } : resolverReactQuery
|
|
87
102
|
|
|
88
103
|
ctx.setOptions({
|
|
89
104
|
output,
|
|
90
|
-
transformers,
|
|
91
105
|
client: {
|
|
92
106
|
bundle: client?.bundle,
|
|
93
107
|
baseURL: client?.baseURL,
|
|
@@ -119,14 +133,14 @@ export const pluginReactQuery = definePlugin<PluginReactQuery>((options) => {
|
|
|
119
133
|
? {
|
|
120
134
|
queryParam: 'id',
|
|
121
135
|
initialPageParam: 0,
|
|
122
|
-
cursorParam:
|
|
123
|
-
nextParam:
|
|
124
|
-
previousParam:
|
|
136
|
+
cursorParam: null,
|
|
137
|
+
nextParam: null,
|
|
138
|
+
previousParam: null,
|
|
125
139
|
...infinite,
|
|
126
140
|
}
|
|
127
141
|
: false,
|
|
128
142
|
suspense,
|
|
129
|
-
customOptions: customOptions ? { name: 'useCustomHookOptions', ...customOptions } :
|
|
143
|
+
customOptions: customOptions ? { name: 'useCustomHookOptions', ...customOptions } : null,
|
|
130
144
|
parser,
|
|
131
145
|
paramsType,
|
|
132
146
|
pathParamsType,
|
|
@@ -154,11 +168,11 @@ export const pluginReactQuery = definePlugin<PluginReactQuery>((options) => {
|
|
|
154
168
|
|
|
155
169
|
if (client?.bundle && !hasClientPlugin && !clientImportPath) {
|
|
156
170
|
ctx.injectFile({
|
|
157
|
-
baseName: '
|
|
158
|
-
path: path.resolve(root, '.kubb/
|
|
171
|
+
baseName: 'client.ts',
|
|
172
|
+
path: path.resolve(root, '.kubb/client.ts'),
|
|
159
173
|
sources: [
|
|
160
174
|
ast.createSource({
|
|
161
|
-
name: '
|
|
175
|
+
name: 'client',
|
|
162
176
|
nodes: [ast.createText(clientName === 'fetch' ? fetchClientSource : axiosClientSource)],
|
|
163
177
|
isExportable: true,
|
|
164
178
|
isIndexable: true,
|