@kubb/plugin-swr 5.0.0-alpha.34 → 5.0.0-alpha.35

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 (41) hide show
  1. package/dist/components-BJSzUg7M.cjs +955 -0
  2. package/dist/components-BJSzUg7M.cjs.map +1 -0
  3. package/dist/components-JQ2KRFCa.js +877 -0
  4. package/dist/components-JQ2KRFCa.js.map +1 -0
  5. package/dist/components.cjs +1 -1
  6. package/dist/components.d.ts +77 -37
  7. package/dist/components.js +1 -1
  8. package/dist/generators-17ulS9mu.cjs +537 -0
  9. package/dist/generators-17ulS9mu.cjs.map +1 -0
  10. package/dist/generators-Cl7nr-FB.js +526 -0
  11. package/dist/generators-Cl7nr-FB.js.map +1 -0
  12. package/dist/generators.cjs +1 -1
  13. package/dist/generators.d.ts +4 -4
  14. package/dist/generators.js +1 -1
  15. package/dist/index.cjs +132 -110
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.ts +22 -2
  18. package/dist/index.js +132 -110
  19. package/dist/index.js.map +1 -1
  20. package/dist/{types-BVDtH9S7.d.ts → types-FA5mH9Ch.d.ts} +46 -90
  21. package/package.json +7 -11
  22. package/src/components/Mutation.tsx +165 -170
  23. package/src/components/MutationKey.tsx +50 -1
  24. package/src/components/Query.tsx +122 -126
  25. package/src/components/QueryKey.tsx +65 -1
  26. package/src/components/QueryOptions.tsx +38 -93
  27. package/src/generators/mutationGenerator.tsx +194 -117
  28. package/src/generators/queryGenerator.tsx +205 -139
  29. package/src/plugin.ts +117 -152
  30. package/src/resolvers/resolverSwr.ts +26 -0
  31. package/src/resolvers/resolverSwrLegacy.ts +17 -0
  32. package/src/types.ts +55 -18
  33. package/src/utils.ts +209 -0
  34. package/dist/components-DaCTPplv.js +0 -756
  35. package/dist/components-DaCTPplv.js.map +0 -1
  36. package/dist/components-Qs8_faOt.cjs +0 -834
  37. package/dist/components-Qs8_faOt.cjs.map +0 -1
  38. package/dist/generators-0YayIrse.js +0 -400
  39. package/dist/generators-0YayIrse.js.map +0 -1
  40. package/dist/generators-Bd4rCa3l.cjs +0 -411
  41. package/dist/generators-Bd4rCa3l.cjs.map +0 -1
@@ -1,182 +1,248 @@
1
1
  import path from 'node:path'
2
- import { useDriver } from '@kubb/core/hooks'
3
- import { ClientLegacy as Client, pluginClientName } from '@kubb/plugin-client'
4
- import { createReactGenerator } from '@kubb/plugin-oas/generators'
5
- import { useOas, useOperationManager } from '@kubb/plugin-oas/hooks'
6
- import { getBanner, getFooter } from '@kubb/plugin-oas/utils'
2
+
3
+ import { ast, defineGenerator } from '@kubb/core'
4
+ import { ClientLegacy as ClientLegacyComponent, pluginClientName } from '@kubb/plugin-client'
7
5
  import { pluginTsName } from '@kubb/plugin-ts'
8
6
  import { pluginZodName } from '@kubb/plugin-zod'
9
- import { File } from '@kubb/renderer-jsx'
10
- import { difference } from 'remeda'
7
+ import { File, jsxRenderer } from '@kubb/renderer-jsx'
11
8
  import { Query, QueryKey, QueryOptions } from '../components'
12
9
  import type { PluginSwr } from '../types'
10
+ import { transformName } from '../utils.ts'
13
11
 
