@kubb/plugin-ts 5.0.0-alpha.9 → 5.0.0-beta.4

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