@kubb/plugin-react-query 5.0.0-beta.3 → 5.0.0-beta.31
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 +34 -83
- package/dist/{components-DTGLu4UV.js → components-BVmVgpLX.js} +379 -279
- package/dist/components-BVmVgpLX.js.map +1 -0
- package/dist/{components-dAKJEn9b.cjs → components-DLUeLMsz.cjs} +409 -279
- package/dist/components-DLUeLMsz.cjs.map +1 -0
- package/dist/components.cjs +1 -1
- package/dist/components.d.ts +4 -76
- package/dist/components.js +1 -1
- package/dist/{generators-CWEQsdO9.cjs → generators--AcF4Y4n.cjs} +332 -410
- package/dist/generators--AcF4Y4n.cjs.map +1 -0
- package/dist/{generators-C_fbcjpG.js → generators-BFn9CLBS.js} +333 -411
- package/dist/generators-BFn9CLBS.js.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 +179 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +30 -1
- package/dist/index.js +179 -26
- package/dist/index.js.map +1 -1
- package/dist/types-DiZPLTXl.d.ts +400 -0
- package/extension.yaml +1507 -0
- package/package.json +16 -18
- package/src/components/InfiniteQuery.tsx +24 -13
- package/src/components/InfiniteQueryOptions.tsx +37 -55
- package/src/components/Mutation.tsx +35 -15
- package/src/components/MutationOptions.tsx +14 -13
- package/src/components/Query.tsx +14 -10
- package/src/components/QueryOptions.tsx +17 -34
- package/src/components/SuspenseInfiniteQuery.tsx +19 -13
- package/src/components/SuspenseInfiniteQueryOptions.tsx +28 -52
- package/src/components/SuspenseQuery.tsx +9 -10
- package/src/generators/customHookOptionsFileGenerator.tsx +18 -14
- package/src/generators/hookOptionsGenerator.tsx +42 -49
- package/src/generators/infiniteQueryGenerator.tsx +55 -76
- package/src/generators/mutationGenerator.tsx +51 -62
- package/src/generators/queryGenerator.tsx +52 -61
- package/src/generators/suspenseInfiniteQueryGenerator.tsx +50 -63
- package/src/generators/suspenseQueryGenerator.tsx +54 -74
- package/src/plugin.ts +45 -31
- package/src/resolvers/resolverReactQuery.ts +102 -6
- package/src/types.ts +199 -61
- package/src/utils.ts +10 -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
|
@@ -1,19 +1,27 @@
|
|
|
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
5
|
import { Client, pluginClientName } from '@kubb/plugin-client'
|
|
4
6
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
5
7
|
import { pluginZodName } from '@kubb/plugin-zod'
|
|
6
|
-
import { File,
|
|
8
|
+
import { File, jsxRendererSync } from '@kubb/renderer-jsx'
|
|
7
9
|
import { difference } from 'remeda'
|
|
8
10
|
import { QueryKey, SuspenseInfiniteQuery, SuspenseInfiniteQueryOptions } from '../components'
|
|
9
11
|
import type { PluginReactQuery } from '../types'
|
|
10
|
-
import { transformName } from '../utils.ts'
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Built-in generator for `useSuspenseInfiniteQuery` hooks. Enabled when both
|
|
15
|
+
* `suspense` and `infinite` are configured. Combines suspense semantics with
|
|
16
|
+
* cursor-based pagination — handlers throw promises while loading and pull
|
|
17
|
+
* additional pages on demand.
|
|
18
|
+
*/
|
|
12
19
|
export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
13
20
|
name: 'react-suspense-infinite-query',
|
|
14
|
-
renderer:
|
|
21
|
+
renderer: jsxRendererSync,
|
|
15
22
|
operation(node, ctx) {
|
|
16
|
-
|
|
23
|
+
if (!ast.isHttpOperationNode(node)) return null
|
|
24
|
+
const { config, driver, resolver, root } = ctx
|
|
17
25
|
const {
|
|
18
26
|
output,
|
|
19
27
|
query,
|
|
@@ -26,7 +34,6 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
|
|
|
26
34
|
parser,
|
|
27
35
|
client: clientOptions,
|
|
28
36
|
group,
|
|
29
|
-
transformers,
|
|
30
37
|
customOptions,
|
|
31
38
|
} = ctx.options
|
|
32
39
|
|
|
@@ -40,13 +47,13 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
|
|
|
40
47
|
!isQuery &&
|
|
41
48
|
difference(mutation ? mutation.methods : [], query ? query.methods : []).some((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 = parser === 'zod' ? 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)
|
|
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,29 +109,27 @@ 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
120
|
{clientOptions.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={clientOptions.importPath} isTypeOnly />}
|
|
134
121
|
</>
|
|
135
122
|
) : (
|
|
136
123
|
<>
|
|
137
|
-
{!shouldUseClientPlugin && <File.Import name={['
|
|
124
|
+
{!shouldUseClientPlugin && <File.Import name={['client']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} />}
|
|
138
125
|
<File.Import
|
|
139
126
|
name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
|
|
140
127
|
root={meta.file.path}
|
|
141
|
-
path={path.resolve(root, '.kubb/
|
|
128
|
+
path={path.resolve(root, '.kubb/client.ts')}
|
|
142
129
|
isTypeOnly
|
|
143
130
|
/>
|
|
144
131
|
{clientOptions.dataReturnType === 'full' && (
|
|
145
|
-
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/
|
|
132
|
+
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} isTypeOnly />
|
|
146
133
|
)}
|
|
147
134
|
</>
|
|
148
135
|
)}
|
|
@@ -1,33 +1,28 @@
|
|
|
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
5
|
import { Client, pluginClientName } from '@kubb/plugin-client'
|
|
4
6
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
5
7
|
import { pluginZodName } from '@kubb/plugin-zod'
|
|
6
|
-
import { File,
|
|
8
|
+
import { File, jsxRendererSync } from '@kubb/renderer-jsx'
|
|
7
9
|
import { difference } from 'remeda'
|
|
8
10
|
import { QueryKey, QueryOptions, SuspenseQuery } from '../components'
|
|
9
11
|
import type { PluginReactQuery } from '../types'
|
|
10
|
-
import { transformName } from '../utils.ts'
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Built-in generator for `useSuspenseQuery` hooks. Enabled when
|
|
15
|
+
* `pluginReactQuery({ suspense: {} })`. Emits one `useFooSuspenseQuery` hook
|
|
16
|
+
* per query operation. Suspense queries throw promises while loading and
|
|
17
|
+
* require a `<Suspense>` boundary in the React tree. TanStack Query v5+ only.
|
|
18
|
+
*/
|
|
12
19
|
export const suspenseQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
13
20
|
name: 'react-suspense-query',
|
|
14
|
-
renderer:
|
|
21
|
+
renderer: jsxRendererSync,
|
|
15
22
|
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
|
|
23
|
+
if (!ast.isHttpOperationNode(node)) return null
|
|
24
|
+
const { config, driver, resolver, root } = ctx
|
|
25
|
+
const { output, query, mutation, suspense, paramsCasing, paramsType, pathParamsType, parser, client: clientOptions, group, customOptions } = ctx.options
|
|
31
26
|
|
|
32
27
|
const pluginTs = driver.getPlugin(pluginTsName)
|
|
33
28
|
if (!pluginTs) return null
|
|
@@ -45,64 +40,50 @@ export const suspenseQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
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 = parser === 'zod' ? 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)
|
|
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,29 +92,27 @@ 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
103
|
{clientOptions.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={clientOptions.importPath} isTypeOnly />}
|
|
125
104
|
</>
|
|
126
105
|
) : (
|
|
127
106
|
<>
|
|
128
|
-
{!shouldUseClientPlugin && <File.Import name={['
|
|
107
|
+
{!shouldUseClientPlugin && <File.Import name={['client']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} />}
|
|
129
108
|
<File.Import
|
|
130
109
|
name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
|
|
131
110
|
root={meta.file.path}
|
|
132
|
-
path={path.resolve(root, '.kubb/
|
|
111
|
+
path={path.resolve(root, '.kubb/client.ts')}
|
|
133
112
|
isTypeOnly
|
|
134
113
|
/>
|
|
135
114
|
{clientOptions.dataReturnType === 'full' && (
|
|
136
|
-
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/
|
|
115
|
+
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} isTypeOnly />
|
|
137
116
|
)}
|
|
138
117
|
</>
|
|
139
118
|
)}
|
|
@@ -181,6 +160,7 @@ export const suspenseQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
181
160
|
paramsType={paramsType}
|
|
182
161
|
pathParamsType={pathParamsType}
|
|
183
162
|
dataReturnType={clientOptions.dataReturnType || 'data'}
|
|
163
|
+
suspense
|
|
184
164
|
/>
|
|
185
165
|
|
|
186
166
|
{suspense && (
|
package/src/plugin.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import {
|
|
3
|
-
import { ast, definePlugin
|
|
2
|
+
import { createGroupConfig } from '@internals/shared'
|
|
3
|
+
import { ast, definePlugin } from '@kubb/core'
|
|
4
4
|
import { 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,8 +20,37 @@ 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
56
|
output = { path: 'hooks', barrelType: 'named' },
|
|
@@ -30,16 +58,15 @@ export const pluginReactQuery = definePlugin<PluginReactQuery>((options) => {
|
|
|
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, { suffix: 'Controller', honorName: true })
|
|
79
94
|
|
|
80
95
|
return {
|
|
81
96
|
name: pluginReactQueryName,
|
|
82
97
|
options,
|
|
83
|
-
dependencies: [pluginTsName, parser === 'zod' ? pluginZodName : undefined].filter(Boolean),
|
|
98
|
+
dependencies: [pluginTsName, parser === 'zod' ? 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,
|
|
@@ -2,21 +2,117 @@ import { camelCase } from '@internals/utils'
|
|
|
2
2
|
import { defineResolver } from '@kubb/core'
|
|
3
3
|
import type { PluginReactQuery } from '../types.ts'
|
|
4
4
|
|
|
5
|
+
function capitalize(name: string): string {
|
|
6
|
+
return `${name.charAt(0).toUpperCase()}${name.slice(1)}`
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
/**
|
|
6
|
-
*
|
|
10
|
+
* Default resolver used by `@kubb/plugin-react-query`. Decides the names and
|
|
11
|
+
* file paths for every generated TanStack Query hook (`useFooQuery`,
|
|
12
|
+
* `useFooMutation`, `useFooInfiniteQuery`, ...) and its companion helpers
|
|
13
|
+
* (`fooQueryKey`, `fooQueryOptions`).
|
|
14
|
+
*
|
|
15
|
+
* Functions and files use camelCase; hooks get the `use` prefix; suspense and
|
|
16
|
+
* infinite variants are suffixed with `Suspense`/`Infinite`.
|
|
7
17
|
*
|
|
8
|
-
*
|
|
18
|
+
* @example Resolve hook and helper names
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { resolverReactQuery } from '@kubb/plugin-react-query'
|
|
9
21
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
22
|
+
* resolverReactQuery.resolveQueryName(operationNode) // 'useGetPetById'
|
|
23
|
+
* resolverReactQuery.resolveMutationName(operationNode) // 'useUpdatePet'
|
|
24
|
+
* resolverReactQuery.resolveQueryKeyName(operationNode) // 'getPetByIdQueryKey'
|
|
25
|
+
* resolverReactQuery.resolveQueryOptionsName(operationNode) // 'getPetByIdQueryOptions'
|
|
26
|
+
* ```
|
|
12
27
|
*/
|
|
13
|
-
export const resolverReactQuery = defineResolver<PluginReactQuery>((
|
|
28
|
+
export const resolverReactQuery = defineResolver<PluginReactQuery>(() => ({
|
|
14
29
|
name: 'default',
|
|
15
30
|
pluginName: 'plugin-react-query',
|
|
16
31
|
default(name, type) {
|
|
17
32
|
return camelCase(name, { isFile: type === 'file' })
|
|
18
33
|
},
|
|
19
34
|
resolveName(name) {
|
|
20
|
-
return
|
|
35
|
+
return this.default(name, 'function')
|
|
36
|
+
},
|
|
37
|
+
resolvePathName(name, type) {
|
|
38
|
+
return this.default(name, type)
|
|
39
|
+
},
|
|
40
|
+
resolveQueryName(node) {
|
|
41
|
+
return `use${capitalize(this.resolveName(node.operationId))}`
|
|
42
|
+
},
|
|
43
|
+
resolveSuspenseQueryName(node) {
|
|
44
|
+
return `use${capitalize(this.resolveName(node.operationId))}Suspense`
|
|
45
|
+
},
|
|
46
|
+
resolveInfiniteQueryName(node) {
|
|
47
|
+
return `use${capitalize(this.resolveName(node.operationId))}Infinite`
|
|
48
|
+
},
|
|
49
|
+
resolveSuspenseInfiniteQueryName(node) {
|
|
50
|
+
return `use${capitalize(this.resolveName(node.operationId))}SuspenseInfinite`
|
|
51
|
+
},
|
|
52
|
+
resolveMutationName(node) {
|
|
53
|
+
return `use${capitalize(this.resolveName(node.operationId))}`
|
|
54
|
+
},
|
|
55
|
+
resolveQueryOptionsName(node) {
|
|
56
|
+
return `${this.resolveName(node.operationId)}QueryOptions`
|
|
57
|
+
},
|
|
58
|
+
resolveSuspenseQueryOptionsName(node) {
|
|
59
|
+
return `${this.resolveName(node.operationId)}SuspenseQueryOptions`
|
|
60
|
+
},
|
|
61
|
+
resolveInfiniteQueryOptionsName(node) {
|
|
62
|
+
return `${this.resolveName(node.operationId)}InfiniteQueryOptions`
|
|
63
|
+
},
|
|
64
|
+
resolveSuspenseInfiniteQueryOptionsName(node) {
|
|
65
|
+
return `${this.resolveName(node.operationId)}SuspenseInfiniteQueryOptions`
|
|
66
|
+
},
|
|
67
|
+
resolveMutationOptionsName(node) {
|
|
68
|
+
return `${this.resolveName(node.operationId)}MutationOptions`
|
|
69
|
+
},
|
|
70
|
+
resolveQueryKeyName(node) {
|
|
71
|
+
return `${this.resolveName(node.operationId)}QueryKey`
|
|
72
|
+
},
|
|
73
|
+
resolveSuspenseQueryKeyName(node) {
|
|
74
|
+
return `${this.resolveName(node.operationId)}SuspenseQueryKey`
|
|
75
|
+
},
|
|
76
|
+
resolveInfiniteQueryKeyName(node) {
|
|
77
|
+
return `${this.resolveName(node.operationId)}InfiniteQueryKey`
|
|
78
|
+
},
|
|
79
|
+
resolveSuspenseInfiniteQueryKeyName(node) {
|
|
80
|
+
return `${this.resolveName(node.operationId)}SuspenseInfiniteQueryKey`
|
|
81
|
+
},
|
|
82
|
+
resolveMutationKeyName(node) {
|
|
83
|
+
return `${this.resolveName(node.operationId)}MutationKey`
|
|
84
|
+
},
|
|
85
|
+
resolveQueryKeyTypeName(node) {
|
|
86
|
+
return `${capitalize(this.resolveName(node.operationId))}QueryKey`
|
|
87
|
+
},
|
|
88
|
+
resolveSuspenseQueryKeyTypeName(node) {
|
|
89
|
+
return `${capitalize(this.resolveName(node.operationId))}SuspenseQueryKey`
|
|
90
|
+
},
|
|
91
|
+
resolveInfiniteQueryKeyTypeName(node) {
|
|
92
|
+
return `${capitalize(this.resolveName(node.operationId))}InfiniteQueryKey`
|
|
93
|
+
},
|
|
94
|
+
resolveSuspenseInfiniteQueryKeyTypeName(node) {
|
|
95
|
+
return `${capitalize(this.resolveName(node.operationId))}SuspenseInfiniteQueryKey`
|
|
96
|
+
},
|
|
97
|
+
resolveMutationTypeName(node) {
|
|
98
|
+
return capitalize(this.resolveName(node.operationId))
|
|
99
|
+
},
|
|
100
|
+
resolveClientName(node) {
|
|
101
|
+
return this.resolveName(node.operationId)
|
|
102
|
+
},
|
|
103
|
+
resolveSuspenseClientName(node) {
|
|
104
|
+
return `${this.resolveName(node.operationId)}Suspense`
|
|
105
|
+
},
|
|
106
|
+
resolveInfiniteClientName(node) {
|
|
107
|
+
return `${this.resolveName(node.operationId)}Infinite`
|
|
108
|
+
},
|
|
109
|
+
resolveSuspenseInfiniteClientName(node) {
|
|
110
|
+
return `${this.resolveName(node.operationId)}SuspenseInfinite`
|
|
111
|
+
},
|
|
112
|
+
resolveHookOptionsName() {
|
|
113
|
+
return 'HookOptions'
|
|
114
|
+
},
|
|
115
|
+
resolveCustomHookOptionsName() {
|
|
116
|
+
return 'getCustomHookOptions'
|
|
21
117
|
},
|
|
22
118
|
}))
|