@kubb/plugin-ts 5.0.0-beta.10 → 5.0.0-beta.103
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 +22 -26
- package/dist/index.cjs +1444 -594
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +641 -271
- package/dist/index.js +1417 -584
- package/dist/index.js.map +1 -1
- package/dist/rolldown-runtime-CNktS9qV.js +17 -0
- package/package.json +8 -22
- package/dist/chunk--u3MIqq1.js +0 -8
- package/extension.yaml +0 -632
- package/src/components/Enum.tsx +0 -83
- package/src/components/Type.tsx +0 -60
- package/src/constants.ts +0 -39
- package/src/factory.ts +0 -982
- package/src/generators/typeGenerator.tsx +0 -296
- package/src/index.ts +0 -9
- package/src/plugin.ts +0 -93
- package/src/printers/functionPrinter.ts +0 -197
- package/src/printers/printerTs.ts +0 -329
- package/src/resolvers/resolverTs.ts +0 -66
- package/src/types.ts +0 -308
- package/src/utils.ts +0 -129
package/dist/index.cjs
CHANGED
|
@@ -13,6 +13,15 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
13
13
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
14
|
var __getProtoOf = Object.getPrototypeOf;
|
|
15
15
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
|
+
var __exportAll = (all, no_symbols) => {
|
|
17
|
+
let target = {};
|
|
18
|
+
for (var name in all) __defProp(target, name, {
|
|
19
|
+
get: all[name],
|
|
20
|
+
enumerable: true
|
|
21
|
+
});
|
|
22
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
23
|
+
return target;
|
|
24
|
+
};
|
|
16
25
|
var __copyProps = (to, from, except, desc) => {
|
|
17
26
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
18
27
|
key = keys[i];
|
|
@@ -29,12 +38,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
29
38
|
}) : target, mod));
|
|
30
39
|
//#endregion
|
|
31
40
|
let _kubb_parser_ts = require("@kubb/parser-ts");
|
|
32
|
-
let
|
|
33
|
-
let
|
|
34
|
-
let remeda = require("remeda");
|
|
41
|
+
let kubb_jsx = require("kubb/jsx");
|
|
42
|
+
let kubb_kit = require("kubb/kit");
|
|
35
43
|
let typescript = require("typescript");
|
|
36
44
|
typescript = __toESM(typescript, 1);
|
|
37
|
-
let
|
|
45
|
+
let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
|
|
38
46
|
//#region ../../internals/utils/src/casing.ts
|
|
39
47
|
/**
|
|
40
48
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
@@ -46,58 +54,41 @@ let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
|
|
|
46
54
|
function toCamelOrPascal(text, pascal) {
|
|
47
55
|
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
48
56
|
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
49
|
-
|
|
50
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
57
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
51
58
|
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
52
59
|
}
|
|
53
60
|
/**
|
|
54
|
-
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
55
|
-
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
56
|
-
* Segments are joined with `/` to form a file path.
|
|
57
|
-
*
|
|
58
|
-
* Only splits on dots followed by a letter so that version numbers
|
|
59
|
-
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
60
|
-
*/
|
|
61
|
-
function applyToFileParts(text, transformPart) {
|
|
62
|
-
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
63
|
-
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
61
|
* Converts `text` to camelCase.
|
|
67
|
-
* When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
|
|
68
62
|
*
|
|
69
|
-
* @example
|
|
70
|
-
* camelCase('hello-world')
|
|
71
|
-
*
|
|
63
|
+
* @example Word boundaries
|
|
64
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
65
|
+
*
|
|
66
|
+
* @example With a prefix
|
|
67
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
72
68
|
*/
|
|
73
|
-
function camelCase(text, {
|
|
74
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
|
|
75
|
-
prefix,
|
|
76
|
-
suffix
|
|
77
|
-
} : {}));
|
|
69
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
78
70
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
79
71
|
}
|
|
80
72
|
/**
|
|
81
73
|
* Converts `text` to PascalCase.
|
|
82
|
-
* When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
|
|
83
74
|
*
|
|
84
|
-
* @example
|
|
85
|
-
* pascalCase('hello-world')
|
|
86
|
-
*
|
|
75
|
+
* @example Word boundaries
|
|
76
|
+
* `pascalCase('hello-world') // 'HelloWorld'`
|
|
77
|
+
*
|
|
78
|
+
* @example With a suffix
|
|
79
|
+
* `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
|
|
87
80
|
*/
|
|
88
|
-
function pascalCase(text, {
|
|
89
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
|
|
90
|
-
prefix,
|
|
91
|
-
suffix
|
|
92
|
-
}) : camelCase(part));
|
|
81
|
+
function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
93
82
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
94
83
|
}
|
|
95
84
|
/**
|
|
96
85
|
* Converts `text` to snake_case.
|
|
97
86
|
*
|
|
98
|
-
* @example
|
|
99
|
-
* snakeCase('helloWorld')
|
|
100
|
-
*
|
|
87
|
+
* @example From camelCase
|
|
88
|
+
* `snakeCase('helloWorld') // 'hello_world'`
|
|
89
|
+
*
|
|
90
|
+
* @example From mixed separators
|
|
91
|
+
* `snakeCase('Hello-World') // 'hello_world'`
|
|
101
92
|
*/
|
|
102
93
|
function snakeCase(text, { prefix = "", suffix = "" } = {}) {
|
|
103
94
|
return `${prefix} ${text} ${suffix}`.trim().replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s\-.]+/g, "_").replace(/[^a-zA-Z0-9_]/g, "").toLowerCase().split("_").filter(Boolean).join("_");
|
|
@@ -105,8 +96,8 @@ function snakeCase(text, { prefix = "", suffix = "" } = {}) {
|
|
|
105
96
|
/**
|
|
106
97
|
* Converts `text` to SCREAMING_SNAKE_CASE.
|
|
107
98
|
*
|
|
108
|
-
* @example
|
|
109
|
-
* screamingSnakeCase('helloWorld') // 'HELLO_WORLD'
|
|
99
|
+
* @example From camelCase
|
|
100
|
+
* `screamingSnakeCase('helloWorld') // 'HELLO_WORLD'`
|
|
110
101
|
*/
|
|
111
102
|
function screamingSnakeCase(text, { prefix = "", suffix = "" } = {}) {
|
|
112
103
|
return snakeCase(text, {
|
|
@@ -115,14 +106,155 @@ function screamingSnakeCase(text, { prefix = "", suffix = "" } = {}) {
|
|
|
115
106
|
}).toUpperCase();
|
|
116
107
|
}
|
|
117
108
|
//#endregion
|
|
118
|
-
//#region ../../internals/utils/src/
|
|
109
|
+
//#region ../../internals/utils/src/reserved.ts
|
|
110
|
+
/**
|
|
111
|
+
* JavaScript and Java reserved words.
|
|
112
|
+
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
113
|
+
*/
|
|
114
|
+
const reservedWords = /* @__PURE__ */ new Set([
|
|
115
|
+
"abstract",
|
|
116
|
+
"arguments",
|
|
117
|
+
"boolean",
|
|
118
|
+
"break",
|
|
119
|
+
"byte",
|
|
120
|
+
"case",
|
|
121
|
+
"catch",
|
|
122
|
+
"char",
|
|
123
|
+
"class",
|
|
124
|
+
"const",
|
|
125
|
+
"continue",
|
|
126
|
+
"debugger",
|
|
127
|
+
"default",
|
|
128
|
+
"delete",
|
|
129
|
+
"do",
|
|
130
|
+
"double",
|
|
131
|
+
"else",
|
|
132
|
+
"enum",
|
|
133
|
+
"eval",
|
|
134
|
+
"export",
|
|
135
|
+
"extends",
|
|
136
|
+
"false",
|
|
137
|
+
"final",
|
|
138
|
+
"finally",
|
|
139
|
+
"float",
|
|
140
|
+
"for",
|
|
141
|
+
"function",
|
|
142
|
+
"goto",
|
|
143
|
+
"if",
|
|
144
|
+
"implements",
|
|
145
|
+
"import",
|
|
146
|
+
"in",
|
|
147
|
+
"instanceof",
|
|
148
|
+
"int",
|
|
149
|
+
"interface",
|
|
150
|
+
"let",
|
|
151
|
+
"long",
|
|
152
|
+
"native",
|
|
153
|
+
"new",
|
|
154
|
+
"null",
|
|
155
|
+
"package",
|
|
156
|
+
"private",
|
|
157
|
+
"protected",
|
|
158
|
+
"public",
|
|
159
|
+
"return",
|
|
160
|
+
"short",
|
|
161
|
+
"static",
|
|
162
|
+
"super",
|
|
163
|
+
"switch",
|
|
164
|
+
"synchronized",
|
|
165
|
+
"this",
|
|
166
|
+
"throw",
|
|
167
|
+
"throws",
|
|
168
|
+
"transient",
|
|
169
|
+
"true",
|
|
170
|
+
"try",
|
|
171
|
+
"typeof",
|
|
172
|
+
"var",
|
|
173
|
+
"void",
|
|
174
|
+
"volatile",
|
|
175
|
+
"while",
|
|
176
|
+
"with",
|
|
177
|
+
"yield",
|
|
178
|
+
"Array",
|
|
179
|
+
"Date",
|
|
180
|
+
"hasOwnProperty",
|
|
181
|
+
"Infinity",
|
|
182
|
+
"isFinite",
|
|
183
|
+
"isNaN",
|
|
184
|
+
"isPrototypeOf",
|
|
185
|
+
"length",
|
|
186
|
+
"Math",
|
|
187
|
+
"name",
|
|
188
|
+
"NaN",
|
|
189
|
+
"Number",
|
|
190
|
+
"Object",
|
|
191
|
+
"prototype",
|
|
192
|
+
"String",
|
|
193
|
+
"toString",
|
|
194
|
+
"undefined",
|
|
195
|
+
"valueOf"
|
|
196
|
+
]);
|
|
197
|
+
/**
|
|
198
|
+
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```ts
|
|
202
|
+
* isValidVarName('status') // true
|
|
203
|
+
* isValidVarName('class') // false (reserved word)
|
|
204
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
function isValidVarName(name) {
|
|
208
|
+
if (!name || reservedWords.has(name)) return false;
|
|
209
|
+
return isIdentifier(name);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Returns `name` when it's a syntactically valid JavaScript variable name,
|
|
213
|
+
* otherwise prefixes it with `_` so the result is a valid identifier.
|
|
214
|
+
*
|
|
215
|
+
* Useful for sanitizing OpenAPI schema names or operation IDs that start with
|
|
216
|
+
* a digit (e.g. `409`, `504AccountCancel`) before using them as exported
|
|
217
|
+
* variable, type, or function names.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```ts
|
|
221
|
+
* ensureValidVarName('409') // '_409'
|
|
222
|
+
* ensureValidVarName('504AccountCancel') // '_504AccountCancel'
|
|
223
|
+
* ensureValidVarName('Pet') // 'Pet'
|
|
224
|
+
* ensureValidVarName('class') // '_class'
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
function ensureValidVarName(name) {
|
|
228
|
+
if (!name || isValidVarName(name)) return name;
|
|
229
|
+
return `_${name}`;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
|
|
233
|
+
*
|
|
234
|
+
* Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
|
|
235
|
+
* even though they are not valid variable names, so use this (not {@link isValidVarName}) when
|
|
236
|
+
* deciding whether an object key needs quoting.
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* isIdentifier('name') // true
|
|
241
|
+
* isIdentifier('x-total')// false
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
function isIdentifier(name) {
|
|
245
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
246
|
+
}
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region ../../internals/utils/src/strings.ts
|
|
119
249
|
/**
|
|
120
250
|
* Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
|
|
121
251
|
* Returns the string unchanged when no balanced quote pair is found.
|
|
122
252
|
*
|
|
123
253
|
* @example
|
|
254
|
+
* ```ts
|
|
124
255
|
* trimQuotes('"hello"') // 'hello'
|
|
125
256
|
* trimQuotes('hello') // 'hello'
|
|
257
|
+
* ```
|
|
126
258
|
*/
|
|
127
259
|
function trimQuotes(text) {
|
|
128
260
|
if (text.length >= 2) {
|
|
@@ -133,8 +265,24 @@ function trimQuotes(text) {
|
|
|
133
265
|
return text;
|
|
134
266
|
}
|
|
135
267
|
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
268
|
+
* Serializes a primitive to a single-quoted string literal, stripping any surrounding quotes first.
|
|
269
|
+
*
|
|
270
|
+
* Escaping runs through `JSON.stringify`, then the result switches to single quotes so the generated
|
|
271
|
+
* code matches the repo style without a formatter.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* stringify('hello') // "'hello'"
|
|
276
|
+
* stringify('"hello"') // "'hello'"
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
function stringify(value) {
|
|
280
|
+
if (value === void 0 || value === null) return "''";
|
|
281
|
+
return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Escapes characters that are not allowed inside JS string literals, covering quotes, backslashes,
|
|
285
|
+
* and the Unicode line terminators U+2028 and U+2029.
|
|
138
286
|
*
|
|
139
287
|
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
|
|
140
288
|
*
|
|
@@ -158,49 +306,59 @@ function jsStringEscape(input) {
|
|
|
158
306
|
});
|
|
159
307
|
}
|
|
160
308
|
//#endregion
|
|
161
|
-
//#region ../../internals/utils/src/
|
|
309
|
+
//#region ../../internals/utils/src/fs.ts
|
|
162
310
|
/**
|
|
163
|
-
*
|
|
311
|
+
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
312
|
+
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
313
|
+
* every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
|
|
164
314
|
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
315
|
+
* Empty segments are dropped before joining. They arise when the name starts with a dot
|
|
316
|
+
* followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
|
|
317
|
+
* an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
|
|
318
|
+
* absolute path, letting generated files escape the configured output directory.
|
|
319
|
+
*
|
|
320
|
+
* @example Nested path from a dotted name
|
|
321
|
+
* `toFilePath('pet.petId') // 'pet/petId'`
|
|
322
|
+
*
|
|
323
|
+
* @example PascalCase the final segment
|
|
324
|
+
* `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
|
|
325
|
+
*
|
|
326
|
+
* @example Suffix applied to the final segment only
|
|
327
|
+
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
168
328
|
*/
|
|
169
|
-
function
|
|
170
|
-
|
|
171
|
-
return
|
|
329
|
+
function toFilePath(name, caseLast = camelCase) {
|
|
330
|
+
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
331
|
+
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
172
332
|
}
|
|
173
333
|
//#endregion
|
|
174
334
|
//#region src/constants.ts
|
|
175
335
|
/**
|
|
176
336
|
* `optionalType` values that cause a property's type to include `| undefined`.
|
|
177
337
|
*/
|
|
178
|
-
const OPTIONAL_ADDS_UNDEFINED = new Set(["undefined", "questionTokenAndUndefined"]);
|
|
338
|
+
const OPTIONAL_ADDS_UNDEFINED = /* @__PURE__ */ new Set(["undefined", "questionTokenAndUndefined"]);
|
|
179
339
|
/**
|
|
180
340
|
* `optionalType` values that render the property key with a `?` token.
|
|
181
341
|
*/
|
|
182
|
-
const OPTIONAL_ADDS_QUESTION_TOKEN = new Set(["questionToken", "questionTokenAndUndefined"]);
|
|
342
|
+
const OPTIONAL_ADDS_QUESTION_TOKEN = /* @__PURE__ */ new Set(["questionToken", "questionTokenAndUndefined"]);
|
|
183
343
|
/**
|
|
184
|
-
* `
|
|
344
|
+
* `enum.type` values that append a `typeSuffix` to the generated enum type alias.
|
|
185
345
|
*/
|
|
186
|
-
const ENUM_TYPES_WITH_KEY_SUFFIX = new Set(["asConst"
|
|
346
|
+
const ENUM_TYPES_WITH_KEY_SUFFIX = /* @__PURE__ */ new Set(["asConst"]);
|
|
187
347
|
/**
|
|
188
|
-
* `
|
|
348
|
+
* `enum.type` values that require a runtime value declaration (object, enum, or literal).
|
|
189
349
|
*/
|
|
190
|
-
const ENUM_TYPES_WITH_RUNTIME_VALUE = new Set([
|
|
350
|
+
const ENUM_TYPES_WITH_RUNTIME_VALUE = /* @__PURE__ */ new Set([
|
|
191
351
|
"enum",
|
|
192
352
|
"asConst",
|
|
193
|
-
"asPascalConst",
|
|
194
353
|
"constEnum",
|
|
195
354
|
"literal",
|
|
196
355
|
void 0
|
|
197
356
|
]);
|
|
198
357
|
/**
|
|
199
|
-
* `
|
|
358
|
+
* `enum.type` values whose type declaration is type-only (no runtime value emitted for the type alias).
|
|
200
359
|
*/
|
|
201
|
-
const ENUM_TYPES_WITH_TYPE_ONLY = new Set([
|
|
360
|
+
const ENUM_TYPES_WITH_TYPE_ONLY = /* @__PURE__ */ new Set([
|
|
202
361
|
"asConst",
|
|
203
|
-
"asPascalConst",
|
|
204
362
|
"literal",
|
|
205
363
|
void 0
|
|
206
364
|
]);
|
|
@@ -215,8 +373,48 @@ const PARAM_RANK = {
|
|
|
215
373
|
};
|
|
216
374
|
//#endregion
|
|
217
375
|
//#region src/factory.ts
|
|
376
|
+
var factory_exports = /* @__PURE__ */ __exportAll({
|
|
377
|
+
appendJSDocToNode: () => appendJSDocToNode,
|
|
378
|
+
buildIndexSignatures: () => buildIndexSignatures,
|
|
379
|
+
buildMemberNodes: () => buildMemberNodes,
|
|
380
|
+
buildPropertyType: () => buildPropertyType,
|
|
381
|
+
buildTupleNode: () => buildTupleNode,
|
|
382
|
+
constToTypeNode: () => constToTypeNode,
|
|
383
|
+
createArrayDeclaration: () => createArrayDeclaration,
|
|
384
|
+
createEnumDeclaration: () => createEnumDeclaration,
|
|
385
|
+
createExportDeclaration: () => createExportDeclaration,
|
|
386
|
+
createImportDeclaration: () => createImportDeclaration,
|
|
387
|
+
createIntersectionDeclaration: () => createIntersectionDeclaration,
|
|
388
|
+
createJSDoc: () => createJSDoc,
|
|
389
|
+
createOmitDeclaration: () => createOmitDeclaration,
|
|
390
|
+
createParameterSignature: () => createParameterSignature,
|
|
391
|
+
createPropertySignature: () => createPropertySignature,
|
|
392
|
+
createQuestionToken: () => createQuestionToken,
|
|
393
|
+
createTypeDeclaration: () => createTypeDeclaration,
|
|
394
|
+
createTypeLiteralNode: () => createTypeLiteralNode,
|
|
395
|
+
createTypeReferenceNode: () => createTypeReferenceNode,
|
|
396
|
+
createUnionDeclaration: () => createUnionDeclaration,
|
|
397
|
+
createUrlTemplateType: () => createUrlTemplateType,
|
|
398
|
+
dateOrStringNode: () => dateOrStringNode,
|
|
399
|
+
isNonNullable: () => isNonNullable$1,
|
|
400
|
+
keywordTypeNodes: () => keywordTypeNodes,
|
|
401
|
+
modifiers: () => modifiers,
|
|
402
|
+
syntaxKind: () => syntaxKind
|
|
403
|
+
});
|
|
218
404
|
const { SyntaxKind, factory } = typescript.default;
|
|
219
405
|
/**
|
|
406
|
+
* Compares two strings by UTF-16 code unit, keeping sorted output identical across platforms
|
|
407
|
+
* regardless of locale.
|
|
408
|
+
*/
|
|
409
|
+
function compareStrings(a, b) {
|
|
410
|
+
if (a < b) return -1;
|
|
411
|
+
if (a > b) return 1;
|
|
412
|
+
return 0;
|
|
413
|
+
}
|
|
414
|
+
function isNumber(value) {
|
|
415
|
+
return typeof value === "number" && !Number.isNaN(value);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
220
418
|
* TypeScript AST modifiers for common keywords (async, export, const, static).
|
|
221
419
|
*/
|
|
222
420
|
const modifiers = {
|
|
@@ -239,8 +437,13 @@ function isNonNullable$1(value) {
|
|
|
239
437
|
__name(isNonNullable$1, "isNonNullable");
|
|
240
438
|
function isValidIdentifier(str) {
|
|
241
439
|
if (!str.length || str.trim() !== str) return false;
|
|
242
|
-
|
|
243
|
-
|
|
440
|
+
let ch = str.codePointAt(0);
|
|
441
|
+
if (!typescript.default.isIdentifierStart(ch, typescript.default.ScriptTarget.Latest)) return false;
|
|
442
|
+
for (let i = ch > 65535 ? 2 : 1; i < str.length; i += ch > 65535 ? 2 : 1) {
|
|
443
|
+
ch = str.codePointAt(i);
|
|
444
|
+
if (!typescript.default.isIdentifierPart(ch, typescript.default.ScriptTarget.Latest)) return false;
|
|
445
|
+
}
|
|
446
|
+
return true;
|
|
244
447
|
}
|
|
245
448
|
function propertyName(name) {
|
|
246
449
|
if (typeof name === "string") return isValidIdentifier(name) ? factory.createIdentifier(name) : factory.createStringLiteral(name);
|
|
@@ -315,6 +518,17 @@ function createParameterSignature(name, { modifiers, dotDotDotToken, questionTok
|
|
|
315
518
|
return factory.createParameterDeclaration(modifiers, dotDotDotToken, name, createQuestionToken(questionToken), type, initializer);
|
|
316
519
|
}
|
|
317
520
|
/**
|
|
521
|
+
* Creates a JSDoc comment node from an array of comment strings.
|
|
522
|
+
* Returns null if no comments are provided.
|
|
523
|
+
*/
|
|
524
|
+
function createJSDoc({ comments }) {
|
|
525
|
+
if (!comments.length) return null;
|
|
526
|
+
return factory.createJSDocComment(factory.createNodeArray(comments.map((comment, i) => {
|
|
527
|
+
if (i === comments.length - 1) return factory.createJSDocText(comment);
|
|
528
|
+
return factory.createJSDocText(`${comment}\n`);
|
|
529
|
+
})));
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
318
532
|
* Attaches JSDoc comments to an AST node as synthetic leading comments.
|
|
319
533
|
* Filters out undefined comments before attaching.
|
|
320
534
|
*
|
|
@@ -352,7 +566,7 @@ function createInterfaceDeclaration({ modifiers, name, typeParameters, members }
|
|
|
352
566
|
* Intelligently selects the syntax based on the type structure and attaches JSDoc comments.
|
|
353
567
|
*/
|
|
354
568
|
function createTypeDeclaration({ syntax, isExportable, comments, name, type }) {
|
|
355
|
-
if (syntax === "interface" &&
|
|
569
|
+
if (syntax === "interface" && typescript.default.isTypeLiteralNode(type)) return appendJSDocToNode({
|
|
356
570
|
node: createInterfaceDeclaration({
|
|
357
571
|
members: [...type.members],
|
|
358
572
|
modifiers: isExportable ? [modifiers.export] : [],
|
|
@@ -372,6 +586,50 @@ function createTypeDeclaration({ syntax, isExportable, comments, name, type }) {
|
|
|
372
586
|
});
|
|
373
587
|
}
|
|
374
588
|
/**
|
|
589
|
+
* Creates an import declaration with support for default imports, named imports, namespace imports, and type-only imports.
|
|
590
|
+
* Optionally rename imported members with `propertyName` and `name` pairs.
|
|
591
|
+
*
|
|
592
|
+
* @example Default import
|
|
593
|
+
* `import Pet from './Pet'`
|
|
594
|
+
*
|
|
595
|
+
* @example Named imports with rename
|
|
596
|
+
* `import { Pet as Cat } from './Pet'`
|
|
597
|
+
*
|
|
598
|
+
* @example Namespace import
|
|
599
|
+
* `import * as Pet from './Pet'`
|
|
600
|
+
*/
|
|
601
|
+
function createImportDeclaration({ name, path, isTypeOnly = false, isNameSpace = false }) {
|
|
602
|
+
if (!Array.isArray(name)) {
|
|
603
|
+
const importPropertyName = isNameSpace ? void 0 : factory.createIdentifier(name);
|
|
604
|
+
const importName = isNameSpace ? factory.createNamespaceImport(factory.createIdentifier(name)) : void 0;
|
|
605
|
+
return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, importPropertyName, importName), factory.createStringLiteral(path), void 0);
|
|
606
|
+
}
|
|
607
|
+
const sortedName = name.toSorted((a, b) => compareStrings(typeof a === "object" ? a.propertyName : a, typeof b === "object" ? b.propertyName : b));
|
|
608
|
+
return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, void 0, factory.createNamedImports(sortedName.map((item) => {
|
|
609
|
+
if (typeof item === "object") {
|
|
610
|
+
const obj = item;
|
|
611
|
+
if (obj.name) return factory.createImportSpecifier(false, factory.createIdentifier(obj.propertyName), factory.createIdentifier(obj.name));
|
|
612
|
+
return factory.createImportSpecifier(false, void 0, factory.createIdentifier(obj.propertyName));
|
|
613
|
+
}
|
|
614
|
+
return factory.createImportSpecifier(false, void 0, factory.createIdentifier(item));
|
|
615
|
+
}))), factory.createStringLiteral(path), void 0);
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Creates an export declaration with support for named exports, namespace exports, and type-only exports.
|
|
619
|
+
* Sorts export names alphabetically for consistent output across platforms.
|
|
620
|
+
*/
|
|
621
|
+
function createExportDeclaration({ path, asAlias, isTypeOnly = false, name }) {
|
|
622
|
+
if (name && !Array.isArray(name) && !asAlias) console.warn(`When using name as string, asAlias should be true ${name}`);
|
|
623
|
+
if (!Array.isArray(name)) {
|
|
624
|
+
const parsedName = name?.match(/^\d/) ? `_${name?.slice(1)}` : name;
|
|
625
|
+
return factory.createExportDeclaration(void 0, isTypeOnly, asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : void 0, factory.createStringLiteral(path), void 0);
|
|
626
|
+
}
|
|
627
|
+
const sortedName = name.toSorted((a, b) => compareStrings(typeof a === "string" ? a : a.text, typeof b === "string" ? b : b.text));
|
|
628
|
+
return factory.createExportDeclaration(void 0, isTypeOnly, factory.createNamedExports(sortedName.map((propertyName) => {
|
|
629
|
+
return factory.createExportSpecifier(false, void 0, typeof propertyName === "string" ? factory.createIdentifier(propertyName) : propertyName);
|
|
630
|
+
})), factory.createStringLiteral(path), void 0);
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
375
633
|
* Apply casing transformation to enum keys
|
|
376
634
|
*/
|
|
377
635
|
function applyEnumKeyCasing(key, casing = "none") {
|
|
@@ -392,43 +650,52 @@ function applyEnumKeyCasing(key, casing = "none") {
|
|
|
392
650
|
* type: 'enum',
|
|
393
651
|
* name: 'petType',
|
|
394
652
|
* typeName: 'PetType',
|
|
395
|
-
* enums: [['cat', 'cat'], ['dog', 'dog']],
|
|
653
|
+
* enums: [['cat', 'cat', 'A cat'], ['dog', 'dog']],
|
|
396
654
|
* })
|
|
397
655
|
* ```
|
|
398
656
|
*/
|
|
399
657
|
function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCasing = "none" }) {
|
|
400
658
|
if (type === "literal" || type === "inlineLiteral") return [void 0, factory.createTypeAliasDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createUnionTypeNode(enums.map(([_key, value]) => {
|
|
401
|
-
if (
|
|
659
|
+
if (isNumber(value)) {
|
|
402
660
|
if (value < 0) return factory.createLiteralTypeNode(factory.createPrefixUnaryExpression(typescript.default.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value))));
|
|
403
661
|
return factory.createLiteralTypeNode(factory.createNumericLiteral(value?.toString()));
|
|
404
662
|
}
|
|
405
663
|
if (typeof value === "boolean") return factory.createLiteralTypeNode(value ? factory.createTrue() : factory.createFalse());
|
|
406
|
-
if (value) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
|
|
664
|
+
if (value !== null && value !== void 0) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
|
|
407
665
|
}).filter((node) => node !== void 0)))];
|
|
408
|
-
if (type === "enum" || type === "constEnum") return [void 0, factory.createEnumDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword), type === "constEnum" ? factory.createToken(typescript.default.SyntaxKind.ConstKeyword) : void 0].filter((modifier) => modifier !== void 0), factory.createIdentifier(typeName), enums.map(([key, value]) => {
|
|
666
|
+
if (type === "enum" || type === "constEnum") return [void 0, factory.createEnumDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword), type === "constEnum" ? factory.createToken(typescript.default.SyntaxKind.ConstKeyword) : void 0].filter((modifier) => modifier !== void 0), factory.createIdentifier(typeName), enums.map(([key, value, description]) => {
|
|
409
667
|
let initializer = factory.createStringLiteral(value?.toString());
|
|
410
|
-
if (Number.parseInt(value.toString(), 10) === value &&
|
|
668
|
+
if (Number.parseInt(value.toString(), 10) === value && isNumber(value)) if (value < 0) initializer = factory.createPrefixUnaryExpression(typescript.default.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
|
|
411
669
|
else initializer = factory.createNumericLiteral(value);
|
|
412
670
|
if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
|
|
413
|
-
if (
|
|
671
|
+
if (isNumber(Number.parseInt(key.toString(), 10))) {
|
|
414
672
|
const casingKey = applyEnumKeyCasing(`${typeName}_${key}`, enumKeyCasing);
|
|
415
|
-
return
|
|
673
|
+
return appendJSDocToNode({
|
|
674
|
+
node: factory.createEnumMember(propertyName(casingKey), initializer),
|
|
675
|
+
comments: [description]
|
|
676
|
+
});
|
|
416
677
|
}
|
|
417
|
-
if (key) {
|
|
678
|
+
if (key !== null && key !== void 0) {
|
|
418
679
|
const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
|
|
419
|
-
return
|
|
680
|
+
return appendJSDocToNode({
|
|
681
|
+
node: factory.createEnumMember(propertyName(casingKey), initializer),
|
|
682
|
+
comments: [description]
|
|
683
|
+
});
|
|
420
684
|
}
|
|
421
685
|
}).filter((member) => member !== void 0))];
|
|
422
686
|
const identifierName = name;
|
|
423
687
|
if (enums.length === 0) return [void 0, factory.createTypeAliasDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createKeywordTypeNode(typescript.default.SyntaxKind.NeverKeyword))];
|
|
424
|
-
return [factory.createVariableStatement([factory.createToken(typescript.default.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier(identifierName), void 0, void 0, factory.createAsExpression(factory.createObjectLiteralExpression(enums.map(([key, value]) => {
|
|
688
|
+
return [factory.createVariableStatement([factory.createToken(typescript.default.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier(identifierName), void 0, void 0, factory.createAsExpression(factory.createObjectLiteralExpression(enums.map(([key, value, description]) => {
|
|
425
689
|
let initializer = factory.createStringLiteral(value?.toString());
|
|
426
|
-
if (
|
|
690
|
+
if (isNumber(value)) if (value < 0) initializer = factory.createPrefixUnaryExpression(typescript.default.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
|
|
427
691
|
else initializer = factory.createNumericLiteral(value);
|
|
428
692
|
if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
|
|
429
|
-
if (key) {
|
|
693
|
+
if (key !== null && key !== void 0) {
|
|
430
694
|
const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
|
|
431
|
-
return
|
|
695
|
+
return appendJSDocToNode({
|
|
696
|
+
node: factory.createPropertyAssignment(propertyName(casingKey), initializer),
|
|
697
|
+
comments: [description]
|
|
698
|
+
});
|
|
432
699
|
}
|
|
433
700
|
}).filter((property) => property !== void 0), true), factory.createTypeReferenceNode(factory.createIdentifier("const"), void 0)))], typescript.default.NodeFlags.Const)), factory.createTypeAliasDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createIndexedAccessTypeNode(factory.createParenthesizedType(factory.createTypeQueryNode(factory.createIdentifier(identifierName), void 0)), factory.createTypeOperatorNode(typescript.default.SyntaxKind.KeyOfKeyword, factory.createTypeQueryNode(factory.createIdentifier(identifierName), void 0))))];
|
|
434
701
|
}
|
|
@@ -512,12 +779,10 @@ const createStringLiteral = factory.createStringLiteral;
|
|
|
512
779
|
* Creates an array type node (e.g., `T[]`).
|
|
513
780
|
*/
|
|
514
781
|
const createArrayTypeNode = factory.createArrayTypeNode;
|
|
515
|
-
factory.createParenthesizedType;
|
|
516
782
|
/**
|
|
517
783
|
* Creates a literal type node (e.g., `'hello'`, `42`, `true`).
|
|
518
784
|
*/
|
|
519
785
|
const createLiteralTypeNode = factory.createLiteralTypeNode;
|
|
520
|
-
factory.createNull;
|
|
521
786
|
/**
|
|
522
787
|
* Creates an identifier node.
|
|
523
788
|
*/
|
|
@@ -542,8 +807,6 @@ const createTrue = factory.createTrue;
|
|
|
542
807
|
* Creates a boolean false literal type node.
|
|
543
808
|
*/
|
|
544
809
|
const createFalse = factory.createFalse;
|
|
545
|
-
factory.createIndexedAccessTypeNode;
|
|
546
|
-
factory.createTypeOperatorNode;
|
|
547
810
|
/**
|
|
548
811
|
* Creates a prefix unary expression (e.g., negative numbers, logical not).
|
|
549
812
|
*/
|
|
@@ -591,32 +854,38 @@ function buildTupleNode(node, print) {
|
|
|
591
854
|
/**
|
|
592
855
|
* Applies `nullable` and optional/nullish `| undefined` union modifiers to a property's resolved base type.
|
|
593
856
|
*/
|
|
594
|
-
function buildPropertyType(schema, baseType, optionalType) {
|
|
857
|
+
function buildPropertyType(schema, baseType, optionalType, optional) {
|
|
595
858
|
const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(optionalType);
|
|
596
|
-
const meta =
|
|
859
|
+
const meta = (0, kubb_kit.syncSchemaRef)(schema);
|
|
597
860
|
let type = baseType;
|
|
598
861
|
if (meta.nullable) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.null] });
|
|
599
|
-
if ((meta.nullish || meta.optional) && addsUndefined) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] });
|
|
862
|
+
if ((optional || meta.nullish || meta.optional) && addsUndefined) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] });
|
|
600
863
|
return type;
|
|
601
864
|
}
|
|
865
|
+
const indexSignaturePrinter = typescript.default.createPrinter();
|
|
866
|
+
const indexSignatureSource = typescript.default.createSourceFile("", "", typescript.default.ScriptTarget.Latest);
|
|
602
867
|
/**
|
|
603
|
-
* Creates TypeScript index
|
|
868
|
+
* Creates a TypeScript index signature for `additionalProperties` and `patternProperties` on an
|
|
869
|
+
* object schema node. TypeScript allows only one string index signature and its value must be
|
|
870
|
+
* assignable from every named property, so both keywords collapse into a single signature: the
|
|
871
|
+
* union of all value types (deduplicated), or `unknown` when the object also has fixed properties.
|
|
872
|
+
* The key regex of `patternProperties` cannot be expressed by an index signature and is dropped.
|
|
604
873
|
*/
|
|
605
874
|
function buildIndexSignatures(node, propertyCount, print) {
|
|
606
|
-
const
|
|
607
|
-
if (node.additionalProperties && node.additionalProperties !== true)
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
const first = Object.values(node.patternProperties)[0];
|
|
613
|
-
if (first) {
|
|
614
|
-
let patternType = print(first) ?? keywordTypeNodes.unknown;
|
|
615
|
-
if (first.nullable) patternType = createUnionDeclaration({ nodes: [patternType, keywordTypeNodes.null] });
|
|
616
|
-
elements.push(createIndexSignature(patternType));
|
|
617
|
-
}
|
|
875
|
+
const valueTypes = [];
|
|
876
|
+
if (node.additionalProperties && node.additionalProperties !== true) valueTypes.push(print(node.additionalProperties) ?? keywordTypeNodes.unknown);
|
|
877
|
+
else if (node.additionalProperties === true) valueTypes.push(keywordTypeNodes.unknown);
|
|
878
|
+
for (const schema of Object.values(node.patternProperties ?? {})) {
|
|
879
|
+
const patternType = print(schema) ?? keywordTypeNodes.unknown;
|
|
880
|
+
valueTypes.push(schema.nullable ? createUnionDeclaration({ nodes: [patternType, keywordTypeNodes.null] }) : patternType);
|
|
618
881
|
}
|
|
619
|
-
return
|
|
882
|
+
if (valueTypes.length === 0) return [];
|
|
883
|
+
const seen = /* @__PURE__ */ new Set();
|
|
884
|
+
const distinct = valueTypes.filter((type) => {
|
|
885
|
+
const key = indexSignaturePrinter.printNode(typescript.default.EmitHint.Unspecified, type, indexSignatureSource);
|
|
886
|
+
return seen.has(key) ? false : (seen.add(key), true);
|
|
887
|
+
});
|
|
888
|
+
return [createIndexSignature(propertyCount > 0 ? keywordTypeNodes.unknown : createUnionDeclaration({ nodes: distinct }))];
|
|
620
889
|
}
|
|
621
890
|
//#endregion
|
|
622
891
|
//#region src/components/Enum.tsx
|
|
@@ -624,236 +893,501 @@ function buildIndexSignatures(node, propertyCount, print) {
|
|
|
624
893
|
* Resolves the runtime identifier name and the TypeScript type name for an enum schema node.
|
|
625
894
|
*
|
|
626
895
|
* The raw `node.name` may be a YAML key such as `"enumNames.Type"` which is not a
|
|
627
|
-
* valid TypeScript identifier. The resolver normalizes it
|
|
628
|
-
*
|
|
896
|
+
* valid TypeScript identifier. The resolver normalizes it. For inline enum properties the adapter
|
|
897
|
+
* already emits a PascalCase+suffix name, so resolution is typically a no-op.
|
|
898
|
+
*
|
|
899
|
+
* When `constCasing` is `'pascalCase'` and `typeSuffix` is empty, the const and the type
|
|
900
|
+
* resolve to the same name, which TypeScript merges into a single value+type declaration.
|
|
629
901
|
*/
|
|
630
|
-
function getEnumNames({ node,
|
|
631
|
-
const resolved = resolver.
|
|
902
|
+
function getEnumNames({ node, enum: enumOptions, resolver }) {
|
|
903
|
+
const resolved = resolver.name(node.name);
|
|
632
904
|
return {
|
|
633
|
-
enumName:
|
|
634
|
-
typeName: ENUM_TYPES_WITH_KEY_SUFFIX.has(
|
|
905
|
+
enumName: enumOptions.constCasing === "pascalCase" ? resolved : camelCase(node.name),
|
|
906
|
+
typeName: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) ? resolver.enum.keyName(node, enumOptions.typeSuffix) : resolved
|
|
635
907
|
};
|
|
636
908
|
}
|
|
637
909
|
/**
|
|
638
910
|
* Renders the enum declaration(s) for a single named `EnumSchemaNode`.
|
|
639
911
|
*
|
|
640
|
-
* Depending on `
|
|
641
|
-
* - A runtime object (`asConst`
|
|
912
|
+
* Depending on `enum.type` this may emit:
|
|
913
|
+
* - A runtime object (`asConst`) plus a `typeof` type alias
|
|
642
914
|
* - A `const enum` or plain `enum` declaration (`constEnum` / `enum`)
|
|
643
915
|
* - A union literal type alias (`literal`)
|
|
644
916
|
*
|
|
645
917
|
* The emitted `File.Source` nodes carry the resolved names so that the barrel
|
|
646
918
|
* index picks up the correct export identifiers.
|
|
647
919
|
*/
|
|
648
|
-
function Enum({ node,
|
|
920
|
+
function Enum({ node, enum: enumOptions, resolver }) {
|
|
649
921
|
const { enumName, typeName } = getEnumNames({
|
|
650
922
|
node,
|
|
651
|
-
|
|
652
|
-
enumTypeSuffix,
|
|
923
|
+
enum: enumOptions,
|
|
653
924
|
resolver
|
|
654
925
|
});
|
|
655
926
|
const [nameNode, typeNode] = createEnumDeclaration({
|
|
656
927
|
name: enumName,
|
|
657
928
|
typeName,
|
|
658
|
-
enums: node.namedEnumValues?.map((v) => [
|
|
659
|
-
|
|
660
|
-
|
|
929
|
+
enums: node.namedEnumValues?.map((v) => [
|
|
930
|
+
trimQuotes(v.name.toString()),
|
|
931
|
+
v.value,
|
|
932
|
+
v.description
|
|
933
|
+
]) ?? node.enumValues?.filter((v) => v !== null && v !== void 0).map((v) => [trimQuotes(v.toString()), v]) ?? [],
|
|
934
|
+
type: enumOptions.type,
|
|
935
|
+
enumKeyCasing: enumOptions.keyCasing
|
|
661
936
|
});
|
|
662
|
-
|
|
937
|
+
const namesMerge = !!nameNode && enumName === typeName;
|
|
938
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [nameNode && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
|
|
663
939
|
name: enumName,
|
|
664
940
|
isExportable: true,
|
|
665
941
|
isIndexable: true,
|
|
666
942
|
isTypeOnly: false,
|
|
667
|
-
children: (0, _kubb_parser_ts.
|
|
668
|
-
}), /* @__PURE__ */ (0,
|
|
943
|
+
children: (0, _kubb_parser_ts.parserTs)().print(nameNode)
|
|
944
|
+
}), /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
|
|
669
945
|
name: typeName,
|
|
670
|
-
isIndexable:
|
|
671
|
-
isExportable: ENUM_TYPES_WITH_RUNTIME_VALUE.has(
|
|
672
|
-
isTypeOnly: ENUM_TYPES_WITH_TYPE_ONLY.has(
|
|
673
|
-
children: (0, _kubb_parser_ts.
|
|
946
|
+
isIndexable: !namesMerge,
|
|
947
|
+
isExportable: !namesMerge && ENUM_TYPES_WITH_RUNTIME_VALUE.has(enumOptions.type),
|
|
948
|
+
isTypeOnly: ENUM_TYPES_WITH_TYPE_ONLY.has(enumOptions.type),
|
|
949
|
+
children: (0, _kubb_parser_ts.parserTs)().print(typeNode)
|
|
674
950
|
})] });
|
|
675
951
|
}
|
|
676
952
|
//#endregion
|
|
677
|
-
//#region src/
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
953
|
+
//#region ../../internals/shared/src/params.ts
|
|
954
|
+
/**
|
|
955
|
+
* Drops parameters that share the same name, keeping the first.
|
|
956
|
+
*
|
|
957
|
+
* A malformed spec can declare the same parameter name twice within one `in` location. Both would
|
|
958
|
+
* resolve to the same output property, so emitting both would yield an object type with a duplicate
|
|
959
|
+
* member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
|
|
960
|
+
* parameter names flow through unchanged, so no two distinct names ever collide here anymore.
|
|
961
|
+
*/
|
|
962
|
+
function dedupeParams(params) {
|
|
963
|
+
const seen = /* @__PURE__ */ new Set();
|
|
964
|
+
return params.filter((param) => {
|
|
965
|
+
if (seen.has(param.name)) return false;
|
|
966
|
+
seen.add(param.name);
|
|
967
|
+
return true;
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
//#endregion
|
|
971
|
+
//#region ../../internals/shared/src/operation.ts
|
|
972
|
+
/**
|
|
973
|
+
* Maps a content type to the PascalCase suffix used to name per-content-type variants
|
|
974
|
+
* (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
|
|
975
|
+
*/
|
|
976
|
+
function getContentTypeSuffix(contentType) {
|
|
977
|
+
const baseType = contentType.split(";")[0].trim();
|
|
978
|
+
if (baseType === "application/json") return "Json";
|
|
979
|
+
if (baseType === "multipart/form-data") return "FormData";
|
|
980
|
+
if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
|
|
981
|
+
const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
982
|
+
if (parts.length === 0) return "Unknown";
|
|
983
|
+
return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
|
|
987
|
+
* (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
|
|
988
|
+
*/
|
|
989
|
+
function getPerContentTypeName(baseName, suffix) {
|
|
990
|
+
if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
|
|
991
|
+
return baseName + suffix;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Resolves per-content-type variant names for a set of content entries, deduplicating suffix
|
|
995
|
+
* collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is
|
|
996
|
+
* the final (possibly counter-augmented) value, so callers can derive parallel names in another
|
|
997
|
+
* namespace (e.g. plugin-faker deriving the matching plugin-ts type name).
|
|
998
|
+
*/
|
|
999
|
+
function resolveContentTypeVariants(entries, baseName) {
|
|
1000
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
1001
|
+
return entries.filter((entry) => entry.schema).map((entry) => {
|
|
1002
|
+
const baseSuffix = getContentTypeSuffix(entry.contentType);
|
|
1003
|
+
let suffix = baseSuffix;
|
|
1004
|
+
let name = getPerContentTypeName(baseName, suffix);
|
|
1005
|
+
let counter = 2;
|
|
1006
|
+
while (usedNames.has(name)) {
|
|
1007
|
+
suffix = `${baseSuffix}${counter++}`;
|
|
1008
|
+
name = getPerContentTypeName(baseName, suffix);
|
|
1009
|
+
}
|
|
1010
|
+
usedNames.add(name);
|
|
686
1011
|
return {
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
resolver
|
|
693
|
-
})
|
|
1012
|
+
name,
|
|
1013
|
+
suffix,
|
|
1014
|
+
schema: entry.schema,
|
|
1015
|
+
keysToOmit: entry.keysToOmit,
|
|
1016
|
+
contentType: entry.contentType
|
|
694
1017
|
};
|
|
695
1018
|
});
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
return
|
|
699
|
-
node,
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
1019
|
+
}
|
|
1020
|
+
function getOperationParameters(node) {
|
|
1021
|
+
return {
|
|
1022
|
+
path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
|
|
1023
|
+
query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
|
|
1024
|
+
header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
|
|
1025
|
+
cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Builds the combined `{ body, path, query, headers }` options object schema for an operation,
|
|
1030
|
+
* referencing the already-resolved body and grouped param names. Shared by `@kubb/plugin-ts`'s
|
|
1031
|
+
* `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same
|
|
1032
|
+
* shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod
|
|
1033
|
+
* printer to emit `z.object(…)` rather than a record.
|
|
1034
|
+
*/
|
|
1035
|
+
function buildOptionsSchema(node, resolver) {
|
|
1036
|
+
const { path, query, header } = getOperationParameters(node);
|
|
1037
|
+
const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
|
|
1038
|
+
const createNever = () => kubb_kit.ast.factory.createSchema({
|
|
1039
|
+
type: "never",
|
|
1040
|
+
primitive: void 0,
|
|
1041
|
+
optional: true
|
|
1042
|
+
});
|
|
1043
|
+
const groups = [
|
|
1044
|
+
{
|
|
1045
|
+
name: "path",
|
|
1046
|
+
params: path,
|
|
1047
|
+
resolve: resolver.param.path
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
name: "query",
|
|
1051
|
+
params: query,
|
|
1052
|
+
resolve: resolver.param.query
|
|
1053
|
+
},
|
|
1054
|
+
{
|
|
1055
|
+
name: "headers",
|
|
1056
|
+
params: header,
|
|
1057
|
+
resolve: resolver.param.headers
|
|
1058
|
+
}
|
|
1059
|
+
];
|
|
1060
|
+
return kubb_kit.ast.factory.createSchema({
|
|
1061
|
+
type: "object",
|
|
1062
|
+
primitive: "object",
|
|
1063
|
+
deprecated: node.deprecated,
|
|
1064
|
+
properties: [kubb_kit.ast.factory.createProperty({
|
|
1065
|
+
name: "body",
|
|
1066
|
+
required: hasBody,
|
|
1067
|
+
schema: hasBody ? kubb_kit.ast.factory.createSchema({
|
|
1068
|
+
type: "ref",
|
|
1069
|
+
name: resolver.response.body(node)
|
|
1070
|
+
}) : createNever()
|
|
1071
|
+
}), ...groups.map(({ name, params, resolve }) => {
|
|
1072
|
+
const required = params.some((param) => param.required);
|
|
1073
|
+
return kubb_kit.ast.factory.createProperty({
|
|
1074
|
+
name,
|
|
1075
|
+
required,
|
|
1076
|
+
schema: params.length > 0 ? kubb_kit.ast.factory.createSchema({
|
|
1077
|
+
type: "ref",
|
|
1078
|
+
name: resolve.call(resolver.param, node, params[0]),
|
|
1079
|
+
optional: !required
|
|
1080
|
+
}) : createNever()
|
|
1081
|
+
});
|
|
1082
|
+
})]
|
|
1083
|
+
});
|
|
711
1084
|
}
|
|
712
1085
|
//#endregion
|
|
713
|
-
//#region ../../internals/shared/src/
|
|
714
|
-
|
|
715
|
-
|
|
1086
|
+
//#region ../../internals/shared/src/resolver.ts
|
|
1087
|
+
/**
|
|
1088
|
+
* Resolves a single operation parameter name with the
|
|
1089
|
+
* `<operationId> <in> <name>` template.
|
|
1090
|
+
*
|
|
1091
|
+
* @example
|
|
1092
|
+
* `operationParamName.call(resolver, node, param) // → 'DeletePetPathPetId'`
|
|
1093
|
+
*/
|
|
1094
|
+
function operationParamName(node, param) {
|
|
1095
|
+
return this.name(`${node.operationId} ${param.in} ${param.name}`);
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Builds the shared `param` namespace. Spread the result into `createResolver`
|
|
1099
|
+
* and override individual methods next to it when a plugin deviates.
|
|
1100
|
+
*
|
|
1101
|
+
* @example
|
|
1102
|
+
* ```ts
|
|
1103
|
+
* createResolver<PluginTs>({ param: createOperationParamResolver(), ... })
|
|
1104
|
+
* ```
|
|
1105
|
+
*/
|
|
1106
|
+
function createOperationParamResolver() {
|
|
1107
|
+
return {
|
|
1108
|
+
name: operationParamName,
|
|
1109
|
+
path(node) {
|
|
1110
|
+
return this.name(`${node.operationId} Path`);
|
|
1111
|
+
},
|
|
1112
|
+
query(node) {
|
|
1113
|
+
return this.name(`${node.operationId} Query`);
|
|
1114
|
+
},
|
|
1115
|
+
headers(node) {
|
|
1116
|
+
return this.name(`${node.operationId} Headers`);
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Builds the shared `response` namespace. Spread the result into
|
|
1122
|
+
* `createResolver` and add plugin-specific methods (`options`, `error`) next
|
|
1123
|
+
* to it.
|
|
1124
|
+
*
|
|
1125
|
+
* @example
|
|
1126
|
+
* ```ts
|
|
1127
|
+
* createResolver<PluginTs>({ response: { ...createOperationResponseResolver(), options(node) {...} }, ... })
|
|
1128
|
+
* ```
|
|
1129
|
+
*/
|
|
1130
|
+
function createOperationResponseResolver() {
|
|
1131
|
+
return {
|
|
1132
|
+
status(node, statusCode) {
|
|
1133
|
+
return this.name(`${node.operationId} Status ${statusCode}`);
|
|
1134
|
+
},
|
|
1135
|
+
body(node) {
|
|
1136
|
+
return this.name(`${node.operationId} Body`);
|
|
1137
|
+
},
|
|
1138
|
+
responses(node) {
|
|
1139
|
+
return this.name(`${node.operationId} Responses`);
|
|
1140
|
+
},
|
|
1141
|
+
response(node) {
|
|
1142
|
+
return this.name(`${node.operationId} Response`);
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Builds a resolver `file` override whose base name runs every path segment
|
|
1148
|
+
* through `toFilePath`, casing the final segment with `caseLast`.
|
|
1149
|
+
*
|
|
1150
|
+
* @example
|
|
1151
|
+
* ```ts
|
|
1152
|
+
* createResolver<PluginTs>({ file: createCasedFile(pascalCase), ... })
|
|
1153
|
+
* ```
|
|
1154
|
+
*/
|
|
1155
|
+
function createCasedFile(caseLast) {
|
|
1156
|
+
return { baseName({ name, extname }) {
|
|
1157
|
+
return `${toFilePath(name, caseLast)}${extname}`;
|
|
1158
|
+
} };
|
|
1159
|
+
}
|
|
1160
|
+
//#endregion
|
|
1161
|
+
//#region ../../internals/shared/src/refs.ts
|
|
1162
|
+
/**
|
|
1163
|
+
* Collects the resolved target name of every pointer-carrying ref in a schema tree, in
|
|
1164
|
+
* first-occurrence order. Use this for name-only checks (e.g. redeclaration detection) where
|
|
1165
|
+
* `resolver.imports` would resolve file paths that are then discarded.
|
|
1166
|
+
*/
|
|
1167
|
+
function collectRefNames(schema) {
|
|
1168
|
+
return kubb_kit.ast.collectSync(schema, { schema: (node) => {
|
|
1169
|
+
const refNode = kubb_kit.ast.narrowSchema(node, "ref");
|
|
1170
|
+
if (!refNode?.ref) return null;
|
|
1171
|
+
return kubb_kit.ast.resolveRefName(refNode);
|
|
1172
|
+
} });
|
|
1173
|
+
}
|
|
1174
|
+
//#endregion
|
|
1175
|
+
//#region ../../internals/shared/src/group.ts
|
|
1176
|
+
/**
|
|
1177
|
+
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
1178
|
+
* shared default naming so every plugin groups output consistently:
|
|
1179
|
+
*
|
|
1180
|
+
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
1181
|
+
* - other groups use the camelCased group (`pet store` → `petStore`).
|
|
1182
|
+
*
|
|
1183
|
+
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
1184
|
+
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
1185
|
+
* per-plugin convention.
|
|
1186
|
+
*
|
|
1187
|
+
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
1188
|
+
*
|
|
1189
|
+
* @example
|
|
1190
|
+
* ```ts
|
|
1191
|
+
* createGroupConfig(group) // shared across every plugin
|
|
1192
|
+
* ```
|
|
1193
|
+
*/
|
|
1194
|
+
function createGroupConfig(group) {
|
|
1195
|
+
if (!group) return null;
|
|
1196
|
+
const defaultName = (ctx) => {
|
|
1197
|
+
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
1198
|
+
return camelCase(ctx.group);
|
|
1199
|
+
};
|
|
716
1200
|
return {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
header: params.filter((param) => param.in === "header"),
|
|
720
|
-
cookie: params.filter((param) => param.in === "cookie")
|
|
1201
|
+
...group,
|
|
1202
|
+
name: group.name ? group.name : defaultName
|
|
721
1203
|
};
|
|
722
1204
|
}
|
|
723
1205
|
//#endregion
|
|
1206
|
+
//#region ../../internals/shared/src/schemaTraversal.ts
|
|
1207
|
+
/**
|
|
1208
|
+
* Maps each property of an object schema to its transformed output. Pairs every result with the
|
|
1209
|
+
* original property so the printer keeps full control over modifiers, getters, and key syntax.
|
|
1210
|
+
*
|
|
1211
|
+
* @example
|
|
1212
|
+
* ```ts
|
|
1213
|
+
* const entries = mapSchemaProperties(node, (schema) => this.transform(schema))
|
|
1214
|
+
* // entries: [{ name: 'id', property, output: 'z.number()' }, ...]
|
|
1215
|
+
* ```
|
|
1216
|
+
*/
|
|
1217
|
+
function mapSchemaProperties(node, transform) {
|
|
1218
|
+
return node.properties.map((property) => ({
|
|
1219
|
+
name: property.name,
|
|
1220
|
+
property,
|
|
1221
|
+
output: transform(property.schema)
|
|
1222
|
+
}));
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Maps each item of an array or tuple schema to its transformed output, pairing every result with
|
|
1226
|
+
* the original item.
|
|
1227
|
+
*/
|
|
1228
|
+
function mapSchemaItems(node, transform) {
|
|
1229
|
+
return (node.items ?? []).map((schema) => ({
|
|
1230
|
+
schema,
|
|
1231
|
+
output: transform(schema)
|
|
1232
|
+
}));
|
|
1233
|
+
}
|
|
1234
|
+
//#endregion
|
|
724
1235
|
//#region src/utils.ts
|
|
725
1236
|
/**
|
|
1237
|
+
* Tells whether a `const` (single-value enum) should render as a bare literal type (`'active'`)
|
|
1238
|
+
* rather than a named enum reference or a runtime enum declaration.
|
|
1239
|
+
*
|
|
1240
|
+
* The parser folds `const` into a single-value enum node. The adapter decides which schemas are
|
|
1241
|
+
* named enums and lists them in `enumSchemaNames`, and references to those names get suffixed (for
|
|
1242
|
+
* example `StatusKey`). A const renders as a literal only when the adapter has not registered it as
|
|
1243
|
+
* a named enum, which keeps the declaration and its references in sync across adapter versions.
|
|
1244
|
+
*/
|
|
1245
|
+
function isInlineConstEnum(node, enumSchemaNames) {
|
|
1246
|
+
return (node.namedEnumValues ?? node.enumValues ?? []).length === 1 && !(node.name && enumSchemaNames?.has(node.name));
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
726
1249
|
* Collects JSDoc annotation strings for a schema node.
|
|
727
1250
|
*
|
|
728
1251
|
* Only uses official JSDoc tags from https://jsdoc.app/: `@description`, `@deprecated`, `@default`, `@example`, `@type`.
|
|
729
1252
|
* Constraint metadata (min/max length, pattern, multipleOf, min/maxProperties) is emitted as plain-text lines.
|
|
730
1253
|
|
|
731
1254
|
*/
|
|
732
|
-
function
|
|
733
|
-
|
|
1255
|
+
function isSchemaOptional(schema) {
|
|
1256
|
+
return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
|
|
1257
|
+
}
|
|
1258
|
+
function formatExample(value) {
|
|
1259
|
+
if (value === null || typeof value !== "object") return String(value);
|
|
1260
|
+
return (JSON.stringify(value) ?? String(value)).replaceAll("*/", "*\\/");
|
|
1261
|
+
}
|
|
1262
|
+
function buildPropertyJSDocComments(schema, optional) {
|
|
1263
|
+
const meta = (0, kubb_kit.syncSchemaRef)(schema);
|
|
734
1264
|
const isArray = meta?.primitive === "array";
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
meta && "
|
|
742
|
-
meta && "
|
|
743
|
-
meta && "
|
|
1265
|
+
const hasDescription = meta && "description" in meta && meta.description;
|
|
1266
|
+
const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
|
|
1267
|
+
const exampleValues = meta?.examples ?? [];
|
|
1268
|
+
const comments = [
|
|
1269
|
+
hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
|
|
1270
|
+
...formatComment,
|
|
1271
|
+
meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : null,
|
|
1272
|
+
!isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : null,
|
|
1273
|
+
!isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
|
|
1274
|
+
meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
|
|
1275
|
+
meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" && typeof meta.default === "string" ? stringify(meta.default) : meta.default}` : null,
|
|
1276
|
+
...exampleValues.map((example) => `@example ${formatExample(example)}`)
|
|
744
1277
|
].filter(Boolean);
|
|
1278
|
+
const typeTag = comments.length && meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null;
|
|
1279
|
+
return [...comments, typeTag].filter(Boolean);
|
|
745
1280
|
}
|
|
746
|
-
|
|
747
|
-
|
|
1281
|
+
/**
|
|
1282
|
+
* Builds the object schema for a group of parameters sharing one `in` location (path, query, or
|
|
1283
|
+
* header), embedding each param's own schema (and JSDoc) directly rather than referencing a
|
|
1284
|
+
* separate per-param type — the group itself is the only type these params get exported as.
|
|
1285
|
+
*/
|
|
1286
|
+
function buildParams({ params }) {
|
|
1287
|
+
return kubb_kit.ast.factory.createSchema({
|
|
748
1288
|
type: "object",
|
|
749
|
-
properties: params.map((param) =>
|
|
1289
|
+
properties: params.map((param) => kubb_kit.ast.factory.createProperty({
|
|
750
1290
|
name: param.name,
|
|
751
1291
|
required: param.required,
|
|
752
|
-
schema:
|
|
753
|
-
|
|
754
|
-
|
|
1292
|
+
schema: kubb_kit.ast.factory.createSchema({
|
|
1293
|
+
...param.schema,
|
|
1294
|
+
optional: !param.required
|
|
755
1295
|
})
|
|
756
1296
|
}))
|
|
757
1297
|
});
|
|
758
1298
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
resolver
|
|
783
|
-
}) : _kubb_core.ast.createSchema({
|
|
784
|
-
type: "never",
|
|
785
|
-
primitive: void 0
|
|
786
|
-
})
|
|
787
|
-
}),
|
|
788
|
-
_kubb_core.ast.createProperty({
|
|
789
|
-
name: "queryParams",
|
|
790
|
-
schema: queryParams.length > 0 ? _kubb_core.ast.createSchema({
|
|
791
|
-
...buildParams(node, {
|
|
792
|
-
params: queryParams,
|
|
793
|
-
resolver
|
|
794
|
-
}),
|
|
795
|
-
optional: true
|
|
796
|
-
}) : _kubb_core.ast.createSchema({
|
|
797
|
-
type: "never",
|
|
798
|
-
primitive: void 0,
|
|
799
|
-
optional: true
|
|
800
|
-
})
|
|
801
|
-
}),
|
|
802
|
-
_kubb_core.ast.createProperty({
|
|
803
|
-
name: "headerParams",
|
|
804
|
-
schema: headerParams.length > 0 ? _kubb_core.ast.createSchema({
|
|
805
|
-
...buildParams(node, {
|
|
806
|
-
params: headerParams,
|
|
807
|
-
resolver
|
|
808
|
-
}),
|
|
809
|
-
optional: true
|
|
810
|
-
}) : _kubb_core.ast.createSchema({
|
|
811
|
-
type: "never",
|
|
812
|
-
primitive: void 0,
|
|
813
|
-
optional: true
|
|
1299
|
+
/**
|
|
1300
|
+
* The schema a status occupies in the `<Name>Responses` record. A status that documents several
|
|
1301
|
+
* content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
|
|
1302
|
+
* on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
|
|
1303
|
+
* query hooks and `result.data` use.
|
|
1304
|
+
*/
|
|
1305
|
+
function buildResponseRecordEntry(node, res, resolver) {
|
|
1306
|
+
const statusName = resolver.response.status(node, res.statusCode);
|
|
1307
|
+
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
1308
|
+
if (variants.length <= 1) return kubb_kit.ast.factory.createSchema({
|
|
1309
|
+
type: "ref",
|
|
1310
|
+
name: statusName
|
|
1311
|
+
});
|
|
1312
|
+
return kubb_kit.ast.factory.createSchema({
|
|
1313
|
+
type: "union",
|
|
1314
|
+
members: resolveContentTypeVariants(variants, statusName).map((variant) => kubb_kit.ast.factory.createSchema({
|
|
1315
|
+
type: "object",
|
|
1316
|
+
properties: [kubb_kit.ast.factory.createProperty({
|
|
1317
|
+
name: "contentType",
|
|
1318
|
+
required: true,
|
|
1319
|
+
schema: kubb_kit.ast.factory.createSchema({
|
|
1320
|
+
type: "enum",
|
|
1321
|
+
enumValues: [variant.contentType]
|
|
814
1322
|
})
|
|
815
|
-
}),
|
|
816
|
-
|
|
817
|
-
name: "url",
|
|
1323
|
+
}), kubb_kit.ast.factory.createProperty({
|
|
1324
|
+
name: "data",
|
|
818
1325
|
required: true,
|
|
819
|
-
schema:
|
|
820
|
-
type: "
|
|
821
|
-
|
|
1326
|
+
schema: kubb_kit.ast.factory.createSchema({
|
|
1327
|
+
type: "ref",
|
|
1328
|
+
name: variant.name
|
|
822
1329
|
})
|
|
823
|
-
})
|
|
824
|
-
|
|
1330
|
+
})]
|
|
1331
|
+
}))
|
|
825
1332
|
});
|
|
826
1333
|
}
|
|
827
1334
|
function buildResponses(node, { resolver }) {
|
|
828
|
-
|
|
829
|
-
return _kubb_core.ast.createSchema({
|
|
1335
|
+
return kubb_kit.ast.factory.createSchema({
|
|
830
1336
|
type: "object",
|
|
831
|
-
properties: node.responses.map((res) =>
|
|
1337
|
+
properties: node.responses.map((res) => kubb_kit.ast.factory.createProperty({
|
|
832
1338
|
name: String(res.statusCode),
|
|
833
1339
|
required: true,
|
|
834
|
-
schema:
|
|
835
|
-
type: "ref",
|
|
836
|
-
name: resolver.resolveResponseStatusName(node, res.statusCode)
|
|
837
|
-
})
|
|
1340
|
+
schema: buildResponseRecordEntry(node, res, resolver)
|
|
838
1341
|
}))
|
|
839
1342
|
});
|
|
840
1343
|
}
|
|
841
1344
|
function buildResponseUnion(node, { resolver }) {
|
|
842
|
-
const responsesWithSchema = node.responses.filter((res) => res.schema);
|
|
1345
|
+
const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
|
|
843
1346
|
if (responsesWithSchema.length === 0) return null;
|
|
844
|
-
return
|
|
1347
|
+
return kubb_kit.ast.factory.createSchema({
|
|
845
1348
|
type: "union",
|
|
846
|
-
members: responsesWithSchema.map((res) =>
|
|
1349
|
+
members: responsesWithSchema.map((res) => kubb_kit.ast.factory.createSchema({
|
|
847
1350
|
type: "ref",
|
|
848
|
-
name: resolver.
|
|
1351
|
+
name: resolver.response.status(node, res.statusCode)
|
|
849
1352
|
}))
|
|
850
1353
|
});
|
|
851
1354
|
}
|
|
852
1355
|
//#endregion
|
|
853
|
-
//#region src/
|
|
854
|
-
function
|
|
855
|
-
|
|
1356
|
+
//#region src/components/Type.tsx
|
|
1357
|
+
function Type({ name, node, printer, enum: enumOptions, resolver }) {
|
|
1358
|
+
const enumSchemaNodes = kubb_kit.ast.collectSync(node, { schema(n) {
|
|
1359
|
+
const enumNode = kubb_kit.ast.narrowSchema(n, kubb_kit.ast.schemaTypes.enum);
|
|
1360
|
+
if (enumNode?.name && !isInlineConstEnum(enumNode, printer.options.enumSchemaNames)) return enumNode;
|
|
1361
|
+
} });
|
|
1362
|
+
const output = printer.print(node);
|
|
1363
|
+
if (!output) return;
|
|
1364
|
+
const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
|
|
1365
|
+
return {
|
|
1366
|
+
node,
|
|
1367
|
+
...getEnumNames({
|
|
1368
|
+
node,
|
|
1369
|
+
enum: enumOptions,
|
|
1370
|
+
resolver
|
|
1371
|
+
})
|
|
1372
|
+
};
|
|
1373
|
+
});
|
|
1374
|
+
const shouldExportEnums = enumOptions.type !== "inlineLiteral";
|
|
1375
|
+
const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
|
|
1376
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Enum, {
|
|
1377
|
+
node,
|
|
1378
|
+
enum: enumOptions,
|
|
1379
|
+
resolver
|
|
1380
|
+
}, node.name)), shouldExportType && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
|
|
1381
|
+
name,
|
|
1382
|
+
isTypeOnly: true,
|
|
1383
|
+
isExportable: true,
|
|
1384
|
+
isIndexable: true,
|
|
1385
|
+
children: output
|
|
1386
|
+
})] });
|
|
856
1387
|
}
|
|
1388
|
+
//#endregion
|
|
1389
|
+
//#region src/printers/printerTs.ts
|
|
1390
|
+
const { isNonNullable } = factory_exports;
|
|
857
1391
|
/**
|
|
858
1392
|
* TypeScript type printer built with `definePrinter`.
|
|
859
1393
|
*
|
|
@@ -867,17 +1401,17 @@ function isNonNullable(value) {
|
|
|
867
1401
|
*
|
|
868
1402
|
* @example Raw type node (no `typeName`)
|
|
869
1403
|
* ```ts
|
|
870
|
-
* const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array',
|
|
1404
|
+
* const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enum: { type: 'inlineLiteral' } })
|
|
871
1405
|
* const typeNode = printer.print(schemaNode) // ts.TypeNode
|
|
872
1406
|
* ```
|
|
873
1407
|
*
|
|
874
1408
|
* @example Full declaration (with `typeName`)
|
|
875
1409
|
* ```ts
|
|
876
|
-
* const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array',
|
|
1410
|
+
* const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enum: { type: 'inlineLiteral' }, typeName: 'MyType' })
|
|
877
1411
|
* const declaration = printer.print(schemaNode) // ts.TypeAliasDeclaration | ts.InterfaceDeclaration
|
|
878
1412
|
* ```
|
|
879
1413
|
*/
|
|
880
|
-
const printerTs =
|
|
1414
|
+
const printerTs = kubb_kit.ast.createPrinter((options) => {
|
|
881
1415
|
const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(options.optionalType);
|
|
882
1416
|
return {
|
|
883
1417
|
name: "typescript",
|
|
@@ -906,28 +1440,31 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
|
|
|
906
1440
|
date: dateOrStringNode,
|
|
907
1441
|
time: dateOrStringNode,
|
|
908
1442
|
ref(node) {
|
|
909
|
-
if (!node.name) return;
|
|
910
|
-
const refName =
|
|
911
|
-
|
|
1443
|
+
if (!node.name) return null;
|
|
1444
|
+
const refName = kubb_kit.ast.resolveRefName(node);
|
|
1445
|
+
if (!refName) return null;
|
|
1446
|
+
const name = node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix && this.options.enumSchemaNames?.has(refName) ? this.options.resolver.enum.keyName({ name: refName }, this.options.enum.typeSuffix) : node.ref ? this.options.resolver.name(refName) : refName;
|
|
1447
|
+
return createTypeReferenceNode(name, void 0);
|
|
912
1448
|
},
|
|
913
1449
|
enum(node) {
|
|
914
1450
|
const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
|
|
915
|
-
if (this.options.
|
|
1451
|
+
if (this.options.enum.type === "inlineLiteral" || !node.name || isInlineConstEnum(node, this.options.enumSchemaNames)) return createUnionDeclaration({
|
|
916
1452
|
withParentheses: true,
|
|
917
1453
|
nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(isNonNullable)
|
|
918
1454
|
}) ?? void 0;
|
|
919
|
-
|
|
1455
|
+
const resolvedName = ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix ? this.options.resolver.enum.keyName(node, this.options.enum.typeSuffix) : this.options.resolver.name(node.name);
|
|
1456
|
+
return createTypeReferenceNode(resolvedName, void 0);
|
|
920
1457
|
},
|
|
921
1458
|
union(node) {
|
|
922
1459
|
const members = node.members ?? [];
|
|
923
1460
|
const hasStringLiteral = members.some((m) => {
|
|
924
|
-
return
|
|
1461
|
+
return kubb_kit.ast.narrowSchema(m, kubb_kit.ast.schemaTypes.enum)?.primitive === "string";
|
|
925
1462
|
});
|
|
926
|
-
const hasPlainString = members.some((m) =>
|
|
1463
|
+
const hasPlainString = members.some((m) => (0, kubb_kit.isStringType)(m));
|
|
927
1464
|
if (hasStringLiteral && hasPlainString) return createUnionDeclaration({
|
|
928
1465
|
withParentheses: true,
|
|
929
1466
|
nodes: members.map((m) => {
|
|
930
|
-
if (
|
|
1467
|
+
if ((0, kubb_kit.isStringType)(m)) return createIntersectionDeclaration({
|
|
931
1468
|
nodes: [keywordTypeNodes.string, createTypeLiteralNode([])],
|
|
932
1469
|
withParentheses: true
|
|
933
1470
|
});
|
|
@@ -943,32 +1480,33 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
|
|
|
943
1480
|
return createIntersectionDeclaration({
|
|
944
1481
|
withParentheses: true,
|
|
945
1482
|
nodes: buildMemberNodes(node.members, this.transform)
|
|
946
|
-
}) ??
|
|
1483
|
+
}) ?? null;
|
|
947
1484
|
},
|
|
948
1485
|
array(node) {
|
|
949
1486
|
return createArrayDeclaration({
|
|
950
|
-
nodes: (node
|
|
1487
|
+
nodes: mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(isNonNullable),
|
|
951
1488
|
arrayType: this.options.arrayType
|
|
952
|
-
}) ??
|
|
1489
|
+
}) ?? null;
|
|
953
1490
|
},
|
|
954
1491
|
tuple(node) {
|
|
955
|
-
return buildTupleNode(node, this.transform);
|
|
1492
|
+
return buildTupleNode(node, this.transform) ?? null;
|
|
956
1493
|
},
|
|
957
1494
|
object(node) {
|
|
958
1495
|
const { transform, options } = this;
|
|
959
1496
|
const addsQuestionToken = OPTIONAL_ADDS_QUESTION_TOKEN.has(options.optionalType);
|
|
960
|
-
const propertyNodes = node.
|
|
961
|
-
const baseType =
|
|
962
|
-
const
|
|
963
|
-
const
|
|
1497
|
+
const propertyNodes = mapSchemaProperties(node, (schema) => transform(schema)).map(({ name, property, output }) => {
|
|
1498
|
+
const baseType = output ?? keywordTypeNodes.unknown;
|
|
1499
|
+
const optional = !property.required || !!property.schema.optional || !!property.schema.nullish;
|
|
1500
|
+
const type = buildPropertyType(property.schema, baseType, options.optionalType, optional);
|
|
1501
|
+
const propMeta = (0, kubb_kit.syncSchemaRef)(property.schema);
|
|
964
1502
|
return appendJSDocToNode({
|
|
965
1503
|
node: createPropertySignature({
|
|
966
|
-
questionToken:
|
|
967
|
-
name
|
|
1504
|
+
questionToken: optional ? addsQuestionToken : false,
|
|
1505
|
+
name,
|
|
968
1506
|
type,
|
|
969
1507
|
readOnly: propMeta?.readOnly
|
|
970
1508
|
}),
|
|
971
|
-
comments: buildPropertyJSDocComments(
|
|
1509
|
+
comments: buildPropertyJSDocComments(property.schema, optional)
|
|
972
1510
|
});
|
|
973
1511
|
});
|
|
974
1512
|
const allElements = [...propertyNodes, ...buildIndexSignatures(node, propertyNodes.length, transform)];
|
|
@@ -979,91 +1517,91 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
|
|
|
979
1517
|
},
|
|
980
1518
|
print(node) {
|
|
981
1519
|
const { name, syntaxType = "type", description, keysToOmit } = this.options;
|
|
982
|
-
|
|
983
|
-
if (!
|
|
984
|
-
const meta =
|
|
1520
|
+
const transformed = this.transform(node);
|
|
1521
|
+
if (!transformed) return null;
|
|
1522
|
+
const meta = (0, kubb_kit.syncSchemaRef)(node);
|
|
985
1523
|
if (!name) {
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
return (0, _kubb_parser_ts.
|
|
1524
|
+
const withNullable = meta.nullable ? createUnionDeclaration({ nodes: [transformed, keywordTypeNodes.null] }) : transformed;
|
|
1525
|
+
const result = (meta.nullish || meta.optional) && addsUndefined ? createUnionDeclaration({ nodes: [withNullable, keywordTypeNodes.undefined] }) : withNullable;
|
|
1526
|
+
return (0, _kubb_parser_ts.parserTs)().print(result);
|
|
989
1527
|
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1528
|
+
const inner = (() => {
|
|
1529
|
+
const omitted = keysToOmit?.length ? createOmitDeclaration({
|
|
1530
|
+
keys: keysToOmit,
|
|
1531
|
+
type: transformed,
|
|
1532
|
+
nonNullable: true
|
|
1533
|
+
}) : transformed;
|
|
1534
|
+
const withNullable = meta.nullable ? createUnionDeclaration({ nodes: [omitted, keywordTypeNodes.null] }) : omitted;
|
|
1535
|
+
return meta.nullish || meta.optional ? createUnionDeclaration({ nodes: [withNullable, keywordTypeNodes.undefined] }) : withNullable;
|
|
1536
|
+
})();
|
|
1537
|
+
const typeNode = createTypeDeclaration({
|
|
999
1538
|
name,
|
|
1000
1539
|
isExportable: true,
|
|
1001
1540
|
type: inner,
|
|
1002
|
-
syntax:
|
|
1541
|
+
syntax: syntaxType === "type" || inner.kind === syntaxKind.union || !!keysToOmit?.length ? "type" : "interface",
|
|
1003
1542
|
comments: buildPropertyJSDocComments({
|
|
1004
1543
|
...meta,
|
|
1005
1544
|
description
|
|
1006
1545
|
})
|
|
1007
|
-
})
|
|
1546
|
+
});
|
|
1547
|
+
return (0, _kubb_parser_ts.parserTs)().print(typeNode);
|
|
1008
1548
|
}
|
|
1009
1549
|
};
|
|
1010
1550
|
});
|
|
1011
1551
|
//#endregion
|
|
1012
1552
|
//#region src/generators/typeGenerator.tsx
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
if (
|
|
1020
|
-
return
|
|
1021
|
-
}
|
|
1022
|
-
function getPerContentTypeName(dataName, suffix) {
|
|
1023
|
-
if (dataName.endsWith("Data")) return suffix.endsWith("Data") ? dataName.slice(0, -4) + suffix : `${dataName.slice(0, -4)}${suffix}Data`;
|
|
1024
|
-
return dataName + suffix;
|
|
1553
|
+
/**
|
|
1554
|
+
* Resolves the imported type name for a referenced schema. An enum schema emitted
|
|
1555
|
+
* as a `const` object imports its suffixed key alias (e.g. `StatusKey`); every other
|
|
1556
|
+
* schema imports its plain resolved name.
|
|
1557
|
+
*/
|
|
1558
|
+
function resolveImportName({ schemaName, enumOptions, enumSchemaNames, resolver }) {
|
|
1559
|
+
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && enumOptions.typeSuffix && enumSchemaNames.has(schemaName)) return resolver.enum.keyName({ name: schemaName }, enumOptions.typeSuffix);
|
|
1560
|
+
return resolver.name(schemaName);
|
|
1025
1561
|
}
|
|
1026
|
-
|
|
1562
|
+
/**
|
|
1563
|
+
* Built-in generator for `@kubb/plugin-ts`. Emits one TypeScript file per
|
|
1564
|
+
* schema in the spec plus per-operation request, response, and parameter
|
|
1565
|
+
* types. Drop-replace with a custom `Generator<PluginTs>` to change how
|
|
1566
|
+
* TypeScript output is produced.
|
|
1567
|
+
*/
|
|
1568
|
+
const typeGenerator = (0, kubb_kit.defineGenerator)({
|
|
1027
1569
|
name: "typescript",
|
|
1028
|
-
renderer:
|
|
1570
|
+
renderer: kubb_jsx.jsxRenderer,
|
|
1029
1571
|
schema(node, ctx) {
|
|
1030
|
-
const {
|
|
1031
|
-
const {
|
|
1572
|
+
const { enum: enumOptions, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options;
|
|
1573
|
+
const { config, resolver, root } = ctx;
|
|
1032
1574
|
if (!node.name) return;
|
|
1033
|
-
const
|
|
1034
|
-
const
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
}));
|
|
1050
|
-
const isEnumSchema = !!_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum);
|
|
1575
|
+
const enumSchemaNames = new Set(ctx.meta.enumNames);
|
|
1576
|
+
const importName = (schemaName) => resolveImportName({
|
|
1577
|
+
schemaName,
|
|
1578
|
+
enumOptions,
|
|
1579
|
+
enumSchemaNames,
|
|
1580
|
+
resolver
|
|
1581
|
+
});
|
|
1582
|
+
const imports = resolver.imports({
|
|
1583
|
+
node,
|
|
1584
|
+
root,
|
|
1585
|
+
output,
|
|
1586
|
+
group: group ?? void 0,
|
|
1587
|
+
name: importName
|
|
1588
|
+
});
|
|
1589
|
+
const enumNode = kubb_kit.ast.narrowSchema(node, kubb_kit.ast.schemaTypes.enum);
|
|
1590
|
+
const isEnumSchema = !!enumNode && !isInlineConstEnum(enumNode, enumSchemaNames);
|
|
1051
1591
|
const meta = {
|
|
1052
|
-
name: ENUM_TYPES_WITH_KEY_SUFFIX.has(
|
|
1053
|
-
file: resolver.
|
|
1592
|
+
name: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && isEnumSchema ? resolver.enum.keyName(node, enumOptions.typeSuffix) : resolver.name(node.name),
|
|
1593
|
+
file: resolver.file({
|
|
1054
1594
|
name: node.name,
|
|
1055
|
-
extname: ".ts"
|
|
1056
|
-
}, {
|
|
1595
|
+
extname: ".ts",
|
|
1057
1596
|
root,
|
|
1058
1597
|
output,
|
|
1059
|
-
group
|
|
1598
|
+
group: group ?? void 0
|
|
1060
1599
|
})
|
|
1061
1600
|
};
|
|
1062
1601
|
const schemaPrinter = printerTs({
|
|
1063
1602
|
optionalType,
|
|
1064
1603
|
arrayType,
|
|
1065
|
-
|
|
1066
|
-
enumTypeSuffix,
|
|
1604
|
+
enum: enumOptions,
|
|
1067
1605
|
name: meta.name,
|
|
1068
1606
|
syntaxType,
|
|
1069
1607
|
description: node.description,
|
|
@@ -1071,19 +1609,27 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1071
1609
|
enumSchemaNames,
|
|
1072
1610
|
nodes: printer?.nodes
|
|
1073
1611
|
});
|
|
1074
|
-
return /* @__PURE__ */ (0,
|
|
1612
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.File, {
|
|
1075
1613
|
baseName: meta.file.baseName,
|
|
1076
1614
|
path: meta.file.path,
|
|
1077
1615
|
meta: meta.file.meta,
|
|
1078
|
-
banner: resolver.
|
|
1616
|
+
banner: resolver.default.banner(ctx.meta, {
|
|
1079
1617
|
output,
|
|
1080
|
-
config
|
|
1618
|
+
config,
|
|
1619
|
+
file: {
|
|
1620
|
+
path: meta.file.path,
|
|
1621
|
+
baseName: meta.file.baseName
|
|
1622
|
+
}
|
|
1081
1623
|
}),
|
|
1082
|
-
footer: resolver.
|
|
1624
|
+
footer: resolver.default.footer(ctx.meta, {
|
|
1083
1625
|
output,
|
|
1084
|
-
config
|
|
1626
|
+
config,
|
|
1627
|
+
file: {
|
|
1628
|
+
path: meta.file.path,
|
|
1629
|
+
baseName: meta.file.baseName
|
|
1630
|
+
}
|
|
1085
1631
|
}),
|
|
1086
|
-
children: [
|
|
1632
|
+
children: [imports.map((imp) => /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
1087
1633
|
root: meta.file.path,
|
|
1088
1634
|
path: imp.path,
|
|
1089
1635
|
name: imp.name,
|
|
@@ -1092,55 +1638,47 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1092
1638
|
node.name,
|
|
1093
1639
|
imp.path,
|
|
1094
1640
|
imp.isTypeOnly
|
|
1095
|
-
].join("-"))), /* @__PURE__ */ (0,
|
|
1641
|
+
].join("-"))), /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Type, {
|
|
1096
1642
|
name: meta.name,
|
|
1097
1643
|
node,
|
|
1098
|
-
|
|
1099
|
-
enumTypeSuffix,
|
|
1100
|
-
enumKeyCasing,
|
|
1644
|
+
enum: enumOptions,
|
|
1101
1645
|
resolver,
|
|
1102
1646
|
printer: schemaPrinter
|
|
1103
1647
|
})]
|
|
1104
1648
|
});
|
|
1105
1649
|
},
|
|
1106
1650
|
operation(node, ctx) {
|
|
1107
|
-
const {
|
|
1108
|
-
const {
|
|
1109
|
-
const
|
|
1110
|
-
const params = _kubb_core.ast.caseParams(node.parameters, paramsCasing);
|
|
1111
|
-
const meta = { file: resolver.resolveFile({
|
|
1651
|
+
const { enum: enumOptions, optionalType, arrayType, syntaxType, group, output, printer } = ctx.options;
|
|
1652
|
+
const { config, resolver, root } = ctx;
|
|
1653
|
+
const meta = { file: resolver.file({
|
|
1112
1654
|
name: node.operationId,
|
|
1113
1655
|
extname: ".ts",
|
|
1114
1656
|
tag: node.tags[0] ?? "default",
|
|
1115
|
-
path: node.path
|
|
1116
|
-
}, {
|
|
1657
|
+
path: node.path,
|
|
1117
1658
|
root,
|
|
1118
1659
|
output,
|
|
1119
|
-
group
|
|
1660
|
+
group: group ?? void 0
|
|
1120
1661
|
}) };
|
|
1121
|
-
const enumSchemaNames = new Set(
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1662
|
+
const enumSchemaNames = new Set(ctx.meta.enumNames);
|
|
1663
|
+
const importName = (schemaName) => resolveImportName({
|
|
1664
|
+
schemaName,
|
|
1665
|
+
enumOptions,
|
|
1666
|
+
enumSchemaNames,
|
|
1667
|
+
resolver
|
|
1668
|
+
});
|
|
1126
1669
|
function renderSchemaType({ schema, name, keysToOmit }) {
|
|
1127
1670
|
if (!schema) return null;
|
|
1128
|
-
const imports =
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
output,
|
|
1136
|
-
group
|
|
1137
|
-
}).path
|
|
1138
|
-
}));
|
|
1671
|
+
const imports = resolver.imports({
|
|
1672
|
+
node: schema,
|
|
1673
|
+
root,
|
|
1674
|
+
output,
|
|
1675
|
+
group: group ?? void 0,
|
|
1676
|
+
name: importName
|
|
1677
|
+
});
|
|
1139
1678
|
const schemaPrinter = printerTs({
|
|
1140
1679
|
optionalType,
|
|
1141
1680
|
arrayType,
|
|
1142
|
-
|
|
1143
|
-
enumTypeSuffix,
|
|
1681
|
+
enum: enumOptions,
|
|
1144
1682
|
name,
|
|
1145
1683
|
syntaxType,
|
|
1146
1684
|
description: schema.description,
|
|
@@ -1149,7 +1687,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1149
1687
|
enumSchemaNames,
|
|
1150
1688
|
nodes: printer?.nodes
|
|
1151
1689
|
});
|
|
1152
|
-
return /* @__PURE__ */ (0,
|
|
1690
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [imports.map((imp) => /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
1153
1691
|
root: meta.file.path,
|
|
1154
1692
|
path: imp.path,
|
|
1155
1693
|
name: imp.name,
|
|
@@ -1158,20 +1696,53 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1158
1696
|
name,
|
|
1159
1697
|
imp.path,
|
|
1160
1698
|
imp.isTypeOnly
|
|
1161
|
-
].join("-"))), /* @__PURE__ */ (0,
|
|
1699
|
+
].join("-"))), /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Type, {
|
|
1162
1700
|
name,
|
|
1163
1701
|
node: schema,
|
|
1164
|
-
|
|
1165
|
-
enumTypeSuffix,
|
|
1166
|
-
enumKeyCasing,
|
|
1702
|
+
enum: enumOptions,
|
|
1167
1703
|
resolver,
|
|
1168
1704
|
printer: schemaPrinter
|
|
1169
1705
|
})] });
|
|
1170
1706
|
}
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
}
|
|
1707
|
+
/**
|
|
1708
|
+
* Emits an individual type per content type plus a plain union alias under `baseName`. Shared by
|
|
1709
|
+
* the request body and multi-content-type responses. The response record discriminates these
|
|
1710
|
+
* variants by content type in {@link buildResponses}; the standalone alias stays a plain union so
|
|
1711
|
+
* query hooks and `result.data` keep the bare body.
|
|
1712
|
+
*/
|
|
1713
|
+
function buildContentTypeVariants(entries, baseName, decorate) {
|
|
1714
|
+
const variants = resolveContentTypeVariants(entries, baseName);
|
|
1715
|
+
const unionSchema = kubb_kit.ast.factory.createSchema({
|
|
1716
|
+
type: "union",
|
|
1717
|
+
members: variants.map((variant) => kubb_kit.ast.factory.createSchema({
|
|
1718
|
+
type: "ref",
|
|
1719
|
+
name: variant.name
|
|
1720
|
+
}))
|
|
1721
|
+
});
|
|
1722
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [variants.map((variant) => renderSchemaType({
|
|
1723
|
+
schema: decorate ? decorate(variant.schema) : variant.schema,
|
|
1724
|
+
name: variant.name,
|
|
1725
|
+
keysToOmit: variant.keysToOmit
|
|
1726
|
+
})), renderSchemaType({
|
|
1727
|
+
schema: unionSchema,
|
|
1728
|
+
name: baseName
|
|
1729
|
+
})] });
|
|
1730
|
+
}
|
|
1731
|
+
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
|
|
1732
|
+
const paramGroupTypes = [
|
|
1733
|
+
pathParams.length > 0 && renderSchemaType({
|
|
1734
|
+
schema: buildParams({ params: pathParams }),
|
|
1735
|
+
name: resolver.param.path(node, pathParams[0])
|
|
1736
|
+
}),
|
|
1737
|
+
queryParams.length > 0 && renderSchemaType({
|
|
1738
|
+
schema: buildParams({ params: queryParams }),
|
|
1739
|
+
name: resolver.param.query(node, queryParams[0])
|
|
1740
|
+
}),
|
|
1741
|
+
headerParams.length > 0 && renderSchemaType({
|
|
1742
|
+
schema: buildParams({ params: headerParams }),
|
|
1743
|
+
name: resolver.param.headers(node, headerParams[0])
|
|
1744
|
+
})
|
|
1745
|
+
];
|
|
1175
1746
|
const requestBodyContent = node.requestBody?.content ?? [];
|
|
1176
1747
|
function buildRequestType() {
|
|
1177
1748
|
if (requestBodyContent.length === 0) return null;
|
|
@@ -1183,67 +1754,40 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1183
1754
|
...entry.schema,
|
|
1184
1755
|
description: node.requestBody.description ?? entry.schema.description
|
|
1185
1756
|
},
|
|
1186
|
-
name: resolver.
|
|
1757
|
+
name: resolver.response.body(node),
|
|
1187
1758
|
keysToOmit: entry.keysToOmit
|
|
1188
1759
|
});
|
|
1189
1760
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
let individualName = getPerContentTypeName(dataName, baseSuffix);
|
|
1195
|
-
let counter = 2;
|
|
1196
|
-
while (usedNames.has(individualName)) individualName = getPerContentTypeName(dataName, `${baseSuffix}${counter++}`);
|
|
1197
|
-
usedNames.add(individualName);
|
|
1198
|
-
return {
|
|
1199
|
-
name: individualName,
|
|
1200
|
-
rendered: renderSchemaType({
|
|
1201
|
-
schema: {
|
|
1202
|
-
...entry.schema,
|
|
1203
|
-
description: node.requestBody.description ?? entry.schema.description
|
|
1204
|
-
},
|
|
1205
|
-
name: individualName,
|
|
1206
|
-
keysToOmit: entry.keysToOmit
|
|
1207
|
-
})
|
|
1208
|
-
};
|
|
1209
|
-
});
|
|
1210
|
-
const unionType = renderSchemaType({
|
|
1211
|
-
schema: _kubb_core.ast.createSchema({
|
|
1212
|
-
type: "union",
|
|
1213
|
-
members: individualItems.map((item) => _kubb_core.ast.createSchema({
|
|
1214
|
-
type: "ref",
|
|
1215
|
-
name: item.name
|
|
1216
|
-
}))
|
|
1217
|
-
}),
|
|
1218
|
-
name: dataName
|
|
1219
|
-
});
|
|
1220
|
-
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [individualItems.map((item) => item.rendered), unionType] });
|
|
1761
|
+
return buildContentTypeVariants(requestBodyContent, resolver.response.body(node), (schema) => ({
|
|
1762
|
+
...schema,
|
|
1763
|
+
description: node.requestBody.description ?? schema.description
|
|
1764
|
+
}));
|
|
1221
1765
|
}
|
|
1222
1766
|
const requestType = buildRequestType();
|
|
1223
|
-
const responseTypes = node.responses.map((res) =>
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1767
|
+
const responseTypes = node.responses.map((res) => {
|
|
1768
|
+
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
1769
|
+
if (variants.length > 1) return buildContentTypeVariants(variants, resolver.response.status(node, res.statusCode));
|
|
1770
|
+
const primary = variants[0] ?? res.content?.[0];
|
|
1771
|
+
return renderSchemaType({
|
|
1772
|
+
schema: primary?.schema ?? null,
|
|
1773
|
+
name: resolver.response.status(node, res.statusCode),
|
|
1774
|
+
keysToOmit: primary?.keysToOmit
|
|
1775
|
+
});
|
|
1776
|
+
});
|
|
1777
|
+
const optionsType = renderSchemaType({
|
|
1778
|
+
schema: buildOptionsSchema(node, resolver),
|
|
1779
|
+
name: resolver.response.options(node)
|
|
1234
1780
|
});
|
|
1235
1781
|
const responsesType = renderSchemaType({
|
|
1236
1782
|
schema: buildResponses(node, { resolver }),
|
|
1237
|
-
name: resolver.
|
|
1783
|
+
name: resolver.response.responses(node)
|
|
1238
1784
|
});
|
|
1239
1785
|
function buildResponseType() {
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
const
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
path: ""
|
|
1246
|
-
})).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : [])).has(responseName)) return null;
|
|
1786
|
+
const hasSchema = (res) => (res.content ?? []).some((entry) => entry.schema);
|
|
1787
|
+
if (!node.responses.some(hasSchema)) return null;
|
|
1788
|
+
const responseName = resolver.response.response(node);
|
|
1789
|
+
const responsesWithSchema = node.responses.filter(hasSchema);
|
|
1790
|
+
if (new Set(responsesWithSchema.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? collectRefNames(entry.schema).map(importName) : []))).has(responseName)) return null;
|
|
1247
1791
|
return renderSchemaType({
|
|
1248
1792
|
schema: {
|
|
1249
1793
|
...buildResponseUnion(node, { resolver }),
|
|
@@ -1253,23 +1797,31 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1253
1797
|
});
|
|
1254
1798
|
}
|
|
1255
1799
|
const responseType = buildResponseType();
|
|
1256
|
-
return /* @__PURE__ */ (0,
|
|
1800
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.File, {
|
|
1257
1801
|
baseName: meta.file.baseName,
|
|
1258
1802
|
path: meta.file.path,
|
|
1259
1803
|
meta: meta.file.meta,
|
|
1260
|
-
banner: resolver.
|
|
1804
|
+
banner: resolver.default.banner(ctx.meta, {
|
|
1261
1805
|
output,
|
|
1262
|
-
config
|
|
1806
|
+
config,
|
|
1807
|
+
file: {
|
|
1808
|
+
path: meta.file.path,
|
|
1809
|
+
baseName: meta.file.baseName
|
|
1810
|
+
}
|
|
1263
1811
|
}),
|
|
1264
|
-
footer: resolver.
|
|
1812
|
+
footer: resolver.default.footer(ctx.meta, {
|
|
1265
1813
|
output,
|
|
1266
|
-
config
|
|
1814
|
+
config,
|
|
1815
|
+
file: {
|
|
1816
|
+
path: meta.file.path,
|
|
1817
|
+
baseName: meta.file.baseName
|
|
1818
|
+
}
|
|
1267
1819
|
}),
|
|
1268
1820
|
children: [
|
|
1269
|
-
|
|
1821
|
+
paramGroupTypes,
|
|
1270
1822
|
responseTypes,
|
|
1271
1823
|
requestType,
|
|
1272
|
-
|
|
1824
|
+
optionsType,
|
|
1273
1825
|
responsesType,
|
|
1274
1826
|
responseType
|
|
1275
1827
|
]
|
|
@@ -1279,101 +1831,84 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1279
1831
|
//#endregion
|
|
1280
1832
|
//#region src/resolvers/resolverTs.ts
|
|
1281
1833
|
/**
|
|
1282
|
-
*
|
|
1283
|
-
*
|
|
1284
|
-
*
|
|
1834
|
+
* Default resolver used by `@kubb/plugin-ts`. Decides the names and file paths
|
|
1835
|
+
* for every generated TypeScript type. Import this in other plugins that need
|
|
1836
|
+
* to reference the exact names `plugin-ts` produces without duplicating the
|
|
1837
|
+
* casing/file-layout rules.
|
|
1285
1838
|
*
|
|
1286
|
-
* The `default`
|
|
1287
|
-
* for
|
|
1839
|
+
* The `default` helpers are supplied by `createResolver`. This plugin overrides the top-level `name`
|
|
1840
|
+
* to use PascalCase for value and type names and `file` to write PascalCase file paths (dotted names
|
|
1841
|
+
* become `/`-joined), and groups the operation-specific naming under the `param`, `response`, and
|
|
1842
|
+
* `enum` namespaces.
|
|
1288
1843
|
*
|
|
1289
|
-
* @example
|
|
1844
|
+
* @example Resolve a type and file name
|
|
1290
1845
|
* ```ts
|
|
1291
|
-
* import {
|
|
1846
|
+
* import { resolverTs } from '@kubb/plugin-ts'
|
|
1292
1847
|
*
|
|
1293
|
-
*
|
|
1294
|
-
*
|
|
1295
|
-
* resolver.resolvePathName('list pets', 'file') // → 'listPets'
|
|
1848
|
+
* resolverTs.name('list pets') // 'ListPets'
|
|
1849
|
+
* resolverTs.response.status(node, 200) // 'ListPetsStatus200'
|
|
1296
1850
|
* ```
|
|
1297
1851
|
*/
|
|
1298
|
-
const resolverTs = (0,
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
return pascalCase(name, { isFile: type === "file" });
|
|
1310
|
-
},
|
|
1311
|
-
resolveParamName(node, param) {
|
|
1312
|
-
return this.resolveTypeName(`${node.operationId} ${param.in} ${param.name}`);
|
|
1313
|
-
},
|
|
1314
|
-
resolveResponseStatusName(node, statusCode) {
|
|
1315
|
-
return this.resolveTypeName(`${node.operationId} Status ${statusCode}`);
|
|
1316
|
-
},
|
|
1317
|
-
resolveDataName(node) {
|
|
1318
|
-
return this.resolveTypeName(`${node.operationId} Data`);
|
|
1319
|
-
},
|
|
1320
|
-
resolveRequestConfigName(node) {
|
|
1321
|
-
return this.resolveTypeName(`${node.operationId} RequestConfig`);
|
|
1322
|
-
},
|
|
1323
|
-
resolveResponsesName(node) {
|
|
1324
|
-
return this.resolveTypeName(`${node.operationId} Responses`);
|
|
1325
|
-
},
|
|
1326
|
-
resolveResponseName(node) {
|
|
1327
|
-
return this.resolveTypeName(`${node.operationId} Response`);
|
|
1328
|
-
},
|
|
1329
|
-
resolveEnumKeyName(node, enumTypeSuffix = "key") {
|
|
1330
|
-
return `${this.resolveTypeName(node.name ?? "")}${enumTypeSuffix}`;
|
|
1331
|
-
},
|
|
1332
|
-
resolvePathParamsName(node, param) {
|
|
1333
|
-
return this.resolveParamName(node, param);
|
|
1334
|
-
},
|
|
1335
|
-
resolveQueryParamsName(node, param) {
|
|
1336
|
-
return this.resolveParamName(node, param);
|
|
1337
|
-
},
|
|
1338
|
-
resolveHeaderParamsName(node, param) {
|
|
1339
|
-
return this.resolveParamName(node, param);
|
|
1852
|
+
const resolverTs = (0, kubb_kit.createResolver)({
|
|
1853
|
+
pluginName: "plugin-ts",
|
|
1854
|
+
name(name) {
|
|
1855
|
+
return ensureValidVarName(pascalCase(name));
|
|
1856
|
+
},
|
|
1857
|
+
file: createCasedFile(pascalCase),
|
|
1858
|
+
param: createOperationParamResolver(),
|
|
1859
|
+
response: {
|
|
1860
|
+
...createOperationResponseResolver(),
|
|
1861
|
+
options(node) {
|
|
1862
|
+
return this.name(`${node.operationId} Options`);
|
|
1340
1863
|
}
|
|
1341
|
-
}
|
|
1864
|
+
},
|
|
1865
|
+
enum: { keyName(node, enumTypeSuffix = "key") {
|
|
1866
|
+
return `${this.name(node.name ?? "")}${enumTypeSuffix}`;
|
|
1867
|
+
} }
|
|
1342
1868
|
});
|
|
1343
1869
|
//#endregion
|
|
1344
1870
|
//#region src/plugin.ts
|
|
1345
1871
|
/**
|
|
1346
|
-
* Canonical plugin name for `@kubb/plugin-ts
|
|
1872
|
+
* Canonical plugin name for `@kubb/plugin-ts`. Used for driver lookups and
|
|
1873
|
+
* cross-plugin dependency references.
|
|
1347
1874
|
*/
|
|
1348
1875
|
const pluginTsName = "plugin-ts";
|
|
1349
1876
|
/**
|
|
1350
|
-
*
|
|
1351
|
-
*
|
|
1352
|
-
*
|
|
1353
|
-
*
|
|
1354
|
-
* and writes barrel files based on `output.barrelType`.
|
|
1877
|
+
* Generates TypeScript `type` aliases and `interface` declarations from an
|
|
1878
|
+
* OpenAPI spec. The foundation that every other Kubb plugin builds on:
|
|
1879
|
+
* clients, query hooks, mocks, and validators all reference the names this
|
|
1880
|
+
* plugin produces.
|
|
1355
1881
|
*
|
|
1356
1882
|
* @example
|
|
1357
1883
|
* ```ts
|
|
1358
|
-
* import
|
|
1884
|
+
* import { defineConfig } from 'kubb/config'
|
|
1885
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1359
1886
|
*
|
|
1360
1887
|
* export default defineConfig({
|
|
1361
|
-
*
|
|
1888
|
+
* input: './petStore.yaml',
|
|
1889
|
+
* output: { path: './src/gen' },
|
|
1890
|
+
* plugins: [
|
|
1891
|
+
* pluginTs({
|
|
1892
|
+
* output: { path: './types' },
|
|
1893
|
+
* enum: { type: 'asConst' },
|
|
1894
|
+
* optionalType: 'questionTokenAndUndefined',
|
|
1895
|
+
* }),
|
|
1896
|
+
* ],
|
|
1362
1897
|
* })
|
|
1363
1898
|
* ```
|
|
1364
1899
|
*/
|
|
1365
|
-
const pluginTs = (0,
|
|
1900
|
+
const pluginTs = (0, kubb_kit.definePlugin)((options) => {
|
|
1366
1901
|
const { output = {
|
|
1367
1902
|
path: "types",
|
|
1368
|
-
|
|
1369
|
-
}, group, exclude = [], include, override = [],
|
|
1370
|
-
const groupConfig = group
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
}
|
|
1903
|
+
barrel: { type: "named" }
|
|
1904
|
+
}, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", printer, resolver: userResolver, macros: userMacros } = options;
|
|
1905
|
+
const groupConfig = createGroupConfig(group);
|
|
1906
|
+
const resolvedEnum = {
|
|
1907
|
+
type: enumOptions.type ?? "asConst",
|
|
1908
|
+
constCasing: enumOptions.constCasing ?? "camelCase",
|
|
1909
|
+
typeSuffix: enumOptions.typeSuffix ?? "Key",
|
|
1910
|
+
keyCasing: enumOptions.keyCasing ?? "none"
|
|
1911
|
+
};
|
|
1377
1912
|
return {
|
|
1378
1913
|
name: pluginTsName,
|
|
1379
1914
|
options,
|
|
@@ -1386,56 +1921,236 @@ const pluginTs = (0, _kubb_core.definePlugin)((options) => {
|
|
|
1386
1921
|
optionalType,
|
|
1387
1922
|
group: groupConfig,
|
|
1388
1923
|
arrayType,
|
|
1389
|
-
|
|
1390
|
-
enumTypeSuffix,
|
|
1391
|
-
enumKeyCasing,
|
|
1924
|
+
enum: resolvedEnum,
|
|
1392
1925
|
syntaxType,
|
|
1393
|
-
paramsCasing,
|
|
1394
1926
|
printer
|
|
1395
1927
|
});
|
|
1396
|
-
ctx.setResolver(userResolver ?
|
|
1397
|
-
|
|
1398
|
-
...userResolver
|
|
1399
|
-
} : resolverTs);
|
|
1400
|
-
if (userTransformer) ctx.setTransformer(userTransformer);
|
|
1928
|
+
ctx.setResolver(userResolver ? kubb_kit.Resolver.merge(resolverTs, userResolver) : resolverTs);
|
|
1929
|
+
if (userMacros?.length) ctx.setMacros(userMacros);
|
|
1401
1930
|
ctx.addGenerator(typeGenerator);
|
|
1402
|
-
for (const gen of userGenerators) ctx.addGenerator(gen);
|
|
1403
1931
|
} }
|
|
1404
1932
|
};
|
|
1405
1933
|
});
|
|
1406
1934
|
//#endregion
|
|
1935
|
+
//#region src/printers/functionParams.ts
|
|
1936
|
+
/**
|
|
1937
|
+
* Narrows a {@link TypeExpression} to a {@link TypeLiteralNode}.
|
|
1938
|
+
*/
|
|
1939
|
+
function isTypeLiteral(type) {
|
|
1940
|
+
return typeof type !== "string" && type.kind === "TypeLiteral";
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Narrows a {@link TypeExpression} to an {@link IndexedAccessTypeNode}.
|
|
1944
|
+
*/
|
|
1945
|
+
function isIndexedAccessType(type) {
|
|
1946
|
+
return typeof type !== "string" && type.kind === "IndexedAccessType";
|
|
1947
|
+
}
|
|
1948
|
+
/**
|
|
1949
|
+
* Creates a {@link TypeLiteralNode} representing an inline anonymous object type.
|
|
1950
|
+
*
|
|
1951
|
+
* @example
|
|
1952
|
+
* ```ts
|
|
1953
|
+
* createTypeLiteral({ members: [{ name: 'petId', type: 'string', optional: false }] })
|
|
1954
|
+
* // { petId: string }
|
|
1955
|
+
* ```
|
|
1956
|
+
*/
|
|
1957
|
+
function createTypeLiteral(props) {
|
|
1958
|
+
return {
|
|
1959
|
+
kind: "TypeLiteral",
|
|
1960
|
+
...props
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
/**
|
|
1964
|
+
* Creates an {@link IndexedAccessTypeNode} representing a single field accessed from a named type.
|
|
1965
|
+
*
|
|
1966
|
+
* @example
|
|
1967
|
+
* ```ts
|
|
1968
|
+
* createIndexedAccessType({ target: 'DeletePetPathParams', key: 'petId' })
|
|
1969
|
+
* // DeletePetPathParams['petId']
|
|
1970
|
+
* ```
|
|
1971
|
+
*/
|
|
1972
|
+
function createIndexedAccessType(props) {
|
|
1973
|
+
return {
|
|
1974
|
+
kind: "IndexedAccessType",
|
|
1975
|
+
...props
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
/**
|
|
1979
|
+
* Creates an {@link ObjectBindingPatternNode} for a destructured parameter binding.
|
|
1980
|
+
*
|
|
1981
|
+
* @example
|
|
1982
|
+
* ```ts
|
|
1983
|
+
* createObjectBindingPattern({ elements: [{ name: 'id' }, { name: 'name' }] })
|
|
1984
|
+
* // { id, name }
|
|
1985
|
+
* ```
|
|
1986
|
+
*/
|
|
1987
|
+
function createObjectBindingPattern(props) {
|
|
1988
|
+
return {
|
|
1989
|
+
kind: "ObjectBindingPattern",
|
|
1990
|
+
...props
|
|
1991
|
+
};
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* Creates a {@link FunctionParameterNode}. `optional` defaults to `false`.
|
|
1995
|
+
* Passing `properties` builds a destructured group: an {@link ObjectBindingPatternNode} name
|
|
1996
|
+
* paired with a {@link TypeLiteralNode} type.
|
|
1997
|
+
*
|
|
1998
|
+
* @example Optional param
|
|
1999
|
+
* ```ts
|
|
2000
|
+
* createFunctionParameter({ name: 'params', type: 'QueryParams', optional: true })
|
|
2001
|
+
* // → params?: QueryParams
|
|
2002
|
+
* ```
|
|
2003
|
+
*
|
|
2004
|
+
* @example Destructured group
|
|
2005
|
+
* ```ts
|
|
2006
|
+
* createFunctionParameter({ properties: [{ name: 'id', type: 'string' }, { name: 'name', type: 'string', optional: true }], default: '{}' })
|
|
2007
|
+
* // → { id, name }: { id: string; name?: string } = {}
|
|
2008
|
+
* ```
|
|
2009
|
+
*/
|
|
2010
|
+
function createFunctionParameter(input) {
|
|
2011
|
+
if ("properties" in input) return {
|
|
2012
|
+
kind: "FunctionParameter",
|
|
2013
|
+
name: createObjectBindingPattern({ elements: input.properties.map((p) => ({ name: p.name })) }),
|
|
2014
|
+
type: createTypeLiteral({ members: input.properties.map((p) => ({
|
|
2015
|
+
name: p.name,
|
|
2016
|
+
type: p.type,
|
|
2017
|
+
optional: p.optional ?? false
|
|
2018
|
+
})) }),
|
|
2019
|
+
optional: input.optional ?? false,
|
|
2020
|
+
...input.default !== void 0 ? { default: input.default } : {}
|
|
2021
|
+
};
|
|
2022
|
+
return {
|
|
2023
|
+
kind: "FunctionParameter",
|
|
2024
|
+
optional: false,
|
|
2025
|
+
...input
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
/**
|
|
2029
|
+
* Creates a {@link FunctionParametersNode} from an ordered list of parameters.
|
|
2030
|
+
*
|
|
2031
|
+
* @example
|
|
2032
|
+
* ```ts
|
|
2033
|
+
* const empty = createFunctionParameters()
|
|
2034
|
+
* // { params: [] }
|
|
2035
|
+
* ```
|
|
2036
|
+
*/
|
|
2037
|
+
function createFunctionParameters(props = {}) {
|
|
2038
|
+
return {
|
|
2039
|
+
kind: "FunctionParameters",
|
|
2040
|
+
params: [],
|
|
2041
|
+
...props
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
//#endregion
|
|
1407
2045
|
//#region src/printers/functionPrinter.ts
|
|
1408
|
-
const kindToHandlerKey = {
|
|
1409
|
-
FunctionParameter: "functionParameter",
|
|
1410
|
-
ParameterGroup: "parameterGroup",
|
|
1411
|
-
FunctionParameters: "functionParameters",
|
|
1412
|
-
ParamsType: "paramsType"
|
|
1413
|
-
};
|
|
1414
2046
|
/**
|
|
1415
|
-
*
|
|
2047
|
+
* Renders a {@link TypeExpression} to its TypeScript source.
|
|
2048
|
+
*
|
|
2049
|
+
* - a `string` is a type reference, returned as-is
|
|
2050
|
+
* - an `IndexedAccessType` becomes `objectType['indexType']`
|
|
2051
|
+
* - a `TypeLiteral` becomes `{ key: Type; key?: Type }`
|
|
1416
2052
|
*
|
|
1417
|
-
*
|
|
1418
|
-
*
|
|
2053
|
+
* `transformType` is applied once to the fully rendered type, matching how the
|
|
2054
|
+
* printer wrapped reference types before the `ts.factory` model.
|
|
2055
|
+
*/
|
|
2056
|
+
function renderType(type, transformType) {
|
|
2057
|
+
const rendered = renderTypeExpression(type);
|
|
2058
|
+
return transformType ? transformType(rendered) : rendered;
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* Renders an object-type key, quoting it as a string literal when it is not a valid
|
|
2062
|
+
* identifier so `{ 'content-type': string }` stays valid TypeScript.
|
|
1419
2063
|
*/
|
|
1420
|
-
|
|
2064
|
+
function renderKey(name) {
|
|
2065
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
2066
|
+
}
|
|
2067
|
+
function renderTypeExpression(type) {
|
|
2068
|
+
if (typeof type === "string") return type;
|
|
2069
|
+
if (isIndexedAccessType(type)) return `${type.target}['${type.key}']`;
|
|
2070
|
+
return `{ ${type.members.map((member) => {
|
|
2071
|
+
const value = renderTypeExpression(member.type);
|
|
2072
|
+
return member.optional ? `${renderKey(member.name)}?: ${value}` : `${renderKey(member.name)}: ${value}`;
|
|
2073
|
+
}).join("; ")} }`;
|
|
2074
|
+
}
|
|
2075
|
+
function groupMembers(param) {
|
|
2076
|
+
if (typeof param.name === "string") return null;
|
|
2077
|
+
return param.type && isTypeLiteral(param.type) ? param.type.members : [];
|
|
2078
|
+
}
|
|
1421
2079
|
function rank(param) {
|
|
1422
|
-
if (param.kind === "ParameterGroup") {
|
|
1423
|
-
if (param.default) return PARAM_RANK.withDefault;
|
|
1424
|
-
return param.optional ?? param.properties.every((p) => p.optional || p.default !== void 0) ? PARAM_RANK.optional : PARAM_RANK.required;
|
|
1425
|
-
}
|
|
1426
2080
|
if (param.rest) return PARAM_RANK.rest;
|
|
1427
2081
|
if (param.default) return PARAM_RANK.withDefault;
|
|
2082
|
+
const members = groupMembers(param);
|
|
2083
|
+
if (members) return members.every((m) => m.optional) ? PARAM_RANK.optional : PARAM_RANK.required;
|
|
1428
2084
|
return param.optional ? PARAM_RANK.optional : PARAM_RANK.required;
|
|
1429
2085
|
}
|
|
1430
2086
|
function sortParams(params) {
|
|
1431
|
-
return
|
|
2087
|
+
return params.toSorted((a, b) => rank(a) - rank(b));
|
|
2088
|
+
}
|
|
2089
|
+
function sortedGroupMembers(name, type) {
|
|
2090
|
+
const members = type && isTypeLiteral(type) ? type.members : [];
|
|
2091
|
+
const memberRank = (optional) => optional ? PARAM_RANK.optional : PARAM_RANK.required;
|
|
2092
|
+
return name.elements.map((element, index) => ({
|
|
2093
|
+
name: element.name,
|
|
2094
|
+
propertyName: element.propertyName,
|
|
2095
|
+
type: members[index]?.type,
|
|
2096
|
+
optional: members[index]?.optional
|
|
2097
|
+
})).toSorted((a, b) => memberRank(a.optional) - memberRank(b.optional));
|
|
2098
|
+
}
|
|
2099
|
+
/**
|
|
2100
|
+
* Renders one binding element: the local name, prefixed with its source key when the
|
|
2101
|
+
* binding renames the property, as in `{ petId: id }`.
|
|
2102
|
+
*/
|
|
2103
|
+
function renderBindingMember(member, transformName) {
|
|
2104
|
+
const local = transformName ? transformName(member.name) : member.name;
|
|
2105
|
+
return member.propertyName ? `${member.propertyName}: ${local}` : local;
|
|
1432
2106
|
}
|
|
1433
|
-
|
|
1434
|
-
|
|
2107
|
+
/**
|
|
2108
|
+
* Renders the type annotation of a destructured group. An inline object type is
|
|
2109
|
+
* rendered from the already-sorted members so its key order matches the binding;
|
|
2110
|
+
* a reference type is rendered as-is.
|
|
2111
|
+
*/
|
|
2112
|
+
function renderGroupType(type, sorted, transformType) {
|
|
2113
|
+
if (!type) return void 0;
|
|
2114
|
+
if (!isTypeLiteral(type)) return renderType(type);
|
|
2115
|
+
const typed = sorted.filter((member) => member.type !== void 0);
|
|
2116
|
+
if (!typed.length) return void 0;
|
|
2117
|
+
return `{ ${typed.map((member) => {
|
|
2118
|
+
const key = renderKey(member.propertyName ?? member.name);
|
|
2119
|
+
const value = renderType(member.type, transformType);
|
|
2120
|
+
return member.optional ? `${key}?: ${value}` : `${key}: ${value}`;
|
|
2121
|
+
}).join("; ")} }`;
|
|
2122
|
+
}
|
|
2123
|
+
function printParameter(node, options) {
|
|
2124
|
+
const { mode, transformName, transformType } = options;
|
|
2125
|
+
const bindingName = node.name;
|
|
2126
|
+
if (mode === "call") {
|
|
2127
|
+
if (typeof bindingName !== "string") return `{ ${sortedGroupMembers(bindingName, node.type).map((member) => renderBindingMember(member)).join(", ")} }`;
|
|
2128
|
+
const name = transformName ? transformName(bindingName) : bindingName;
|
|
2129
|
+
return node.rest ? `...${name}` : name;
|
|
2130
|
+
}
|
|
2131
|
+
if (typeof bindingName !== "string") {
|
|
2132
|
+
const sorted = sortedGroupMembers(bindingName, node.type);
|
|
2133
|
+
const binding = `{ ${sorted.map((member) => renderBindingMember(member, transformName)).join(", ")} }`;
|
|
2134
|
+
const allOptional = sorted.every((member) => member.optional);
|
|
2135
|
+
const type = renderGroupType(node.type, sorted, transformType);
|
|
2136
|
+
if (type) {
|
|
2137
|
+
if (allOptional) return `${binding}: ${type} = ${node.default ?? "{}"}`;
|
|
2138
|
+
return node.default ? `${binding}: ${type} = ${node.default}` : `${binding}: ${type}`;
|
|
2139
|
+
}
|
|
2140
|
+
return node.default ? `${binding} = ${node.default}` : binding;
|
|
2141
|
+
}
|
|
2142
|
+
const name = transformName ? transformName(bindingName) : bindingName;
|
|
2143
|
+
const type = node.type ? renderType(node.type, transformType) : void 0;
|
|
2144
|
+
if (node.rest) return type ? `...${name}: ${type}` : `...${name}`;
|
|
2145
|
+
if (type) {
|
|
2146
|
+
if (node.optional) return `${name}?: ${type}`;
|
|
2147
|
+
return node.default ? `${name}: ${type} = ${node.default}` : `${name}: ${type}`;
|
|
2148
|
+
}
|
|
2149
|
+
return node.default ? `${name} = ${node.default}` : name;
|
|
1435
2150
|
}
|
|
1436
2151
|
/**
|
|
1437
|
-
* Default function-signature printer.
|
|
1438
|
-
*
|
|
2152
|
+
* Default function-signature printer. Renders a parameter list in one of two modes:
|
|
2153
|
+
* `declaration` for the function signature and `call` for the call arguments.
|
|
1439
2154
|
*
|
|
1440
2155
|
* @example
|
|
1441
2156
|
* ```ts
|
|
@@ -1451,75 +2166,210 @@ function sortChildParams(params) {
|
|
|
1451
2166
|
* printer.print(sig) // → "petId: string, config: Config = {}"
|
|
1452
2167
|
* ```
|
|
1453
2168
|
*/
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
2169
|
+
function functionPrinter(options) {
|
|
2170
|
+
return {
|
|
2171
|
+
name: "functionParameters",
|
|
2172
|
+
options,
|
|
2173
|
+
print(node) {
|
|
2174
|
+
return sortParams(node.params).map((p) => printParameter(p, options)).filter(Boolean).join(", ");
|
|
2175
|
+
}
|
|
2176
|
+
};
|
|
2177
|
+
}
|
|
2178
|
+
//#endregion
|
|
2179
|
+
//#region src/printers/operationParams.ts
|
|
2180
|
+
/**
|
|
2181
|
+
* Resolves the {@link TypeExpression} for an individual parameter.
|
|
2182
|
+
*
|
|
2183
|
+
* Without a resolver, it falls back to the schema primitive (a plain type-name string). When the
|
|
2184
|
+
* parameter belongs to a named group, it emits an `IndexedAccessType` like `GroupParams['petId']`,
|
|
2185
|
+
* otherwise the resolved individual name.
|
|
2186
|
+
*/
|
|
2187
|
+
function resolveParamType({ node, param, resolver }) {
|
|
2188
|
+
if (!resolver) return param.schema.primitive ?? "unknown";
|
|
2189
|
+
const individualName = resolver.param.name(node, param);
|
|
2190
|
+
const groupLocation = param.in === "path" || param.in === "query" || param.in === "header" ? param.in : void 0;
|
|
2191
|
+
const groupResolvers = {
|
|
2192
|
+
path: resolver.param.path,
|
|
2193
|
+
query: resolver.param.query,
|
|
2194
|
+
header: resolver.param.headers
|
|
2195
|
+
};
|
|
2196
|
+
const groupName = groupLocation ? groupResolvers[groupLocation].call(resolver, node, param) : void 0;
|
|
2197
|
+
if (groupName && groupName !== individualName) return createIndexedAccessType({
|
|
2198
|
+
target: groupName,
|
|
2199
|
+
key: param.name
|
|
2200
|
+
});
|
|
2201
|
+
return individualName;
|
|
2202
|
+
}
|
|
2203
|
+
/**
|
|
2204
|
+
* Derives a {@link ParamGroupType} for a query or header group from the resolver.
|
|
2205
|
+
*
|
|
2206
|
+
* Returns `null` when there is no resolver, no params, or the group name equals the
|
|
2207
|
+
* individual param name (so there is no real group to emit).
|
|
2208
|
+
*/
|
|
2209
|
+
function resolveGroupType({ node, params, group, resolver }) {
|
|
2210
|
+
if (!resolver || !params.length) return null;
|
|
2211
|
+
const firstParam = params[0];
|
|
2212
|
+
const groupName = (group === "query" ? resolver.param.query : resolver.param.headers).call(resolver, node, firstParam);
|
|
2213
|
+
if (groupName === resolver.param.name(node, firstParam)) return null;
|
|
2214
|
+
return {
|
|
2215
|
+
type: groupName,
|
|
2216
|
+
optional: params.every((p) => !p.required)
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
/**
|
|
2220
|
+
* Converts an `OperationNode` into function parameters for code generation.
|
|
2221
|
+
*
|
|
2222
|
+
* Centralizes parameter grouping logic for all plugins. `paramsType` chooses between one
|
|
2223
|
+
* destructured object parameter (`object`) and separate top-level parameters (`inline`), while
|
|
2224
|
+
* `pathParamsType` controls how path params render in inline mode. Provide a `resolver` for type
|
|
2225
|
+
* name resolution and `extraParams` for plugin-specific trailing parameters such as an `options` object.
|
|
2226
|
+
*/
|
|
2227
|
+
function createOperationParams(node, options) {
|
|
2228
|
+
const { paramsType, pathParamsType, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options;
|
|
2229
|
+
const dataName = paramNames?.data ?? "data";
|
|
2230
|
+
const paramsName = paramNames?.params ?? "params";
|
|
2231
|
+
const headersName = paramNames?.headers ?? "headers";
|
|
2232
|
+
const pathName = paramNames?.path ?? "pathParams";
|
|
2233
|
+
const wrapType = (type) => typeWrapper ? typeWrapper(type) : type;
|
|
2234
|
+
const wrapTypeExpression = (type) => typeof type === "string" ? wrapType(type) : type;
|
|
2235
|
+
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
|
|
2236
|
+
const toProperty = (param) => ({
|
|
2237
|
+
name: param.name,
|
|
2238
|
+
type: wrapTypeExpression(resolveParamType({
|
|
2239
|
+
node,
|
|
2240
|
+
param,
|
|
2241
|
+
resolver
|
|
2242
|
+
})),
|
|
2243
|
+
optional: !param.required
|
|
2244
|
+
});
|
|
2245
|
+
const emptyObjectDefault = (props) => props.every((p) => p.optional) ? "{}" : void 0;
|
|
2246
|
+
const bodyType = node.requestBody?.content?.[0]?.schema ? wrapType(resolver?.response.body(node) ?? "unknown") : void 0;
|
|
2247
|
+
const bodyProperty = bodyType ? [{
|
|
2248
|
+
name: dataName,
|
|
2249
|
+
type: bodyType,
|
|
2250
|
+
optional: !(node.requestBody?.required ?? false)
|
|
2251
|
+
}] : [];
|
|
2252
|
+
const trailingGroups = [{
|
|
2253
|
+
name: paramsName,
|
|
2254
|
+
node,
|
|
2255
|
+
params: queryParams,
|
|
2256
|
+
groupType: resolveGroupType({
|
|
2257
|
+
node,
|
|
2258
|
+
params: queryParams,
|
|
2259
|
+
group: "query",
|
|
2260
|
+
resolver
|
|
2261
|
+
}),
|
|
2262
|
+
resolver,
|
|
2263
|
+
wrapType
|
|
2264
|
+
}, {
|
|
2265
|
+
name: headersName,
|
|
2266
|
+
node,
|
|
2267
|
+
params: headerParams,
|
|
2268
|
+
groupType: resolveGroupType({
|
|
2269
|
+
node,
|
|
2270
|
+
params: headerParams,
|
|
2271
|
+
group: "header",
|
|
2272
|
+
resolver
|
|
2273
|
+
}),
|
|
2274
|
+
resolver,
|
|
2275
|
+
wrapType
|
|
2276
|
+
}];
|
|
2277
|
+
const params = [];
|
|
2278
|
+
if (paramsType === "object") {
|
|
2279
|
+
const children = [
|
|
2280
|
+
...pathParams.map(toProperty),
|
|
2281
|
+
...bodyProperty,
|
|
2282
|
+
...trailingGroups.flatMap(buildGroupProperty)
|
|
2283
|
+
];
|
|
2284
|
+
if (children.length) params.push(createFunctionParameter({
|
|
2285
|
+
properties: children,
|
|
2286
|
+
default: emptyObjectDefault(children)
|
|
2287
|
+
}));
|
|
2288
|
+
} else {
|
|
2289
|
+
if (pathParamsType === "inlineSpread" && pathParams.length) {
|
|
2290
|
+
const spreadType = resolver?.param.path(node, pathParams[0]);
|
|
2291
|
+
params.push(createFunctionParameter({
|
|
2292
|
+
name: pathName,
|
|
2293
|
+
type: spreadType ? wrapType(spreadType) : void 0,
|
|
2294
|
+
rest: true
|
|
2295
|
+
}));
|
|
2296
|
+
} else if (pathParamsType === "inline") params.push(...pathParams.map((p) => createFunctionParameter(toProperty(p))));
|
|
2297
|
+
else if (pathParams.length) {
|
|
2298
|
+
const pathChildren = pathParams.map(toProperty);
|
|
2299
|
+
params.push(createFunctionParameter({
|
|
2300
|
+
properties: pathChildren,
|
|
2301
|
+
default: pathParamsDefault ?? emptyObjectDefault(pathChildren)
|
|
2302
|
+
}));
|
|
1512
2303
|
}
|
|
2304
|
+
params.push(...bodyProperty.map((p) => createFunctionParameter(p)));
|
|
2305
|
+
params.push(...trailingGroups.flatMap(buildGroupParam));
|
|
1513
2306
|
}
|
|
1514
|
-
|
|
2307
|
+
params.push(...extraParams);
|
|
2308
|
+
return createFunctionParameters({ params });
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Builds the property descriptor for a query or header group.
|
|
2312
|
+
* Returns an empty array when there are no params to emit.
|
|
2313
|
+
*
|
|
2314
|
+
* A pre-resolved `groupType` emits `name: GroupType`. Otherwise it builds an inline
|
|
2315
|
+
* {@link TypeLiteralNode} from the individual params.
|
|
2316
|
+
*/
|
|
2317
|
+
function buildGroupProperty({ name, node, params, groupType, resolver, wrapType }) {
|
|
2318
|
+
if (groupType) return [{
|
|
2319
|
+
name,
|
|
2320
|
+
type: typeof groupType.type === "string" ? wrapType(groupType.type) : groupType.type,
|
|
2321
|
+
optional: groupType.optional
|
|
2322
|
+
}];
|
|
2323
|
+
if (params.length) return [{
|
|
2324
|
+
name,
|
|
2325
|
+
type: buildTypeLiteral({
|
|
2326
|
+
node,
|
|
2327
|
+
params,
|
|
2328
|
+
resolver
|
|
2329
|
+
}),
|
|
2330
|
+
optional: params.every((p) => !p.required)
|
|
2331
|
+
}];
|
|
2332
|
+
return [];
|
|
2333
|
+
}
|
|
2334
|
+
/**
|
|
2335
|
+
* Builds a single {@link FunctionParameterNode} for a query or header group.
|
|
2336
|
+
* Returns an empty array when there are no params to emit.
|
|
2337
|
+
*/
|
|
2338
|
+
function buildGroupParam(args) {
|
|
2339
|
+
return buildGroupProperty(args).map((p) => createFunctionParameter(p));
|
|
2340
|
+
}
|
|
2341
|
+
/**
|
|
2342
|
+
* Builds a {@link TypeLiteralNode} for an inline anonymous type grouping named fields.
|
|
2343
|
+
*
|
|
2344
|
+
* Used when query or header parameters have no dedicated group type name.
|
|
2345
|
+
*/
|
|
2346
|
+
function buildTypeLiteral({ node, params, resolver }) {
|
|
2347
|
+
return createTypeLiteral({ members: params.map((p) => ({
|
|
2348
|
+
name: p.name,
|
|
2349
|
+
type: resolveParamType({
|
|
2350
|
+
node,
|
|
2351
|
+
param: p,
|
|
2352
|
+
resolver
|
|
2353
|
+
}),
|
|
2354
|
+
optional: !p.required
|
|
2355
|
+
})) });
|
|
2356
|
+
}
|
|
1515
2357
|
//#endregion
|
|
1516
2358
|
exports.Enum = Enum;
|
|
1517
2359
|
exports.Type = Type;
|
|
2360
|
+
exports.buildParams = buildParams;
|
|
2361
|
+
exports.createFunctionParameter = createFunctionParameter;
|
|
2362
|
+
exports.createFunctionParameters = createFunctionParameters;
|
|
2363
|
+
exports.createIndexedAccessType = createIndexedAccessType;
|
|
2364
|
+
exports.createObjectBindingPattern = createObjectBindingPattern;
|
|
2365
|
+
exports.createOperationParams = createOperationParams;
|
|
2366
|
+
exports.createTypeLiteral = createTypeLiteral;
|
|
1518
2367
|
exports.default = pluginTs;
|
|
1519
2368
|
exports.functionPrinter = functionPrinter;
|
|
1520
2369
|
exports.pluginTs = pluginTs;
|
|
1521
2370
|
exports.pluginTsName = pluginTsName;
|
|
1522
2371
|
exports.printerTs = printerTs;
|
|
2372
|
+
exports.renderType = renderType;
|
|
1523
2373
|
exports.resolverTs = resolverTs;
|
|
1524
2374
|
exports.typeGenerator = typeGenerator;
|
|
1525
2375
|
|