14
- export const queryGenerator = createReactGenerator<PluginSwr>({
12
+ export const queryGenerator = defineGenerator<PluginSwr>({
15
13
  name: 'swr-query',
16
- Operation({ config, operation, generator, plugin }) {
17
- const {
18
- options,
19
- options: { output },
20
- } = plugin
21
- const driver = useDriver()
22
- const root = path.resolve(config.root, config.output.path)
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 importPath = options.query ? options.query.importPath : 'swr/mutation'
14
+ renderer: jsxRenderer,
15
+ operation(node, ctx) {
16
+ const { adapter, config, driver, resolver, root } = ctx
17
+ const { output, query, paramsCasing, paramsType, pathParamsType, parser, client: clientOptions, group, transformers } = ctx.options
32
18
 
33
- const query = {
34
- name: getName(operation, { type: 'function', prefix: 'use' }),
35
- typeName: getName(operation, { type: 'type' }),
36
- file: getFile(operation, { prefix: 'use' }),
37
- }
19
+ const pluginTs = driver.getPlugin(pluginTsName)
20
+ if (!pluginTs?.resolver) return null
21
+ const tsResolver = pluginTs.resolver
38
22
 
39
- const hasClientPlugin = !!driver.getPlugin(pluginClientName)
40
- // Class-based clients are not compatible with query hooks, so we generate inline clients
41
- const shouldUseClientPlugin = hasClientPlugin && options.client.clientType !== 'class'
42
- const client = {
43
- name: shouldUseClientPlugin
44
- ? getName(operation, {
45
- type: 'function',
46
- pluginName: pluginClientName,
47
- })
48
- : getName(operation, {
49
- type: 'function',
50
- }),
51
- file: getFile(operation, { pluginName: pluginClientName }),
52
- }
23
+ // Determine if this operation is a query
24
+ const isQuery = !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase())
53
25
 
54
- const queryOptions = {
55
- name: getName(operation, { type: 'function', suffix: 'QueryOptions' }),
56
- }
57
- const queryKey = {
58
- name: getName(operation, { type: 'const', suffix: 'QueryKey' }),
59
- typeName: getName(operation, { type: 'type', suffix: 'QueryKey' }),
60
- }
26
+ if (!isQuery) return null
61
27
 
62
- const type = {
63
- file: getFile(operation, { pluginName: pluginTsName }),
64
- //todo remove type?
65
- schemas: getSchemas(operation, {
66
- pluginName: pluginTsName,
67
- type: 'type',
68
- }),
69
- }
28
+ const importPath = query ? query.importPath : 'swr'
70
29
 
71
- const zod = {
72
- file: getFile(operation, { pluginName: pluginZodName }),
73
- schemas: getSchemas(operation, {
74
- pluginName: pluginZodName,
75
- type: 'function',
76
- }),
77
- }
30
+ // Resolve names — apply transformers.name to each constructed name to match the old
31
+ // createPlugin resolveName lifecycle (e.g. `findPetsByTagsQueryKey` `findPetsByTagsQueryKeySWR`)
32
+ const baseName = resolver.resolveName(node.operationId)
33
+ const queryName = transformName(`use${baseName.charAt(0).toUpperCase()}${baseName.slice(1)}`, 'function', transformers)
34
+ const queryOptionsName = transformName(`${baseName}QueryOptions`, 'function', transformers)
35
+ const queryKeyName = transformName(`${baseName}QueryKey`, 'const', transformers)
36
+ const queryKeyTypeName = transformName(`${baseName.charAt(0).toUpperCase()}${baseName.slice(1)}QueryKey`, 'type', transformers)
37
+ const clientName = baseName
78
38
 
79
- if (!isQuery || isMutation) {
80
- return null
39
+ const meta = {
40
+ file: resolver.resolveFile({ name: queryName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
41
+ fileTs: tsResolver.resolveFile(
42
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
43
+ { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group },
44
+ ),
81
45
  }
82
46
 
47
+ const casedParams = ast.caseParams(node.parameters, paramsCasing)
48
+ const pathParams = casedParams.filter((p) => p.in === 'path')
49
+ const queryParams = casedParams.filter((p) => p.in === 'query')
50
+ const headerParams = casedParams.filter((p) => p.in === 'header')
51
+
52
+ const importedTypeNames = [
53
+ ...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
54
+ ...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
55
+ ...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
56
+ node.requestBody?.schema ? tsResolver.resolveDataName(node) : undefined,
57
+ tsResolver.resolveResponseName(node),
58
+ ...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode)),
59
+ ].filter(Boolean)
60
+
61
+ const pluginZodRaw = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
62
+ const pluginZod = pluginZodRaw?.name === pluginZodName ? pluginZodRaw : undefined
63
+ const zodResolver = pluginZod?.resolver
64
+ const fileZod = zodResolver
65
+ ? zodResolver.resolveFile(
66
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
67
+ { root, output: pluginZod?.options?.output ?? output, group: pluginZod?.options?.group },
68
+ )
69
+ : undefined
70
+ const zodSchemaNames =
71
+ zodResolver && parser === 'zod'
72
+ ? [zodResolver.resolveResponseName?.(node), node.requestBody?.schema ? zodResolver.resolveDataName?.(node) : undefined].filter(Boolean)
73
+ : []
74
+
75
+ const clientPlugin = driver.getPlugin(pluginClientName)
76
+ const hasClientPlugin = clientPlugin?.name === pluginClientName
77
+ const shouldUseClientPlugin = hasClientPlugin && clientOptions.clientType !== 'class'
78
+
79
+ const clientFile = shouldUseClientPlugin
80
+ ? clientPlugin?.resolver?.resolveFile(
81
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
82
+ {
83
+ root,
84
+ output: clientPlugin?.options?.output ?? output,
85
+ group: clientPlugin?.options?.group,
86
+ },
87
+ )
88
+ : undefined
89
+
90
+ const resolvedClientName = shouldUseClientPlugin ? (clientPlugin?.resolver?.resolveName(node.operationId) ?? clientName) : clientName
91
+
83
92
  return (
84
93
  <File
85
- baseName={query.file.baseName}
86
- path={query.file.path}
87
- meta={query.file.meta}
88
- banner={getBanner({ oas, output, config: driver.config })}
89
- footer={getFooter({ oas, output })}
94
+ baseName={meta.file.baseName}
95
+ path={meta.file.path}
96
+ meta={meta.file.meta}
97
+ banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
98
+ footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
90
99
  >
91
- {options.parser === 'zod' && (
92
- <File.Import name={[zod.schemas.response.name, zod.schemas.request?.name].filter(Boolean)} root={query.file.path} path={zod.file.path} />
100
+ {parser === 'zod' && fileZod && zodSchemaNames.length > 0 && (
101
+ <File.Import name={zodSchemaNames as string[]} root={meta.file.path} path={fileZod.path} />
93
102
  )}
94
- {options.client.importPath ? (
103
+ {clientOptions.importPath ? (
95
104
  <>
96
- {!shouldUseClientPlugin && <File.Import name={'fetch'} path={options.client.importPath} />}
97
- <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={options.client.importPath} isTypeOnly />
98
- {options.client.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={options.client.importPath} isTypeOnly />}
105
+ {!shouldUseClientPlugin && <File.Import name={'fetch'} path={clientOptions.importPath} />}
106
+ <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={clientOptions.importPath} isTypeOnly />
107
+ {clientOptions.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={clientOptions.importPath} isTypeOnly />}
99
108
  </>
100
109
  ) : (
101
110
  <>
102
- {!shouldUseClientPlugin && <File.Import name={['fetch']} root={query.file.path} path={path.resolve(root, '.kubb/fetch.ts')} />}
111
+ {!shouldUseClientPlugin && <File.Import name={['fetch']} root={meta.file.path} path={path.resolve(root, '.kubb/fetch.ts')} />}
103
112
  <File.Import
104
113
  name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
105
- root={query.file.path}
114
+ root={meta.file.path}
106
115
  path={path.resolve(root, '.kubb/fetch.ts')}
107
116
  isTypeOnly
108
117
  />
109
- {options.client.dataReturnType === 'full' && (
110
- <File.Import name={['ResponseConfig']} root={query.file.path} path={path.resolve(root, '.kubb/fetch.ts')} isTypeOnly />
118
+ {clientOptions.dataReturnType === 'full' && (
119
+ <File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/fetch.ts')} isTypeOnly />
111
120
  )}
112
121
  </>
113
122
  )}
114
- {shouldUseClientPlugin && <File.Import name={[client.name]} root={query.file.path} path={client.file.path} />}
115
- {!shouldUseClientPlugin && <File.Import name={['buildFormData']} root={query.file.path} path={path.resolve(root, '.kubb/config.ts')} />}
116
-
117
- <File.Import
118
- name={[
119
- type.schemas.request?.name,
120
- type.schemas.response.name,
121
- type.schemas.pathParams?.name,
122
- type.schemas.queryParams?.name,
123
- type.schemas.headerParams?.name,
124
- ...(type.schemas.statusCodes?.map((item) => item.name) || []),
125
- ].filter(Boolean)}
126
- root={query.file.path}
127
- path={type.file.path}
128
- isTypeOnly
129
- />
123
+ <File.Import name="useSWR" path={importPath} />
124
+ {shouldUseClientPlugin && clientFile && <File.Import name={[resolvedClientName]} root={meta.file.path} path={clientFile.path} />}
125
+ {!shouldUseClientPlugin && <File.Import name={['buildFormData']} root={meta.file.path} path={path.resolve(root, '.kubb/config.ts')} />}
126
+ {meta.fileTs && importedTypeNames.length > 0 && (
127
+ <File.Import name={Array.from(new Set(importedTypeNames))} root={meta.file.path} path={meta.fileTs.path} isTypeOnly />
128
+ )}
129
+
130
130
  <QueryKey
131
- name={queryKey.name}
132
- typeName={queryKey.typeName}
133
- operation={operation}
134
- pathParamsType={options.pathParamsType}
135
- typeSchemas={type.schemas}
136
- paramsCasing={options.paramsCasing}
137
- transformer={options.queryKey}
131
+ name={queryKeyName}
132
+ typeName={queryKeyTypeName}
133
+ node={node}
134
+ tsResolver={tsResolver}
135
+ pathParamsType={pathParamsType}
136
+ paramsCasing={paramsCasing}
137
+ transformer={ctx.options.queryKey}
138
138
  />
139
+
139
140
  {!shouldUseClientPlugin && (
140
- <Client
141
- name={client.name}
142
- baseURL={options.client.baseURL}
143
- operation={operation}
144
- typeSchemas={type.schemas}
145
- zodSchemas={zod.schemas}
146
- dataReturnType={options.client.dataReturnType || 'data'}
147
- paramsCasing={options.client.paramsCasing || options.paramsCasing}
148
- paramsType={options.paramsType}
149
- pathParamsType={options.pathParamsType}
150
- parser={options.parser}
141
+ <ClientLegacyComponent
142
+ name={resolvedClientName}
143
+ baseURL={clientOptions.baseURL}
144
+ operation={{
145
+ path: node.path,
146
+ method: node.method,
147
+ getDescription: () => node.description,
148
+ getSummary: () => node.summary,
149
+ isDeprecated: () => node.deprecated ?? false,
150
+ getContentType: () => node.requestBody?.contentType ?? 'application/json',
151
+ }}
152
+ typeSchemas={buildLegacyTypeSchemas(node, tsResolver)}
153
+ zodSchemas={zodResolver ? buildLegacyTypeSchemas(node, zodResolver) : undefined}
154
+ dataReturnType={clientOptions.dataReturnType || 'data'}
155
+ paramsCasing={clientOptions.paramsCasing || paramsCasing}
156
+ paramsType={paramsType}
157
+ pathParamsType={pathParamsType}
158
+ parser={parser}
151
159
  />
152
160
  )}
161
+
153
162
  <QueryOptions
154
- name={queryOptions.name}
155
- clientName={client.name}
156
- typeSchemas={type.schemas}
157
- paramsType={options.paramsType}
158
- paramsCasing={options.paramsCasing}
159
- pathParamsType={options.pathParamsType}
163
+ name={queryOptionsName}
164
+ clientName={resolvedClientName}
165
+ node={node}
166
+ tsResolver={tsResolver}
167
+ paramsCasing={paramsCasing}
168
+ paramsType={paramsType}
169
+ pathParamsType={pathParamsType}
160
170
  />
161
- {options.query && (
162
- <>
163
- <File.Import name="useSWR" path={importPath} />
164
- <File.Import name={['SWRResponse']} path={importPath} isTypeOnly />
165
- <Query
166
- name={query.name}
167
- queryOptionsName={queryOptions.name}
168
- typeSchemas={type.schemas}
169
- paramsType={options.paramsType}
170
- pathParamsType={options.pathParamsType}
171
- operation={operation}
172
- dataReturnType={options.client.dataReturnType || 'data'}
173
- queryKeyName={queryKey.name}
174
- paramsCasing={options.paramsCasing}
175
- queryKeyTypeName={queryKey.typeName}
176
- />
177
- </>
171
+
172
+ {query && (
173
+ <Query
174
+ name={queryName}
175
+ queryOptionsName={queryOptionsName}
176
+ queryKeyName={queryKeyName}
177
+ queryKeyTypeName={queryKeyTypeName}
178
+ node={node}
179
+ tsResolver={tsResolver}
180
+ dataReturnType={clientOptions.dataReturnType || 'data'}
181
+ paramsType={paramsType}
182
+ paramsCasing={paramsCasing}
183
+ pathParamsType={pathParamsType}
184
+ />
178
185
  )}
179
186
  </File>
180
187
  )
181
188
  },
182
189
  })
190
+
191
+ /**
192
+ * Builds a legacy-compatible OperationSchemas object from OperationNode + resolver.
193
+ * Used for the ClientLegacy component which still expects the old format.
194
+ */
195
+ // biome-ignore lint/suspicious/noExplicitAny: bridge between v5 resolver types and legacy OperationSchemas format
196
+ function buildLegacyTypeSchemas(node: ast.OperationNode, resolver: any) {
197
+ const pathParams = node.parameters.filter((p) => p.in === 'path')
198
+ const queryParams = node.parameters.filter((p) => p.in === 'query')
199
+ const headerParams = node.parameters.filter((p) => p.in === 'header')
200
+
201
+ const buildSchemaProps = (params: typeof pathParams) => {
202
+ const properties: Record<string, { type: string }> = {}
203
+ const required: string[] = []
204
+ for (const p of params) {
205
+ properties[p.name] = { type: p.schema?.primitive ?? 'unknown' }
206
+ if (p.required) required.push(p.name)
207
+ }
208
+ return { properties, required }
209
+ }
210
+
211
+ return {
212
+ response: { name: resolver.resolveResponseName(node) },
213
+ request: node.requestBody?.schema
214
+ ? {
215
+ name: resolver.resolveDataName(node),
216
+ schema: { required: node.requestBody.required ? ['body'] : [] },
217
+ }
218
+ : undefined,
219
+ pathParams:
220
+ pathParams.length > 0 && resolver.resolvePathParamsName
221
+ ? {
222
+ name: resolver.resolvePathParamsName(node, pathParams[0]!),
223
+ schema: buildSchemaProps(pathParams),
224
+ }
225
+ : undefined,
226
+ queryParams:
227
+ queryParams.length > 0 && resolver.resolveQueryParamsName
228
+ ? {
229
+ name: resolver.resolveQueryParamsName(node, queryParams[0]!),
230
+ schema: buildSchemaProps(queryParams),
231
+ }
232
+ : undefined,
233
+ headerParams:
234
+ headerParams.length > 0 && resolver.resolveHeaderParamsName
235
+ ? {
236
+ name: resolver.resolveHeaderParamsName(node, headerParams[0]!),
237
+ schema: buildSchemaProps(headerParams),
238
+ }
239
+ : undefined,
240
+ errors: node.responses
241
+ .filter((r) => {
242
+ const code = Number.parseInt(r.statusCode, 10)
243
+ return code >= 400 || r.statusCode === 'default'
244
+ })
245
+ .map((r) => ({ name: resolver.resolveResponseStatusName(node, r.statusCode) })),
246
+ statusCodes: node.responses.map((r) => ({ name: resolver.resolveResponseStatusName(node, r.statusCode) })),
247
+ }
248
+ }