@kubb/plugin-ts 5.0.0-alpha.8 → 5.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +17 -10
- package/README.md +1 -3
- package/dist/index.cjs +1452 -4
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +574 -4
- package/dist/index.js +1418 -2
- package/dist/index.js.map +1 -0
- package/package.json +44 -64
- package/src/components/{v2/Enum.tsx → Enum.tsx} +33 -17
- package/src/components/Type.tsx +31 -161
- package/src/constants.ts +15 -5
- package/src/factory.ts +283 -35
- package/src/generators/typeGenerator.tsx +189 -424
- package/src/index.ts +9 -2
- package/src/plugin.ts +67 -205
- package/src/printers/functionPrinter.ts +197 -0
- package/src/printers/printerTs.ts +325 -0
- package/src/resolvers/resolverTs.ts +66 -0
- package/src/types.ts +238 -94
- package/src/utils.ts +130 -0
- package/dist/components-CRu8IKY3.js +0 -729
- package/dist/components-CRu8IKY3.js.map +0 -1
- package/dist/components-DeNDKlzf.cjs +0 -982
- package/dist/components-DeNDKlzf.cjs.map +0 -1
- package/dist/components.cjs +0 -3
- package/dist/components.d.ts +0 -36
- package/dist/components.js +0 -2
- package/dist/generators.cjs +0 -4
- package/dist/generators.d.ts +0 -480
- package/dist/generators.js +0 -2
- package/dist/plugin-D5NGPj0v.js +0 -1232
- package/dist/plugin-D5NGPj0v.js.map +0 -1
- package/dist/plugin-MLTxoa8p.cjs +0 -1279
- package/dist/plugin-MLTxoa8p.cjs.map +0 -1
- package/dist/types-CsvB6X5Y.d.ts +0 -167
- package/src/components/index.ts +0 -1
- package/src/components/v2/Type.tsx +0 -59
- package/src/generators/index.ts +0 -2
- package/src/generators/v2/typeGenerator.tsx +0 -171
- package/src/generators/v2/utils.ts +0 -140
- package/src/parser.ts +0 -389
- package/src/printer.ts +0 -368
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,1452 @@
|
|
|
1
|
-
Object.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
//#region \0rolldown/runtime.js
|
|
6
|
+
var __create = Object.create;
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
11
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
14
|
+
key = keys[i];
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
16
|
+
get: ((k) => from[k]).bind(null, key),
|
|
17
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
26
|
+
//#endregion
|
|
27
|
+
let _kubb_parser_ts = require("@kubb/parser-ts");
|
|
28
|
+
let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
|
|
29
|
+
let _kubb_core = require("@kubb/core");
|
|
30
|
+
let remeda = require("remeda");
|
|
31
|
+
let typescript = require("typescript");
|
|
32
|
+
typescript = __toESM(typescript, 1);
|
|
33
|
+
let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
|
|
34
|
+
//#region ../../internals/utils/src/casing.ts
|
|
35
|
+
/**
|
|
36
|
+
* Shared implementation for camelCase and PascalCase conversion.
|
|
37
|
+
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
38
|
+
* and capitalizes each word according to `pascal`.
|
|
39
|
+
*
|
|
40
|
+
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
41
|
+
*/
|
|
42
|
+
function toCamelOrPascal(text, pascal) {
|
|
43
|
+
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) => {
|
|
44
|
+
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
45
|
+
if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
|
|
46
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
47
|
+
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
51
|
+
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
52
|
+
* Segments are joined with `/` to form a file path.
|
|
53
|
+
*
|
|
54
|
+
* Only splits on dots followed by a letter so that version numbers
|
|
55
|
+
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
56
|
+
*/
|
|
57
|
+
function applyToFileParts(text, transformPart) {
|
|
58
|
+
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
59
|
+
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Converts `text` to camelCase.
|
|
63
|
+
* When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* camelCase('hello-world') // 'helloWorld'
|
|
67
|
+
* camelCase('pet.petId', { isFile: true }) // 'pet/petId'
|
|
68
|
+
*/
|
|
69
|
+
function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
70
|
+
if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
|
|
71
|
+
prefix,
|
|
72
|
+
suffix
|
|
73
|
+
} : {}));
|
|
74
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Converts `text` to PascalCase.
|
|
78
|
+
* When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* pascalCase('hello-world') // 'HelloWorld'
|
|
82
|
+
* pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
|
|
83
|
+
*/
|
|
84
|
+
function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
85
|
+
if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
|
|
86
|
+
prefix,
|
|
87
|
+
suffix
|
|
88
|
+
}) : camelCase(part));
|
|
89
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Converts `text` to snake_case.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* snakeCase('helloWorld') // 'hello_world'
|
|
96
|
+
* snakeCase('Hello-World') // 'hello_world'
|
|
97
|
+
*/
|
|
98
|
+
function snakeCase(text, { prefix = "", suffix = "" } = {}) {
|
|
99
|
+
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("_");
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Converts `text` to SCREAMING_SNAKE_CASE.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* screamingSnakeCase('helloWorld') // 'HELLO_WORLD'
|
|
106
|
+
*/
|
|
107
|
+
function screamingSnakeCase(text, { prefix = "", suffix = "" } = {}) {
|
|
108
|
+
return snakeCase(text, {
|
|
109
|
+
prefix,
|
|
110
|
+
suffix
|
|
111
|
+
}).toUpperCase();
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region ../../internals/utils/src/string.ts
|
|
115
|
+
/**
|
|
116
|
+
* Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
|
|
117
|
+
* Returns the string unchanged when no balanced quote pair is found.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* trimQuotes('"hello"') // 'hello'
|
|
121
|
+
* trimQuotes('hello') // 'hello'
|
|
122
|
+
*/
|
|
123
|
+
function trimQuotes(text) {
|
|
124
|
+
if (text.length >= 2) {
|
|
125
|
+
const first = text[0];
|
|
126
|
+
const last = text[text.length - 1];
|
|
127
|
+
if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
|
|
128
|
+
}
|
|
129
|
+
return text;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Escapes characters that are not allowed inside JS string literals.
|
|
133
|
+
* Handles quotes, backslashes, and Unicode line terminators (U+2028 / U+2029).
|
|
134
|
+
*
|
|
135
|
+
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* jsStringEscape('say "hi"\nbye') // 'say \\"hi\\"\\nbye'
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
function jsStringEscape(input) {
|
|
143
|
+
return `${input}`.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
|
|
144
|
+
switch (character) {
|
|
145
|
+
case "\"":
|
|
146
|
+
case "'":
|
|
147
|
+
case "\\": return `\\${character}`;
|
|
148
|
+
case "\n": return "\\n";
|
|
149
|
+
case "\r": return "\\r";
|
|
150
|
+
case "\u2028": return "\\u2028";
|
|
151
|
+
case "\u2029": return "\\u2029";
|
|
152
|
+
default: return "";
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region ../../internals/utils/src/object.ts
|
|
158
|
+
/**
|
|
159
|
+
* Serializes a primitive value to a JSON string literal, stripping any surrounding quote characters first.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* stringify('hello') // '"hello"'
|
|
163
|
+
* stringify('"hello"') // '"hello"'
|
|
164
|
+
*/
|
|
165
|
+
function stringify(value) {
|
|
166
|
+
if (value === void 0 || value === null) return "\"\"";
|
|
167
|
+
return JSON.stringify(trimQuotes(value.toString()));
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/constants.ts
|
|
171
|
+
/**
|
|
172
|
+
* `optionalType` values that cause a property's type to include `| undefined`.
|
|
173
|
+
*/
|
|
174
|
+
const OPTIONAL_ADDS_UNDEFINED = new Set(["undefined", "questionTokenAndUndefined"]);
|
|
175
|
+
/**
|
|
176
|
+
* `optionalType` values that render the property key with a `?` token.
|
|
177
|
+
*/
|
|
178
|
+
const OPTIONAL_ADDS_QUESTION_TOKEN = new Set(["questionToken", "questionTokenAndUndefined"]);
|
|
179
|
+
/**
|
|
180
|
+
* `enumType` values that append a `Key` suffix to the generated enum type alias.
|
|
181
|
+
*/
|
|
182
|
+
const ENUM_TYPES_WITH_KEY_SUFFIX = new Set(["asConst", "asPascalConst"]);
|
|
183
|
+
/**
|
|
184
|
+
* `enumType` values that require a runtime value declaration (object, enum, or literal).
|
|
185
|
+
*/
|
|
186
|
+
const ENUM_TYPES_WITH_RUNTIME_VALUE = new Set([
|
|
187
|
+
"enum",
|
|
188
|
+
"asConst",
|
|
189
|
+
"asPascalConst",
|
|
190
|
+
"constEnum",
|
|
191
|
+
"literal",
|
|
192
|
+
void 0
|
|
193
|
+
]);
|
|
194
|
+
/**
|
|
195
|
+
* `enumType` values whose type declaration is type-only (no runtime value emitted for the type alias).
|
|
196
|
+
*/
|
|
197
|
+
const ENUM_TYPES_WITH_TYPE_ONLY = new Set([
|
|
198
|
+
"asConst",
|
|
199
|
+
"asPascalConst",
|
|
200
|
+
"literal",
|
|
201
|
+
void 0
|
|
202
|
+
]);
|
|
203
|
+
/**
|
|
204
|
+
* Ordering priority for function parameters: lower = sorted earlier.
|
|
205
|
+
*/
|
|
206
|
+
const PARAM_RANK = {
|
|
207
|
+
required: 0,
|
|
208
|
+
optional: 1,
|
|
209
|
+
withDefault: 2,
|
|
210
|
+
rest: 3
|
|
211
|
+
};
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region src/factory.ts
|
|
214
|
+
const { SyntaxKind, factory } = typescript.default;
|
|
215
|
+
/**
|
|
216
|
+
* TypeScript AST modifiers for common keywords (async, export, const, static).
|
|
217
|
+
*/
|
|
218
|
+
const modifiers = {
|
|
219
|
+
async: factory.createModifier(typescript.default.SyntaxKind.AsyncKeyword),
|
|
220
|
+
export: factory.createModifier(typescript.default.SyntaxKind.ExportKeyword),
|
|
221
|
+
const: factory.createModifier(typescript.default.SyntaxKind.ConstKeyword),
|
|
222
|
+
static: factory.createModifier(typescript.default.SyntaxKind.StaticKeyword)
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* TypeScript syntax kind constants for union, literal, and string types.
|
|
226
|
+
*/
|
|
227
|
+
const syntaxKind = {
|
|
228
|
+
union: SyntaxKind.UnionType,
|
|
229
|
+
literalType: SyntaxKind.LiteralType,
|
|
230
|
+
stringLiteral: SyntaxKind.StringLiteral
|
|
231
|
+
};
|
|
232
|
+
function isValidIdentifier(str) {
|
|
233
|
+
if (!str.length || str.trim() !== str) return false;
|
|
234
|
+
const node = typescript.default.parseIsolatedEntityName(str, typescript.default.ScriptTarget.Latest);
|
|
235
|
+
return !!node && node.kind === typescript.default.SyntaxKind.Identifier && typescript.default.identifierToKeywordKind(node.kind) === void 0;
|
|
236
|
+
}
|
|
237
|
+
function propertyName(name) {
|
|
238
|
+
if (typeof name === "string") return isValidIdentifier(name) ? factory.createIdentifier(name) : factory.createStringLiteral(name);
|
|
239
|
+
return name;
|
|
240
|
+
}
|
|
241
|
+
const questionToken = factory.createToken(typescript.default.SyntaxKind.QuestionToken);
|
|
242
|
+
/**
|
|
243
|
+
* Creates a question token for optional type annotations.
|
|
244
|
+
* Pass `true` to use the cached token, or provide a pre-created token.
|
|
245
|
+
*/
|
|
246
|
+
function createQuestionToken(token) {
|
|
247
|
+
if (!token) return;
|
|
248
|
+
if (token === true) return questionToken;
|
|
249
|
+
return token;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Creates a TypeScript intersection type node from multiple type nodes.
|
|
253
|
+
* Returns the single node if only one is provided, or wraps in parentheses if requested.
|
|
254
|
+
*/
|
|
255
|
+
function createIntersectionDeclaration({ nodes, withParentheses }) {
|
|
256
|
+
if (!nodes.length) return null;
|
|
257
|
+
if (nodes.length === 1) return nodes[0] || null;
|
|
258
|
+
const node = factory.createIntersectionTypeNode(nodes);
|
|
259
|
+
if (withParentheses) return factory.createParenthesizedType(node);
|
|
260
|
+
return node;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Creates a TypeScript array type node.
|
|
264
|
+
* Use `arrayType: 'array'` for bracket syntax (`T[]`), or `'generic'` for `Array<T>`.
|
|
265
|
+
*
|
|
266
|
+
* @example Array bracket syntax
|
|
267
|
+
* `createArrayDeclaration({ nodes: [stringType], arrayType: 'array' }) // → string[]`
|
|
268
|
+
*
|
|
269
|
+
* @example Generic Array syntax
|
|
270
|
+
* `createArrayDeclaration({ nodes: [stringType], arrayType: 'generic' }) // → Array<string>`
|
|
271
|
+
*/
|
|
272
|
+
function createArrayDeclaration({ nodes, arrayType = "array" }) {
|
|
273
|
+
if (!nodes.length) return factory.createTupleTypeNode([]);
|
|
274
|
+
if (nodes.length === 1) {
|
|
275
|
+
const node = nodes[0];
|
|
276
|
+
if (!node) return null;
|
|
277
|
+
if (arrayType === "generic") return factory.createTypeReferenceNode(factory.createIdentifier("Array"), [node]);
|
|
278
|
+
return factory.createArrayTypeNode(node);
|
|
279
|
+
}
|
|
280
|
+
const unionType = factory.createUnionTypeNode(nodes);
|
|
281
|
+
if (arrayType === "generic") return factory.createTypeReferenceNode(factory.createIdentifier("Array"), [unionType]);
|
|
282
|
+
return factory.createArrayTypeNode(factory.createParenthesizedType(unionType));
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Minimum nodes length of 2
|
|
286
|
+
* @example Union type example
|
|
287
|
+
* `string | number`
|
|
288
|
+
*/
|
|
289
|
+
function createUnionDeclaration({ nodes, withParentheses }) {
|
|
290
|
+
if (!nodes.length) return keywordTypeNodes.any;
|
|
291
|
+
if (nodes.length === 1) return nodes[0];
|
|
292
|
+
const node = factory.createUnionTypeNode(nodes);
|
|
293
|
+
if (withParentheses) return factory.createParenthesizedType(node);
|
|
294
|
+
return node;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Creates a TypeScript property signature for object/interface members.
|
|
298
|
+
* Supports optional markers, readonly modifiers, and type annotations.
|
|
299
|
+
*/
|
|
300
|
+
function createPropertySignature({ readOnly, modifiers = [], name, questionToken, type }) {
|
|
301
|
+
return factory.createPropertySignature([...modifiers, readOnly ? factory.createToken(typescript.default.SyntaxKind.ReadonlyKeyword) : void 0].filter(Boolean), propertyName(name), createQuestionToken(questionToken), type);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Creates a function parameter declaration with optional markers, rest parameters, and type annotations.
|
|
305
|
+
*/
|
|
306
|
+
function createParameterSignature(name, { modifiers, dotDotDotToken, questionToken, type, initializer }) {
|
|
307
|
+
return factory.createParameterDeclaration(modifiers, dotDotDotToken, name, createQuestionToken(questionToken), type, initializer);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Attaches JSDoc comments to an AST node as synthetic leading comments.
|
|
311
|
+
* Filters out undefined comments before attaching.
|
|
312
|
+
*
|
|
313
|
+
* @see https://github.com/microsoft/TypeScript/issues/44151
|
|
314
|
+
*/
|
|
315
|
+
function appendJSDocToNode({ node, comments }) {
|
|
316
|
+
const filteredComments = comments.filter(Boolean);
|
|
317
|
+
if (!filteredComments.length) return node;
|
|
318
|
+
const text = filteredComments.reduce((acc = "", comment = "") => {
|
|
319
|
+
return `${acc}\n * ${comment.replaceAll("*/", "*\\/")}`;
|
|
320
|
+
}, "*");
|
|
321
|
+
return typescript.default.addSyntheticLeadingComment(node, typescript.default.SyntaxKind.MultiLineCommentTrivia, `${text || "*"}\n`, true);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Creates a TypeScript index signature for dynamic property access.
|
|
325
|
+
* Defines the key type (default: `string`) and value type on an object.
|
|
326
|
+
*/
|
|
327
|
+
function createIndexSignature(type, { modifiers, indexName = "key", indexType = factory.createKeywordTypeNode(typescript.default.SyntaxKind.StringKeyword) } = {}) {
|
|
328
|
+
return factory.createIndexSignature(modifiers, [createParameterSignature(indexName, { type: indexType })], type);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Creates a TypeScript type alias declaration with optional modifiers and type parameters.
|
|
332
|
+
*/
|
|
333
|
+
function createTypeAliasDeclaration({ modifiers, name, typeParameters, type }) {
|
|
334
|
+
return factory.createTypeAliasDeclaration(modifiers, name, typeParameters, type);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Creates a TypeScript interface declaration with optional modifiers, type parameters, and members.
|
|
338
|
+
*/
|
|
339
|
+
function createInterfaceDeclaration({ modifiers, name, typeParameters, members }) {
|
|
340
|
+
return factory.createInterfaceDeclaration(modifiers, name, typeParameters, void 0, members);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Creates a TypeScript type declaration as either a type alias or interface.
|
|
344
|
+
* Intelligently selects the syntax based on the type structure and attaches JSDoc comments.
|
|
345
|
+
*/
|
|
346
|
+
function createTypeDeclaration({ syntax, isExportable, comments, name, type }) {
|
|
347
|
+
if (syntax === "interface" && "members" in type) return appendJSDocToNode({
|
|
348
|
+
node: createInterfaceDeclaration({
|
|
349
|
+
members: [...type.members],
|
|
350
|
+
modifiers: isExportable ? [modifiers.export] : [],
|
|
351
|
+
name,
|
|
352
|
+
typeParameters: void 0
|
|
353
|
+
}),
|
|
354
|
+
comments
|
|
355
|
+
});
|
|
356
|
+
return appendJSDocToNode({
|
|
357
|
+
node: createTypeAliasDeclaration({
|
|
358
|
+
type,
|
|
359
|
+
modifiers: isExportable ? [modifiers.export] : [],
|
|
360
|
+
name,
|
|
361
|
+
typeParameters: void 0
|
|
362
|
+
}),
|
|
363
|
+
comments
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Apply casing transformation to enum keys
|
|
368
|
+
*/
|
|
369
|
+
function applyEnumKeyCasing(key, casing = "none") {
|
|
370
|
+
if (casing === "none") return key;
|
|
371
|
+
if (casing === "screamingSnakeCase") return screamingSnakeCase(key);
|
|
372
|
+
if (casing === "snakeCase") return snakeCase(key);
|
|
373
|
+
if (casing === "pascalCase") return pascalCase(key);
|
|
374
|
+
if (casing === "camelCase") return camelCase(key);
|
|
375
|
+
return key;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Creates a TypeScript enum declaration or equivalent construct in various formats.
|
|
379
|
+
* Returns a tuple of [name node, type node] - name node may be undefined for certain types.
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```ts
|
|
383
|
+
* const [name, type] = createEnumDeclaration({
|
|
384
|
+
* type: 'enum',
|
|
385
|
+
* name: 'petType',
|
|
386
|
+
* typeName: 'PetType',
|
|
387
|
+
* enums: [['cat', 'cat'], ['dog', 'dog']],
|
|
388
|
+
* })
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCasing = "none" }) {
|
|
392
|
+
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]) => {
|
|
393
|
+
if ((0, remeda.isNumber)(value)) {
|
|
394
|
+
if (value < 0) return factory.createLiteralTypeNode(factory.createPrefixUnaryExpression(typescript.default.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value))));
|
|
395
|
+
return factory.createLiteralTypeNode(factory.createNumericLiteral(value?.toString()));
|
|
396
|
+
}
|
|
397
|
+
if (typeof value === "boolean") return factory.createLiteralTypeNode(value ? factory.createTrue() : factory.createFalse());
|
|
398
|
+
if (value) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
|
|
399
|
+
}).filter(Boolean)))];
|
|
400
|
+
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(Boolean), factory.createIdentifier(typeName), enums.map(([key, value]) => {
|
|
401
|
+
let initializer = factory.createStringLiteral(value?.toString());
|
|
402
|
+
if (Number.parseInt(value.toString(), 10) === value && (0, remeda.isNumber)(Number.parseInt(value.toString(), 10))) if (value < 0) initializer = factory.createPrefixUnaryExpression(typescript.default.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
|
|
403
|
+
else initializer = factory.createNumericLiteral(value);
|
|
404
|
+
if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
|
|
405
|
+
if ((0, remeda.isNumber)(Number.parseInt(key.toString(), 10))) {
|
|
406
|
+
const casingKey = applyEnumKeyCasing(`${typeName}_${key}`, enumKeyCasing);
|
|
407
|
+
return factory.createEnumMember(propertyName(casingKey), initializer);
|
|
408
|
+
}
|
|
409
|
+
if (key) {
|
|
410
|
+
const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
|
|
411
|
+
return factory.createEnumMember(propertyName(casingKey), initializer);
|
|
412
|
+
}
|
|
413
|
+
}).filter(Boolean))];
|
|
414
|
+
const identifierName = name;
|
|
415
|
+
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))];
|
|
416
|
+
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]) => {
|
|
417
|
+
let initializer = factory.createStringLiteral(value?.toString());
|
|
418
|
+
if ((0, remeda.isNumber)(value)) if (value < 0) initializer = factory.createPrefixUnaryExpression(typescript.default.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
|
|
419
|
+
else initializer = factory.createNumericLiteral(value);
|
|
420
|
+
if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
|
|
421
|
+
if (key) {
|
|
422
|
+
const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
|
|
423
|
+
return factory.createPropertyAssignment(propertyName(casingKey), initializer);
|
|
424
|
+
}
|
|
425
|
+
}).filter(Boolean), 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))))];
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Creates a TypeScript `Omit<T, Keys>` type reference node.
|
|
429
|
+
* Optionally wraps the type in `NonNullable<T>` if `nonNullable` is true.
|
|
430
|
+
*/
|
|
431
|
+
function createOmitDeclaration({ keys, type, nonNullable }) {
|
|
432
|
+
const node = nonNullable ? factory.createTypeReferenceNode(factory.createIdentifier("NonNullable"), [type]) : type;
|
|
433
|
+
if (Array.isArray(keys)) return factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [node, factory.createUnionTypeNode(keys.map((key) => {
|
|
434
|
+
return factory.createLiteralTypeNode(factory.createStringLiteral(key));
|
|
435
|
+
}))]);
|
|
436
|
+
return factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [node, factory.createLiteralTypeNode(factory.createStringLiteral(keys))]);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Pre-built TypeScript keyword type nodes for common primitive types.
|
|
440
|
+
* Use these to avoid repeatedly creating the same type nodes.
|
|
441
|
+
*/
|
|
442
|
+
const keywordTypeNodes = {
|
|
443
|
+
any: factory.createKeywordTypeNode(typescript.default.SyntaxKind.AnyKeyword),
|
|
444
|
+
unknown: factory.createKeywordTypeNode(typescript.default.SyntaxKind.UnknownKeyword),
|
|
445
|
+
void: factory.createKeywordTypeNode(typescript.default.SyntaxKind.VoidKeyword),
|
|
446
|
+
number: factory.createKeywordTypeNode(typescript.default.SyntaxKind.NumberKeyword),
|
|
447
|
+
integer: factory.createKeywordTypeNode(typescript.default.SyntaxKind.NumberKeyword),
|
|
448
|
+
bigint: factory.createKeywordTypeNode(typescript.default.SyntaxKind.BigIntKeyword),
|
|
449
|
+
object: factory.createKeywordTypeNode(typescript.default.SyntaxKind.ObjectKeyword),
|
|
450
|
+
string: factory.createKeywordTypeNode(typescript.default.SyntaxKind.StringKeyword),
|
|
451
|
+
boolean: factory.createKeywordTypeNode(typescript.default.SyntaxKind.BooleanKeyword),
|
|
452
|
+
undefined: factory.createKeywordTypeNode(typescript.default.SyntaxKind.UndefinedKeyword),
|
|
453
|
+
null: factory.createLiteralTypeNode(factory.createToken(typescript.default.SyntaxKind.NullKeyword)),
|
|
454
|
+
never: factory.createKeywordTypeNode(typescript.default.SyntaxKind.NeverKeyword)
|
|
455
|
+
};
|
|
456
|
+
/**
|
|
457
|
+
* Converts a path like '/pet/{petId}/uploadImage' to a template literal type
|
|
458
|
+
* like `/pet/${string}/uploadImage`
|
|
459
|
+
*/
|
|
460
|
+
/**
|
|
461
|
+
* Converts an OAS-style path (e.g. `/pets/{petId}`) or an Express-style path
|
|
462
|
+
* (e.g. `/pets/:petId`) to a TypeScript template literal type
|
|
463
|
+
* like `` `/pets/${string}` ``.
|
|
464
|
+
*/
|
|
465
|
+
function createUrlTemplateType(path) {
|
|
466
|
+
const normalized = path.replace(/:([^/]+)/g, "{$1}");
|
|
467
|
+
if (!normalized.includes("{")) return factory.createLiteralTypeNode(factory.createStringLiteral(normalized));
|
|
468
|
+
const segments = normalized.split(/(\{[^}]+\})/);
|
|
469
|
+
const parts = [];
|
|
470
|
+
const parameterIndices = [];
|
|
471
|
+
segments.forEach((segment) => {
|
|
472
|
+
if (segment.startsWith("{") && segment.endsWith("}")) {
|
|
473
|
+
parameterIndices.push(parts.length);
|
|
474
|
+
parts.push(segment);
|
|
475
|
+
} else if (segment) parts.push(segment);
|
|
476
|
+
});
|
|
477
|
+
const head = typescript.default.factory.createTemplateHead(parts[0] || "");
|
|
478
|
+
const templateSpans = [];
|
|
479
|
+
parameterIndices.forEach((paramIndex, i) => {
|
|
480
|
+
const isLast = i === parameterIndices.length - 1;
|
|
481
|
+
const nextPart = parts[paramIndex + 1] || "";
|
|
482
|
+
const literal = isLast ? typescript.default.factory.createTemplateTail(nextPart) : typescript.default.factory.createTemplateMiddle(nextPart);
|
|
483
|
+
templateSpans.push(typescript.default.factory.createTemplateLiteralTypeSpan(keywordTypeNodes.string, literal));
|
|
484
|
+
});
|
|
485
|
+
return typescript.default.factory.createTemplateLiteralType(head, templateSpans);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Creates a TypeScript type literal node (anonymous object type).
|
|
489
|
+
*/
|
|
490
|
+
const createTypeLiteralNode = factory.createTypeLiteralNode;
|
|
491
|
+
/**
|
|
492
|
+
* Creates a TypeScript type reference node (e.g., `Array<string>`, `Record<K, V>`).
|
|
493
|
+
*/
|
|
494
|
+
const createTypeReferenceNode = factory.createTypeReferenceNode;
|
|
495
|
+
/**
|
|
496
|
+
* Creates a numeric literal type node.
|
|
497
|
+
*/
|
|
498
|
+
const createNumericLiteral = factory.createNumericLiteral;
|
|
499
|
+
/**
|
|
500
|
+
* Creates a string literal type node.
|
|
501
|
+
*/
|
|
502
|
+
const createStringLiteral = factory.createStringLiteral;
|
|
503
|
+
/**
|
|
504
|
+
* Creates an array type node (e.g., `T[]`).
|
|
505
|
+
*/
|
|
506
|
+
const createArrayTypeNode = factory.createArrayTypeNode;
|
|
507
|
+
factory.createParenthesizedType;
|
|
508
|
+
/**
|
|
509
|
+
* Creates a literal type node (e.g., `'hello'`, `42`, `true`).
|
|
510
|
+
*/
|
|
511
|
+
const createLiteralTypeNode = factory.createLiteralTypeNode;
|
|
512
|
+
factory.createNull;
|
|
513
|
+
/**
|
|
514
|
+
* Creates an identifier node.
|
|
515
|
+
*/
|
|
516
|
+
const createIdentifier = factory.createIdentifier;
|
|
517
|
+
/**
|
|
518
|
+
* Creates an optional type node (e.g., `T | undefined`).
|
|
519
|
+
*/
|
|
520
|
+
const createOptionalTypeNode = factory.createOptionalTypeNode;
|
|
521
|
+
/**
|
|
522
|
+
* Creates a tuple type node (e.g., `[string, number]`).
|
|
523
|
+
*/
|
|
524
|
+
const createTupleTypeNode = factory.createTupleTypeNode;
|
|
525
|
+
/**
|
|
526
|
+
* Creates a rest type node for variadic tuple elements (e.g., `...T[]`).
|
|
527
|
+
*/
|
|
528
|
+
const createRestTypeNode = factory.createRestTypeNode;
|
|
529
|
+
/**
|
|
530
|
+
* Creates a boolean true literal type node.
|
|
531
|
+
*/
|
|
532
|
+
const createTrue = factory.createTrue;
|
|
533
|
+
/**
|
|
534
|
+
* Creates a boolean false literal type node.
|
|
535
|
+
*/
|
|
536
|
+
const createFalse = factory.createFalse;
|
|
537
|
+
factory.createIndexedAccessTypeNode;
|
|
538
|
+
factory.createTypeOperatorNode;
|
|
539
|
+
/**
|
|
540
|
+
* Creates a prefix unary expression (e.g., negative numbers, logical not).
|
|
541
|
+
*/
|
|
542
|
+
const createPrefixUnaryExpression = factory.createPrefixUnaryExpression;
|
|
543
|
+
/**
|
|
544
|
+
* Converts a primitive const value to a TypeScript literal type node.
|
|
545
|
+
* Handles negative numbers via a prefix unary expression.
|
|
546
|
+
*/
|
|
547
|
+
function constToTypeNode(value, format) {
|
|
548
|
+
if (format === "boolean") return createLiteralTypeNode(value === true ? createTrue() : createFalse());
|
|
549
|
+
if (format === "number" && typeof value === "number") {
|
|
550
|
+
if (value < 0) return createLiteralTypeNode(createPrefixUnaryExpression(SyntaxKind.MinusToken, createNumericLiteral(Math.abs(value))));
|
|
551
|
+
return createLiteralTypeNode(createNumericLiteral(value));
|
|
552
|
+
}
|
|
553
|
+
return createLiteralTypeNode(createStringLiteral(String(value)));
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Returns a `Date` reference type node when `representation` is `'date'`, otherwise falls back to `string`.
|
|
557
|
+
*/
|
|
558
|
+
function dateOrStringNode(node) {
|
|
559
|
+
return node.representation === "date" ? createTypeReferenceNode(createIdentifier("Date")) : keywordTypeNodes.string;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Maps an array of `SchemaNode`s through the printer, filtering out `null` and `undefined` results.
|
|
563
|
+
*/
|
|
564
|
+
function buildMemberNodes(members, print) {
|
|
565
|
+
return (members ?? []).map(print).filter(Boolean);
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Builds a TypeScript tuple type node from an array schema's `items`,
|
|
569
|
+
* applying min/max slice and optional/rest element rules.
|
|
570
|
+
*/
|
|
571
|
+
function buildTupleNode(node, print) {
|
|
572
|
+
let items = (node.items ?? []).map(print).filter(Boolean);
|
|
573
|
+
const restNode = node.rest ? print(node.rest) ?? void 0 : void 0;
|
|
574
|
+
const { min, max } = node;
|
|
575
|
+
if (max !== void 0) {
|
|
576
|
+
items = items.slice(0, max);
|
|
577
|
+
if (items.length < max && restNode) items = [...items, ...Array(max - items.length).fill(restNode)];
|
|
578
|
+
}
|
|
579
|
+
if (min !== void 0) items = items.map((item, i) => i >= min ? createOptionalTypeNode(item) : item);
|
|
580
|
+
if (max === void 0 && restNode) items.push(createRestTypeNode(createArrayTypeNode(restNode)));
|
|
581
|
+
return createTupleTypeNode(items);
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Applies `nullable` and optional/nullish `| undefined` union modifiers to a property's resolved base type.
|
|
585
|
+
*/
|
|
586
|
+
function buildPropertyType(schema, baseType, optionalType) {
|
|
587
|
+
const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(optionalType);
|
|
588
|
+
const meta = _kubb_core.ast.syncSchemaRef(schema);
|
|
589
|
+
let type = baseType;
|
|
590
|
+
if (meta.nullable) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.null] });
|
|
591
|
+
if ((meta.nullish || meta.optional) && addsUndefined) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] });
|
|
592
|
+
return type;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Creates TypeScript index signatures for `additionalProperties` and `patternProperties` on an object schema node.
|
|
596
|
+
*/
|
|
597
|
+
function buildIndexSignatures(node, propertyCount, print) {
|
|
598
|
+
const elements = [];
|
|
599
|
+
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
600
|
+
const additionalType = print(node.additionalProperties) ?? keywordTypeNodes.unknown;
|
|
601
|
+
elements.push(createIndexSignature(propertyCount > 0 ? keywordTypeNodes.unknown : additionalType));
|
|
602
|
+
} else if (node.additionalProperties === true) elements.push(createIndexSignature(keywordTypeNodes.unknown));
|
|
603
|
+
if (node.patternProperties) {
|
|
604
|
+
const first = Object.values(node.patternProperties)[0];
|
|
605
|
+
if (first) {
|
|
606
|
+
let patternType = print(first) ?? keywordTypeNodes.unknown;
|
|
607
|
+
if (first.nullable) patternType = createUnionDeclaration({ nodes: [patternType, keywordTypeNodes.null] });
|
|
608
|
+
elements.push(createIndexSignature(patternType));
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return elements;
|
|
612
|
+
}
|
|
613
|
+
//#endregion
|
|
614
|
+
//#region src/components/Enum.tsx
|
|
615
|
+
/**
|
|
616
|
+
* Resolves the runtime identifier name and the TypeScript type name for an enum schema node.
|
|
617
|
+
*
|
|
618
|
+
* The raw `node.name` may be a YAML key such as `"enumNames.Type"` which is not a
|
|
619
|
+
* valid TypeScript identifier. The resolver normalizes it; for inline enum
|
|
620
|
+
* properties the adapter already emits a PascalCase+suffix name so resolution is typically a no-op.
|
|
621
|
+
*/
|
|
622
|
+
function getEnumNames({ node, enumType, enumTypeSuffix, resolver }) {
|
|
623
|
+
const resolved = resolver.default(node.name, "type");
|
|
624
|
+
return {
|
|
625
|
+
enumName: enumType === "asPascalConst" ? resolved : camelCase(node.name),
|
|
626
|
+
typeName: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) ? resolver.resolveEnumKeyName(node, enumTypeSuffix) : resolved
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Renders the enum declaration(s) for a single named `EnumSchemaNode`.
|
|
631
|
+
*
|
|
632
|
+
* Depending on `enumType` this may emit:
|
|
633
|
+
* - A runtime object (`asConst` / `asPascalConst`) plus a `typeof` type alias
|
|
634
|
+
* - A `const enum` or plain `enum` declaration (`constEnum` / `enum`)
|
|
635
|
+
* - A union literal type alias (`literal`)
|
|
636
|
+
*
|
|
637
|
+
* The emitted `File.Source` nodes carry the resolved names so that the barrel
|
|
638
|
+
* index picks up the correct export identifiers.
|
|
639
|
+
*/
|
|
640
|
+
function Enum({ node, enumType, enumTypeSuffix, enumKeyCasing, resolver }) {
|
|
641
|
+
const { enumName, typeName } = getEnumNames({
|
|
642
|
+
node,
|
|
643
|
+
enumType,
|
|
644
|
+
enumTypeSuffix,
|
|
645
|
+
resolver
|
|
646
|
+
});
|
|
647
|
+
const [nameNode, typeNode] = createEnumDeclaration({
|
|
648
|
+
name: enumName,
|
|
649
|
+
typeName,
|
|
650
|
+
enums: node.namedEnumValues?.map((v) => [trimQuotes(v.name.toString()), v.value]) ?? node.enumValues?.filter((v) => v !== null && v !== void 0).map((v) => [trimQuotes(v.toString()), v]) ?? [],
|
|
651
|
+
type: enumType,
|
|
652
|
+
enumKeyCasing
|
|
653
|
+
});
|
|
654
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [nameNode && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
|
|
655
|
+
name: enumName,
|
|
656
|
+
isExportable: true,
|
|
657
|
+
isIndexable: true,
|
|
658
|
+
isTypeOnly: false,
|
|
659
|
+
children: (0, _kubb_parser_ts.safePrint)(nameNode)
|
|
660
|
+
}), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
|
|
661
|
+
name: typeName,
|
|
662
|
+
isIndexable: true,
|
|
663
|
+
isExportable: ENUM_TYPES_WITH_RUNTIME_VALUE.has(enumType),
|
|
664
|
+
isTypeOnly: ENUM_TYPES_WITH_TYPE_ONLY.has(enumType),
|
|
665
|
+
children: (0, _kubb_parser_ts.safePrint)(typeNode)
|
|
666
|
+
})] });
|
|
667
|
+
}
|
|
668
|
+
//#endregion
|
|
669
|
+
//#region src/components/Type.tsx
|
|
670
|
+
function Type({ name, node, printer, enumType, enumTypeSuffix, enumKeyCasing, resolver }) {
|
|
671
|
+
const enumSchemaNodes = _kubb_core.ast.collect(node, { schema(n) {
|
|
672
|
+
const enumNode = _kubb_core.ast.narrowSchema(n, _kubb_core.ast.schemaTypes.enum);
|
|
673
|
+
if (enumNode?.name) return enumNode;
|
|
674
|
+
} });
|
|
675
|
+
const output = printer.print(node);
|
|
676
|
+
if (!output) return;
|
|
677
|
+
const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
|
|
678
|
+
return {
|
|
679
|
+
node,
|
|
680
|
+
...getEnumNames({
|
|
681
|
+
node,
|
|
682
|
+
enumType,
|
|
683
|
+
enumTypeSuffix,
|
|
684
|
+
resolver
|
|
685
|
+
})
|
|
686
|
+
};
|
|
687
|
+
});
|
|
688
|
+
const shouldExportEnums = enumType !== "inlineLiteral";
|
|
689
|
+
const shouldExportType = enumType === "inlineLiteral" || enums.every((item) => item.typeName !== name);
|
|
690
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Enum, {
|
|
691
|
+
node,
|
|
692
|
+
enumType,
|
|
693
|
+
enumTypeSuffix,
|
|
694
|
+
enumKeyCasing,
|
|
695
|
+
resolver
|
|
696
|
+
}, node.name)), shouldExportType && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
|
|
697
|
+
name,
|
|
698
|
+
isTypeOnly: true,
|
|
699
|
+
isExportable: true,
|
|
700
|
+
isIndexable: true,
|
|
701
|
+
children: output
|
|
702
|
+
})] });
|
|
703
|
+
}
|
|
704
|
+
//#endregion
|
|
705
|
+
//#region src/utils.ts
|
|
706
|
+
/**
|
|
707
|
+
* Collects JSDoc annotation strings for a schema node.
|
|
708
|
+
*
|
|
709
|
+
* Only uses official JSDoc tags from https://jsdoc.app/: `@description`, `@deprecated`, `@default`, `@example`, `@type`.
|
|
710
|
+
* Constraint metadata (min/max length, pattern, multipleOf, min/maxProperties) is emitted as plain-text lines.
|
|
711
|
+
|
|
712
|
+
*/
|
|
713
|
+
function buildPropertyJSDocComments(schema) {
|
|
714
|
+
const meta = _kubb_core.ast.syncSchemaRef(schema);
|
|
715
|
+
const isArray = meta?.primitive === "array";
|
|
716
|
+
return [
|
|
717
|
+
meta && "description" in meta && meta.description ? `@description ${jsStringEscape(meta.description)}` : void 0,
|
|
718
|
+
meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : void 0,
|
|
719
|
+
!isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : void 0,
|
|
720
|
+
!isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : void 0,
|
|
721
|
+
meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : void 0,
|
|
722
|
+
meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" ? stringify(meta.default) : meta.default}` : void 0,
|
|
723
|
+
meta && "example" in meta && meta.example !== void 0 ? `@example ${meta.example}` : void 0,
|
|
724
|
+
meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, "optional" in schema && schema.optional ? " | undefined" : void 0].filter(Boolean).join("") : void 0
|
|
725
|
+
].filter(Boolean);
|
|
726
|
+
}
|
|
727
|
+
function buildParams(node, { params, resolver }) {
|
|
728
|
+
return _kubb_core.ast.createSchema({
|
|
729
|
+
type: "object",
|
|
730
|
+
properties: params.map((param) => _kubb_core.ast.createProperty({
|
|
731
|
+
name: param.name,
|
|
732
|
+
required: param.required,
|
|
733
|
+
schema: _kubb_core.ast.createSchema({
|
|
734
|
+
type: "ref",
|
|
735
|
+
name: resolver.resolveParamName(node, param)
|
|
736
|
+
})
|
|
737
|
+
}))
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
function buildData(node, { resolver }) {
|
|
741
|
+
const pathParams = node.parameters.filter((p) => p.in === "path");
|
|
742
|
+
const queryParams = node.parameters.filter((p) => p.in === "query");
|
|
743
|
+
const headerParams = node.parameters.filter((p) => p.in === "header");
|
|
744
|
+
return _kubb_core.ast.createSchema({
|
|
745
|
+
type: "object",
|
|
746
|
+
deprecated: node.deprecated,
|
|
747
|
+
properties: [
|
|
748
|
+
_kubb_core.ast.createProperty({
|
|
749
|
+
name: "data",
|
|
750
|
+
schema: node.requestBody?.content?.[0]?.schema ? _kubb_core.ast.createSchema({
|
|
751
|
+
type: "ref",
|
|
752
|
+
name: resolver.resolveDataName(node),
|
|
753
|
+
optional: true
|
|
754
|
+
}) : _kubb_core.ast.createSchema({
|
|
755
|
+
type: "never",
|
|
756
|
+
primitive: void 0,
|
|
757
|
+
optional: true
|
|
758
|
+
})
|
|
759
|
+
}),
|
|
760
|
+
_kubb_core.ast.createProperty({
|
|
761
|
+
name: "pathParams",
|
|
762
|
+
required: pathParams.length > 0,
|
|
763
|
+
schema: pathParams.length > 0 ? buildParams(node, {
|
|
764
|
+
params: pathParams,
|
|
765
|
+
resolver
|
|
766
|
+
}) : _kubb_core.ast.createSchema({
|
|
767
|
+
type: "never",
|
|
768
|
+
primitive: void 0
|
|
769
|
+
})
|
|
770
|
+
}),
|
|
771
|
+
_kubb_core.ast.createProperty({
|
|
772
|
+
name: "queryParams",
|
|
773
|
+
schema: queryParams.length > 0 ? _kubb_core.ast.createSchema({
|
|
774
|
+
...buildParams(node, {
|
|
775
|
+
params: queryParams,
|
|
776
|
+
resolver
|
|
777
|
+
}),
|
|
778
|
+
optional: true
|
|
779
|
+
}) : _kubb_core.ast.createSchema({
|
|
780
|
+
type: "never",
|
|
781
|
+
primitive: void 0,
|
|
782
|
+
optional: true
|
|
783
|
+
})
|
|
784
|
+
}),
|
|
785
|
+
_kubb_core.ast.createProperty({
|
|
786
|
+
name: "headerParams",
|
|
787
|
+
schema: headerParams.length > 0 ? _kubb_core.ast.createSchema({
|
|
788
|
+
...buildParams(node, {
|
|
789
|
+
params: headerParams,
|
|
790
|
+
resolver
|
|
791
|
+
}),
|
|
792
|
+
optional: true
|
|
793
|
+
}) : _kubb_core.ast.createSchema({
|
|
794
|
+
type: "never",
|
|
795
|
+
primitive: void 0,
|
|
796
|
+
optional: true
|
|
797
|
+
})
|
|
798
|
+
}),
|
|
799
|
+
_kubb_core.ast.createProperty({
|
|
800
|
+
name: "url",
|
|
801
|
+
required: true,
|
|
802
|
+
schema: _kubb_core.ast.createSchema({
|
|
803
|
+
type: "url",
|
|
804
|
+
path: node.path
|
|
805
|
+
})
|
|
806
|
+
})
|
|
807
|
+
]
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
function buildResponses(node, { resolver }) {
|
|
811
|
+
if (node.responses.length === 0) return null;
|
|
812
|
+
return _kubb_core.ast.createSchema({
|
|
813
|
+
type: "object",
|
|
814
|
+
properties: node.responses.map((res) => _kubb_core.ast.createProperty({
|
|
815
|
+
name: String(res.statusCode),
|
|
816
|
+
required: true,
|
|
817
|
+
schema: _kubb_core.ast.createSchema({
|
|
818
|
+
type: "ref",
|
|
819
|
+
name: resolver.resolveResponseStatusName(node, res.statusCode)
|
|
820
|
+
})
|
|
821
|
+
}))
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
function buildResponseUnion(node, { resolver }) {
|
|
825
|
+
const responsesWithSchema = node.responses.filter((res) => res.schema);
|
|
826
|
+
if (responsesWithSchema.length === 0) return null;
|
|
827
|
+
return _kubb_core.ast.createSchema({
|
|
828
|
+
type: "union",
|
|
829
|
+
members: responsesWithSchema.map((res) => _kubb_core.ast.createSchema({
|
|
830
|
+
type: "ref",
|
|
831
|
+
name: resolver.resolveResponseStatusName(node, res.statusCode)
|
|
832
|
+
}))
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
//#endregion
|
|
836
|
+
//#region src/printers/printerTs.ts
|
|
837
|
+
/**
|
|
838
|
+
* TypeScript type printer built with `definePrinter`.
|
|
839
|
+
*
|
|
840
|
+
* Converts a `SchemaNode` AST node into a TypeScript AST node:
|
|
841
|
+
* - **`printer.print(node)`** — when `options.typeName` is set, returns a full
|
|
842
|
+
* `type Name = …` or `interface Name { … }` declaration (`ts.Node`).
|
|
843
|
+
* Without `typeName`, returns the raw `ts.TypeNode` for the schema.
|
|
844
|
+
*
|
|
845
|
+
* Dispatches on `node.type` to the appropriate handler in `nodes`. Options are closed
|
|
846
|
+
* over per printer instance, so each call to `printerTs(options)` produces an independent printer.
|
|
847
|
+
*
|
|
848
|
+
* @example Raw type node (no `typeName`)
|
|
849
|
+
* ```ts
|
|
850
|
+
* const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enumType: 'inlineLiteral' })
|
|
851
|
+
* const typeNode = printer.print(schemaNode) // ts.TypeNode
|
|
852
|
+
* ```
|
|
853
|
+
*
|
|
854
|
+
* @example Full declaration (with `typeName`)
|
|
855
|
+
* ```ts
|
|
856
|
+
* const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enumType: 'inlineLiteral', typeName: 'MyType' })
|
|
857
|
+
* const declaration = printer.print(schemaNode) // ts.TypeAliasDeclaration | ts.InterfaceDeclaration
|
|
858
|
+
* ```
|
|
859
|
+
*/
|
|
860
|
+
const printerTs = _kubb_core.ast.definePrinter((options) => {
|
|
861
|
+
const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(options.optionalType);
|
|
862
|
+
return {
|
|
863
|
+
name: "typescript",
|
|
864
|
+
options,
|
|
865
|
+
nodes: {
|
|
866
|
+
any: () => keywordTypeNodes.any,
|
|
867
|
+
unknown: () => keywordTypeNodes.unknown,
|
|
868
|
+
void: () => keywordTypeNodes.void,
|
|
869
|
+
never: () => keywordTypeNodes.never,
|
|
870
|
+
boolean: () => keywordTypeNodes.boolean,
|
|
871
|
+
null: () => keywordTypeNodes.null,
|
|
872
|
+
blob: () => createTypeReferenceNode("Blob", []),
|
|
873
|
+
string: () => keywordTypeNodes.string,
|
|
874
|
+
uuid: () => keywordTypeNodes.string,
|
|
875
|
+
email: () => keywordTypeNodes.string,
|
|
876
|
+
url: (node) => {
|
|
877
|
+
if (node.path) return createUrlTemplateType(node.path);
|
|
878
|
+
return keywordTypeNodes.string;
|
|
879
|
+
},
|
|
880
|
+
ipv4: () => keywordTypeNodes.string,
|
|
881
|
+
ipv6: () => keywordTypeNodes.string,
|
|
882
|
+
datetime: () => keywordTypeNodes.string,
|
|
883
|
+
number: () => keywordTypeNodes.number,
|
|
884
|
+
integer: () => keywordTypeNodes.number,
|
|
885
|
+
bigint: () => keywordTypeNodes.bigint,
|
|
886
|
+
date: dateOrStringNode,
|
|
887
|
+
time: dateOrStringNode,
|
|
888
|
+
ref(node) {
|
|
889
|
+
if (!node.name) return;
|
|
890
|
+
const refName = node.ref ? _kubb_core.ast.extractRefName(node.ref) ?? node.name : node.name;
|
|
891
|
+
return createTypeReferenceNode(node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enumType) && this.options.enumTypeSuffix && this.options.enumSchemaNames?.has(refName) ? this.options.resolver.resolveEnumKeyName({ name: refName }, this.options.enumTypeSuffix) : node.ref ? this.options.resolver.default(refName, "type") : refName, void 0);
|
|
892
|
+
},
|
|
893
|
+
enum(node) {
|
|
894
|
+
const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
|
|
895
|
+
if (this.options.enumType === "inlineLiteral" || !node.name) return createUnionDeclaration({
|
|
896
|
+
withParentheses: true,
|
|
897
|
+
nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(Boolean)
|
|
898
|
+
}) ?? void 0;
|
|
899
|
+
return createTypeReferenceNode(ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enumType) && this.options.enumTypeSuffix ? this.options.resolver.resolveEnumKeyName(node, this.options.enumTypeSuffix) : this.options.resolver.default(node.name, "type"), void 0);
|
|
900
|
+
},
|
|
901
|
+
union(node) {
|
|
902
|
+
const members = node.members ?? [];
|
|
903
|
+
const hasStringLiteral = members.some((m) => {
|
|
904
|
+
return _kubb_core.ast.narrowSchema(m, _kubb_core.ast.schemaTypes.enum)?.primitive === "string";
|
|
905
|
+
});
|
|
906
|
+
const hasPlainString = members.some((m) => _kubb_core.ast.isStringType(m));
|
|
907
|
+
if (hasStringLiteral && hasPlainString) return createUnionDeclaration({
|
|
908
|
+
withParentheses: true,
|
|
909
|
+
nodes: members.map((m) => {
|
|
910
|
+
if (_kubb_core.ast.isStringType(m)) return createIntersectionDeclaration({
|
|
911
|
+
nodes: [keywordTypeNodes.string, createTypeLiteralNode([])],
|
|
912
|
+
withParentheses: true
|
|
913
|
+
});
|
|
914
|
+
return this.transform(m);
|
|
915
|
+
}).filter(Boolean)
|
|
916
|
+
}) ?? void 0;
|
|
917
|
+
return createUnionDeclaration({
|
|
918
|
+
withParentheses: true,
|
|
919
|
+
nodes: buildMemberNodes(members, this.transform)
|
|
920
|
+
}) ?? void 0;
|
|
921
|
+
},
|
|
922
|
+
intersection(node) {
|
|
923
|
+
return createIntersectionDeclaration({
|
|
924
|
+
withParentheses: true,
|
|
925
|
+
nodes: buildMemberNodes(node.members, this.transform)
|
|
926
|
+
}) ?? void 0;
|
|
927
|
+
},
|
|
928
|
+
array(node) {
|
|
929
|
+
return createArrayDeclaration({
|
|
930
|
+
nodes: (node.items ?? []).map((item) => this.transform(item)).filter(Boolean),
|
|
931
|
+
arrayType: this.options.arrayType
|
|
932
|
+
}) ?? void 0;
|
|
933
|
+
},
|
|
934
|
+
tuple(node) {
|
|
935
|
+
return buildTupleNode(node, this.transform);
|
|
936
|
+
},
|
|
937
|
+
object(node) {
|
|
938
|
+
const { transform, options } = this;
|
|
939
|
+
const addsQuestionToken = OPTIONAL_ADDS_QUESTION_TOKEN.has(options.optionalType);
|
|
940
|
+
const propertyNodes = node.properties.map((prop) => {
|
|
941
|
+
const baseType = transform(prop.schema) ?? keywordTypeNodes.unknown;
|
|
942
|
+
const type = buildPropertyType(prop.schema, baseType, options.optionalType);
|
|
943
|
+
const propMeta = _kubb_core.ast.syncSchemaRef(prop.schema);
|
|
944
|
+
return appendJSDocToNode({
|
|
945
|
+
node: createPropertySignature({
|
|
946
|
+
questionToken: prop.schema.optional || prop.schema.nullish ? addsQuestionToken : false,
|
|
947
|
+
name: prop.name,
|
|
948
|
+
type,
|
|
949
|
+
readOnly: propMeta?.readOnly
|
|
950
|
+
}),
|
|
951
|
+
comments: buildPropertyJSDocComments(prop.schema)
|
|
952
|
+
});
|
|
953
|
+
});
|
|
954
|
+
const allElements = [...propertyNodes, ...buildIndexSignatures(node, propertyNodes.length, transform)];
|
|
955
|
+
if (!allElements.length) return keywordTypeNodes.object;
|
|
956
|
+
return createTypeLiteralNode(allElements);
|
|
957
|
+
},
|
|
958
|
+
...options.nodes
|
|
959
|
+
},
|
|
960
|
+
print(node) {
|
|
961
|
+
const { name, syntaxType = "type", description, keysToOmit } = this.options;
|
|
962
|
+
let base = this.transform(node);
|
|
963
|
+
if (!base) return null;
|
|
964
|
+
const meta = _kubb_core.ast.syncSchemaRef(node);
|
|
965
|
+
if (!name) {
|
|
966
|
+
if (meta.nullable) base = createUnionDeclaration({ nodes: [base, keywordTypeNodes.null] });
|
|
967
|
+
if ((meta.nullish || meta.optional) && addsUndefined) base = createUnionDeclaration({ nodes: [base, keywordTypeNodes.undefined] });
|
|
968
|
+
return (0, _kubb_parser_ts.safePrint)(base);
|
|
969
|
+
}
|
|
970
|
+
let inner = keysToOmit?.length ? createOmitDeclaration({
|
|
971
|
+
keys: keysToOmit,
|
|
972
|
+
type: base,
|
|
973
|
+
nonNullable: true
|
|
974
|
+
}) : base;
|
|
975
|
+
if (meta.nullable) inner = createUnionDeclaration({ nodes: [inner, keywordTypeNodes.null] });
|
|
976
|
+
if (meta.nullish || meta.optional) inner = createUnionDeclaration({ nodes: [inner, keywordTypeNodes.undefined] });
|
|
977
|
+
const useTypeGeneration = syntaxType === "type" || inner.kind === syntaxKind.union || !!keysToOmit?.length;
|
|
978
|
+
return (0, _kubb_parser_ts.safePrint)(createTypeDeclaration({
|
|
979
|
+
name,
|
|
980
|
+
isExportable: true,
|
|
981
|
+
type: inner,
|
|
982
|
+
syntax: useTypeGeneration ? "type" : "interface",
|
|
983
|
+
comments: buildPropertyJSDocComments({
|
|
984
|
+
...meta,
|
|
985
|
+
description
|
|
986
|
+
})
|
|
987
|
+
}));
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
});
|
|
991
|
+
//#endregion
|
|
992
|
+
//#region src/generators/typeGenerator.tsx
|
|
993
|
+
const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
994
|
+
name: "typescript",
|
|
995
|
+
renderer: _kubb_renderer_jsx.jsxRenderer,
|
|
996
|
+
schema(node, ctx) {
|
|
997
|
+
const { enumType, enumTypeSuffix, enumKeyCasing, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options;
|
|
998
|
+
const { adapter, config, resolver, root } = ctx;
|
|
999
|
+
if (!node.name) return;
|
|
1000
|
+
const mode = ctx.getMode(output);
|
|
1001
|
+
const enumSchemaNames = new Set((adapter.inputNode?.schemas ?? []).filter((s) => _kubb_core.ast.narrowSchema(s, _kubb_core.ast.schemaTypes.enum) && s.name).map((s) => s.name));
|
|
1002
|
+
function resolveImportName(schemaName) {
|
|
1003
|
+
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix);
|
|
1004
|
+
return resolver.resolveTypeName(schemaName);
|
|
1005
|
+
}
|
|
1006
|
+
const imports = adapter.getImports(node, (schemaName) => ({
|
|
1007
|
+
name: resolveImportName(schemaName),
|
|
1008
|
+
path: resolver.resolveFile({
|
|
1009
|
+
name: schemaName,
|
|
1010
|
+
extname: ".ts"
|
|
1011
|
+
}, {
|
|
1012
|
+
root,
|
|
1013
|
+
output,
|
|
1014
|
+
group
|
|
1015
|
+
}).path
|
|
1016
|
+
}));
|
|
1017
|
+
const isEnumSchema = !!_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum);
|
|
1018
|
+
const meta = {
|
|
1019
|
+
name: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && isEnumSchema ? resolver.resolveEnumKeyName(node, enumTypeSuffix) : resolver.resolveTypeName(node.name),
|
|
1020
|
+
file: resolver.resolveFile({
|
|
1021
|
+
name: node.name,
|
|
1022
|
+
extname: ".ts"
|
|
1023
|
+
}, {
|
|
1024
|
+
root,
|
|
1025
|
+
output,
|
|
1026
|
+
group
|
|
1027
|
+
})
|
|
1028
|
+
};
|
|
1029
|
+
const schemaPrinter = printerTs({
|
|
1030
|
+
optionalType,
|
|
1031
|
+
arrayType,
|
|
1032
|
+
enumType,
|
|
1033
|
+
enumTypeSuffix,
|
|
1034
|
+
name: meta.name,
|
|
1035
|
+
syntaxType,
|
|
1036
|
+
description: node.description,
|
|
1037
|
+
resolver,
|
|
1038
|
+
enumSchemaNames,
|
|
1039
|
+
nodes: printer?.nodes
|
|
1040
|
+
});
|
|
1041
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
|
|
1042
|
+
baseName: meta.file.baseName,
|
|
1043
|
+
path: meta.file.path,
|
|
1044
|
+
meta: meta.file.meta,
|
|
1045
|
+
banner: resolver.resolveBanner(adapter.inputNode, {
|
|
1046
|
+
output,
|
|
1047
|
+
config
|
|
1048
|
+
}),
|
|
1049
|
+
footer: resolver.resolveFooter(adapter.inputNode, {
|
|
1050
|
+
output,
|
|
1051
|
+
config
|
|
1052
|
+
}),
|
|
1053
|
+
children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
1054
|
+
root: meta.file.path,
|
|
1055
|
+
path: imp.path,
|
|
1056
|
+
name: imp.name,
|
|
1057
|
+
isTypeOnly: true
|
|
1058
|
+
}, [
|
|
1059
|
+
node.name,
|
|
1060
|
+
imp.path,
|
|
1061
|
+
imp.isTypeOnly
|
|
1062
|
+
].join("-"))), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Type, {
|
|
1063
|
+
name: meta.name,
|
|
1064
|
+
node,
|
|
1065
|
+
enumType,
|
|
1066
|
+
enumTypeSuffix,
|
|
1067
|
+
enumKeyCasing,
|
|
1068
|
+
resolver,
|
|
1069
|
+
printer: schemaPrinter
|
|
1070
|
+
})]
|
|
1071
|
+
});
|
|
1072
|
+
},
|
|
1073
|
+
operation(node, ctx) {
|
|
1074
|
+
const { enumType, enumTypeSuffix, enumKeyCasing, optionalType, arrayType, syntaxType, paramsCasing, group, output, printer } = ctx.options;
|
|
1075
|
+
const { adapter, config, resolver, root } = ctx;
|
|
1076
|
+
const mode = ctx.getMode(output);
|
|
1077
|
+
const params = _kubb_core.ast.caseParams(node.parameters, paramsCasing);
|
|
1078
|
+
const meta = { file: resolver.resolveFile({
|
|
1079
|
+
name: node.operationId,
|
|
1080
|
+
extname: ".ts",
|
|
1081
|
+
tag: node.tags[0] ?? "default",
|
|
1082
|
+
path: node.path
|
|
1083
|
+
}, {
|
|
1084
|
+
root,
|
|
1085
|
+
output,
|
|
1086
|
+
group
|
|
1087
|
+
}) };
|
|
1088
|
+
const enumSchemaNames = new Set((adapter.inputNode?.schemas ?? []).filter((s) => _kubb_core.ast.narrowSchema(s, _kubb_core.ast.schemaTypes.enum) && s.name).map((s) => s.name));
|
|
1089
|
+
function resolveImportName(schemaName) {
|
|
1090
|
+
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix);
|
|
1091
|
+
return resolver.resolveTypeName(schemaName);
|
|
1092
|
+
}
|
|
1093
|
+
function renderSchemaType({ schema, name, keysToOmit }) {
|
|
1094
|
+
if (!schema) return null;
|
|
1095
|
+
const imports = adapter.getImports(schema, (schemaName) => ({
|
|
1096
|
+
name: resolveImportName(schemaName),
|
|
1097
|
+
path: resolver.resolveFile({
|
|
1098
|
+
name: schemaName,
|
|
1099
|
+
extname: ".ts"
|
|
1100
|
+
}, {
|
|
1101
|
+
root,
|
|
1102
|
+
output,
|
|
1103
|
+
group
|
|
1104
|
+
}).path
|
|
1105
|
+
}));
|
|
1106
|
+
const schemaPrinter = printerTs({
|
|
1107
|
+
optionalType,
|
|
1108
|
+
arrayType,
|
|
1109
|
+
enumType,
|
|
1110
|
+
enumTypeSuffix,
|
|
1111
|
+
name,
|
|
1112
|
+
syntaxType,
|
|
1113
|
+
description: schema.description,
|
|
1114
|
+
keysToOmit,
|
|
1115
|
+
resolver,
|
|
1116
|
+
enumSchemaNames,
|
|
1117
|
+
nodes: printer?.nodes
|
|
1118
|
+
});
|
|
1119
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
1120
|
+
root: meta.file.path,
|
|
1121
|
+
path: imp.path,
|
|
1122
|
+
name: imp.name,
|
|
1123
|
+
isTypeOnly: true
|
|
1124
|
+
}, [
|
|
1125
|
+
name,
|
|
1126
|
+
imp.path,
|
|
1127
|
+
imp.isTypeOnly
|
|
1128
|
+
].join("-"))), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Type, {
|
|
1129
|
+
name,
|
|
1130
|
+
node: schema,
|
|
1131
|
+
enumType,
|
|
1132
|
+
enumTypeSuffix,
|
|
1133
|
+
enumKeyCasing,
|
|
1134
|
+
resolver,
|
|
1135
|
+
printer: schemaPrinter
|
|
1136
|
+
})] });
|
|
1137
|
+
}
|
|
1138
|
+
const paramTypes = params.map((param) => renderSchemaType({
|
|
1139
|
+
schema: param.schema,
|
|
1140
|
+
name: resolver.resolveParamName(node, param)
|
|
1141
|
+
}));
|
|
1142
|
+
const requestType = node.requestBody?.content?.[0]?.schema ? renderSchemaType({
|
|
1143
|
+
schema: {
|
|
1144
|
+
...node.requestBody.content[0].schema,
|
|
1145
|
+
description: node.requestBody.description ?? node.requestBody.content[0].schema.description
|
|
1146
|
+
},
|
|
1147
|
+
name: resolver.resolveDataName(node),
|
|
1148
|
+
keysToOmit: node.requestBody.content[0].keysToOmit
|
|
1149
|
+
}) : null;
|
|
1150
|
+
const responseTypes = node.responses.map((res) => renderSchemaType({
|
|
1151
|
+
schema: res.schema,
|
|
1152
|
+
name: resolver.resolveResponseStatusName(node, res.statusCode),
|
|
1153
|
+
keysToOmit: res.keysToOmit
|
|
1154
|
+
}));
|
|
1155
|
+
const dataType = renderSchemaType({
|
|
1156
|
+
schema: buildData({
|
|
1157
|
+
...node,
|
|
1158
|
+
parameters: params
|
|
1159
|
+
}, { resolver }),
|
|
1160
|
+
name: resolver.resolveRequestConfigName(node)
|
|
1161
|
+
});
|
|
1162
|
+
const responsesType = renderSchemaType({
|
|
1163
|
+
schema: buildResponses(node, { resolver }),
|
|
1164
|
+
name: resolver.resolveResponsesName(node)
|
|
1165
|
+
});
|
|
1166
|
+
const responseType = (() => {
|
|
1167
|
+
if (!node.responses.some((res) => res.schema)) return null;
|
|
1168
|
+
const responseName = resolver.resolveResponseName(node);
|
|
1169
|
+
const responsesWithSchema = node.responses.filter((res) => res.schema);
|
|
1170
|
+
if (new Set(responsesWithSchema.flatMap((res) => res.schema ? adapter.getImports(res.schema, (schemaName) => ({
|
|
1171
|
+
name: resolveImportName(schemaName),
|
|
1172
|
+
path: ""
|
|
1173
|
+
})).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : [])).has(responseName)) return null;
|
|
1174
|
+
return renderSchemaType({
|
|
1175
|
+
schema: {
|
|
1176
|
+
...buildResponseUnion(node, { resolver }),
|
|
1177
|
+
description: "Union of all possible responses"
|
|
1178
|
+
},
|
|
1179
|
+
name: responseName
|
|
1180
|
+
});
|
|
1181
|
+
})();
|
|
1182
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
|
|
1183
|
+
baseName: meta.file.baseName,
|
|
1184
|
+
path: meta.file.path,
|
|
1185
|
+
meta: meta.file.meta,
|
|
1186
|
+
banner: resolver.resolveBanner(adapter.inputNode, {
|
|
1187
|
+
output,
|
|
1188
|
+
config
|
|
1189
|
+
}),
|
|
1190
|
+
footer: resolver.resolveFooter(adapter.inputNode, {
|
|
1191
|
+
output,
|
|
1192
|
+
config
|
|
1193
|
+
}),
|
|
1194
|
+
children: [
|
|
1195
|
+
paramTypes,
|
|
1196
|
+
responseTypes,
|
|
1197
|
+
requestType,
|
|
1198
|
+
dataType,
|
|
1199
|
+
responsesType,
|
|
1200
|
+
responseType
|
|
1201
|
+
]
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
//#endregion
|
|
1206
|
+
//#region src/resolvers/resolverTs.ts
|
|
1207
|
+
/**
|
|
1208
|
+
* Resolver for `@kubb/plugin-ts` that provides the default naming and path-resolution
|
|
1209
|
+
* helpers used by the plugin. Import this in other plugins to resolve the exact names and
|
|
1210
|
+
* paths that `plugin-ts` generates without hardcoding the conventions.
|
|
1211
|
+
*
|
|
1212
|
+
* The `default` method is automatically injected by `defineResolver` — it uses `camelCase`
|
|
1213
|
+
* for identifiers/files and `pascalCase` for type names.
|
|
1214
|
+
*
|
|
1215
|
+
* @example
|
|
1216
|
+
* ```ts
|
|
1217
|
+
* import { resolver } from '@kubb/plugin-ts'
|
|
1218
|
+
*
|
|
1219
|
+
* resolver.default('list pets', 'type') // → 'ListPets'
|
|
1220
|
+
* resolver.resolveName('list pets status 200') // → 'ListPetsStatus200'
|
|
1221
|
+
* resolver.resolvePathName('list pets', 'file') // → 'listPets'
|
|
1222
|
+
* ```
|
|
1223
|
+
*/
|
|
1224
|
+
const resolverTs = (0, _kubb_core.defineResolver)((ctx) => {
|
|
1225
|
+
return {
|
|
1226
|
+
name: "default",
|
|
1227
|
+
pluginName: "plugin-ts",
|
|
1228
|
+
default(name, type) {
|
|
1229
|
+
return pascalCase(name, { isFile: type === "file" });
|
|
1230
|
+
},
|
|
1231
|
+
resolveTypeName(name) {
|
|
1232
|
+
return pascalCase(name);
|
|
1233
|
+
},
|
|
1234
|
+
resolvePathName(name, type) {
|
|
1235
|
+
return pascalCase(name, { isFile: type === "file" });
|
|
1236
|
+
},
|
|
1237
|
+
resolveParamName(node, param) {
|
|
1238
|
+
return ctx.resolveTypeName(`${node.operationId} ${param.in} ${param.name}`);
|
|
1239
|
+
},
|
|
1240
|
+
resolveResponseStatusName(node, statusCode) {
|
|
1241
|
+
return ctx.resolveTypeName(`${node.operationId} Status ${statusCode}`);
|
|
1242
|
+
},
|
|
1243
|
+
resolveDataName(node) {
|
|
1244
|
+
return ctx.resolveTypeName(`${node.operationId} Data`);
|
|
1245
|
+
},
|
|
1246
|
+
resolveRequestConfigName(node) {
|
|
1247
|
+
return ctx.resolveTypeName(`${node.operationId} RequestConfig`);
|
|
1248
|
+
},
|
|
1249
|
+
resolveResponsesName(node) {
|
|
1250
|
+
return ctx.resolveTypeName(`${node.operationId} Responses`);
|
|
1251
|
+
},
|
|
1252
|
+
resolveResponseName(node) {
|
|
1253
|
+
return ctx.resolveTypeName(`${node.operationId} Response`);
|
|
1254
|
+
},
|
|
1255
|
+
resolveEnumKeyName(node, enumTypeSuffix = "key") {
|
|
1256
|
+
return `${ctx.resolveTypeName(node.name ?? "")}${enumTypeSuffix}`;
|
|
1257
|
+
},
|
|
1258
|
+
resolvePathParamsName(node, param) {
|
|
1259
|
+
return ctx.resolveParamName(node, param);
|
|
1260
|
+
},
|
|
1261
|
+
resolveQueryParamsName(node, param) {
|
|
1262
|
+
return ctx.resolveParamName(node, param);
|
|
1263
|
+
},
|
|
1264
|
+
resolveHeaderParamsName(node, param) {
|
|
1265
|
+
return ctx.resolveParamName(node, param);
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
});
|
|
1269
|
+
//#endregion
|
|
1270
|
+
//#region src/plugin.ts
|
|
1271
|
+
/**
|
|
1272
|
+
* Canonical plugin name for `@kubb/plugin-ts`, used to identify the plugin in driver lookups and warnings.
|
|
1273
|
+
*/
|
|
1274
|
+
const pluginTsName = "plugin-ts";
|
|
1275
|
+
/**
|
|
1276
|
+
* The `@kubb/plugin-ts` plugin factory.
|
|
1277
|
+
*
|
|
1278
|
+
* Generates TypeScript type declarations from an OpenAPI/AST `RootNode`.
|
|
1279
|
+
* Walks schemas and operations, delegates rendering to the active generators,
|
|
1280
|
+
* and writes barrel files based on `output.barrelType`.
|
|
1281
|
+
*
|
|
1282
|
+
* @example
|
|
1283
|
+
* ```ts
|
|
1284
|
+
* import pluginTs from '@kubb/plugin-ts'
|
|
1285
|
+
*
|
|
1286
|
+
* export default defineConfig({
|
|
1287
|
+
* plugins: [pluginTs({ output: { path: 'types' }, enumType: 'asConst' })],
|
|
1288
|
+
* })
|
|
1289
|
+
* ```
|
|
1290
|
+
*/
|
|
1291
|
+
const pluginTs = (0, _kubb_core.definePlugin)((options) => {
|
|
1292
|
+
const { output = {
|
|
1293
|
+
path: "types",
|
|
1294
|
+
barrelType: "named"
|
|
1295
|
+
}, group, exclude = [], include, override = [], enumType = "asConst", enumTypeSuffix = "Key", enumKeyCasing = "none", optionalType = "questionToken", arrayType = "array", syntaxType = "type", paramsCasing, printer, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
|
|
1296
|
+
const groupConfig = group ? {
|
|
1297
|
+
...group,
|
|
1298
|
+
name: (ctx) => {
|
|
1299
|
+
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
1300
|
+
return `${camelCase(ctx.group)}Controller`;
|
|
1301
|
+
}
|
|
1302
|
+
} : void 0;
|
|
1303
|
+
return {
|
|
1304
|
+
name: pluginTsName,
|
|
1305
|
+
options,
|
|
1306
|
+
hooks: { "kubb:plugin:setup"(ctx) {
|
|
1307
|
+
ctx.setOptions({
|
|
1308
|
+
output,
|
|
1309
|
+
exclude,
|
|
1310
|
+
include,
|
|
1311
|
+
override,
|
|
1312
|
+
optionalType,
|
|
1313
|
+
group: groupConfig,
|
|
1314
|
+
arrayType,
|
|
1315
|
+
enumType,
|
|
1316
|
+
enumTypeSuffix,
|
|
1317
|
+
enumKeyCasing,
|
|
1318
|
+
syntaxType,
|
|
1319
|
+
paramsCasing,
|
|
1320
|
+
printer
|
|
1321
|
+
});
|
|
1322
|
+
ctx.setResolver(userResolver ? {
|
|
1323
|
+
...resolverTs,
|
|
1324
|
+
...userResolver
|
|
1325
|
+
} : resolverTs);
|
|
1326
|
+
if (userTransformer) ctx.setTransformer(userTransformer);
|
|
1327
|
+
ctx.addGenerator(typeGenerator);
|
|
1328
|
+
for (const gen of userGenerators) ctx.addGenerator(gen);
|
|
1329
|
+
} }
|
|
1330
|
+
};
|
|
1331
|
+
});
|
|
1332
|
+
//#endregion
|
|
1333
|
+
//#region src/printers/functionPrinter.ts
|
|
1334
|
+
const kindToHandlerKey = {
|
|
1335
|
+
FunctionParameter: "functionParameter",
|
|
1336
|
+
ParameterGroup: "parameterGroup",
|
|
1337
|
+
FunctionParameters: "functionParameters",
|
|
1338
|
+
ParamsType: "paramsType"
|
|
1339
|
+
};
|
|
1340
|
+
/**
|
|
1341
|
+
* Creates a function-parameter printer factory.
|
|
1342
|
+
*
|
|
1343
|
+
* Uses `createPrinterFactory` and dispatches handlers by `node.kind`
|
|
1344
|
+
* (for function nodes) rather than by `node.type` (for schema nodes).
|
|
1345
|
+
*/
|
|
1346
|
+
const defineFunctionPrinter = _kubb_core.ast.createPrinterFactory((node) => kindToHandlerKey[node.kind]);
|
|
1347
|
+
function rank(param) {
|
|
1348
|
+
if (param.kind === "ParameterGroup") {
|
|
1349
|
+
if (param.default) return PARAM_RANK.withDefault;
|
|
1350
|
+
return param.optional ?? param.properties.every((p) => p.optional || p.default !== void 0) ? PARAM_RANK.optional : PARAM_RANK.required;
|
|
1351
|
+
}
|
|
1352
|
+
if (param.rest) return PARAM_RANK.rest;
|
|
1353
|
+
if (param.default) return PARAM_RANK.withDefault;
|
|
1354
|
+
return param.optional ? PARAM_RANK.optional : PARAM_RANK.required;
|
|
1355
|
+
}
|
|
1356
|
+
function sortParams(params) {
|
|
1357
|
+
return [...params].sort((a, b) => rank(a) - rank(b));
|
|
1358
|
+
}
|
|
1359
|
+
function sortChildParams(params) {
|
|
1360
|
+
return [...params].sort((a, b) => rank(a) - rank(b));
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Default function-signature printer.
|
|
1364
|
+
* Covers the four standard output modes used across Kubb plugins.
|
|
1365
|
+
*
|
|
1366
|
+
* @example
|
|
1367
|
+
* ```ts
|
|
1368
|
+
* const printer = functionPrinter({ mode: 'declaration' })
|
|
1369
|
+
*
|
|
1370
|
+
* const sig = createFunctionParameters({
|
|
1371
|
+
* params: [
|
|
1372
|
+
* createFunctionParameter({ name: 'petId', type: 'string', optional: false }),
|
|
1373
|
+
* createFunctionParameter({ name: 'config', type: 'Config', optional: false, default: '{}' }),
|
|
1374
|
+
* ],
|
|
1375
|
+
* })
|
|
1376
|
+
*
|
|
1377
|
+
* printer.print(sig) // → "petId: string, config: Config = {}"
|
|
1378
|
+
* ```
|
|
1379
|
+
*/
|
|
1380
|
+
const functionPrinter = defineFunctionPrinter((options) => ({
|
|
1381
|
+
name: "functionParameters",
|
|
1382
|
+
options,
|
|
1383
|
+
nodes: {
|
|
1384
|
+
paramsType(node) {
|
|
1385
|
+
if (node.kind !== "ParamsType") return null;
|
|
1386
|
+
if (node.variant === "member") return `${node.base}['${node.key}']`;
|
|
1387
|
+
if (node.variant === "struct") return `{ ${node.properties.map((p) => {
|
|
1388
|
+
const typeStr = this.transform(p.type);
|
|
1389
|
+
const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(p.name) ? p.name : JSON.stringify(p.name);
|
|
1390
|
+
return p.optional ? `${key}?: ${typeStr}` : `${key}: ${typeStr}`;
|
|
1391
|
+
}).join("; ")} }`;
|
|
1392
|
+
if (node.variant === "reference") return node.name;
|
|
1393
|
+
return null;
|
|
1394
|
+
},
|
|
1395
|
+
functionParameter(node) {
|
|
1396
|
+
const { mode, transformName, transformType } = this.options;
|
|
1397
|
+
const name = transformName ? transformName(node.name) : node.name;
|
|
1398
|
+
const rawType = node.type ? this.transform(node.type) : void 0;
|
|
1399
|
+
const type = rawType != null && transformType ? transformType(rawType) : rawType;
|
|
1400
|
+
if (mode === "keys" || mode === "values") return node.rest ? `...${name}` : name;
|
|
1401
|
+
if (mode === "call") return node.rest ? `...${name}` : name;
|
|
1402
|
+
if (node.rest) return type ? `...${name}: ${type}` : `...${name}`;
|
|
1403
|
+
if (type) {
|
|
1404
|
+
if (node.optional) return `${name}?: ${type}`;
|
|
1405
|
+
return node.default ? `${name}: ${type} = ${node.default}` : `${name}: ${type}`;
|
|
1406
|
+
}
|
|
1407
|
+
return node.default ? `${name} = ${node.default}` : name;
|
|
1408
|
+
},
|
|
1409
|
+
parameterGroup(node) {
|
|
1410
|
+
const { mode, transformName, transformType } = this.options;
|
|
1411
|
+
const sorted = sortChildParams(node.properties);
|
|
1412
|
+
const isOptional = node.optional ?? sorted.every((p) => p.optional || p.default !== void 0);
|
|
1413
|
+
if (node.inline) return sorted.map((p) => this.transform(p)).filter(Boolean).join(", ");
|
|
1414
|
+
if (mode === "keys" || mode === "values") return `{ ${sorted.map((p) => p.name).join(", ")} }`;
|
|
1415
|
+
if (mode === "call") return `{ ${sorted.map((p) => p.name).join(", ")} }`;
|
|
1416
|
+
const names = sorted.map((p) => {
|
|
1417
|
+
return transformName ? transformName(p.name) : p.name;
|
|
1418
|
+
});
|
|
1419
|
+
const nameStr = names.length ? `{ ${names.join(", ")} }` : void 0;
|
|
1420
|
+
if (!nameStr) return null;
|
|
1421
|
+
let typeAnnotation = node.type ? this.transform(node.type) ?? void 0 : void 0;
|
|
1422
|
+
if (!typeAnnotation) {
|
|
1423
|
+
const typeParts = sorted.filter((p) => p.type).map((p) => {
|
|
1424
|
+
const rawT = p.type ? this.transform(p.type) : void 0;
|
|
1425
|
+
const t = rawT != null && transformType ? transformType(rawT) : rawT;
|
|
1426
|
+
return p.optional || p.default !== void 0 ? `${p.name}?: ${t}` : `${p.name}: ${t}`;
|
|
1427
|
+
});
|
|
1428
|
+
typeAnnotation = typeParts.length ? `{ ${typeParts.join("; ")} }` : void 0;
|
|
1429
|
+
}
|
|
1430
|
+
if (typeAnnotation) {
|
|
1431
|
+
if (isOptional) return `${nameStr}: ${typeAnnotation} = ${node.default ?? "{}"}`;
|
|
1432
|
+
return node.default ? `${nameStr}: ${typeAnnotation} = ${node.default}` : `${nameStr}: ${typeAnnotation}`;
|
|
1433
|
+
}
|
|
1434
|
+
return node.default ? `${nameStr} = ${node.default}` : nameStr;
|
|
1435
|
+
},
|
|
1436
|
+
functionParameters(node) {
|
|
1437
|
+
return sortParams(node.params).map((p) => this.transform(p)).filter(Boolean).join(", ");
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}));
|
|
1441
|
+
//#endregion
|
|
1442
|
+
exports.Enum = Enum;
|
|
1443
|
+
exports.Type = Type;
|
|
1444
|
+
exports.default = pluginTs;
|
|
1445
|
+
exports.functionPrinter = functionPrinter;
|
|
1446
|
+
exports.pluginTs = pluginTs;
|
|
1447
|
+
exports.pluginTsName = pluginTsName;
|
|
1448
|
+
exports.printerTs = printerTs;
|
|
1449
|
+
exports.resolverTs = resolverTs;
|
|
1450
|
+
exports.typeGenerator = typeGenerator;
|
|
1451
|
+
|
|
1452
|
+
//# sourceMappingURL=index.cjs.map
|