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