@kubb/plugin-react-query 5.0.0-alpha.9 → 5.0.0-beta.3

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.
Files changed (52) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +1 -3
  3. package/dist/components-DTGLu4UV.js +1451 -0
  4. package/dist/components-DTGLu4UV.js.map +1 -0
  5. package/dist/components-dAKJEn9b.cjs +1571 -0
  6. package/dist/components-dAKJEn9b.cjs.map +1 -0
  7. package/dist/components.cjs +1 -1
  8. package/dist/components.d.ts +105 -161
  9. package/dist/components.js +1 -1
  10. package/dist/generators-CWEQsdO9.cjs +1502 -0
  11. package/dist/generators-CWEQsdO9.cjs.map +1 -0
  12. package/dist/generators-C_fbcjpG.js +1460 -0
  13. package/dist/generators-C_fbcjpG.js.map +1 -0
  14. package/dist/generators.cjs +1 -1
  15. package/dist/generators.d.ts +9 -505
  16. package/dist/generators.js +1 -1
  17. package/dist/index.cjs +114 -126
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.ts +4 -4
  20. package/dist/index.js +110 -126
  21. package/dist/index.js.map +1 -1
  22. package/dist/{types-D5S7Ny9r.d.ts → types-DfaFRSBf.d.ts} +100 -86
  23. package/package.json +59 -62
  24. package/src/components/InfiniteQuery.tsx +75 -139
  25. package/src/components/InfiniteQueryOptions.tsx +62 -164
  26. package/src/components/Mutation.tsx +58 -113
  27. package/src/components/MutationOptions.tsx +61 -80
  28. package/src/components/Query.tsx +67 -140
  29. package/src/components/QueryOptions.tsx +75 -135
  30. package/src/components/SuspenseInfiniteQuery.tsx +75 -139
  31. package/src/components/SuspenseInfiniteQueryOptions.tsx +62 -164
  32. package/src/components/SuspenseQuery.tsx +67 -150
  33. package/src/generators/customHookOptionsFileGenerator.tsx +33 -45
  34. package/src/generators/hookOptionsGenerator.tsx +115 -175
  35. package/src/generators/infiniteQueryGenerator.tsx +183 -176
  36. package/src/generators/mutationGenerator.tsx +127 -138
  37. package/src/generators/queryGenerator.tsx +141 -141
  38. package/src/generators/suspenseInfiniteQueryGenerator.tsx +175 -155
  39. package/src/generators/suspenseQueryGenerator.tsx +149 -148
  40. package/src/index.ts +1 -1
  41. package/src/plugin.ts +133 -183
  42. package/src/resolvers/resolverReactQuery.ts +22 -0
  43. package/src/types.ts +67 -45
  44. package/src/utils.ts +40 -0
  45. package/dist/components-BHQT9ZLc.cjs +0 -1634
  46. package/dist/components-BHQT9ZLc.cjs.map +0 -1
  47. package/dist/components-CpyHYGOw.js +0 -1520
  48. package/dist/components-CpyHYGOw.js.map +0 -1
  49. package/dist/generators-DP07m3rH.cjs +0 -1469
  50. package/dist/generators-DP07m3rH.cjs.map +0 -1
  51. package/dist/generators-DkQwKTc2.js +0 -1427
  52. package/dist/generators-DkQwKTc2.js.map +0 -1
@@ -1,201 +1,221 @@
1
1
  import path from 'node:path'
2
- import { usePluginDriver } from '@kubb/core/hooks'
3
- import { pluginClientName } from '@kubb/plugin-client'
4
- import { Client } from '@kubb/plugin-client/components'
5
- import { createReactGenerator } from '@kubb/plugin-oas/generators'
6
- import { useOas, useOperationManager } from '@kubb/plugin-oas/hooks'
7
- import { getBanner, getFooter } from '@kubb/plugin-oas/utils'
2
+ import { ast, defineGenerator } from '@kubb/core'
3
+ import { Client, pluginClientName } from '@kubb/plugin-client'
8
4
  import { pluginTsName } from '@kubb/plugin-ts'
9
5
  import { pluginZodName } from '@kubb/plugin-zod'
10
- import { File } from '@kubb/react-fabric'
6
+ import { File, jsxRenderer } from '@kubb/renderer-jsx'
11
7
  import { difference } from 'remeda'
