@kubb/plugin-ts 5.0.0-beta.3 → 5.0.0-beta.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -5
- package/dist/index.cjs +343 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +81 -58
- package/dist/index.js +342 -110
- package/dist/index.js.map +1 -1
- package/extension.yaml +1078 -0
- package/package.json +11 -13
- package/src/components/Enum.tsx +3 -3
- package/src/factory.ts +23 -20
- package/src/generators/typeGenerator.tsx +101 -29
- package/src/plugin.ts +18 -9
- package/src/printers/printerTs.ts +31 -30
- package/src/resolvers/resolverTs.ts +28 -25
- package/src/types.ts +44 -37
- package/src/utils.ts +23 -13
package/dist/index.cjs
CHANGED
|
@@ -5,6 +5,10 @@ Object.defineProperties(exports, {
|
|
|
5
5
|
//#region \0rolldown/runtime.js
|
|
6
6
|
var __create = Object.create;
|
|
7
7
|
var __defProp = Object.defineProperty;
|
|
8
|
+
var __name = (target, value) => __defProp(target, "name", {
|
|
9
|
+
value,
|
|
10
|
+
configurable: true
|
|
11
|
+
});
|
|
8
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
13
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
14
|
var __getProtoOf = Object.getPrototypeOf;
|
|
@@ -167,6 +171,129 @@ function stringify(value) {
|
|
|
167
171
|
return JSON.stringify(trimQuotes(value.toString()));
|
|
168
172
|
}
|
|
169
173
|
//#endregion
|
|
174
|
+
//#region ../../internals/utils/src/reserved.ts
|
|
175
|
+
/**
|
|
176
|
+
* JavaScript and Java reserved words.
|
|
177
|
+
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
178
|
+
*/
|
|
179
|
+
const reservedWords = new Set([
|
|
180
|
+
"abstract",
|
|
181
|
+
"arguments",
|
|
182
|
+
"boolean",
|
|
183
|
+
"break",
|
|
184
|
+
"byte",
|
|
185
|
+
"case",
|
|
186
|
+
"catch",
|
|
187
|
+
"char",
|
|
188
|
+
"class",
|
|
189
|
+
"const",
|
|
190
|
+
"continue",
|
|
191
|
+
"debugger",
|
|
192
|
+
"default",
|
|
193
|
+
"delete",
|
|
194
|
+
"do",
|
|
195
|
+
"double",
|
|
196
|
+
"else",
|
|
197
|
+
"enum",
|
|
198
|
+
"eval",
|
|
199
|
+
"export",
|
|
200
|
+
"extends",
|
|
201
|
+
"false",
|
|
202
|
+
"final",
|
|
203
|
+
"finally",
|
|
204
|
+
"float",
|
|
205
|
+
"for",
|
|
206
|
+
"function",
|
|
207
|
+
"goto",
|
|
208
|
+
"if",
|
|
209
|
+
"implements",
|
|
210
|
+
"import",
|
|
211
|
+
"in",
|
|
212
|
+
"instanceof",
|
|
213
|
+
"int",
|
|
214
|
+
"interface",
|
|
215
|
+
"let",
|
|
216
|
+
"long",
|
|
217
|
+
"native",
|
|
218
|
+
"new",
|
|
219
|
+
"null",
|
|
220
|
+
"package",
|
|
221
|
+
"private",
|
|
222
|
+
"protected",
|
|
223
|
+
"public",
|
|
224
|
+
"return",
|
|
225
|
+
"short",
|
|
226
|
+
"static",
|
|
227
|
+
"super",
|
|
228
|
+
"switch",
|
|
229
|
+
"synchronized",
|
|
230
|
+
"this",
|
|
231
|
+
"throw",
|
|
232
|
+
"throws",
|
|
233
|
+
"transient",
|
|
234
|
+
"true",
|
|
235
|
+
"try",
|
|
236
|
+
"typeof",
|
|
237
|
+
"var",
|
|
238
|
+
"void",
|
|
239
|
+
"volatile",
|
|
240
|
+
"while",
|
|
241
|
+
"with",
|
|
242
|
+
"yield",
|
|
243
|
+
"Array",
|
|
244
|
+
"Date",
|
|
245
|
+
"hasOwnProperty",
|
|
246
|
+
"Infinity",
|
|
247
|
+
"isFinite",
|
|
248
|
+
"isNaN",
|
|
249
|
+
"isPrototypeOf",
|
|
250
|
+
"length",
|
|
251
|
+
"Math",
|
|
252
|
+
"name",
|
|
253
|
+
"NaN",
|
|
254
|
+
"Number",
|
|
255
|
+
"Object",
|
|
256
|
+
"prototype",
|
|
257
|
+
"String",
|
|
258
|
+
"toString",
|
|
259
|
+
"undefined",
|
|
260
|
+
"valueOf"
|
|
261
|
+
]);
|
|
262
|
+
/**
|
|
263
|
+
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```ts
|
|
267
|
+
* isValidVarName('status') // true
|
|
268
|
+
* isValidVarName('class') // false (reserved word)
|
|
269
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
270
|
+
* ```
|
|
271
|
+
*/
|
|
272
|
+
function isValidVarName(name) {
|
|
273
|
+
if (!name || reservedWords.has(name)) return false;
|
|
274
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Returns `name` when it's a syntactically valid JavaScript variable name,
|
|
278
|
+
* otherwise prefixes it with `_` so the result is a valid identifier.
|
|
279
|
+
*
|
|
280
|
+
* Useful for sanitizing OpenAPI schema names or operation IDs that start with
|
|
281
|
+
* a digit (e.g. `409`, `504AccountCancel`) before using them as exported
|
|
282
|
+
* variable, type, or function names.
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```ts
|
|
286
|
+
* ensureValidVarName('409') // '_409'
|
|
287
|
+
* ensureValidVarName('504AccountCancel') // '_504AccountCancel'
|
|
288
|
+
* ensureValidVarName('Pet') // 'Pet'
|
|
289
|
+
* ensureValidVarName('class') // '_class'
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
function ensureValidVarName(name) {
|
|
293
|
+
if (!name || isValidVarName(name)) return name;
|
|
294
|
+
return `_${name}`;
|
|
295
|
+
}
|
|
296
|
+
//#endregion
|
|
170
297
|
//#region src/constants.ts
|
|
171
298
|
/**
|
|
172
299
|
* `optionalType` values that cause a property's type to include `| undefined`.
|
|
@@ -229,6 +356,10 @@ const syntaxKind = {
|
|
|
229
356
|
literalType: SyntaxKind.LiteralType,
|
|
230
357
|
stringLiteral: SyntaxKind.StringLiteral
|
|
231
358
|
};
|
|
359
|
+
function isNonNullable$1(value) {
|
|
360
|
+
return value !== null && value !== void 0;
|
|
361
|
+
}
|
|
362
|
+
__name(isNonNullable$1, "isNonNullable");
|
|
232
363
|
function isValidIdentifier(str) {
|
|
233
364
|
if (!str.length || str.trim() !== str) return false;
|
|
234
365
|
const node = typescript.default.parseIsolatedEntityName(str, typescript.default.ScriptTarget.Latest);
|
|
@@ -298,7 +429,7 @@ function createUnionDeclaration({ nodes, withParentheses }) {
|
|
|
298
429
|
* Supports optional markers, readonly modifiers, and type annotations.
|
|
299
430
|
*/
|
|
300
431
|
function createPropertySignature({ readOnly, modifiers = [], name, questionToken, type }) {
|
|
301
|
-
return factory.createPropertySignature([...modifiers, readOnly ? factory.createToken(typescript.default.SyntaxKind.ReadonlyKeyword) : void 0].filter(
|
|
432
|
+
return factory.createPropertySignature([...modifiers, readOnly ? factory.createToken(typescript.default.SyntaxKind.ReadonlyKeyword) : void 0].filter((modifier) => modifier !== void 0), propertyName(name), createQuestionToken(questionToken), type);
|
|
302
433
|
}
|
|
303
434
|
/**
|
|
304
435
|
* Creates a function parameter declaration with optional markers, rest parameters, and type annotations.
|
|
@@ -396,8 +527,8 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
|
|
|
396
527
|
}
|
|
397
528
|
if (typeof value === "boolean") return factory.createLiteralTypeNode(value ? factory.createTrue() : factory.createFalse());
|
|
398
529
|
if (value) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
|
|
399
|
-
}).filter(
|
|
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(
|
|
530
|
+
}).filter((node) => node !== void 0)))];
|
|
531
|
+
if (type === "enum" || type === "constEnum") return [void 0, factory.createEnumDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword), type === "constEnum" ? factory.createToken(typescript.default.SyntaxKind.ConstKeyword) : void 0].filter((modifier) => modifier !== void 0), factory.createIdentifier(typeName), enums.map(([key, value]) => {
|
|
401
532
|
let initializer = factory.createStringLiteral(value?.toString());
|
|
402
533
|
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
534
|
else initializer = factory.createNumericLiteral(value);
|
|
@@ -410,7 +541,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
|
|
|
410
541
|
const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
|
|
411
542
|
return factory.createEnumMember(propertyName(casingKey), initializer);
|
|
412
543
|
}
|
|
413
|
-
}).filter(
|
|
544
|
+
}).filter((member) => member !== void 0))];
|
|
414
545
|
const identifierName = name;
|
|
415
546
|
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
547
|
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]) => {
|
|
@@ -422,7 +553,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
|
|
|
422
553
|
const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
|
|
423
554
|
return factory.createPropertyAssignment(propertyName(casingKey), initializer);
|
|
424
555
|
}
|
|
425
|
-
}).filter(
|
|
556
|
+
}).filter((property) => property !== void 0), true), factory.createTypeReferenceNode(factory.createIdentifier("const"), void 0)))], typescript.default.NodeFlags.Const)), factory.createTypeAliasDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createIndexedAccessTypeNode(factory.createParenthesizedType(factory.createTypeQueryNode(factory.createIdentifier(identifierName), void 0)), factory.createTypeOperatorNode(typescript.default.SyntaxKind.KeyOfKeyword, factory.createTypeQueryNode(factory.createIdentifier(identifierName), void 0))))];
|
|
426
557
|
}
|
|
427
558
|
/**
|
|
428
559
|
* Creates a TypeScript `Omit<T, Keys>` type reference node.
|
|
@@ -562,14 +693,14 @@ function dateOrStringNode(node) {
|
|
|
562
693
|
* Maps an array of `SchemaNode`s through the printer, filtering out `null` and `undefined` results.
|
|
563
694
|
*/
|
|
564
695
|
function buildMemberNodes(members, print) {
|
|
565
|
-
return (members ?? []).map(print).filter(
|
|
696
|
+
return (members ?? []).map(print).filter(isNonNullable$1);
|
|
566
697
|
}
|
|
567
698
|
/**
|
|
568
699
|
* Builds a TypeScript tuple type node from an array schema's `items`,
|
|
569
700
|
* applying min/max slice and optional/rest element rules.
|
|
570
701
|
*/
|
|
571
702
|
function buildTupleNode(node, print) {
|
|
572
|
-
let items = (node.items ?? []).map(print).filter(
|
|
703
|
+
let items = (node.items ?? []).map(print).filter(isNonNullable$1);
|
|
573
704
|
const restNode = node.rest ? print(node.rest) ?? void 0 : void 0;
|
|
574
705
|
const { min, max } = node;
|
|
575
706
|
if (max !== void 0) {
|
|
@@ -656,13 +787,13 @@ function Enum({ node, enumType, enumTypeSuffix, enumKeyCasing, resolver }) {
|
|
|
656
787
|
isExportable: true,
|
|
657
788
|
isIndexable: true,
|
|
658
789
|
isTypeOnly: false,
|
|
659
|
-
children:
|
|
790
|
+
children: _kubb_parser_ts.parserTs.print(nameNode)
|
|
660
791
|
}), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
|
|
661
792
|
name: typeName,
|
|
662
793
|
isIndexable: true,
|
|
663
794
|
isExportable: ENUM_TYPES_WITH_RUNTIME_VALUE.has(enumType),
|
|
664
795
|
isTypeOnly: ENUM_TYPES_WITH_TYPE_ONLY.has(enumType),
|
|
665
|
-
children:
|
|
796
|
+
children: _kubb_parser_ts.parserTs.print(typeNode)
|
|
666
797
|
})] });
|
|
667
798
|
}
|
|
668
799
|
//#endregion
|
|
@@ -702,6 +833,17 @@ function Type({ name, node, printer, enumType, enumTypeSuffix, enumKeyCasing, re
|
|
|
702
833
|
})] });
|
|
703
834
|
}
|
|
704
835
|
//#endregion
|
|
836
|
+
//#region ../../internals/shared/src/operation.ts
|
|
837
|
+
function getOperationParameters(node, options = {}) {
|
|
838
|
+
const params = _kubb_core.ast.caseParams(node.parameters, options.paramsCasing);
|
|
839
|
+
return {
|
|
840
|
+
path: params.filter((param) => param.in === "path"),
|
|
841
|
+
query: params.filter((param) => param.in === "query"),
|
|
842
|
+
header: params.filter((param) => param.in === "header"),
|
|
843
|
+
cookie: params.filter((param) => param.in === "cookie")
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
//#endregion
|
|
705
847
|
//#region src/utils.ts
|
|
706
848
|
/**
|
|
707
849
|
* Collects JSDoc annotation strings for a schema node.
|
|
@@ -713,15 +855,18 @@ function Type({ name, node, printer, enumType, enumTypeSuffix, enumKeyCasing, re
|
|
|
713
855
|
function buildPropertyJSDocComments(schema) {
|
|
714
856
|
const meta = _kubb_core.ast.syncSchemaRef(schema);
|
|
715
857
|
const isArray = meta?.primitive === "array";
|
|
858
|
+
const hasDescription = meta && "description" in meta && meta.description;
|
|
859
|
+
const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
|
|
716
860
|
return [
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
!isArray && meta && "
|
|
721
|
-
meta && "
|
|
722
|
-
meta && "
|
|
723
|
-
meta && "
|
|
724
|
-
meta && "
|
|
861
|
+
hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
|
|
862
|
+
...formatComment,
|
|
863
|
+
meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : null,
|
|
864
|
+
!isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : null,
|
|
865
|
+
!isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
|
|
866
|
+
meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
|
|
867
|
+
meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" ? stringify(meta.default) : meta.default}` : null,
|
|
868
|
+
meta && "example" in meta && meta.example !== void 0 ? `@example ${meta.example}` : null,
|
|
869
|
+
meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, "optional" in schema && schema.optional ? " | undefined" : null].filter(Boolean).join("") : null
|
|
725
870
|
].filter(Boolean);
|
|
726
871
|
}
|
|
727
872
|
function buildParams(node, { params, resolver }) {
|
|
@@ -738,9 +883,7 @@ function buildParams(node, { params, resolver }) {
|
|
|
738
883
|
});
|
|
739
884
|
}
|
|
740
885
|
function buildData(node, { resolver }) {
|
|
741
|
-
const pathParams
|
|
742
|
-
const queryParams = node.parameters.filter((p) => p.in === "query");
|
|
743
|
-
const headerParams = node.parameters.filter((p) => p.in === "header");
|
|
886
|
+
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
|
|
744
887
|
return _kubb_core.ast.createSchema({
|
|
745
888
|
type: "object",
|
|
746
889
|
deprecated: node.deprecated,
|
|
@@ -822,7 +965,7 @@ function buildResponses(node, { resolver }) {
|
|
|
822
965
|
});
|
|
823
966
|
}
|
|
824
967
|
function buildResponseUnion(node, { resolver }) {
|
|
825
|
-
const responsesWithSchema = node.responses.filter((res) => res.schema);
|
|
968
|
+
const responsesWithSchema = node.responses.filter((res) => res.content?.[0]?.schema);
|
|
826
969
|
if (responsesWithSchema.length === 0) return null;
|
|
827
970
|
return _kubb_core.ast.createSchema({
|
|
828
971
|
type: "union",
|
|
@@ -834,6 +977,9 @@ function buildResponseUnion(node, { resolver }) {
|
|
|
834
977
|
}
|
|
835
978
|
//#endregion
|
|
836
979
|
//#region src/printers/printerTs.ts
|
|
980
|
+
function isNonNullable(value) {
|
|
981
|
+
return value !== null && value !== void 0;
|
|
982
|
+
}
|
|
837
983
|
/**
|
|
838
984
|
* TypeScript type printer built with `definePrinter`.
|
|
839
985
|
*
|
|
@@ -886,7 +1032,7 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
|
|
|
886
1032
|
date: dateOrStringNode,
|
|
887
1033
|
time: dateOrStringNode,
|
|
888
1034
|
ref(node) {
|
|
889
|
-
if (!node.name) return;
|
|
1035
|
+
if (!node.name) return null;
|
|
890
1036
|
const refName = node.ref ? _kubb_core.ast.extractRefName(node.ref) ?? node.name : node.name;
|
|
891
1037
|
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
1038
|
},
|
|
@@ -894,7 +1040,7 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
|
|
|
894
1040
|
const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
|
|
895
1041
|
if (this.options.enumType === "inlineLiteral" || !node.name) return createUnionDeclaration({
|
|
896
1042
|
withParentheses: true,
|
|
897
|
-
nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(
|
|
1043
|
+
nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(isNonNullable)
|
|
898
1044
|
}) ?? void 0;
|
|
899
1045
|
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
1046
|
},
|
|
@@ -912,7 +1058,7 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
|
|
|
912
1058
|
withParentheses: true
|
|
913
1059
|
});
|
|
914
1060
|
return this.transform(m);
|
|
915
|
-
}).filter(
|
|
1061
|
+
}).filter(isNonNullable)
|
|
916
1062
|
}) ?? void 0;
|
|
917
1063
|
return createUnionDeclaration({
|
|
918
1064
|
withParentheses: true,
|
|
@@ -923,16 +1069,16 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
|
|
|
923
1069
|
return createIntersectionDeclaration({
|
|
924
1070
|
withParentheses: true,
|
|
925
1071
|
nodes: buildMemberNodes(node.members, this.transform)
|
|
926
|
-
}) ??
|
|
1072
|
+
}) ?? null;
|
|
927
1073
|
},
|
|
928
1074
|
array(node) {
|
|
929
1075
|
return createArrayDeclaration({
|
|
930
|
-
nodes: (node.items ?? []).map((item) => this.transform(item)).filter(
|
|
1076
|
+
nodes: (node.items ?? []).map((item) => this.transform(item)).filter(isNonNullable),
|
|
931
1077
|
arrayType: this.options.arrayType
|
|
932
|
-
}) ??
|
|
1078
|
+
}) ?? null;
|
|
933
1079
|
},
|
|
934
1080
|
tuple(node) {
|
|
935
|
-
return buildTupleNode(node, this.transform);
|
|
1081
|
+
return buildTupleNode(node, this.transform) ?? null;
|
|
936
1082
|
},
|
|
937
1083
|
object(node) {
|
|
938
1084
|
const { transform, options } = this;
|
|
@@ -959,46 +1105,67 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
|
|
|
959
1105
|
},
|
|
960
1106
|
print(node) {
|
|
961
1107
|
const { name, syntaxType = "type", description, keysToOmit } = this.options;
|
|
962
|
-
|
|
963
|
-
if (!
|
|
1108
|
+
const transformed = this.transform(node);
|
|
1109
|
+
if (!transformed) return null;
|
|
964
1110
|
const meta = _kubb_core.ast.syncSchemaRef(node);
|
|
965
1111
|
if (!name) {
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
return
|
|
1112
|
+
const withNullable = meta.nullable ? createUnionDeclaration({ nodes: [transformed, keywordTypeNodes.null] }) : transformed;
|
|
1113
|
+
const result = (meta.nullish || meta.optional) && addsUndefined ? createUnionDeclaration({ nodes: [withNullable, keywordTypeNodes.undefined] }) : withNullable;
|
|
1114
|
+
return _kubb_parser_ts.parserTs.print(result);
|
|
969
1115
|
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1116
|
+
const inner = (() => {
|
|
1117
|
+
const omitted = keysToOmit?.length ? createOmitDeclaration({
|
|
1118
|
+
keys: keysToOmit,
|
|
1119
|
+
type: transformed,
|
|
1120
|
+
nonNullable: true
|
|
1121
|
+
}) : transformed;
|
|
1122
|
+
const withNullable = meta.nullable ? createUnionDeclaration({ nodes: [omitted, keywordTypeNodes.null] }) : omitted;
|
|
1123
|
+
return meta.nullish || meta.optional ? createUnionDeclaration({ nodes: [withNullable, keywordTypeNodes.undefined] }) : withNullable;
|
|
1124
|
+
})();
|
|
1125
|
+
const typeNode = createTypeDeclaration({
|
|
979
1126
|
name,
|
|
980
1127
|
isExportable: true,
|
|
981
1128
|
type: inner,
|
|
982
|
-
syntax:
|
|
1129
|
+
syntax: syntaxType === "type" || inner.kind === syntaxKind.union || !!keysToOmit?.length ? "type" : "interface",
|
|
983
1130
|
comments: buildPropertyJSDocComments({
|
|
984
1131
|
...meta,
|
|
985
1132
|
description
|
|
986
1133
|
})
|
|
987
|
-
})
|
|
1134
|
+
});
|
|
1135
|
+
return _kubb_parser_ts.parserTs.print(typeNode);
|
|
988
1136
|
}
|
|
989
1137
|
};
|
|
990
1138
|
});
|
|
991
1139
|
//#endregion
|
|
992
1140
|
//#region src/generators/typeGenerator.tsx
|
|
1141
|
+
function getContentTypeSuffix(contentType) {
|
|
1142
|
+
const baseType = contentType.split(";")[0].trim();
|
|
1143
|
+
if (baseType === "application/json") return "Json";
|
|
1144
|
+
if (baseType === "multipart/form-data") return "FormData";
|
|
1145
|
+
if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
|
|
1146
|
+
const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
1147
|
+
if (parts.length === 0) return "Unknown";
|
|
1148
|
+
return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
1149
|
+
}
|
|
1150
|
+
function getPerContentTypeName(dataName, suffix) {
|
|
1151
|
+
if (dataName.endsWith("Data")) return suffix.endsWith("Data") ? dataName.slice(0, -4) + suffix : `${dataName.slice(0, -4)}${suffix}Data`;
|
|
1152
|
+
return dataName + suffix;
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Built-in generator for `@kubb/plugin-ts`. Emits one TypeScript file per
|
|
1156
|
+
* schema in the spec plus per-operation request, response, and parameter
|
|
1157
|
+
* types. Drop-replace with a custom `Generator<PluginTs>` to change how
|
|
1158
|
+
* TypeScript output is produced.
|
|
1159
|
+
*/
|
|
993
1160
|
const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
994
1161
|
name: "typescript",
|
|
995
|
-
renderer: _kubb_renderer_jsx.
|
|
1162
|
+
renderer: _kubb_renderer_jsx.jsxRendererSync,
|
|
996
1163
|
schema(node, ctx) {
|
|
997
1164
|
const { enumType, enumTypeSuffix, enumKeyCasing, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options;
|
|
998
1165
|
const { adapter, config, resolver, root } = ctx;
|
|
999
1166
|
if (!node.name) return;
|
|
1000
1167
|
const mode = ctx.getMode(output);
|
|
1001
|
-
const enumSchemaNames = new Set(
|
|
1168
|
+
const enumSchemaNames = new Set(ctx.meta.enumNames);
|
|
1002
1169
|
function resolveImportName(schemaName) {
|
|
1003
1170
|
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix);
|
|
1004
1171
|
return resolver.resolveTypeName(schemaName);
|
|
@@ -1011,7 +1178,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1011
1178
|
}, {
|
|
1012
1179
|
root,
|
|
1013
1180
|
output,
|
|
1014
|
-
group
|
|
1181
|
+
group: group ?? void 0
|
|
1015
1182
|
}).path
|
|
1016
1183
|
}));
|
|
1017
1184
|
const isEnumSchema = !!_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum);
|
|
@@ -1023,7 +1190,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1023
1190
|
}, {
|
|
1024
1191
|
root,
|
|
1025
1192
|
output,
|
|
1026
|
-
group
|
|
1193
|
+
group: group ?? void 0
|
|
1027
1194
|
})
|
|
1028
1195
|
};
|
|
1029
1196
|
const schemaPrinter = printerTs({
|
|
@@ -1042,13 +1209,21 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1042
1209
|
baseName: meta.file.baseName,
|
|
1043
1210
|
path: meta.file.path,
|
|
1044
1211
|
meta: meta.file.meta,
|
|
1045
|
-
banner: resolver.resolveBanner(
|
|
1212
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
1046
1213
|
output,
|
|
1047
|
-
config
|
|
1214
|
+
config,
|
|
1215
|
+
file: {
|
|
1216
|
+
path: meta.file.path,
|
|
1217
|
+
baseName: meta.file.baseName
|
|
1218
|
+
}
|
|
1048
1219
|
}),
|
|
1049
|
-
footer: resolver.resolveFooter(
|
|
1220
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
1050
1221
|
output,
|
|
1051
|
-
config
|
|
1222
|
+
config,
|
|
1223
|
+
file: {
|
|
1224
|
+
path: meta.file.path,
|
|
1225
|
+
baseName: meta.file.baseName
|
|
1226
|
+
}
|
|
1052
1227
|
}),
|
|
1053
1228
|
children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
1054
1229
|
root: meta.file.path,
|
|
@@ -1083,9 +1258,9 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1083
1258
|
}, {
|
|
1084
1259
|
root,
|
|
1085
1260
|
output,
|
|
1086
|
-
group
|
|
1261
|
+
group: group ?? void 0
|
|
1087
1262
|
}) };
|
|
1088
|
-
const enumSchemaNames = new Set(
|
|
1263
|
+
const enumSchemaNames = new Set(ctx.meta.enumNames);
|
|
1089
1264
|
function resolveImportName(schemaName) {
|
|
1090
1265
|
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix);
|
|
1091
1266
|
return resolver.resolveTypeName(schemaName);
|
|
@@ -1100,7 +1275,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1100
1275
|
}, {
|
|
1101
1276
|
root,
|
|
1102
1277
|
output,
|
|
1103
|
-
group
|
|
1278
|
+
group: group ?? void 0
|
|
1104
1279
|
}).path
|
|
1105
1280
|
}));
|
|
1106
1281
|
const schemaPrinter = printerTs({
|
|
@@ -1139,18 +1314,58 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1139
1314
|
schema: param.schema,
|
|
1140
1315
|
name: resolver.resolveParamName(node, param)
|
|
1141
1316
|
}));
|
|
1142
|
-
const
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1317
|
+
const requestBodyContent = node.requestBody?.content ?? [];
|
|
1318
|
+
function buildRequestType() {
|
|
1319
|
+
if (requestBodyContent.length === 0) return null;
|
|
1320
|
+
if (requestBodyContent.length === 1) {
|
|
1321
|
+
const entry = requestBodyContent[0];
|
|
1322
|
+
if (!entry.schema) return null;
|
|
1323
|
+
return renderSchemaType({
|
|
1324
|
+
schema: {
|
|
1325
|
+
...entry.schema,
|
|
1326
|
+
description: node.requestBody.description ?? entry.schema.description
|
|
1327
|
+
},
|
|
1328
|
+
name: resolver.resolveDataName(node),
|
|
1329
|
+
keysToOmit: entry.keysToOmit
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
const dataName = resolver.resolveDataName(node);
|
|
1333
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
1334
|
+
const individualItems = requestBodyContent.filter((entry) => entry.schema).map((entry) => {
|
|
1335
|
+
const baseSuffix = getContentTypeSuffix(entry.contentType);
|
|
1336
|
+
let individualName = getPerContentTypeName(dataName, baseSuffix);
|
|
1337
|
+
let counter = 2;
|
|
1338
|
+
while (usedNames.has(individualName)) individualName = getPerContentTypeName(dataName, `${baseSuffix}${counter++}`);
|
|
1339
|
+
usedNames.add(individualName);
|
|
1340
|
+
return {
|
|
1341
|
+
name: individualName,
|
|
1342
|
+
rendered: renderSchemaType({
|
|
1343
|
+
schema: {
|
|
1344
|
+
...entry.schema,
|
|
1345
|
+
description: node.requestBody.description ?? entry.schema.description
|
|
1346
|
+
},
|
|
1347
|
+
name: individualName,
|
|
1348
|
+
keysToOmit: entry.keysToOmit
|
|
1349
|
+
})
|
|
1350
|
+
};
|
|
1351
|
+
});
|
|
1352
|
+
const unionType = renderSchemaType({
|
|
1353
|
+
schema: _kubb_core.ast.createSchema({
|
|
1354
|
+
type: "union",
|
|
1355
|
+
members: individualItems.map((item) => _kubb_core.ast.createSchema({
|
|
1356
|
+
type: "ref",
|
|
1357
|
+
name: item.name
|
|
1358
|
+
}))
|
|
1359
|
+
}),
|
|
1360
|
+
name: dataName
|
|
1361
|
+
});
|
|
1362
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [individualItems.map((item) => item.rendered), unionType] });
|
|
1363
|
+
}
|
|
1364
|
+
const requestType = buildRequestType();
|
|
1150
1365
|
const responseTypes = node.responses.map((res) => renderSchemaType({
|
|
1151
|
-
schema: res.schema,
|
|
1366
|
+
schema: res.content?.[0]?.schema ?? null,
|
|
1152
1367
|
name: resolver.resolveResponseStatusName(node, res.statusCode),
|
|
1153
|
-
keysToOmit: res.keysToOmit
|
|
1368
|
+
keysToOmit: res.content?.[0]?.keysToOmit
|
|
1154
1369
|
}));
|
|
1155
1370
|
const dataType = renderSchemaType({
|
|
1156
1371
|
schema: buildData({
|
|
@@ -1163,11 +1378,11 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1163
1378
|
schema: buildResponses(node, { resolver }),
|
|
1164
1379
|
name: resolver.resolveResponsesName(node)
|
|
1165
1380
|
});
|
|
1166
|
-
|
|
1167
|
-
if (!node.responses.some((res) => res.schema)) return null;
|
|
1381
|
+
function buildResponseType() {
|
|
1382
|
+
if (!node.responses.some((res) => res.content?.[0]?.schema)) return null;
|
|
1168
1383
|
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) => ({
|
|
1384
|
+
const responsesWithSchema = node.responses.filter((res) => res.content?.[0]?.schema);
|
|
1385
|
+
if (new Set(responsesWithSchema.flatMap((res) => res.content?.[0]?.schema ? adapter.getImports(res.content[0].schema, (schemaName) => ({
|
|
1171
1386
|
name: resolveImportName(schemaName),
|
|
1172
1387
|
path: ""
|
|
1173
1388
|
})).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : [])).has(responseName)) return null;
|
|
@@ -1178,18 +1393,27 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1178
1393
|
},
|
|
1179
1394
|
name: responseName
|
|
1180
1395
|
});
|
|
1181
|
-
}
|
|
1396
|
+
}
|
|
1397
|
+
const responseType = buildResponseType();
|
|
1182
1398
|
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
|
|
1183
1399
|
baseName: meta.file.baseName,
|
|
1184
1400
|
path: meta.file.path,
|
|
1185
1401
|
meta: meta.file.meta,
|
|
1186
|
-
banner: resolver.resolveBanner(
|
|
1402
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
1187
1403
|
output,
|
|
1188
|
-
config
|
|
1404
|
+
config,
|
|
1405
|
+
file: {
|
|
1406
|
+
path: meta.file.path,
|
|
1407
|
+
baseName: meta.file.baseName
|
|
1408
|
+
}
|
|
1189
1409
|
}),
|
|
1190
|
-
footer: resolver.resolveFooter(
|
|
1410
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
1191
1411
|
output,
|
|
1192
|
-
config
|
|
1412
|
+
config,
|
|
1413
|
+
file: {
|
|
1414
|
+
path: meta.file.path,
|
|
1415
|
+
baseName: meta.file.baseName
|
|
1416
|
+
}
|
|
1193
1417
|
}),
|
|
1194
1418
|
children: [
|
|
1195
1419
|
paramTypes,
|
|
@@ -1205,86 +1429,98 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1205
1429
|
//#endregion
|
|
1206
1430
|
//#region src/resolvers/resolverTs.ts
|
|
1207
1431
|
/**
|
|
1208
|
-
*
|
|
1209
|
-
*
|
|
1210
|
-
*
|
|
1432
|
+
* Default resolver used by `@kubb/plugin-ts`. Decides the names and file paths
|
|
1433
|
+
* for every generated TypeScript type. Import this in other plugins that need
|
|
1434
|
+
* to reference the exact names `plugin-ts` produces without duplicating the
|
|
1435
|
+
* casing/file-layout rules.
|
|
1211
1436
|
*
|
|
1212
|
-
* The `default` method is
|
|
1213
|
-
*
|
|
1437
|
+
* The `default` method is supplied by `defineResolver`. It uses PascalCase for
|
|
1438
|
+
* type names and PascalCase-with-isFile for files.
|
|
1214
1439
|
*
|
|
1215
|
-
* @example
|
|
1440
|
+
* @example Resolve a type and file name
|
|
1216
1441
|
* ```ts
|
|
1217
|
-
* import {
|
|
1442
|
+
* import { resolverTs } from '@kubb/plugin-ts'
|
|
1218
1443
|
*
|
|
1219
|
-
*
|
|
1220
|
-
*
|
|
1221
|
-
*
|
|
1444
|
+
* resolverTs.default('list pets', 'type') // 'ListPets'
|
|
1445
|
+
* resolverTs.resolvePathName('list pets', 'file') // 'ListPets'
|
|
1446
|
+
* resolverTs.resolveResponseStatusName(node, 200) // 'ListPetsStatus200'
|
|
1222
1447
|
* ```
|
|
1223
1448
|
*/
|
|
1224
|
-
const resolverTs = (0, _kubb_core.defineResolver)((
|
|
1449
|
+
const resolverTs = (0, _kubb_core.defineResolver)(() => {
|
|
1225
1450
|
return {
|
|
1226
1451
|
name: "default",
|
|
1227
1452
|
pluginName: "plugin-ts",
|
|
1228
1453
|
default(name, type) {
|
|
1229
|
-
|
|
1454
|
+
const resolved = pascalCase(name, { isFile: type === "file" });
|
|
1455
|
+
return type === "file" ? resolved : ensureValidVarName(resolved);
|
|
1230
1456
|
},
|
|
1231
1457
|
resolveTypeName(name) {
|
|
1232
|
-
return pascalCase(name);
|
|
1458
|
+
return ensureValidVarName(pascalCase(name));
|
|
1233
1459
|
},
|
|
1234
1460
|
resolvePathName(name, type) {
|
|
1235
|
-
|
|
1461
|
+
const resolved = pascalCase(name, { isFile: type === "file" });
|
|
1462
|
+
return type === "file" ? resolved : ensureValidVarName(resolved);
|
|
1236
1463
|
},
|
|
1237
1464
|
resolveParamName(node, param) {
|
|
1238
|
-
return
|
|
1465
|
+
return this.resolveTypeName(`${node.operationId} ${param.in} ${param.name}`);
|
|
1239
1466
|
},
|
|
1240
1467
|
resolveResponseStatusName(node, statusCode) {
|
|
1241
|
-
return
|
|
1468
|
+
return this.resolveTypeName(`${node.operationId} Status ${statusCode}`);
|
|
1242
1469
|
},
|
|
1243
1470
|
resolveDataName(node) {
|
|
1244
|
-
return
|
|
1471
|
+
return this.resolveTypeName(`${node.operationId} Data`);
|
|
1245
1472
|
},
|
|
1246
1473
|
resolveRequestConfigName(node) {
|
|
1247
|
-
return
|
|
1474
|
+
return this.resolveTypeName(`${node.operationId} RequestConfig`);
|
|
1248
1475
|
},
|
|
1249
1476
|
resolveResponsesName(node) {
|
|
1250
|
-
return
|
|
1477
|
+
return this.resolveTypeName(`${node.operationId} Responses`);
|
|
1251
1478
|
},
|
|
1252
1479
|
resolveResponseName(node) {
|
|
1253
|
-
return
|
|
1480
|
+
return this.resolveTypeName(`${node.operationId} Response`);
|
|
1254
1481
|
},
|
|
1255
1482
|
resolveEnumKeyName(node, enumTypeSuffix = "key") {
|
|
1256
|
-
return `${
|
|
1483
|
+
return `${this.resolveTypeName(node.name ?? "")}${enumTypeSuffix}`;
|
|
1257
1484
|
},
|
|
1258
1485
|
resolvePathParamsName(node, param) {
|
|
1259
|
-
return
|
|
1486
|
+
return this.resolveParamName(node, param);
|
|
1260
1487
|
},
|
|
1261
1488
|
resolveQueryParamsName(node, param) {
|
|
1262
|
-
return
|
|
1489
|
+
return this.resolveParamName(node, param);
|
|
1263
1490
|
},
|
|
1264
1491
|
resolveHeaderParamsName(node, param) {
|
|
1265
|
-
return
|
|
1492
|
+
return this.resolveParamName(node, param);
|
|
1266
1493
|
}
|
|
1267
1494
|
};
|
|
1268
1495
|
});
|
|
1269
1496
|
//#endregion
|
|
1270
1497
|
//#region src/plugin.ts
|
|
1271
1498
|
/**
|
|
1272
|
-
* Canonical plugin name for `@kubb/plugin-ts
|
|
1499
|
+
* Canonical plugin name for `@kubb/plugin-ts`. Used for driver lookups and
|
|
1500
|
+
* cross-plugin dependency references.
|
|
1273
1501
|
*/
|
|
1274
1502
|
const pluginTsName = "plugin-ts";
|
|
1275
1503
|
/**
|
|
1276
|
-
*
|
|
1277
|
-
*
|
|
1278
|
-
*
|
|
1279
|
-
*
|
|
1280
|
-
* and writes barrel files based on `output.barrelType`.
|
|
1504
|
+
* Generates TypeScript `type` aliases and `interface` declarations from an
|
|
1505
|
+
* OpenAPI spec. The foundation that every other Kubb plugin builds on:
|
|
1506
|
+
* clients, query hooks, mocks, and validators all reference the names this
|
|
1507
|
+
* plugin produces.
|
|
1281
1508
|
*
|
|
1282
1509
|
* @example
|
|
1283
1510
|
* ```ts
|
|
1284
|
-
* import
|
|
1511
|
+
* import { defineConfig } from 'kubb'
|
|
1512
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1285
1513
|
*
|
|
1286
1514
|
* export default defineConfig({
|
|
1287
|
-
*
|
|
1515
|
+
* input: { path: './petStore.yaml' },
|
|
1516
|
+
* output: { path: './src/gen' },
|
|
1517
|
+
* plugins: [
|
|
1518
|
+
* pluginTs({
|
|
1519
|
+
* output: { path: './types' },
|
|
1520
|
+
* enumType: 'asConst',
|
|
1521
|
+
* optionalType: 'questionTokenAndUndefined',
|
|
1522
|
+
* }),
|
|
1523
|
+
* ],
|
|
1288
1524
|
* })
|
|
1289
1525
|
* ```
|
|
1290
1526
|
*/
|
|
@@ -1299,7 +1535,7 @@ const pluginTs = (0, _kubb_core.definePlugin)((options) => {
|
|
|
1299
1535
|
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
1300
1536
|
return `${camelCase(ctx.group)}Controller`;
|
|
1301
1537
|
}
|
|
1302
|
-
} :
|
|
1538
|
+
} : null;
|
|
1303
1539
|
return {
|
|
1304
1540
|
name: pluginTsName,
|
|
1305
1541
|
options,
|