@kubb/plugin-client 5.0.0-alpha.8 → 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 (60) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +4 -4
  3. package/dist/clients/axios.cjs +2 -2
  4. package/dist/clients/axios.cjs.map +1 -1
  5. package/dist/clients/axios.d.ts +4 -4
  6. package/dist/clients/axios.js +1 -1
  7. package/dist/clients/axios.js.map +1 -1
  8. package/dist/clients/fetch.cjs +1 -1
  9. package/dist/clients/fetch.cjs.map +1 -1
  10. package/dist/clients/fetch.d.ts +2 -2
  11. package/dist/clients/fetch.js +1 -1
  12. package/dist/clients/fetch.js.map +1 -1
  13. package/dist/index.cjs +1739 -97
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +324 -4
  16. package/dist/index.js +1725 -95
  17. package/dist/index.js.map +1 -1
  18. package/dist/templates/clients/axios.source.cjs +1 -1
  19. package/dist/templates/clients/axios.source.js +1 -1
  20. package/dist/templates/clients/fetch.source.cjs +1 -1
  21. package/dist/templates/clients/fetch.source.js +1 -1
  22. package/package.json +67 -84
  23. package/src/clients/axios.ts +5 -1
  24. package/src/clients/fetch.ts +5 -1
  25. package/src/components/ClassClient.tsx +45 -142
  26. package/src/components/Client.tsx +90 -129
  27. package/src/components/Operations.tsx +10 -10
  28. package/src/components/StaticClassClient.tsx +44 -138
  29. package/src/components/Url.tsx +38 -48
  30. package/src/components/WrapperClient.tsx +3 -3
  31. package/src/functionParams.ts +118 -0
  32. package/src/generators/classClientGenerator.tsx +148 -171
  33. package/src/generators/clientGenerator.tsx +95 -82
  34. package/src/generators/groupedClientGenerator.tsx +50 -52
  35. package/src/generators/operationsGenerator.tsx +11 -18
  36. package/src/generators/staticClassClientGenerator.tsx +178 -183
  37. package/src/index.ts +9 -2
  38. package/src/plugin.ts +115 -145
  39. package/src/resolvers/resolverClient.ts +22 -0
  40. package/src/types.ts +104 -44
  41. package/src/utils.ts +180 -0
  42. package/templates/clients/axios.ts +5 -2
  43. package/templates/clients/fetch.ts +5 -2
  44. package/dist/StaticClassClient-By-aMAe4.cjs +0 -677
  45. package/dist/StaticClassClient-By-aMAe4.cjs.map +0 -1
  46. package/dist/StaticClassClient-CCn9g9eF.js +0 -636
  47. package/dist/StaticClassClient-CCn9g9eF.js.map +0 -1
  48. package/dist/components.cjs +0 -7
  49. package/dist/components.d.ts +0 -216
  50. package/dist/components.js +0 -2
  51. package/dist/generators-BYUJaeZP.js +0 -723
  52. package/dist/generators-BYUJaeZP.js.map +0 -1
  53. package/dist/generators-DTxD9FDY.cjs +0 -753
  54. package/dist/generators-DTxD9FDY.cjs.map +0 -1
  55. package/dist/generators.cjs +0 -7
  56. package/dist/generators.d.ts +0 -488
  57. package/dist/generators.js +0 -2
  58. package/dist/types-DBQdg-BV.d.ts +0 -169
  59. package/src/components/index.ts +0 -5
  60. package/src/generators/index.ts +0 -5
@@ -1,36 +1,40 @@
1
1
  import { buildJSDoc, URLPath } from '@internals/utils'
2
- import type { Operation } from '@kubb/oas'
3
- import type { OperationSchemas } from '@kubb/plugin-oas'
4
- import { getComments } from '@kubb/plugin-oas/utils'
5
- import { File, FunctionParams } from '@kubb/react-fabric'
6
- import type { FabricReactNode } from '@kubb/react-fabric/types'
2
+ import type { ast } from '@kubb/core'
3
+ import type { ResolverTs } from '@kubb/plugin-ts'
4
+ import { functionPrinter } from '@kubb/plugin-ts'
5
+ import type { ResolverZod } from '@kubb/plugin-zod'
6
+ import { File } from '@kubb/renderer-jsx'
7
+ import type { KubbReactNode } from '@kubb/renderer-jsx/types'
7
8
  import type { PluginClient } from '../types.ts'
