@kubb/plugin-react-query 5.0.0-beta.3 → 5.0.0-beta.30
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-CDmg-RPi.js} +275 -255
- package/dist/components-CDmg-RPi.js.map +1 -0
- package/dist/{components-dAKJEn9b.cjs → components-MPBTffPl.cjs} +299 -255
- package/dist/components-MPBTffPl.cjs.map +1 -0
- package/dist/components.cjs +1 -1
- package/dist/components.d.ts +1 -75
- package/dist/components.js +1 -1
- package/dist/{generators-C_fbcjpG.js → generators-Bma51Uar.js} +301 -261
- package/dist/generators-Bma51Uar.js.map +1 -0
- package/dist/{generators-CWEQsdO9.cjs → generators-BtsWNz-6.cjs} +299 -259
- package/dist/generators-BtsWNz-6.cjs.map +1 -0
- package/dist/generators.cjs +1 -1
- package/dist/generators.d.ts +41 -1
- package/dist/generators.js +1 -1
- package/dist/index.cjs +143 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +30 -1
- package/dist/index.js +143 -20
- 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 +19 -13
- package/src/components/InfiniteQueryOptions.tsx +29 -47
- package/src/components/Mutation.tsx +35 -15
- package/src/components/MutationOptions.tsx +14 -13
- package/src/components/Query.tsx +9 -10
- package/src/components/QueryOptions.tsx +6 -27
- package/src/components/SuspenseInfiniteQuery.tsx +19 -13
- package/src/components/SuspenseInfiniteQueryOptions.tsx +29 -47
- package/src/components/SuspenseQuery.tsx +9 -10
- package/src/generators/customHookOptionsFileGenerator.tsx +18 -14
- package/src/generators/hookOptionsGenerator.tsx +36 -33
- package/src/generators/infiniteQueryGenerator.tsx +46 -64
- package/src/generators/mutationGenerator.tsx +42 -50
- package/src/generators/queryGenerator.tsx +43 -49
- package/src/generators/suspenseInfiniteQueryGenerator.tsx +41 -51
- package/src/generators/suspenseQueryGenerator.tsx +44 -62
- package/src/plugin.ts +42 -16
- 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,26 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import {
|
|
2
|
+
import { getOperationParameters, resolveOperationTypeNames } from '@internals/shared'
|
|
3
|
+
import { resolveZodSchemaNames } from '@internals/tanstack-query'
|
|
4
|
+
import { 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
|
-
const {
|
|
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
|
|
|
@@ -40,13 +46,13 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
|
|
|
40
46
|
!isQuery &&
|
|
41
47
|
difference(mutation ? mutation.methods : [], query ? query.methods : []).some((method) => node.method.toLowerCase() === method.toLowerCase())
|
|
42
48
|
const isSuspense = !!suspense
|
|
43
|
-
const infiniteOptions = infinite && typeof infinite === 'object' ? infinite :
|
|
49
|
+
const infiniteOptions = infinite && typeof infinite === 'object' ? infinite : null
|
|
44
50
|
|
|
45
51
|
if (!isQuery || isMutation || !isSuspense || !infiniteOptions) return null
|
|
46
52
|
|
|
47
53
|
// Validate queryParam exists in operation's query parameters
|
|
48
54
|
const normalizeKey = (key: string) => key.replace(/\?$/, '')
|
|
49
|
-
const queryParamKeys = node
|
|
55
|
+
const queryParamKeys = getOperationParameters(node).query.map((p) => p.name)
|
|
50
56
|
const hasQueryParam = infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false
|
|
51
57
|
const hasCursorParam = !infiniteOptions.cursorParam || true
|
|
52
58
|
|
|
@@ -54,53 +60,39 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
|
|
|
54
60
|
|
|
55
61
|
const importPath = query ? query.importPath : '@tanstack/react-query'
|
|
56
62
|
|
|
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)
|
|
63
|
+
const queryName = resolver.resolveSuspenseInfiniteQueryName(node)
|
|
64
|
+
const queryOptionsName = resolver.resolveSuspenseInfiniteQueryOptionsName(node)
|
|
65
|
+
const queryKeyName = resolver.resolveSuspenseInfiniteQueryKeyName(node)
|
|
66
|
+
const queryKeyTypeName = resolver.resolveSuspenseInfiniteQueryKeyTypeName(node)
|
|
67
|
+
const clientBaseName = resolver.resolveSuspenseInfiniteClientName(node)
|
|
64
68
|
|
|
65
69
|
const meta = {
|
|
66
|
-
file: resolver.resolveFile(
|
|
70
|
+
file: resolver.resolveFile(
|
|
71
|
+
{ name: queryName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
72
|
+
{ root, output, group: group ?? undefined },
|
|
73
|
+
),
|
|
67
74
|
fileTs: tsResolver.resolveFile(
|
|
68
75
|
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
69
|
-
{ root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group },
|
|
76
|
+
{ root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group ?? undefined },
|
|
70
77
|
),
|
|
71
78
|
}
|
|
72
79
|
|
|
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
|
|
80
|
+
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing, order: 'body-response-first' })
|
|
81
|
+
|
|
82
|
+
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : null
|
|
83
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
|
|
89
84
|
const fileZod = zodResolver
|
|
90
85
|
? zodResolver.resolveFile(
|
|
91
86
|
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
92
|
-
{ root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group },
|
|
87
|
+
{ root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group ?? undefined },
|
|
93
88
|
)
|
|
94
|
-
:
|
|
95
|
-
const zodSchemaNames =
|
|
96
|
-
zodResolver && parser === 'zod'
|
|
97
|
-
? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : undefined].filter(Boolean)
|
|
98
|
-
: []
|
|
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
98
|
? clientResolver?.resolveFile(
|
|
@@ -108,10 +100,10 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
|
|
|
108
100
|
{
|
|
109
101
|
root,
|
|
110
102
|
output: clientPlugin?.options?.output ?? output,
|
|
111
|
-
group: clientPlugin?.options?.group,
|
|
103
|
+
group: clientPlugin?.options?.group ?? undefined,
|
|
112
104
|
},
|
|
113
105
|
)
|
|
114
|
-
:
|
|
106
|
+
: null
|
|
115
107
|
|
|
116
108
|
const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientBaseName) : clientBaseName
|
|
117
109
|
|
|
@@ -120,29 +112,27 @@ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>(
|
|
|
120
112
|
baseName={meta.file.baseName}
|
|
121
113
|
path={meta.file.path}
|
|
122
114
|
meta={meta.file.meta}
|
|
123
|
-
banner={resolver.resolveBanner(
|
|
124
|
-
footer={resolver.resolveFooter(
|
|
115
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
116
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
125
117
|
>
|
|
126
|
-
{
|
|
127
|
-
<File.Import name={zodSchemaNames as string[]} root={meta.file.path} path={fileZod.path} />
|
|
128
|
-
)}
|
|
118
|
+
{fileZod && zodSchemaNames.length > 0 && <File.Import name={zodSchemaNames} root={meta.file.path} path={fileZod.path} />}
|
|
129
119
|
{clientOptions.importPath ? (
|
|
130
120
|
<>
|
|
131
|
-
{!shouldUseClientPlugin && <File.Import name={'
|
|
121
|
+
{!shouldUseClientPlugin && <File.Import name={'client'} path={clientOptions.importPath} />}
|
|
132
122
|
<File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={clientOptions.importPath} isTypeOnly />
|
|
133
123
|
{clientOptions.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={clientOptions.importPath} isTypeOnly />}
|
|
134
124
|
</>
|
|
135
125
|
) : (
|
|
136
126
|
<>
|
|
137
|
-
{!shouldUseClientPlugin && <File.Import name={['
|
|
127
|
+
{!shouldUseClientPlugin && <File.Import name={['client']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} />}
|
|
138
128
|
<File.Import
|
|
139
129
|
name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
|
|
140
130
|
root={meta.file.path}
|
|
141
|
-
path={path.resolve(root, '.kubb/
|
|
131
|
+
path={path.resolve(root, '.kubb/client.ts')}
|
|
142
132
|
isTypeOnly
|
|
143
133
|
/>
|
|
144
134
|
{clientOptions.dataReturnType === 'full' && (
|
|
145
|
-
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/
|
|
135
|
+
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} isTypeOnly />
|
|
146
136
|
)}
|
|
147
137
|
</>
|
|
148
138
|
)}
|
|
@@ -1,33 +1,27 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import {
|
|
2
|
+
import { resolveOperationTypeNames } from '@internals/shared'
|
|
3
|
+
import { resolveZodSchemaNames } from '@internals/tanstack-query'
|
|
4
|
+
import { 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
|
-
const {
|
|
17
|
-
const {
|
|
18
|
-
output,
|
|
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
|
+
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
|
|
@@ -45,53 +39,43 @@ export const suspenseQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
45
39
|
|
|
46
40
|
const importPath = query ? query.importPath : '@tanstack/react-query'
|
|
47
41
|
|
|
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)
|
|
42
|
+
const queryName = resolver.resolveSuspenseQueryName(node)
|
|
43
|
+
const queryOptionsName = resolver.resolveSuspenseQueryOptionsName(node)
|
|
44
|
+
const queryKeyName = resolver.resolveSuspenseQueryKeyName(node)
|
|
45
|
+
const queryKeyTypeName = resolver.resolveSuspenseQueryKeyTypeName(node)
|
|
46
|
+
const clientName = resolver.resolveSuspenseClientName(node)
|
|
55
47
|
|
|
56
48
|
const meta = {
|
|
57
|
-
file: resolver.resolveFile(
|
|
49
|
+
file: resolver.resolveFile(
|
|
50
|
+
{ name: queryName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
51
|
+
{ root, output, group: group ?? undefined },
|
|
52
|
+
),
|
|
58
53
|
fileTs: tsResolver.resolveFile(
|
|
59
54
|
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
60
|
-
{ root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group },
|
|
55
|
+
{ root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group ?? undefined },
|
|
61
56
|
),
|
|
62
57
|
}
|
|
63
58
|
|
|
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
|
|
59
|
+
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, {
|
|
60
|
+
paramsCasing,
|
|
61
|
+
exclude: [queryKeyTypeName],
|
|
62
|
+
order: 'body-response-first',
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : null
|
|
66
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
|
|
80
67
|
const fileZod = zodResolver
|
|
81
68
|
? zodResolver.resolveFile(
|
|
82
69
|
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
83
|
-
{ root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group },
|
|
70
|
+
{ root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group ?? undefined },
|
|
84
71
|
)
|
|
85
|
-
:
|
|
86
|
-
const zodSchemaNames =
|
|
87
|
-
zodResolver && parser === 'zod'
|
|
88
|
-
? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : undefined].filter(Boolean)
|
|
89
|
-
: []
|
|
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
81
|
? clientResolver?.resolveFile(
|
|
@@ -99,10 +83,10 @@ export const suspenseQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
99
83
|
{
|
|
100
84
|
root,
|
|
101
85
|
output: clientPlugin?.options?.output ?? output,
|
|
102
|
-
group: clientPlugin?.options?.group,
|
|
86
|
+
group: clientPlugin?.options?.group ?? undefined,
|
|
103
87
|
},
|
|
104
88
|
)
|
|
105
|
-
:
|
|
89
|
+
: null
|
|
106
90
|
|
|
107
91
|
const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientName) : clientName
|
|
108
92
|
|
|
@@ -111,29 +95,27 @@ export const suspenseQueryGenerator = defineGenerator<PluginReactQuery>({
|
|
|
111
95
|
baseName={meta.file.baseName}
|
|
112
96
|
path={meta.file.path}
|
|
113
97
|
meta={meta.file.meta}
|
|
114
|
-
banner={resolver.resolveBanner(
|
|
115
|
-
footer={resolver.resolveFooter(
|
|
98
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
99
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
116
100
|
>
|
|
117
|
-
{
|
|
118
|
-
<File.Import name={zodSchemaNames as string[]} root={meta.file.path} path={fileZod.path} />
|
|
119
|
-
)}
|
|
101
|
+
{fileZod && zodSchemaNames.length > 0 && <File.Import name={zodSchemaNames} root={meta.file.path} path={fileZod.path} />}
|
|
120
102
|
{clientOptions.importPath ? (
|
|
121
103
|
<>
|
|
122
|
-
{!shouldUseClientPlugin && <File.Import name={'
|
|
104
|
+
{!shouldUseClientPlugin && <File.Import name={'client'} path={clientOptions.importPath} />}
|
|
123
105
|
<File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={clientOptions.importPath} isTypeOnly />
|
|
124
106
|
{clientOptions.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={clientOptions.importPath} isTypeOnly />}
|
|
125
107
|
</>
|
|
126
108
|
) : (
|
|
127
109
|
<>
|
|
128
|
-
{!shouldUseClientPlugin && <File.Import name={['
|
|
110
|
+
{!shouldUseClientPlugin && <File.Import name={['client']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} />}
|
|
129
111
|
<File.Import
|
|
130
112
|
name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
|
|
131
113
|
root={meta.file.path}
|
|
132
|
-
path={path.resolve(root, '.kubb/
|
|
114
|
+
path={path.resolve(root, '.kubb/client.ts')}
|
|
133
115
|
isTypeOnly
|
|
134
116
|
/>
|
|
135
117
|
{clientOptions.dataReturnType === 'full' && (
|
|
136
|
-
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/
|
|
118
|
+
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} isTypeOnly />
|
|
137
119
|
)}
|
|
138
120
|
</>
|
|
139
121
|
)}
|
package/src/plugin.ts
CHANGED
|
@@ -7,8 +7,7 @@ import { source as fetchClientSource } from '@kubb/plugin-client/templates/clien
|
|
|
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' },
|
|
@@ -33,13 +61,12 @@ export const pluginReactQuery = definePlugin<PluginReactQuery>((options) => {
|
|
|
33
61
|
parser = 'client',
|
|
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,7 +88,7 @@ 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
93
|
const groupConfig = group
|
|
67
94
|
? ({
|
|
@@ -75,19 +102,18 @@ export const pluginReactQuery = definePlugin<PluginReactQuery>((options) => {
|
|
|
75
102
|
return `${camelCase(ctx.group)}Controller`
|
|
76
103
|
},
|
|
77
104
|
} satisfies Group)
|
|
78
|
-
:
|
|
105
|
+
: null
|
|
79
106
|
|
|
80
107
|
return {
|
|
81
108
|
name: pluginReactQueryName,
|
|
82
109
|
options,
|
|
83
|
-
dependencies: [pluginTsName, parser === 'zod' ? pluginZodName : undefined].filter(Boolean),
|
|
110
|
+
dependencies: [pluginTsName, parser === 'zod' ? pluginZodName : undefined].filter((dependency): dependency is string => Boolean(dependency)),
|
|
84
111
|
hooks: {
|
|
85
112
|
'kubb:plugin:setup'(ctx) {
|
|
86
113
|
const resolver = userResolver ? { ...resolverReactQuery, ...userResolver } : resolverReactQuery
|
|
87
114
|
|
|
88
115
|
ctx.setOptions({
|
|
89
116
|
output,
|
|
90
|
-
transformers,
|
|
91
117
|
client: {
|
|
92
118
|
bundle: client?.bundle,
|
|
93
119
|
baseURL: client?.baseURL,
|
|
@@ -119,14 +145,14 @@ export const pluginReactQuery = definePlugin<PluginReactQuery>((options) => {
|
|
|
119
145
|
? {
|
|
120
146
|
queryParam: 'id',
|
|
121
147
|
initialPageParam: 0,
|
|
122
|
-
cursorParam:
|
|
123
|
-
nextParam:
|
|
124
|
-
previousParam:
|
|
148
|
+
cursorParam: null,
|
|
149
|
+
nextParam: null,
|
|
150
|
+
previousParam: null,
|
|
125
151
|
...infinite,
|
|
126
152
|
}
|
|
127
153
|
: false,
|
|
128
154
|
suspense,
|
|
129
|
-
customOptions: customOptions ? { name: 'useCustomHookOptions', ...customOptions } :
|
|
155
|
+
customOptions: customOptions ? { name: 'useCustomHookOptions', ...customOptions } : null,
|
|
130
156
|
parser,
|
|
131
157
|
paramsType,
|
|
132
158
|
pathParamsType,
|
|
@@ -154,11 +180,11 @@ export const pluginReactQuery = definePlugin<PluginReactQuery>((options) => {
|
|
|
154
180
|
|
|
155
181
|
if (client?.bundle && !hasClientPlugin && !clientImportPath) {
|
|
156
182
|
ctx.injectFile({
|
|
157
|
-
baseName: '
|
|
158
|
-
path: path.resolve(root, '.kubb/
|
|
183
|
+
baseName: 'client.ts',
|
|
184
|
+
path: path.resolve(root, '.kubb/client.ts'),
|
|
159
185
|
sources: [
|
|
160
186
|
ast.createSource({
|
|
161
|
-
name: '
|
|
187
|
+
name: 'client',
|
|
162
188
|
nodes: [ast.createText(clientName === 'fetch' ? fetchClientSource : axiosClientSource)],
|
|
163
189
|
isExportable: true,
|
|
164
190
|
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
|
}))
|