12
8
  import { QueryKey, SuspenseInfiniteQuery, SuspenseInfiniteQueryOptions } from '../components'
13
9
  import type { PluginReactQuery } from '../types'
10
+ import { transformName } from '../utils.ts'
14
11
 
15
- export const suspenseInfiniteQueryGenerator = createReactGenerator<PluginReactQuery>({
12
+ export const suspenseInfiniteQueryGenerator = defineGenerator<PluginReactQuery>({
16
13
  name: 'react-suspense-infinite-query',
17
- Operation({ config, operation, generator, plugin }) {
14
+ renderer: jsxRenderer,
15
+ operation(node, ctx) {
16
+ const { adapter, config, driver, resolver, root } = ctx
18
17
  const {
19
- options,
20
- options: { output },
21
- } = plugin
22
- const driver = usePluginDriver()
18
+ output,
19
+ query,
20
+ mutation,
21
+ infinite,
22
+ suspense,
23
+ paramsCasing,
24
+ paramsType,
25
+ pathParamsType,
26
+ parser,
27
+ client: clientOptions,
28
+ group,
29
+ transformers,
30
+ customOptions,
31
+ } = ctx.options
23
32
 
24
- const oas = useOas()
25
- const { getSchemas, getName, getFile } = useOperationManager(generator)
33
+ const pluginTs = driver.getPlugin(pluginTsName)
34
+ if (!pluginTs) return null
35
+ const tsResolver = driver.getResolver(pluginTsName)
26
36
 
27
- const isQuery = typeof options.query === 'boolean' ? true : options.query?.methods.some((method) => operation.method === method)
28
- const isMutation = difference(options.mutation ? options.mutation.methods : [], options.query ? options.query.methods : []).some(
29
- (method) => operation.method === method,
30
- )
31
- const isSuspense = !!options.suspense
32
- const infiniteOptions = options.infinite && typeof options.infinite === 'object' ? options.infinite : undefined
37
+ const isQuery = query === false || (!!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase()))
38
+ const isMutation =
39
+ mutation !== false &&
40
+ !isQuery &&
41
+ difference(mutation ? mutation.methods : [], query ? query.methods : []).some((method) => node.method.toLowerCase() === method.toLowerCase())
42
+ const isSuspense = !!suspense
43
+ const infiniteOptions = infinite && typeof infinite === 'object' ? infinite : undefined
33
44
 
34
- const importPath = options.query ? options.query.importPath : '@tanstack/react-query'
45
+ if (!isQuery || isMutation || !isSuspense || !infiniteOptions) return null
35
46
 
36
- const query = {
37
- name: getName(operation, { type: 'function', prefix: 'use', suffix: 'suspenseInfinite' }),
38
- typeName: getName(operation, { type: 'type' }),
39
- file: getFile(operation, { prefix: 'use', suffix: 'suspenseInfinite' }),
40
- }
47
+ // Validate queryParam exists in operation's query parameters
48
+ const normalizeKey = (key: string) => key.replace(/\?$/, '')
49
+ const queryParamKeys = node.parameters.filter((p) => p.in === 'query').map((p) => p.name)
50
+ const hasQueryParam = infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false
51
+ const hasCursorParam = !infiniteOptions.cursorParam || true
41
52
 
42
- const hasClientPlugin = !!driver.getPluginByName(pluginClientName)
43
- // Class-based clients are not compatible with query hooks, so we generate inline clients
44
- const shouldUseClientPlugin = hasClientPlugin && options.client.clientType !== 'class'
45
- const client = {
46
- name: shouldUseClientPlugin
47
- ? getName(operation, {
48
- type: 'function',
49
- pluginName: pluginClientName,
50
- })
51
- : getName(operation, {
52
- type: 'function',
53
- suffix: 'suspenseInfinite',
54
- }),
55
- file: getFile(operation, { pluginName: pluginClientName }),
56
- }
53
+ if (!hasQueryParam || !hasCursorParam) return null
57
54
 
58
- const queryOptions = {
59
- name: getName(operation, { type: 'function', suffix: 'SuspenseInfiniteQueryOptions' }),
60
- }
55
+ const importPath = query ? query.importPath : '@tanstack/react-query'
61
56
 
62
- const queryKey = {
63
- name: getName(operation, { type: 'const', suffix: 'SuspenseInfiniteQueryKey' }),
64
- typeName: getName(operation, { type: 'type', suffix: 'SuspenseInfiniteQueryKey' }),
65
- }
57
+ const baseName = resolver.resolveName(node.operationId)
58
+ const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1)
59
+ const queryName = transformName(`use${capitalize(baseName)}SuspenseInfinite`, 'function', transformers)
60
+ const queryOptionsName = transformName(`${baseName}SuspenseInfiniteQueryOptions`, 'function', transformers)
61
+ const queryKeyName = transformName(`${baseName}SuspenseInfiniteQueryKey`, 'const', transformers)
62
+ const queryKeyTypeName = transformName(`${capitalize(baseName)}SuspenseInfiniteQueryKey`, 'type', transformers)
63
+ const clientBaseName = transformName(`${baseName}SuspenseInfinite`, 'function', transformers)
66
64
 
67
- const type = {
68
- file: getFile(operation, { pluginName: pluginTsName }),
69
- //todo remove type?
70
- schemas: getSchemas(operation, { pluginName: pluginTsName, type: 'type' }),
65
+ const meta = {
66
+ file: resolver.resolveFile({ name: queryName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
67
+ fileTs: tsResolver.resolveFile(
68
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
69
+ { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group },
70
+ ),
71
71
  }
72
72
 
73
- const zod = {
74
- file: getFile(operation, { pluginName: pluginZodName }),
75
- schemas: getSchemas(operation, { pluginName: pluginZodName, type: 'function' }),
76
- }
73
+ const casedParams = ast.caseParams(node.parameters, paramsCasing)
74
+ const pathParams = casedParams.filter((p) => p.in === 'path')
75
+ const queryParams = casedParams.filter((p) => p.in === 'query')
76
+ const headerParams = casedParams.filter((p) => p.in === 'header')
77
77
 
78
- if (!isQuery || isMutation || !isSuspense || !infiniteOptions) {
79
- return null
80
- }
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
89
+ const fileZod = zodResolver
90
+ ? zodResolver.resolveFile(
91
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
92
+ { root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group },
93
+ )
94
+ : undefined
95
+ const zodSchemaNames =
96
+ zodResolver && parser === 'zod'
97
+ ? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : undefined].filter(Boolean)
98
+ : []
99
+
100
+ const clientPlugin = driver.getPlugin(pluginClientName)
101
+ const hasClientPlugin = clientPlugin?.name === pluginClientName
102
+ const shouldUseClientPlugin = hasClientPlugin && clientOptions.clientType !== 'class'
103
+ const clientResolver = shouldUseClientPlugin ? driver.getResolver(pluginClientName) : undefined
104
+
105
+ const clientFile = shouldUseClientPlugin
106
+ ? clientResolver?.resolveFile(
107
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
108
+ {
109
+ root,
110
+ output: clientPlugin?.options?.output ?? output,
111
+ group: clientPlugin?.options?.group,
112
+ },
113
+ )
114
+ : undefined
115
+
116
+ const resolvedClientName = shouldUseClientPlugin ? (clientResolver?.resolveName(node.operationId) ?? clientBaseName) : clientBaseName
81
117
 
82
118
  return (
83
119
  <File
84
- baseName={query.file.baseName}
85
- path={query.file.path}
86
- meta={query.file.meta}
87
- banner={getBanner({ oas, output, config: driver.config })}
88
- footer={getFooter({ oas, output })}
120
+ baseName={meta.file.baseName}
121
+ path={meta.file.path}
122
+ meta={meta.file.meta}
123
+ banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
124
+ footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
89
125
  >
90
- {options.parser === 'zod' && (
91
- <File.Import name={[zod.schemas.response.name, zod.schemas.request?.name].filter(Boolean)} root={query.file.path} path={zod.file.path} />
126
+ {parser === 'zod' && fileZod && zodSchemaNames.length > 0 && (
127
+ <File.Import name={zodSchemaNames as string[]} root={meta.file.path} path={fileZod.path} />
92
128
  )}
93
- {options.client.importPath ? (
129
+ {clientOptions.importPath ? (
94
130
  <>
95
- {!shouldUseClientPlugin && <File.Import name={'fetch'} path={options.client.importPath} />}
96
- <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={options.client.importPath} isTypeOnly />
97
- {options.client.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={options.client.importPath} isTypeOnly />}
131
+ {!shouldUseClientPlugin && <File.Import name={'fetch'} path={clientOptions.importPath} />}
132
+ <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={clientOptions.importPath} isTypeOnly />
133
+ {clientOptions.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={clientOptions.importPath} isTypeOnly />}
98
134
  </>
99
135
  ) : (
100
136
  <>
101
- {!shouldUseClientPlugin && (
102
- <File.Import name={['fetch']} root={query.file.path} path={path.resolve(config.root, config.output.path, '.kubb/fetch.ts')} />
103
- )}
137
+ {!shouldUseClientPlugin && <File.Import name={['fetch']} root={meta.file.path} path={path.resolve(root, '.kubb/fetch.ts')} />}
104
138
  <File.Import
105
139
  name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
106
- root={query.file.path}
107
- path={path.resolve(config.root, config.output.path, '.kubb/fetch.ts')}
140
+ root={meta.file.path}
141
+ path={path.resolve(root, '.kubb/fetch.ts')}
108
142
  isTypeOnly
109
143
  />
110
- {options.client.dataReturnType === 'full' && (
111
- <File.Import name={['ResponseConfig']} root={query.file.path} path={path.resolve(config.root, config.output.path, '.kubb/fetch.ts')} isTypeOnly />
144
+ {clientOptions.dataReturnType === 'full' && (
145
+ <File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/fetch.ts')} isTypeOnly />
112
146
  )}
113
147
  </>
114
148
  )}
115
-
116
- {shouldUseClientPlugin && <File.Import name={[client.name]} root={query.file.path} path={client.file.path} />}
117
- {!shouldUseClientPlugin && (
118
- <File.Import name={['buildFormData']} root={query.file.path} path={path.resolve(config.root, config.output.path, '.kubb/config.ts')} />
149
+ {shouldUseClientPlugin && clientFile && <File.Import name={[resolvedClientName]} root={meta.file.path} path={clientFile.path} />}
150
+ {!shouldUseClientPlugin && <File.Import name={['buildFormData']} root={meta.file.path} path={path.resolve(root, '.kubb/config.ts')} />}
151
+ {customOptions && <File.Import name={[customOptions.name]} path={customOptions.importPath} />}
152
+ {meta.fileTs && importedTypeNames.length > 0 && (
153
+ <File.Import name={Array.from(new Set(importedTypeNames))} root={meta.file.path} path={meta.fileTs.path} isTypeOnly />
119
154
  )}
120
- {options.customOptions && <File.Import name={[options.customOptions.name]} path={options.customOptions.importPath} />}
121
- <File.Import
122
- name={[
123
- type.schemas.request?.name,
124
- type.schemas.response.name,
125
- type.schemas.pathParams?.name,
126
- type.schemas.queryParams?.name,
127
- type.schemas.headerParams?.name,
128
- ...(type.schemas.statusCodes?.map((item) => item.name) || []),
129
- ].filter(Boolean)}
130
- root={query.file.path}
131
- path={type.file.path}
132
- isTypeOnly
133
- />
155
+
134
156
  <QueryKey
135
- name={queryKey.name}
136
- typeName={queryKey.typeName}
137
- operation={operation}
138
- paramsCasing={options.paramsCasing}
139
- pathParamsType={options.pathParamsType}
140
- typeSchemas={type.schemas}
141
- transformer={options.queryKey}
157
+ name={queryKeyName}
158
+ typeName={queryKeyTypeName}
159
+ node={node}
160
+ tsResolver={tsResolver}
161
+ pathParamsType={pathParamsType}
162
+ paramsCasing={paramsCasing}
163
+ transformer={ctx.options.queryKey}
142
164
  />
165
+
143
166
  {!shouldUseClientPlugin && (
144
167
  <Client
145
- name={client.name}
146
- baseURL={options.client.baseURL}
147
- operation={operation}
148
- typeSchemas={type.schemas}
149
- zodSchemas={zod.schemas}
150
- dataReturnType={options.client.dataReturnType || 'data'}
151
- paramsCasing={options.client?.paramsCasing || options.paramsCasing}
152
- paramsType={options.paramsType}
153
- pathParamsType={options.pathParamsType}
154
- parser={options.parser}
168
+ name={resolvedClientName}
169
+ baseURL={clientOptions.baseURL}
170
+ dataReturnType={clientOptions.dataReturnType || 'data'}
171
+ paramsCasing={clientOptions.paramsCasing || paramsCasing}
172
+ paramsType={paramsType}
173
+ pathParamsType={pathParamsType}
174
+ parser={parser}
175
+ node={node}
176
+ tsResolver={tsResolver}
177
+ zodResolver={zodResolver}
155
178
  />
156
179
  )}
157
- {infiniteOptions && (
158
- <>
159
- <File.Import name={['InfiniteData']} isTypeOnly path={importPath} />
160
- <File.Import name={['infiniteQueryOptions']} path={importPath} />
161
- <SuspenseInfiniteQueryOptions
162
- name={queryOptions.name}
163
- clientName={client.name}
164
- queryKeyName={queryKey.name}
165
- typeSchemas={type.schemas}
166
- paramsCasing={options.paramsCasing}
167
- paramsType={options.paramsType}
168
- pathParamsType={options.pathParamsType}
169
- dataReturnType={options.client.dataReturnType || 'data'}
170
- cursorParam={infiniteOptions.cursorParam}
171
- nextParam={infiniteOptions.nextParam}
172
- previousParam={infiniteOptions.previousParam}
173
- initialPageParam={infiniteOptions.initialPageParam}
174
- queryParam={infiniteOptions.queryParam}
175
- />
176
- </>
177
- )}
178
- {infiniteOptions && (
179
- <>
180
- <File.Import name={['useSuspenseInfiniteQuery']} path={importPath} />
181
- <File.Import name={['QueryKey', 'QueryClient', 'UseSuspenseInfiniteQueryOptions', 'UseSuspenseInfiniteQueryResult']} path={importPath} isTypeOnly />
182
- <SuspenseInfiniteQuery
183
- name={query.name}
184
- queryOptionsName={queryOptions.name}
185
- typeSchemas={type.schemas}
186
- paramsCasing={options.paramsCasing}
187
- paramsType={options.paramsType}
188
- pathParamsType={options.pathParamsType}
189
- operation={operation}
190
- dataReturnType={options.client.dataReturnType || 'data'}
191
- queryKeyName={queryKey.name}
192
- queryKeyTypeName={queryKey.typeName}
193
- customOptions={options.customOptions}
194
- initialPageParam={infiniteOptions.initialPageParam}
195
- queryParam={infiniteOptions.queryParam}
196
- />
197
- </>
198
- )}
180
+
181
+ <File.Import name={['InfiniteData']} isTypeOnly path={importPath} />
182
+ <File.Import name={['infiniteQueryOptions']} path={importPath} />
183
+
184
+ <SuspenseInfiniteQueryOptions
185
+ name={queryOptionsName}
186
+ clientName={resolvedClientName}
187
+ queryKeyName={queryKeyName}
188
+ node={node}
189
+ tsResolver={tsResolver}
190
+ paramsCasing={paramsCasing}
191
+ paramsType={paramsType}
192
+ pathParamsType={pathParamsType}
193
+ dataReturnType={clientOptions.dataReturnType || 'data'}
194
+ cursorParam={infiniteOptions.cursorParam}
195
+ nextParam={infiniteOptions.nextParam}
196
+ previousParam={infiniteOptions.previousParam}
197
+ initialPageParam={infiniteOptions.initialPageParam}
198
+ queryParam={infiniteOptions.queryParam}
199
+ />
200
+
201
+ <File.Import name={['useSuspenseInfiniteQuery']} path={importPath} />
202
+ <File.Import name={['QueryKey', 'QueryClient', 'UseSuspenseInfiniteQueryOptions', 'UseSuspenseInfiniteQueryResult']} path={importPath} isTypeOnly />
203
+
204
+ <SuspenseInfiniteQuery
205
+ name={queryName}
206
+ queryOptionsName={queryOptionsName}
207
+ queryKeyName={queryKeyName}
208
+ queryKeyTypeName={queryKeyTypeName}
209
+ node={node}
210
+ tsResolver={tsResolver}
211
+ paramsCasing={paramsCasing}
212
+ paramsType={paramsType}
213
+ pathParamsType={pathParamsType}
214
+ dataReturnType={clientOptions.dataReturnType || 'data'}
215
+ initialPageParam={infiniteOptions.initialPageParam}
216
+ queryParam={infiniteOptions.queryParam}
217
+ customOptions={customOptions}
218
+ />
199
219
  </File>
200
220
  )
201
221
  },