9
+ import { buildClassClientParams, buildFormDataLine, buildGenerics, buildHeaders, buildRequestDataLine, buildReturnStatement, getComments } from '../utils.ts'
8
10
  import { Client } from './Client.tsx'
9
11
 
12
+ type OperationData = {
13
+ node: ast.OperationNode
14
+ name: string
15
+ tsResolver: ResolverTs
16
+ zodResolver?: ResolverZod
17
+ }
18
+
10
19
  type Props = {
11
20
  name: string
12
21
  isExportable?: boolean
13
22
  isIndexable?: boolean
14
- operations: Array<{
15
- operation: Operation
16
- name: string
17
- typeSchemas: OperationSchemas
18
- zodSchemas: OperationSchemas | undefined
19
- }>
23
+ operations: Array<OperationData>
20
24
  baseURL: string | undefined
21
25
  dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
22
26
  paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
23
27
  paramsType: PluginClient['resolvedOptions']['pathParamsType']
24
28
  pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
25
29
  parser: PluginClient['resolvedOptions']['parser'] | undefined
26
- children?: FabricReactNode
30
+ children?: KubbReactNode
27
31
  }
28
32
 
29
33
  type GenerateMethodProps = {
30
- operation: Operation
34
+ node: ast.OperationNode
31
35
  name: string
32
- typeSchemas: OperationSchemas
33
- zodSchemas: OperationSchemas | undefined
36
+ tsResolver: ResolverTs
37
+ zodResolver?: ResolverZod
34
38
  baseURL: string | undefined
35
39
  dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
36
40
  parser: PluginClient['resolvedOptions']['parser'] | undefined
@@ -39,115 +43,13 @@ type GenerateMethodProps = {
39
43
  pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
40
44
  }
41
45
 
42
- function buildHeaders(contentType: string, hasHeaderParams: boolean): Array<string> {
43
- return [
44
- contentType !== 'application/json' && contentType !== 'multipart/form-data' ? `'Content-Type': '${contentType}'` : undefined,
45
- hasHeaderParams ? '...headers' : undefined,
46
- ].filter(Boolean) as Array<string>
47
- }
48
-
49
- function buildGenerics(typeSchemas: OperationSchemas): Array<string> {
50
- const TError = `ResponseErrorConfig<${typeSchemas.errors?.map((item) => item.name).join(' | ') || 'Error'}>`
51
- return [typeSchemas.response.name, TError, typeSchemas.request?.name || 'unknown'].filter(Boolean)
52
- }
53
-
54
- function buildClientParams({
55
- operation,
56
- path,
57
- baseURL,
58
- typeSchemas,
59
- isFormData,
60
- headers,
61
- }: {
62
- operation: Operation
63
- path: URLPath
64
- baseURL: string | undefined
65
- typeSchemas: OperationSchemas
66
- isFormData: boolean
67
- headers: Array<string>
68
- }) {
69
- return FunctionParams.factory({
70
- config: {
71
- mode: 'object',
72
- children: {
73
- requestConfig: {
74
- mode: 'inlineSpread',
75
- },
76
- method: {
77
- value: JSON.stringify(operation.method.toUpperCase()),
78
- },
79
- url: {
80
- value: path.template,
81
- },
82
- baseURL: baseURL
83
- ? {
84
- value: JSON.stringify(baseURL),
85
- }
86
- : undefined,
87
- params: typeSchemas.queryParams?.name ? {} : undefined,
88
- data: typeSchemas.request?.name
89
- ? {
90
- value: isFormData ? 'formData as FormData' : 'requestData',
91
- }
92
- : undefined,
93
- headers: headers.length
94
- ? {
95
- value: `{ ${headers.join(', ')}, ...requestConfig.headers }`,
96
- }
97
- : undefined,
98
- },
99
- },
100
- })
101
- }
102
-
103
- function buildRequestDataLine({
104
- parser,
105
- zodSchemas,
106
- typeSchemas,
107
- }: {
108
- parser: PluginClient['resolvedOptions']['parser'] | undefined
109
- zodSchemas: OperationSchemas | undefined
110
- typeSchemas: OperationSchemas
111
- }): string {
112
- if (parser === 'zod' && zodSchemas?.request?.name) {
113
- return `const requestData = ${zodSchemas.request.name}.parse(data)`
114
- }
115
- if (typeSchemas?.request?.name) {
116
- return 'const requestData = data'
117
- }
118
- return ''
119
- }
120
-
121
- function buildFormDataLine(isFormData: boolean, hasRequest: boolean): string {
122
- return isFormData && hasRequest ? 'const formData = buildFormData(requestData)' : ''
123
- }
124
-
125
- function buildReturnStatement({
126
- dataReturnType,
127
- parser,
128
- zodSchemas,
129
- }: {
130
- dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
131
- parser: PluginClient['resolvedOptions']['parser'] | undefined
132
- zodSchemas: OperationSchemas | undefined
133
- }): string {
134
- if (dataReturnType === 'full' && parser === 'zod' && zodSchemas) {
135
- return `return {...res, data: ${zodSchemas.response.name}.parse(res.data)}`
136
- }
137
- if (dataReturnType === 'data' && parser === 'zod' && zodSchemas) {
138
- return `return ${zodSchemas.response.name}.parse(res.data)`
139
- }
140
- if (dataReturnType === 'full' && parser === 'client') {
141
- return 'return res'
142
- }
143
- return 'return res.data'
144
- }
46
+ const declarationPrinter = functionPrinter({ mode: 'declaration' })
145
47
 
146
48
  function generateMethod({
147
- operation,
49
+ node,
148
50
  name,
149
- typeSchemas,
150
- zodSchemas,
51
+ tsResolver,
52
+ zodResolver,
151
53
  baseURL,
152
54
  dataReturnType,
153
55
  parser,
@@ -155,18 +57,23 @@ function generateMethod({
155
57
  paramsCasing,
156
58
  pathParamsType,
157
59
  }: GenerateMethodProps): string {
158
- const path = new URLPath(operation.path, { casing: paramsCasing })
159
- const contentType = operation.getContentType()
60
+ const path = new URLPath(node.path, { casing: paramsCasing })
61
+ const contentType = node.requestBody?.content?.[0]?.contentType ?? 'application/json'
160
62
  const isFormData = contentType === 'multipart/form-data'
161
- const headers = buildHeaders(contentType, !!typeSchemas.headerParams?.name)
162
- const generics = buildGenerics(typeSchemas)
163
- const params = Client.getParams({ paramsType, paramsCasing, pathParamsType, typeSchemas, isConfigurable: true })
164
- const clientParams = buildClientParams({ operation, path, baseURL, typeSchemas, isFormData, headers })
165
- const jsdoc = buildJSDoc(getComments(operation))
63
+ const headerParamsName =
64
+ node.parameters.filter((p) => p.in === 'header').length > 0
65
+ ? tsResolver.resolveHeaderParamsName(node, node.parameters.filter((p) => p.in === 'header')[0]!)
66
+ : undefined
67
+ const headers = buildHeaders(contentType, !!headerParamsName)
68
+ const generics = buildGenerics(node, tsResolver)
69
+ const paramsNode = Client.getParams({ paramsType, paramsCasing, pathParamsType, node, tsResolver, isConfigurable: true })
70
+ const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
71
+ const clientParams = buildClassClientParams({ node, path, baseURL, tsResolver, isFormData, headers })
72
+ const jsdoc = buildJSDoc(getComments(node))
166
73
 
167
- const requestDataLine = buildRequestDataLine({ parser, zodSchemas, typeSchemas })
168
- const formDataLine = buildFormDataLine(isFormData, !!typeSchemas?.request?.name)
169
- const returnStatement = buildReturnStatement({ dataReturnType, parser, zodSchemas })
74
+ const requestDataLine = buildRequestDataLine({ parser, node, zodResolver })
75
+ const formDataLine = buildFormDataLine(isFormData, !!node.requestBody?.content?.[0]?.schema)
76
+ const returnStatement = buildReturnStatement({ dataReturnType, parser, node, zodResolver })
170
77
 
171
78
  const methodBody = [
172
79
  'const { client: request = fetch, ...requestConfig } = mergeConfig(this.#config, config)',
@@ -180,8 +87,7 @@ function generateMethod({
180
87
  .map((line) => ` ${line}`)
181
88
  .join('\n')
182
89
 
183
- // Indent static method by 2 spaces, body by 4 spaces (matching snapshot)
184
- return `${jsdoc} static async ${name}(${params.toConstructor()}) {\n${methodBody}\n }`
90
+ return `${jsdoc} static async ${name}(${paramsSignature}) {\n${methodBody}\n }`
185
91
  }
186
92
 
187
93
  export function StaticClassClient({
@@ -196,13 +102,13 @@ export function StaticClassClient({
196
102
  paramsCasing,
197
103
  pathParamsType,
198
104
  children,
199
- }: Props): FabricReactNode {
200
- const methods = operations.map(({ operation, name: methodName, typeSchemas, zodSchemas }) =>
105
+ }: Props): KubbReactNode {
106
+ const methods = operations.map(({ node, name: methodName, tsResolver, zodResolver }) =>
201
107
  generateMethod({
202
- operation,
108
+ node,
203
109
  name: methodName,
204
- typeSchemas,
205
- zodSchemas,
110
+ tsResolver,
111
+ zodResolver,
206
112
  baseURL,
207
113
  dataReturnType,
208
114
  parser,
@@ -1,15 +1,13 @@
1
1
  import { isValidVarName, URLPath } from '@internals/utils'
2
- import { getDefaultValue, type Operation } from '@kubb/oas'
3
- import type { OperationSchemas } from '@kubb/plugin-oas'
4
- import { getParamsMapping, getPathParams } from '@kubb/plugin-oas/utils'
5
- import { Const, File, Function, FunctionParams } from '@kubb/react-fabric'
6
- import type { FabricReactNode } from '@kubb/react-fabric/types'
2
+ import { ast } from '@kubb/core'
3
+ import type { ResolverTs } from '@kubb/plugin-ts'
4
+ import { functionPrinter } from '@kubb/plugin-ts'
5
+ import { Const, File, Function } from '@kubb/renderer-jsx'
6
+ import type { KubbReactNode } from '@kubb/renderer-jsx/types'
7
7
  import type { PluginClient } from '../types.ts'
8
+ import { buildParamsMapping } from '../utils.ts'
8
9
 
9
10
  type Props = {
10
- /**
11
- * Name of the function
12
- */
13
11
  name: string
14
12
  isExportable?: boolean
15
13
  isIndexable?: boolean
@@ -18,45 +16,33 @@ type Props = {
18
16
  paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
19
17
  paramsType: PluginClient['resolvedOptions']['pathParamsType']
20
18
  pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
21
- typeSchemas: OperationSchemas
22
- operation: Operation
19
+ node: ast.OperationNode
20
+ tsResolver: ResolverTs
23
21
  }
24
22
 
25
23
  type GetParamsProps = {
26
24
  paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
27
25
  paramsType: PluginClient['resolvedOptions']['paramsType']
28
26
  pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
29
- typeSchemas: OperationSchemas
27
+ node: ast.OperationNode
28
+ tsResolver: ResolverTs
30
29
  }
31
30
 
32
- function getParams({ paramsType, paramsCasing, pathParamsType, typeSchemas }: GetParamsProps) {
33
- if (paramsType === 'object') {
34
- const pathParams = getPathParams(typeSchemas.pathParams, {
35
- typed: true,
36
- casing: paramsCasing,
37
- })
31
+ const declarationPrinter = functionPrinter({ mode: 'declaration' })
38
32
 
39
- return FunctionParams.factory({
40
- data: {
41
- mode: 'object',
42
- children: {
43
- ...pathParams,
44
- },
45
- },
46
- })
33
+ function getParams({ paramsType, paramsCasing, pathParamsType, node, tsResolver }: GetParamsProps): ast.FunctionParametersNode {
34
+ // Build a URL-only node with only path params (no body, query, header)
35
+ const urlNode: ast.OperationNode = {
36
+ ...node,
37
+ parameters: node.parameters.filter((p) => p.in === 'path'),
38
+ requestBody: undefined,
47
39
  }
48
40
 
49
- return FunctionParams.factory({
50
- pathParams: typeSchemas.pathParams?.name
51
- ? {
52
- mode: pathParamsType === 'object' ? 'object' : 'inlineSpread',
53
- children: getPathParams(typeSchemas.pathParams, {
54
- typed: true,
55
- casing: paramsCasing,
56
- }),
57
- default: getDefaultValue(typeSchemas.pathParams?.schema),
58
- }
59
- : undefined,
41
+ return ast.createOperationParams(urlNode, {
42
+ paramsType: paramsType === 'object' ? 'object' : 'inline',
43
+ pathParamsType: paramsType === 'object' ? 'object' : pathParamsType === 'object' ? 'object' : 'inline',
44
+ paramsCasing,
45
+ resolver: tsResolver,
60
46
  })
61
47
  }
62
48
 
@@ -64,34 +50,38 @@ export function Url({
64
50
  name,
65
51
  isExportable = true,
66
52
  isIndexable = true,
67
- typeSchemas,
68
53
  baseURL,
69
54
  paramsType,
70
55
  paramsCasing,
71
56
  pathParamsType,
72
- operation,
73
- }: Props): FabricReactNode {
74
- const path = new URLPath(operation.path)
75
- const params = getParams({
57
+ node,
58
+ tsResolver,
59
+ }: Props): KubbReactNode {
60
+ const path = new URLPath(node.path)
61
+
62
+ const paramsNode = getParams({
76
63
  paramsType,
77
64
  paramsCasing,
78
65
  pathParamsType,
79
- typeSchemas,
66
+ node,
67
+ tsResolver,
80
68
  })
69
+ const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
81
70
 
82
- // Generate pathParams mapping when paramsCasing is used
83
- const pathParamsMapping = paramsCasing ? getParamsMapping(typeSchemas.pathParams, { casing: paramsCasing }) : undefined
71
+ const originalPathParams = node.parameters.filter((p) => p.in === 'path')
72
+ const casedPathParams = ast.caseParams(originalPathParams, paramsCasing)
73
+ const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) : undefined
84
74
 
85
75
  return (
86
76
  <File.Source name={name} isExportable={isExportable} isIndexable={isIndexable}>
87
- <Function name={name} export={isExportable} params={params.toConstructor()}>
77
+ <Function name={name} export={isExportable} params={paramsSignature}>
88
78
  {pathParamsMapping &&
89
79
  Object.entries(pathParamsMapping)
90
- .filter(([originalName, camelCaseName]) => originalName !== camelCaseName && isValidVarName(originalName))
80
+ .filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName)
91
81
  .map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`)
92
82
  .join('\n')}
93
- {pathParamsMapping && <br />}
94
- <Const name={'res'}>{`{ method: '${operation.method.toUpperCase()}', url: ${path.toTemplateString({ prefix: baseURL })} as const }`}</Const>
83
+ {pathParamsMapping && Object.keys(pathParamsMapping).length > 0 && <br />}
84
+ <Const name={'res'}>{`{ method: '${node.method.toUpperCase()}', url: ${path.toTemplateString({ prefix: baseURL })} as const }`}</Const>
95
85
  <br />
96
86
  return res
97
87
  </Function>
@@ -1,6 +1,6 @@
1
1
  import { camelCase } from '@internals/utils'
2
- import { File } from '@kubb/react-fabric'
3
- import type { FabricReactNode } from '@kubb/react-fabric/types'
2
+ import { File } from '@kubb/renderer-jsx'
3
+ import type { KubbReactNode } from '@kubb/renderer-jsx/types'
4
4
 
5
5
  type Props = {
6
6
  name: string
@@ -9,7 +9,7 @@ type Props = {
9
9
  isIndexable?: boolean
10
10
  }
11
11
 
12
- export function WrapperClient({ name, classNames, isExportable = true, isIndexable = true }: Props): FabricReactNode {
12
+ export function WrapperClient({ name, classNames, isExportable = true, isIndexable = true }: Props): KubbReactNode {
13
13
  const properties = classNames.map((className) => ` readonly ${camelCase(className)}: ${className}`).join('\n')
14
14
  const assignments = classNames.map((className) => ` this.${camelCase(className)} = new ${className}(config)`).join('\n')
15
15
 
@@ -0,0 +1,118 @@
1
+ import { ast } from '@kubb/core'
2
+ import { functionPrinter } from '@kubb/plugin-ts'
3
+
4
+ type ParamLeaf = {
5
+ type?: string
6
+ optional?: boolean
7
+ default?: string
8
+ value?: string
9
+ mode?: 'inlineSpread'
10
+ }
11
+
12
+ type ParamGroup = {
13
+ mode: 'object' | 'inlineSpread'
14
+ children: Record<string, ParamLeaf | undefined>
15
+ default?: string
16
+ }
17
+
18
+ type ParamSpec = ParamLeaf | ParamGroup
19
+
20
+ const declarationPrinter = functionPrinter({ mode: 'declaration' })
21
+ const callPrinter = functionPrinter({ mode: 'call' })
22
+
23
+ function isGroup(spec: ParamSpec): spec is ParamGroup {
24
+ return 'children' in spec
25
+ }
26
+
27
+ function createType(type?: string) {
28
+ return type ? ast.createParamsType({ variant: 'reference', name: type }) : undefined
29
+ }
30
+
31
+ function createDeclarationLeaf(name: string, spec: ParamLeaf): ast.FunctionParameterNode {
32
+ if (spec.default !== undefined) {
33
+ return ast.createFunctionParameter({
34
+ name,
35
+ type: createType(spec.type),
36
+ default: spec.default,
37
+ rest: spec.mode === 'inlineSpread',
38
+ })
39
+ }
40
+
41
+ return ast.createFunctionParameter({
42
+ name,
43
+ type: createType(spec.type),
44
+ optional: !!spec.optional,
45
+ rest: spec.mode === 'inlineSpread',
46
+ })
47
+ }
48
+
49
+ function createDeclarationParam(name: string, spec: ParamSpec): ast.FunctionParameterNode | ast.ParameterGroupNode {
50
+ if (isGroup(spec)) {
51
+ return ast.createParameterGroup({
52
+ inline: spec.mode === 'inlineSpread',
53
+ default: spec.default,
54
+ properties: Object.entries(spec.children)
55
+ .filter(([, child]) => child !== undefined)
56
+ .map(([childName, child]) => createDeclarationLeaf(childName, child!)),
57
+ })
58
+ }
59
+
60
+ return createDeclarationLeaf(name, spec)
61
+ }
62
+
63
+ function createCallParam(name: string, spec: ParamSpec): ast.FunctionParameterNode | ast.ParameterGroupNode {
64
+ if (isGroup(spec)) {
65
+ return ast.createParameterGroup({
66
+ inline: spec.mode === 'inlineSpread',
67
+ properties: Object.entries(spec.children)
68
+ .filter(([, child]) => child !== undefined)
69
+ .map(([childName, child]) =>
70
+ ast.createFunctionParameter({
71
+ name:
72
+ child?.mode === 'inlineSpread'
73
+ ? spec.mode === 'inlineSpread'
74
+ ? (child.value ?? childName)
75
+ : `...${child.value ?? childName}`
76
+ : child?.value
77
+ ? `${childName}: ${child.value}`
78
+ : childName,
79
+ rest: spec.mode === 'inlineSpread' && child?.mode === 'inlineSpread',
80
+ }),
81
+ ),
82
+ })
83
+ }
84
+
85
+ return ast.createFunctionParameter({
86
+ name: spec.value ?? name,
87
+ rest: spec.mode === 'inlineSpread',
88
+ })
89
+ }
90
+
91
+ /**
92
+ * Creates function parameter builders for generating function signatures and calls.
93
+ * Returns utilities to output constructor signatures (`toConstructor()`) or call expressions (`toCall()`).
94
+ */
95
+ export function createFunctionParams(params: Record<string, ParamSpec | undefined>) {
96
+ const entries = Object.entries(params).filter(([, spec]) => spec !== undefined) as Array<[string, ParamSpec]>
97
+
98
+ return {
99
+ toConstructor() {
100
+ return (
101
+ declarationPrinter.print(
102
+ ast.createFunctionParameters({
103
+ params: entries.map(([name, spec]) => createDeclarationParam(name, spec)),
104
+ }),
105
+ ) ?? ''
106
+ )
107
+ },
108
+ toCall() {
109
+ return (
110
+ callPrinter.print(
111
+ ast.createFunctionParameters({
112
+ params: entries.map(([name, spec]) => createCallParam(name, spec)),
113
+ }),
114
+ ) ?? ''
115
+ )
116
+ },
117
+ }
118
+ }