@kubb/plugin-ts 5.0.0-beta.56 → 5.0.0-beta.73
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.
- package/README.md +2 -2
- package/dist/index.cjs +624 -156
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +377 -36
- package/dist/index.js +611 -159
- package/dist/index.js.map +1 -1
- package/dist/rolldown-runtime-CNktS9qV.js +17 -0
- package/package.json +8 -10
- package/dist/chunk-C0LytTxp.js +0 -8
- package/src/components/Enum.tsx +0 -87
- package/src/components/Type.tsx +0 -55
- package/src/constants.ts +0 -39
- package/src/factory.ts +0 -970
- package/src/generators/typeGenerator.tsx +0 -274
- package/src/index.ts +0 -9
- package/src/plugin.ts +0 -95
- package/src/printers/functionPrinter.ts +0 -197
- package/src/printers/printerTs.ts +0 -315
- package/src/resolvers/resolverTs.ts +0 -69
- package/src/types.ts +0 -335
- package/src/utils.ts +0 -140
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import { resolveContentTypeVariants } from '@internals/shared'
|
|
2
|
-
import { ast, defineGenerator } from '@kubb/core'
|
|
3
|
-
import { File, jsxRenderer } from '@kubb/renderer-jsx'
|
|
4
|
-
import { Type } from '../components/Type.tsx'
|
|
5
|
-
import { ENUM_TYPES_WITH_KEY_SUFFIX } from '../constants.ts'
|
|
6
|
-
import { printerTs } from '../printers/printerTs.ts'
|
|
7
|
-
import type { PluginTs } from '../types'
|
|
8
|
-
import { buildData, buildResponses, buildResponseUnion } from '../utils.ts'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Built-in generator for `@kubb/plugin-ts`. Emits one TypeScript file per
|
|
12
|
-
* schema in the spec plus per-operation request, response, and parameter
|
|
13
|
-
* types. Drop-replace with a custom `Generator<PluginTs>` to change how
|
|
14
|
-
* TypeScript output is produced.
|
|
15
|
-
*/
|
|
16
|
-
export const typeGenerator = defineGenerator<PluginTs>({
|
|
17
|
-
name: 'typescript',
|
|
18
|
-
renderer: jsxRenderer,
|
|
19
|
-
schema(node, ctx) {
|
|
20
|
-
const { enum: enumOptions, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options
|
|
21
|
-
const { adapter, config, resolver, root } = ctx
|
|
22
|
-
|
|
23
|
-
if (!node.name) {
|
|
24
|
-
return
|
|
25
|
-
}
|
|
26
|
-
// Build a set of schema names that are enums so the ref handler and getImports
|
|
27
|
-
// callback can use the suffixed type name (e.g. `StatusKey`) for those refs.
|
|
28
|
-
const enumSchemaNames = new Set<string>(ctx.meta.enumNames)
|
|
29
|
-
|
|
30
|
-
function resolveImportName(schemaName: string): string {
|
|
31
|
-
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && enumOptions.typeSuffix && enumSchemaNames.has(schemaName)) {
|
|
32
|
-
return resolver.resolveEnumKeyName({ name: schemaName }, enumOptions.typeSuffix)
|
|
33
|
-
}
|
|
34
|
-
return resolver.resolveTypeName(schemaName)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const imports = adapter.getImports(node, (schemaName) => ({
|
|
38
|
-
name: resolveImportName(schemaName),
|
|
39
|
-
path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group: group ?? undefined }).path,
|
|
40
|
-
}))
|
|
41
|
-
|
|
42
|
-
const isEnumSchema = !!ast.narrowSchema(node, ast.schemaTypes.enum)
|
|
43
|
-
|
|
44
|
-
const meta = {
|
|
45
|
-
name:
|
|
46
|
-
ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && isEnumSchema
|
|
47
|
-
? resolver.resolveEnumKeyName(node, enumOptions.typeSuffix)
|
|
48
|
-
: resolver.resolveTypeName(node.name),
|
|
49
|
-
file: resolver.resolveFile({ name: node.name, extname: '.ts' }, { root, output, group: group ?? undefined }),
|
|
50
|
-
} as const
|
|
51
|
-
|
|
52
|
-
const schemaPrinter = printerTs({
|
|
53
|
-
optionalType,
|
|
54
|
-
arrayType,
|
|
55
|
-
enum: enumOptions,
|
|
56
|
-
name: meta.name,
|
|
57
|
-
syntaxType,
|
|
58
|
-
description: node.description,
|
|
59
|
-
resolver,
|
|
60
|
-
enumSchemaNames,
|
|
61
|
-
nodes: printer?.nodes,
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<File
|
|
66
|
-
baseName={meta.file.baseName}
|
|
67
|
-
path={meta.file.path}
|
|
68
|
-
meta={meta.file.meta}
|
|
69
|
-
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
70
|
-
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
71
|
-
>
|
|
72
|
-
{imports.map((imp) => (
|
|
73
|
-
<File.Import key={[node.name, imp.path, imp.isTypeOnly].join('-')} root={meta.file.path} path={imp.path} name={imp.name} isTypeOnly />
|
|
74
|
-
))}
|
|
75
|
-
<Type name={meta.name} node={node} enum={enumOptions} resolver={resolver} printer={schemaPrinter} />
|
|
76
|
-
</File>
|
|
77
|
-
)
|
|
78
|
-
},
|
|
79
|
-
operation(node, ctx) {
|
|
80
|
-
const { enum: enumOptions, optionalType, arrayType, syntaxType, paramsCasing, group, output, printer } = ctx.options
|
|
81
|
-
const { adapter, config, resolver, root } = ctx
|
|
82
|
-
|
|
83
|
-
const params = ast.caseParams(node.parameters, paramsCasing)
|
|
84
|
-
|
|
85
|
-
const meta = {
|
|
86
|
-
file: resolver.resolveFile(
|
|
87
|
-
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
88
|
-
{ root, output, group: group ?? undefined },
|
|
89
|
-
),
|
|
90
|
-
} as const
|
|
91
|
-
|
|
92
|
-
// Build a set of schema names that are enums so the ref handler and getImports
|
|
93
|
-
// callback can use the suffixed type name (e.g. `StatusKey`) for those refs.
|
|
94
|
-
const enumSchemaNames = new Set<string>(ctx.meta.enumNames)
|
|
95
|
-
|
|
96
|
-
function resolveImportName(schemaName: string): string {
|
|
97
|
-
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && enumOptions.typeSuffix && enumSchemaNames.has(schemaName)) {
|
|
98
|
-
return resolver.resolveEnumKeyName({ name: schemaName }, enumOptions.typeSuffix)
|
|
99
|
-
}
|
|
100
|
-
return resolver.resolveTypeName(schemaName)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function renderSchemaType({ schema, name, keysToOmit }: { schema: ast.SchemaNode | null; name: string; keysToOmit?: Array<string> | null }) {
|
|
104
|
-
if (!schema) return null
|
|
105
|
-
|
|
106
|
-
const imports = adapter.getImports(schema, (schemaName) => ({
|
|
107
|
-
name: resolveImportName(schemaName),
|
|
108
|
-
path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group: group ?? undefined }).path,
|
|
109
|
-
}))
|
|
110
|
-
|
|
111
|
-
const schemaPrinter = printerTs({
|
|
112
|
-
optionalType,
|
|
113
|
-
arrayType,
|
|
114
|
-
enum: enumOptions,
|
|
115
|
-
name,
|
|
116
|
-
syntaxType,
|
|
117
|
-
description: schema.description,
|
|
118
|
-
keysToOmit,
|
|
119
|
-
resolver,
|
|
120
|
-
enumSchemaNames,
|
|
121
|
-
nodes: printer?.nodes,
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<>
|
|
126
|
-
{imports.map((imp) => (
|
|
127
|
-
<File.Import key={[name, imp.path, imp.isTypeOnly].join('-')} root={meta.file.path} path={imp.path} name={imp.name} isTypeOnly />
|
|
128
|
-
))}
|
|
129
|
-
<Type name={name} node={schema} enum={enumOptions} resolver={resolver} printer={schemaPrinter} />
|
|
130
|
-
</>
|
|
131
|
-
)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Emits an individual type per content type plus a union alias under `baseName`.
|
|
136
|
-
* Shared by the request body and multi-content-type responses.
|
|
137
|
-
*/
|
|
138
|
-
function buildContentTypeVariants(
|
|
139
|
-
entries: Array<{ contentType: string; schema?: ast.SchemaNode | null; keysToOmit?: Array<string> | null }>,
|
|
140
|
-
baseName: string,
|
|
141
|
-
decorate?: (schema: ast.SchemaNode) => ast.SchemaNode,
|
|
142
|
-
) {
|
|
143
|
-
const variants = resolveContentTypeVariants(entries, baseName)
|
|
144
|
-
const unionSchema = ast.createSchema({
|
|
145
|
-
type: 'union',
|
|
146
|
-
members: variants.map((variant) => ast.createSchema({ type: 'ref', name: variant.name })),
|
|
147
|
-
})
|
|
148
|
-
return (
|
|
149
|
-
<>
|
|
150
|
-
{variants.map((variant) =>
|
|
151
|
-
renderSchemaType({
|
|
152
|
-
schema: decorate ? decorate(variant.schema) : variant.schema,
|
|
153
|
-
name: variant.name,
|
|
154
|
-
keysToOmit: variant.keysToOmit,
|
|
155
|
-
}),
|
|
156
|
-
)}
|
|
157
|
-
{renderSchemaType({ schema: unionSchema, name: baseName })}
|
|
158
|
-
</>
|
|
159
|
-
)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const paramTypes = params.map((param) =>
|
|
163
|
-
renderSchemaType({
|
|
164
|
-
schema: param.schema,
|
|
165
|
-
name: resolver.resolveParamName(node, param),
|
|
166
|
-
}),
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
const requestBodyContent = node.requestBody?.content ?? []
|
|
170
|
-
|
|
171
|
-
function buildRequestType() {
|
|
172
|
-
if (requestBodyContent.length === 0) return null
|
|
173
|
-
if (requestBodyContent.length === 1) {
|
|
174
|
-
const entry = requestBodyContent[0]!
|
|
175
|
-
if (!entry.schema) return null
|
|
176
|
-
return renderSchemaType({
|
|
177
|
-
schema: {
|
|
178
|
-
...entry.schema,
|
|
179
|
-
description: node.requestBody!.description ?? entry.schema.description,
|
|
180
|
-
},
|
|
181
|
-
name: resolver.resolveDataName(node),
|
|
182
|
-
keysToOmit: entry.keysToOmit,
|
|
183
|
-
})
|
|
184
|
-
}
|
|
185
|
-
// Multiple content types — generate individual types + union alias
|
|
186
|
-
return buildContentTypeVariants(requestBodyContent, resolver.resolveDataName(node), (schema) => ({
|
|
187
|
-
...schema,
|
|
188
|
-
description: node.requestBody!.description ?? schema.description,
|
|
189
|
-
}))
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const requestType = buildRequestType()
|
|
193
|
-
|
|
194
|
-
const responseTypes = node.responses.map((res) => {
|
|
195
|
-
const variants = (res.content ?? []).filter((entry) => entry.schema)
|
|
196
|
-
// Multiple content types for a single status code — generate a union of the variants.
|
|
197
|
-
if (variants.length > 1) {
|
|
198
|
-
return buildContentTypeVariants(variants, resolver.resolveResponseStatusName(node, res.statusCode))
|
|
199
|
-
}
|
|
200
|
-
const primary = variants[0] ?? res.content?.[0]
|
|
201
|
-
return renderSchemaType({
|
|
202
|
-
schema: primary?.schema ?? null,
|
|
203
|
-
name: resolver.resolveResponseStatusName(node, res.statusCode),
|
|
204
|
-
keysToOmit: primary?.keysToOmit,
|
|
205
|
-
})
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
const dataType = renderSchemaType({
|
|
209
|
-
schema: buildData({ ...node, parameters: params }, { resolver }),
|
|
210
|
-
name: resolver.resolveRequestConfigName(node),
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
const responsesType = renderSchemaType({
|
|
214
|
-
schema: buildResponses(node, { resolver }),
|
|
215
|
-
name: resolver.resolveResponsesName(node),
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
function buildResponseType() {
|
|
219
|
-
const hasSchema = (res: ast.ResponseNode) => (res.content ?? []).some((entry) => entry.schema)
|
|
220
|
-
if (!node.responses.some(hasSchema)) {
|
|
221
|
-
return null
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const responseName = resolver.resolveResponseName(node)
|
|
225
|
-
|
|
226
|
-
const responsesWithSchema = node.responses.filter(hasSchema)
|
|
227
|
-
const importedNames = new Set(
|
|
228
|
-
responsesWithSchema.flatMap((res) =>
|
|
229
|
-
(res.content ?? []).flatMap((entry) =>
|
|
230
|
-
entry.schema
|
|
231
|
-
? adapter
|
|
232
|
-
.getImports(entry.schema, (schemaName) => ({
|
|
233
|
-
name: resolveImportName(schemaName),
|
|
234
|
-
path: '',
|
|
235
|
-
}))
|
|
236
|
-
.flatMap((imp) => (Array.isArray(imp.name) ? imp.name : [imp.name]))
|
|
237
|
-
: [],
|
|
238
|
-
),
|
|
239
|
-
),
|
|
240
|
-
)
|
|
241
|
-
|
|
242
|
-
if (importedNames.has(responseName)) {
|
|
243
|
-
return null
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return renderSchemaType({
|
|
247
|
-
schema: {
|
|
248
|
-
...buildResponseUnion(node, { resolver })!,
|
|
249
|
-
description: 'Union of all possible responses',
|
|
250
|
-
},
|
|
251
|
-
name: responseName,
|
|
252
|
-
})
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const responseType = buildResponseType()
|
|
256
|
-
|
|
257
|
-
return (
|
|
258
|
-
<File
|
|
259
|
-
baseName={meta.file.baseName}
|
|
260
|
-
path={meta.file.path}
|
|
261
|
-
meta={meta.file.meta}
|
|
262
|
-
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
263
|
-
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
264
|
-
>
|
|
265
|
-
{paramTypes}
|
|
266
|
-
{responseTypes}
|
|
267
|
-
{requestType}
|
|
268
|
-
{dataType}
|
|
269
|
-
{responsesType}
|
|
270
|
-
{responseType}
|
|
271
|
-
</File>
|
|
272
|
-
)
|
|
273
|
-
},
|
|
274
|
-
})
|
package/src/index.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export { Enum } from './components/Enum.tsx'
|
|
2
|
-
export { Type } from './components/Type.tsx'
|
|
3
|
-
export { typeGenerator } from './generators/typeGenerator.tsx'
|
|
4
|
-
export { default, pluginTs, pluginTsName } from './plugin.ts'
|
|
5
|
-
export { functionPrinter } from './printers/functionPrinter.ts'
|
|
6
|
-
export type { PrinterTsFactory, PrinterTsNodes, PrinterTsOptions } from './printers/printerTs.ts'
|
|
7
|
-
export { printerTs } from './printers/printerTs.ts'
|
|
8
|
-
export { resolverTs } from './resolvers/resolverTs.ts'
|
|
9
|
-
export type { PluginTs, ResolverTs } from './types.ts'
|
package/src/plugin.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { createGroupConfig } from '@internals/shared'
|
|
2
|
-
import { definePlugin } from '@kubb/core'
|
|
3
|
-
import { typeGenerator } from './generators/typeGenerator.tsx'
|
|
4
|
-
import { resolverTs } from './resolvers/resolverTs.ts'
|
|
5
|
-
import type { PluginTs } from './types.ts'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Canonical plugin name for `@kubb/plugin-ts`. Used for driver lookups and
|
|
9
|
-
* cross-plugin dependency references.
|
|
10
|
-
*/
|
|
11
|
-
export const pluginTsName = 'plugin-ts' satisfies PluginTs['name']
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Generates TypeScript `type` aliases and `interface` declarations from an
|
|
15
|
-
* OpenAPI spec. The foundation that every other Kubb plugin builds on:
|
|
16
|
-
* clients, query hooks, mocks, and validators all reference the names this
|
|
17
|
-
* plugin produces.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```ts
|
|
21
|
-
* import { defineConfig } from 'kubb'
|
|
22
|
-
* import { pluginTs } from '@kubb/plugin-ts'
|
|
23
|
-
*
|
|
24
|
-
* export default defineConfig({
|
|
25
|
-
* input: { path: './petStore.yaml' },
|
|
26
|
-
* output: { path: './src/gen' },
|
|
27
|
-
* plugins: [
|
|
28
|
-
* pluginTs({
|
|
29
|
-
* output: { path: './types' },
|
|
30
|
-
* enum: { type: 'asConst' },
|
|
31
|
-
* optionalType: 'questionTokenAndUndefined',
|
|
32
|
-
* }),
|
|
33
|
-
* ],
|
|
34
|
-
* })
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
export const pluginTs = definePlugin<PluginTs>((options) => {
|
|
38
|
-
const {
|
|
39
|
-
output = { path: 'types', barrel: { type: 'named' } },
|
|
40
|
-
group,
|
|
41
|
-
exclude = [],
|
|
42
|
-
include,
|
|
43
|
-
override = [],
|
|
44
|
-
enum: enumOptions = {},
|
|
45
|
-
optionalType = 'questionToken',
|
|
46
|
-
arrayType = 'array',
|
|
47
|
-
syntaxType = 'type',
|
|
48
|
-
paramsCasing,
|
|
49
|
-
printer,
|
|
50
|
-
resolver: userResolver,
|
|
51
|
-
transformer: userTransformer,
|
|
52
|
-
generators: userGenerators = [],
|
|
53
|
-
} = options
|
|
54
|
-
|
|
55
|
-
const groupConfig = createGroupConfig(group)
|
|
56
|
-
|
|
57
|
-
const resolvedEnum = {
|
|
58
|
-
type: enumOptions.type ?? 'asConst',
|
|
59
|
-
constCasing: enumOptions.constCasing ?? 'camelCase',
|
|
60
|
-
typeSuffix: enumOptions.typeSuffix ?? 'Key',
|
|
61
|
-
keyCasing: enumOptions.keyCasing ?? 'none',
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
name: pluginTsName,
|
|
66
|
-
options,
|
|
67
|
-
hooks: {
|
|
68
|
-
'kubb:plugin:setup'(ctx) {
|
|
69
|
-
ctx.setOptions({
|
|
70
|
-
output,
|
|
71
|
-
exclude,
|
|
72
|
-
include,
|
|
73
|
-
override,
|
|
74
|
-
optionalType,
|
|
75
|
-
group: groupConfig,
|
|
76
|
-
arrayType,
|
|
77
|
-
enum: resolvedEnum,
|
|
78
|
-
syntaxType,
|
|
79
|
-
paramsCasing,
|
|
80
|
-
printer,
|
|
81
|
-
})
|
|
82
|
-
ctx.setResolver(userResolver ? { ...resolverTs, ...userResolver } : resolverTs)
|
|
83
|
-
if (userTransformer) {
|
|
84
|
-
ctx.setTransformer(userTransformer)
|
|
85
|
-
}
|
|
86
|
-
ctx.addGenerator(typeGenerator)
|
|
87
|
-
for (const gen of userGenerators) {
|
|
88
|
-
ctx.addGenerator(gen)
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
export default pluginTs
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { ast } from '@kubb/core'
|
|
2
|
-
import { PARAM_RANK } from '../constants.ts'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Maps each function-printer handler key to its concrete node type.
|
|
6
|
-
*/
|
|
7
|
-
export type FunctionNodeByType = {
|
|
8
|
-
functionParameter: ast.FunctionParameterNode
|
|
9
|
-
parameterGroup: ast.ParameterGroupNode
|
|
10
|
-
functionParameters: ast.FunctionParametersNode
|
|
11
|
-
paramsType: ast.ParamsTypeNode
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const kindToHandlerKey = {
|
|
15
|
-
FunctionParameter: 'functionParameter',
|
|
16
|
-
ParameterGroup: 'parameterGroup',
|
|
17
|
-
FunctionParameters: 'functionParameters',
|
|
18
|
-
ParamsType: 'paramsType',
|
|
19
|
-
} satisfies Record<string, ast.FunctionNodeType>
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Creates a function-parameter printer factory.
|
|
23
|
-
*
|
|
24
|
-
* Uses `createPrinterFactory` and dispatches handlers by `node.kind`
|
|
25
|
-
* (for function nodes) rather than by `node.type` (for schema nodes).
|
|
26
|
-
*/
|
|
27
|
-
export const defineFunctionPrinter = ast.createPrinterFactory<ast.FunctionParamNode, ast.FunctionNodeType, FunctionNodeByType>(
|
|
28
|
-
(node) => kindToHandlerKey[node.kind],
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
export type FunctionPrinterOptions = {
|
|
32
|
-
/**
|
|
33
|
-
* Rendering modes supported by `functionPrinter`.
|
|
34
|
-
*
|
|
35
|
-
* | Mode | Output example | Use case |
|
|
36
|
-
* |---------------|---------------------------------------------|---------------------------------|
|
|
37
|
-
* | `declaration` | `id: string, config: Config = {}` | Function parameter declaration |
|
|
38
|
-
* | `call` | `id, { method, url }` | Function call arguments |
|
|
39
|
-
* | `keys` | `{ id, config }` | Key names only (destructuring) |
|
|
40
|
-
* | `values` | `{ id: id, config: config }` | Key/value object entries |
|
|
41
|
-
*/
|
|
42
|
-
mode: 'declaration' | 'call' | 'keys' | 'values'
|
|
43
|
-
/**
|
|
44
|
-
* Optional transformation applied to every parameter name before printing.
|
|
45
|
-
*/
|
|
46
|
-
transformName?: (name: string) => string
|
|
47
|
-
/**
|
|
48
|
-
* Optional transformation applied to every type string before printing.
|
|
49
|
-
*/
|
|
50
|
-
transformType?: (type: string) => string
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
type DefaultPrinter = ast.PrinterFactoryOptions<'functionParameters', FunctionPrinterOptions, string>
|
|
54
|
-
|
|
55
|
-
function rank(param: ast.FunctionParameterNode | ast.ParameterGroupNode): number {
|
|
56
|
-
if (param.kind === 'ParameterGroup') {
|
|
57
|
-
if (param.default) return PARAM_RANK.withDefault
|
|
58
|
-
const isOptional = param.optional ?? param.properties.every((p) => p.optional || p.default !== undefined)
|
|
59
|
-
return isOptional ? PARAM_RANK.optional : PARAM_RANK.required
|
|
60
|
-
}
|
|
61
|
-
if (param.rest) return PARAM_RANK.rest
|
|
62
|
-
if (param.default) return PARAM_RANK.withDefault
|
|
63
|
-
return param.optional ? PARAM_RANK.optional : PARAM_RANK.required
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function sortParams(params: ReadonlyArray<ast.FunctionParameterNode | ast.ParameterGroupNode>): Array<ast.FunctionParameterNode | ast.ParameterGroupNode> {
|
|
67
|
-
return params.toSorted((a, b) => rank(a) - rank(b))
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function sortChildParams(params: Array<ast.FunctionParameterNode>): Array<ast.FunctionParameterNode> {
|
|
71
|
-
return params.toSorted((a, b) => rank(a) - rank(b))
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Default function-signature printer.
|
|
76
|
-
* Covers the four standard output modes used across Kubb plugins.
|
|
77
|
-
*
|
|
78
|
-
* @example
|
|
79
|
-
* ```ts
|
|
80
|
-
* const printer = functionPrinter({ mode: 'declaration' })
|
|
81
|
-
*
|
|
82
|
-
* const sig = createFunctionParameters({
|
|
83
|
-
* params: [
|
|
84
|
-
* createFunctionParameter({ name: 'petId', type: 'string', optional: false }),
|
|
85
|
-
* createFunctionParameter({ name: 'config', type: 'Config', optional: false, default: '{}' }),
|
|
86
|
-
* ],
|
|
87
|
-
* })
|
|
88
|
-
*
|
|
89
|
-
* printer.print(sig) // → "petId: string, config: Config = {}"
|
|
90
|
-
* ```
|
|
91
|
-
*/
|
|
92
|
-
export const functionPrinter = defineFunctionPrinter<DefaultPrinter>((options) => ({
|
|
93
|
-
name: 'functionParameters',
|
|
94
|
-
options,
|
|
95
|
-
nodes: {
|
|
96
|
-
paramsType(node) {
|
|
97
|
-
if (node.kind !== 'ParamsType') return null
|
|
98
|
-
if (node.variant === 'member') {
|
|
99
|
-
return `${node.base}['${node.key}']`
|
|
100
|
-
}
|
|
101
|
-
if (node.variant === 'struct') {
|
|
102
|
-
const parts = node.properties.map((p) => {
|
|
103
|
-
const typeStr = this.transform(p.type)
|
|
104
|
-
const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(p.name) ? p.name : JSON.stringify(p.name)
|
|
105
|
-
return p.optional ? `${key}?: ${typeStr}` : `${key}: ${typeStr}`
|
|
106
|
-
})
|
|
107
|
-
return `{ ${parts.join('; ')} }`
|
|
108
|
-
}
|
|
109
|
-
if (node.variant === 'reference') {
|
|
110
|
-
return node.name
|
|
111
|
-
}
|
|
112
|
-
return null
|
|
113
|
-
},
|
|
114
|
-
functionParameter(node) {
|
|
115
|
-
const { mode, transformName, transformType } = this.options
|
|
116
|
-
const name = transformName ? transformName(node.name) : node.name
|
|
117
|
-
|
|
118
|
-
const rawType = node.type ? this.transform(node.type) : undefined
|
|
119
|
-
const type = rawType != null && transformType ? transformType(rawType) : rawType
|
|
120
|
-
|
|
121
|
-
if (mode === 'keys' || mode === 'values') {
|
|
122
|
-
return node.rest ? `...${name}` : name
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (mode === 'call') {
|
|
126
|
-
return node.rest ? `...${name}` : name
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (node.rest) {
|
|
130
|
-
return type ? `...${name}: ${type}` : `...${name}`
|
|
131
|
-
}
|
|
132
|
-
if (type) {
|
|
133
|
-
if (node.optional) return `${name}?: ${type}`
|
|
134
|
-
return node.default ? `${name}: ${type} = ${node.default}` : `${name}: ${type}`
|
|
135
|
-
}
|
|
136
|
-
return node.default ? `${name} = ${node.default}` : name
|
|
137
|
-
},
|
|
138
|
-
parameterGroup(node) {
|
|
139
|
-
const { mode, transformName, transformType } = this.options
|
|
140
|
-
const sorted = sortChildParams(node.properties)
|
|
141
|
-
const isOptional = node.optional ?? sorted.every((p) => p.optional || p.default !== undefined)
|
|
142
|
-
|
|
143
|
-
if (node.inline) {
|
|
144
|
-
return sorted
|
|
145
|
-
.map((p) => this.transform(p))
|
|
146
|
-
.filter(Boolean)
|
|
147
|
-
.join(', ')
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (mode === 'keys' || mode === 'values') {
|
|
151
|
-
const keys = sorted.map((p) => p.name).join(', ')
|
|
152
|
-
return `{ ${keys} }`
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (mode === 'call') {
|
|
156
|
-
const keys = sorted.map((p) => p.name).join(', ')
|
|
157
|
-
return `{ ${keys} }`
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const names = sorted.map((p) => {
|
|
161
|
-
const n = transformName ? transformName(p.name) : p.name
|
|
162
|
-
|
|
163
|
-
return n
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
const nameStr = names.length ? `{ ${names.join(', ')} }` : undefined
|
|
167
|
-
if (!nameStr) return null
|
|
168
|
-
|
|
169
|
-
let typeAnnotation: string | undefined = node.type ? (this.transform(node.type) ?? undefined) : undefined
|
|
170
|
-
if (!typeAnnotation) {
|
|
171
|
-
const typeParts = sorted
|
|
172
|
-
.filter((p) => p.type)
|
|
173
|
-
.map((p) => {
|
|
174
|
-
const rawT = p.type ? this.transform(p.type) : undefined
|
|
175
|
-
const t = rawT != null && transformType ? transformType(rawT) : rawT
|
|
176
|
-
return p.optional || p.default !== undefined ? `${p.name}?: ${t}` : `${p.name}: ${t}`
|
|
177
|
-
})
|
|
178
|
-
typeAnnotation = typeParts.length ? `{ ${typeParts.join('; ')} }` : undefined
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (typeAnnotation) {
|
|
182
|
-
if (isOptional) return `${nameStr}: ${typeAnnotation} = ${node.default ?? '{}'}`
|
|
183
|
-
return node.default ? `${nameStr}: ${typeAnnotation} = ${node.default}` : `${nameStr}: ${typeAnnotation}`
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return node.default ? `${nameStr} = ${node.default}` : nameStr
|
|
187
|
-
},
|
|
188
|
-
functionParameters(node) {
|
|
189
|
-
const sorted = sortParams(node.params)
|
|
190
|
-
|
|
191
|
-
return sorted
|
|
192
|
-
.map((p) => this.transform(p))
|
|
193
|
-
.filter(Boolean)
|
|
194
|
-
.join(', ')
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
}))
|