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

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