@kubb/plugin-ts 5.0.0-beta.42 → 5.0.0-beta.56
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/dist/index.cjs +106 -165
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +94 -92
- package/dist/index.js +104 -163
- package/dist/index.js.map +1 -1
- package/package.json +8 -15
- package/src/components/Enum.tsx +30 -26
- package/src/components/Type.tsx +6 -11
- package/src/constants.ts +7 -7
- package/src/factory.ts +5 -38
- package/src/generators/typeGenerator.tsx +22 -42
- package/src/plugin.ts +12 -9
- package/src/printers/printerTs.ts +13 -24
- package/src/resolvers/resolverTs.ts +6 -6
- package/src/types.ts +77 -57
- package/src/utils.ts +1 -1
- package/extension.yaml +0 -1080
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { t as __name } from "./chunk-C0LytTxp.js";
|
|
2
|
+
import { extractRefName, jsStringEscape, stringify, trimQuotes } from "@kubb/ast/utils";
|
|
2
3
|
import { parserTs } from "@kubb/parser-ts";
|
|
3
|
-
import { File,
|
|
4
|
+
import { File, jsxRenderer } from "@kubb/renderer-jsx";
|
|
4
5
|
import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
|
|
5
6
|
import ts from "typescript";
|
|
6
7
|
import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
|
|
@@ -15,58 +16,41 @@ import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
|
|
|
15
16
|
function toCamelOrPascal(text, pascal) {
|
|
16
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) => {
|
|
17
18
|
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
18
|
-
|
|
19
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
19
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
20
20
|
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
24
|
-
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
25
|
-
* Segments are joined with `/` to form a file path.
|
|
26
|
-
*
|
|
27
|
-
* Only splits on dots followed by a letter so that version numbers
|
|
28
|
-
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
29
|
-
*/
|
|
30
|
-
function applyToFileParts(text, transformPart) {
|
|
31
|
-
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
32
|
-
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
23
|
* Converts `text` to camelCase.
|
|
36
|
-
* When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
|
|
37
24
|
*
|
|
38
|
-
* @example
|
|
39
|
-
* camelCase('hello-world')
|
|
40
|
-
*
|
|
25
|
+
* @example Word boundaries
|
|
26
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
27
|
+
*
|
|
28
|
+
* @example With a prefix
|
|
29
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
41
30
|
*/
|
|
42
|
-
function camelCase(text, {
|
|
43
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
|
|
44
|
-
prefix,
|
|
45
|
-
suffix
|
|
46
|
-
} : {}));
|
|
31
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
47
32
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
48
33
|
}
|
|
49
34
|
/**
|
|
50
35
|
* Converts `text` to PascalCase.
|
|
51
|
-
* When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
|
|
52
36
|
*
|
|
53
|
-
* @example
|
|
54
|
-
* pascalCase('hello-world')
|
|
55
|
-
*
|
|
37
|
+
* @example Word boundaries
|
|
38
|
+
* `pascalCase('hello-world') // 'HelloWorld'`
|
|
39
|
+
*
|
|
40
|
+
* @example With a suffix
|
|
41
|
+
* `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
|
|
56
42
|
*/
|
|
57
|
-
function pascalCase(text, {
|
|
58
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
|
|
59
|
-
prefix,
|
|
60
|
-
suffix
|
|
61
|
-
}) : camelCase(part));
|
|
43
|
+
function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
62
44
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
63
45
|
}
|
|
64
46
|
/**
|
|
65
47
|
* Converts `text` to snake_case.
|
|
66
48
|
*
|
|
67
|
-
* @example
|
|
68
|
-
* snakeCase('helloWorld')
|
|
69
|
-
*
|
|
49
|
+
* @example From camelCase
|
|
50
|
+
* `snakeCase('helloWorld') // 'hello_world'`
|
|
51
|
+
*
|
|
52
|
+
* @example From mixed separators
|
|
53
|
+
* `snakeCase('Hello-World') // 'hello_world'`
|
|
70
54
|
*/
|
|
71
55
|
function snakeCase(text, { prefix = "", suffix = "" } = {}) {
|
|
72
56
|
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,8 +58,8 @@ function snakeCase(text, { prefix = "", suffix = "" } = {}) {
|
|
|
74
58
|
/**
|
|
75
59
|
* Converts `text` to SCREAMING_SNAKE_CASE.
|
|
76
60
|
*
|
|
77
|
-
* @example
|
|
78
|
-
* screamingSnakeCase('helloWorld') // 'HELLO_WORLD'
|
|
61
|
+
* @example From camelCase
|
|
62
|
+
* `screamingSnakeCase('helloWorld') // 'HELLO_WORLD'`
|
|
79
63
|
*/
|
|
80
64
|
function screamingSnakeCase(text, { prefix = "", suffix = "" } = {}) {
|
|
81
65
|
return snakeCase(text, {
|
|
@@ -84,60 +68,29 @@ function screamingSnakeCase(text, { prefix = "", suffix = "" } = {}) {
|
|
|
84
68
|
}).toUpperCase();
|
|
85
69
|
}
|
|
86
70
|
//#endregion
|
|
87
|
-
//#region ../../internals/utils/src/
|
|
71
|
+
//#region ../../internals/utils/src/fs.ts
|
|
88
72
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
73
|
+
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
74
|
+
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
75
|
+
* every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
|
|
91
76
|
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
|
|
96
|
-
function trimQuotes(text) {
|
|
97
|
-
if (text.length >= 2) {
|
|
98
|
-
const first = text[0];
|
|
99
|
-
const last = text[text.length - 1];
|
|
100
|
-
if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
|
|
101
|
-
}
|
|
102
|
-
return text;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Escapes characters that are not allowed inside JS string literals.
|
|
106
|
-
* Handles quotes, backslashes, and Unicode line terminators (U+2028 / U+2029).
|
|
77
|
+
* Empty segments are dropped before joining. They arise when the name starts with a dot
|
|
78
|
+
* followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
|
|
79
|
+
* an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
|
|
80
|
+
* absolute path, letting generated files escape the configured output directory.
|
|
107
81
|
*
|
|
108
|
-
* @
|
|
82
|
+
* @example Nested path from a dotted name
|
|
83
|
+
* `toFilePath('pet.petId') // 'pet/petId'`
|
|
109
84
|
*
|
|
110
|
-
* @example
|
|
111
|
-
*
|
|
112
|
-
* jsStringEscape('say "hi"\nbye') // 'say \\"hi\\"\\nbye'
|
|
113
|
-
* ```
|
|
114
|
-
*/
|
|
115
|
-
function jsStringEscape(input) {
|
|
116
|
-
return `${input}`.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
|
|
117
|
-
switch (character) {
|
|
118
|
-
case "\"":
|
|
119
|
-
case "'":
|
|
120
|
-
case "\\": return `\\${character}`;
|
|
121
|
-
case "\n": return "\\n";
|
|
122
|
-
case "\r": return "\\r";
|
|
123
|
-
case "\u2028": return "\\u2028";
|
|
124
|
-
case "\u2029": return "\\u2029";
|
|
125
|
-
default: return "";
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
//#endregion
|
|
130
|
-
//#region ../../internals/utils/src/object.ts
|
|
131
|
-
/**
|
|
132
|
-
* Serializes a primitive value to a JSON string literal, stripping any surrounding quote characters first.
|
|
85
|
+
* @example PascalCase the final segment
|
|
86
|
+
* `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
|
|
133
87
|
*
|
|
134
|
-
* @example
|
|
135
|
-
*
|
|
136
|
-
* stringify('"hello"') // '"hello"'
|
|
88
|
+
* @example Suffix applied to the final segment only
|
|
89
|
+
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
137
90
|
*/
|
|
138
|
-
function
|
|
139
|
-
|
|
140
|
-
return
|
|
91
|
+
function toFilePath(name, caseLast = camelCase) {
|
|
92
|
+
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
93
|
+
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
141
94
|
}
|
|
142
95
|
//#endregion
|
|
143
96
|
//#region ../../internals/utils/src/reserved.ts
|
|
@@ -273,26 +226,24 @@ const OPTIONAL_ADDS_UNDEFINED = new Set(["undefined", "questionTokenAndUndefined
|
|
|
273
226
|
*/
|
|
274
227
|
const OPTIONAL_ADDS_QUESTION_TOKEN = new Set(["questionToken", "questionTokenAndUndefined"]);
|
|
275
228
|
/**
|
|
276
|
-
* `
|
|
229
|
+
* `enum.type` values that append a `typeSuffix` to the generated enum type alias.
|
|
277
230
|
*/
|
|
278
|
-
const ENUM_TYPES_WITH_KEY_SUFFIX = new Set(["asConst"
|
|
231
|
+
const ENUM_TYPES_WITH_KEY_SUFFIX = new Set(["asConst"]);
|
|
279
232
|
/**
|
|
280
|
-
* `
|
|
233
|
+
* `enum.type` values that require a runtime value declaration (object, enum, or literal).
|
|
281
234
|
*/
|
|
282
235
|
const ENUM_TYPES_WITH_RUNTIME_VALUE = new Set([
|
|
283
236
|
"enum",
|
|
284
237
|
"asConst",
|
|
285
|
-
"asPascalConst",
|
|
286
238
|
"constEnum",
|
|
287
239
|
"literal",
|
|
288
240
|
void 0
|
|
289
241
|
]);
|
|
290
242
|
/**
|
|
291
|
-
* `
|
|
243
|
+
* `enum.type` values whose type declaration is type-only (no runtime value emitted for the type alias).
|
|
292
244
|
*/
|
|
293
245
|
const ENUM_TYPES_WITH_TYPE_ONLY = new Set([
|
|
294
246
|
"asConst",
|
|
295
|
-
"asPascalConst",
|
|
296
247
|
"literal",
|
|
297
248
|
void 0
|
|
298
249
|
]);
|
|
@@ -612,12 +563,10 @@ const createStringLiteral = factory.createStringLiteral;
|
|
|
612
563
|
* Creates an array type node (e.g., `T[]`).
|
|
613
564
|
*/
|
|
614
565
|
const createArrayTypeNode = factory.createArrayTypeNode;
|
|
615
|
-
factory.createParenthesizedType;
|
|
616
566
|
/**
|
|
617
567
|
* Creates a literal type node (e.g., `'hello'`, `42`, `true`).
|
|
618
568
|
*/
|
|
619
569
|
const createLiteralTypeNode = factory.createLiteralTypeNode;
|
|
620
|
-
factory.createNull;
|
|
621
570
|
/**
|
|
622
571
|
* Creates an identifier node.
|
|
623
572
|
*/
|
|
@@ -642,8 +591,6 @@ const createTrue = factory.createTrue;
|
|
|
642
591
|
* Creates a boolean false literal type node.
|
|
643
592
|
*/
|
|
644
593
|
const createFalse = factory.createFalse;
|
|
645
|
-
factory.createIndexedAccessTypeNode;
|
|
646
|
-
factory.createTypeOperatorNode;
|
|
647
594
|
/**
|
|
648
595
|
* Creates a prefix unary expression (e.g., negative numbers, logical not).
|
|
649
596
|
*/
|
|
@@ -724,41 +671,44 @@ function buildIndexSignatures(node, propertyCount, print) {
|
|
|
724
671
|
* Resolves the runtime identifier name and the TypeScript type name for an enum schema node.
|
|
725
672
|
*
|
|
726
673
|
* The raw `node.name` may be a YAML key such as `"enumNames.Type"` which is not a
|
|
727
|
-
* valid TypeScript identifier. The resolver normalizes it
|
|
728
|
-
*
|
|
674
|
+
* valid TypeScript identifier. The resolver normalizes it. For inline enum properties the adapter
|
|
675
|
+
* already emits a PascalCase+suffix name, so resolution is typically a no-op.
|
|
676
|
+
*
|
|
677
|
+
* When `constCasing` is `'pascalCase'` and `typeSuffix` is empty, the const and the type
|
|
678
|
+
* resolve to the same name, which TypeScript merges into a single value+type declaration.
|
|
729
679
|
*/
|
|
730
|
-
function getEnumNames({ node,
|
|
680
|
+
function getEnumNames({ node, enum: enumOptions, resolver }) {
|
|
731
681
|
const resolved = resolver.default(node.name, "type");
|
|
732
682
|
return {
|
|
733
|
-
enumName:
|
|
734
|
-
typeName: ENUM_TYPES_WITH_KEY_SUFFIX.has(
|
|
683
|
+
enumName: enumOptions.constCasing === "pascalCase" ? resolved : camelCase(node.name),
|
|
684
|
+
typeName: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) ? resolver.resolveEnumKeyName(node, enumOptions.typeSuffix) : resolved
|
|
735
685
|
};
|
|
736
686
|
}
|
|
737
687
|
/**
|
|
738
688
|
* Renders the enum declaration(s) for a single named `EnumSchemaNode`.
|
|
739
689
|
*
|
|
740
|
-
* Depending on `
|
|
741
|
-
* - A runtime object (`asConst`
|
|
690
|
+
* Depending on `enum.type` this may emit:
|
|
691
|
+
* - A runtime object (`asConst`) plus a `typeof` type alias
|
|
742
692
|
* - A `const enum` or plain `enum` declaration (`constEnum` / `enum`)
|
|
743
693
|
* - A union literal type alias (`literal`)
|
|
744
694
|
*
|
|
745
695
|
* The emitted `File.Source` nodes carry the resolved names so that the barrel
|
|
746
696
|
* index picks up the correct export identifiers.
|
|
747
697
|
*/
|
|
748
|
-
function Enum({ node,
|
|
698
|
+
function Enum({ node, enum: enumOptions, resolver }) {
|
|
749
699
|
const { enumName, typeName } = getEnumNames({
|
|
750
700
|
node,
|
|
751
|
-
|
|
752
|
-
enumTypeSuffix,
|
|
701
|
+
enum: enumOptions,
|
|
753
702
|
resolver
|
|
754
703
|
});
|
|
755
704
|
const [nameNode, typeNode] = createEnumDeclaration({
|
|
756
705
|
name: enumName,
|
|
757
706
|
typeName,
|
|
758
707
|
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]) ?? [],
|
|
759
|
-
type:
|
|
760
|
-
enumKeyCasing
|
|
708
|
+
type: enumOptions.type,
|
|
709
|
+
enumKeyCasing: enumOptions.keyCasing
|
|
761
710
|
});
|
|
711
|
+
const namesMerge = !!nameNode && enumName === typeName;
|
|
762
712
|
return /* @__PURE__ */ jsxs(Fragment, { children: [nameNode && /* @__PURE__ */ jsx(File.Source, {
|
|
763
713
|
name: enumName,
|
|
764
714
|
isExportable: true,
|
|
@@ -767,15 +717,15 @@ function Enum({ node, enumType, enumTypeSuffix, enumKeyCasing, resolver }) {
|
|
|
767
717
|
children: parserTs.print(nameNode)
|
|
768
718
|
}), /* @__PURE__ */ jsx(File.Source, {
|
|
769
719
|
name: typeName,
|
|
770
|
-
isIndexable:
|
|
771
|
-
isExportable: ENUM_TYPES_WITH_RUNTIME_VALUE.has(
|
|
772
|
-
isTypeOnly: ENUM_TYPES_WITH_TYPE_ONLY.has(
|
|
720
|
+
isIndexable: !namesMerge,
|
|
721
|
+
isExportable: !namesMerge && ENUM_TYPES_WITH_RUNTIME_VALUE.has(enumOptions.type),
|
|
722
|
+
isTypeOnly: ENUM_TYPES_WITH_TYPE_ONLY.has(enumOptions.type),
|
|
773
723
|
children: parserTs.print(typeNode)
|
|
774
724
|
})] });
|
|
775
725
|
}
|
|
776
726
|
//#endregion
|
|
777
727
|
//#region src/components/Type.tsx
|
|
778
|
-
function Type({ name, node, printer,
|
|
728
|
+
function Type({ name, node, printer, enum: enumOptions, resolver }) {
|
|
779
729
|
const enumSchemaNodes = ast.collect(node, { schema(n) {
|
|
780
730
|
const enumNode = ast.narrowSchema(n, ast.schemaTypes.enum);
|
|
781
731
|
if (enumNode?.name) return enumNode;
|
|
@@ -787,19 +737,16 @@ function Type({ name, node, printer, enumType, enumTypeSuffix, enumKeyCasing, re
|
|
|
787
737
|
node,
|
|
788
738
|
...getEnumNames({
|
|
789
739
|
node,
|
|
790
|
-
|
|
791
|
-
enumTypeSuffix,
|
|
740
|
+
enum: enumOptions,
|
|
792
741
|
resolver
|
|
793
742
|
})
|
|
794
743
|
};
|
|
795
744
|
});
|
|
796
|
-
const shouldExportEnums =
|
|
797
|
-
const shouldExportType =
|
|
745
|
+
const shouldExportEnums = enumOptions.type !== "inlineLiteral";
|
|
746
|
+
const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
|
|
798
747
|
return /* @__PURE__ */ jsxs(Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ jsx(Enum, {
|
|
799
748
|
node,
|
|
800
|
-
|
|
801
|
-
enumTypeSuffix,
|
|
802
|
-
enumKeyCasing,
|
|
749
|
+
enum: enumOptions,
|
|
803
750
|
resolver
|
|
804
751
|
}, node.name)), shouldExportType && /* @__PURE__ */ jsx(File.Source, {
|
|
805
752
|
name,
|
|
@@ -875,26 +822,24 @@ function getOperationParameters(node, options = {}) {
|
|
|
875
822
|
* shared default naming so every plugin groups output consistently:
|
|
876
823
|
*
|
|
877
824
|
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
878
|
-
* - other groups use
|
|
825
|
+
* - other groups use the camelCased group (`pet store` → `petStore`).
|
|
879
826
|
*
|
|
880
827
|
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
881
828
|
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
882
829
|
* per-plugin convention.
|
|
883
830
|
*
|
|
884
831
|
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
885
|
-
* @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
|
|
886
832
|
*
|
|
887
833
|
* @example
|
|
888
834
|
* ```ts
|
|
889
|
-
* createGroupConfig(group
|
|
890
|
-
* createGroupConfig(group, { suffix: 'Requests' }) // plugin-cypress, plugin-mcp
|
|
835
|
+
* createGroupConfig(group) // shared across every plugin
|
|
891
836
|
* ```
|
|
892
837
|
*/
|
|
893
|
-
function createGroupConfig(group
|
|
838
|
+
function createGroupConfig(group) {
|
|
894
839
|
if (!group) return null;
|
|
895
840
|
const defaultName = (ctx) => {
|
|
896
841
|
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
897
|
-
return
|
|
842
|
+
return camelCase(ctx.group);
|
|
898
843
|
};
|
|
899
844
|
return {
|
|
900
845
|
...group,
|
|
@@ -1051,13 +996,13 @@ function isNonNullable(value) {
|
|
|
1051
996
|
*
|
|
1052
997
|
* @example Raw type node (no `typeName`)
|
|
1053
998
|
* ```ts
|
|
1054
|
-
* const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array',
|
|
999
|
+
* const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enum: { type: 'inlineLiteral' } })
|
|
1055
1000
|
* const typeNode = printer.print(schemaNode) // ts.TypeNode
|
|
1056
1001
|
* ```
|
|
1057
1002
|
*
|
|
1058
1003
|
* @example Full declaration (with `typeName`)
|
|
1059
1004
|
* ```ts
|
|
1060
|
-
* const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array',
|
|
1005
|
+
* const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enum: { type: 'inlineLiteral' }, typeName: 'MyType' })
|
|
1061
1006
|
* const declaration = printer.print(schemaNode) // ts.TypeAliasDeclaration | ts.InterfaceDeclaration
|
|
1062
1007
|
* ```
|
|
1063
1008
|
*/
|
|
@@ -1091,16 +1036,16 @@ const printerTs = ast.definePrinter((options) => {
|
|
|
1091
1036
|
time: dateOrStringNode,
|
|
1092
1037
|
ref(node) {
|
|
1093
1038
|
if (!node.name) return null;
|
|
1094
|
-
const refName = node.ref ?
|
|
1095
|
-
return createTypeReferenceNode(node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.
|
|
1039
|
+
const refName = node.ref ? extractRefName(node.ref) ?? node.name : node.name;
|
|
1040
|
+
return createTypeReferenceNode(node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix && this.options.enumSchemaNames?.has(refName) ? this.options.resolver.resolveEnumKeyName({ name: refName }, this.options.enum.typeSuffix) : node.ref ? this.options.resolver.default(refName, "type") : refName, void 0);
|
|
1096
1041
|
},
|
|
1097
1042
|
enum(node) {
|
|
1098
1043
|
const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
|
|
1099
|
-
if (this.options.
|
|
1044
|
+
if (this.options.enum.type === "inlineLiteral" || !node.name) return createUnionDeclaration({
|
|
1100
1045
|
withParentheses: true,
|
|
1101
1046
|
nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(isNonNullable)
|
|
1102
1047
|
}) ?? void 0;
|
|
1103
|
-
return createTypeReferenceNode(ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.
|
|
1048
|
+
return createTypeReferenceNode(ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix ? this.options.resolver.resolveEnumKeyName(node, this.options.enum.typeSuffix) : this.options.resolver.default(node.name, "type"), void 0);
|
|
1104
1049
|
},
|
|
1105
1050
|
union(node) {
|
|
1106
1051
|
const members = node.members ?? [];
|
|
@@ -1204,15 +1149,14 @@ const printerTs = ast.definePrinter((options) => {
|
|
|
1204
1149
|
*/
|
|
1205
1150
|
const typeGenerator = defineGenerator({
|
|
1206
1151
|
name: "typescript",
|
|
1207
|
-
renderer:
|
|
1152
|
+
renderer: jsxRenderer,
|
|
1208
1153
|
schema(node, ctx) {
|
|
1209
|
-
const {
|
|
1154
|
+
const { enum: enumOptions, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options;
|
|
1210
1155
|
const { adapter, config, resolver, root } = ctx;
|
|
1211
1156
|
if (!node.name) return;
|
|
1212
|
-
const mode = ctx.getMode(output);
|
|
1213
1157
|
const enumSchemaNames = new Set(ctx.meta.enumNames);
|
|
1214
1158
|
function resolveImportName(schemaName) {
|
|
1215
|
-
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(
|
|
1159
|
+
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && enumOptions.typeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumOptions.typeSuffix);
|
|
1216
1160
|
return resolver.resolveTypeName(schemaName);
|
|
1217
1161
|
}
|
|
1218
1162
|
const imports = adapter.getImports(node, (schemaName) => ({
|
|
@@ -1228,7 +1172,7 @@ const typeGenerator = defineGenerator({
|
|
|
1228
1172
|
}));
|
|
1229
1173
|
const isEnumSchema = !!ast.narrowSchema(node, ast.schemaTypes.enum);
|
|
1230
1174
|
const meta = {
|
|
1231
|
-
name: ENUM_TYPES_WITH_KEY_SUFFIX.has(
|
|
1175
|
+
name: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && isEnumSchema ? resolver.resolveEnumKeyName(node, enumOptions.typeSuffix) : resolver.resolveTypeName(node.name),
|
|
1232
1176
|
file: resolver.resolveFile({
|
|
1233
1177
|
name: node.name,
|
|
1234
1178
|
extname: ".ts"
|
|
@@ -1241,8 +1185,7 @@ const typeGenerator = defineGenerator({
|
|
|
1241
1185
|
const schemaPrinter = printerTs({
|
|
1242
1186
|
optionalType,
|
|
1243
1187
|
arrayType,
|
|
1244
|
-
|
|
1245
|
-
enumTypeSuffix,
|
|
1188
|
+
enum: enumOptions,
|
|
1246
1189
|
name: meta.name,
|
|
1247
1190
|
syntaxType,
|
|
1248
1191
|
description: node.description,
|
|
@@ -1270,7 +1213,7 @@ const typeGenerator = defineGenerator({
|
|
|
1270
1213
|
baseName: meta.file.baseName
|
|
1271
1214
|
}
|
|
1272
1215
|
}),
|
|
1273
|
-
children: [
|
|
1216
|
+
children: [imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
|
|
1274
1217
|
root: meta.file.path,
|
|
1275
1218
|
path: imp.path,
|
|
1276
1219
|
name: imp.name,
|
|
@@ -1282,18 +1225,15 @@ const typeGenerator = defineGenerator({
|
|
|
1282
1225
|
].join("-"))), /* @__PURE__ */ jsx(Type, {
|
|
1283
1226
|
name: meta.name,
|
|
1284
1227
|
node,
|
|
1285
|
-
|
|
1286
|
-
enumTypeSuffix,
|
|
1287
|
-
enumKeyCasing,
|
|
1228
|
+
enum: enumOptions,
|
|
1288
1229
|
resolver,
|
|
1289
1230
|
printer: schemaPrinter
|
|
1290
1231
|
})]
|
|
1291
1232
|
});
|
|
1292
1233
|
},
|
|
1293
1234
|
operation(node, ctx) {
|
|
1294
|
-
const {
|
|
1235
|
+
const { enum: enumOptions, optionalType, arrayType, syntaxType, paramsCasing, group, output, printer } = ctx.options;
|
|
1295
1236
|
const { adapter, config, resolver, root } = ctx;
|
|
1296
|
-
const mode = ctx.getMode(output);
|
|
1297
1237
|
const params = ast.caseParams(node.parameters, paramsCasing);
|
|
1298
1238
|
const meta = { file: resolver.resolveFile({
|
|
1299
1239
|
name: node.operationId,
|
|
@@ -1307,7 +1247,7 @@ const typeGenerator = defineGenerator({
|
|
|
1307
1247
|
}) };
|
|
1308
1248
|
const enumSchemaNames = new Set(ctx.meta.enumNames);
|
|
1309
1249
|
function resolveImportName(schemaName) {
|
|
1310
|
-
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(
|
|
1250
|
+
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && enumOptions.typeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumOptions.typeSuffix);
|
|
1311
1251
|
return resolver.resolveTypeName(schemaName);
|
|
1312
1252
|
}
|
|
1313
1253
|
function renderSchemaType({ schema, name, keysToOmit }) {
|
|
@@ -1326,8 +1266,7 @@ const typeGenerator = defineGenerator({
|
|
|
1326
1266
|
const schemaPrinter = printerTs({
|
|
1327
1267
|
optionalType,
|
|
1328
1268
|
arrayType,
|
|
1329
|
-
|
|
1330
|
-
enumTypeSuffix,
|
|
1269
|
+
enum: enumOptions,
|
|
1331
1270
|
name,
|
|
1332
1271
|
syntaxType,
|
|
1333
1272
|
description: schema.description,
|
|
@@ -1336,7 +1275,7 @@ const typeGenerator = defineGenerator({
|
|
|
1336
1275
|
enumSchemaNames,
|
|
1337
1276
|
nodes: printer?.nodes
|
|
1338
1277
|
});
|
|
1339
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1278
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
|
|
1340
1279
|
root: meta.file.path,
|
|
1341
1280
|
path: imp.path,
|
|
1342
1281
|
name: imp.name,
|
|
@@ -1348,9 +1287,7 @@ const typeGenerator = defineGenerator({
|
|
|
1348
1287
|
].join("-"))), /* @__PURE__ */ jsx(Type, {
|
|
1349
1288
|
name,
|
|
1350
1289
|
node: schema,
|
|
1351
|
-
|
|
1352
|
-
enumTypeSuffix,
|
|
1353
|
-
enumKeyCasing,
|
|
1290
|
+
enum: enumOptions,
|
|
1354
1291
|
resolver,
|
|
1355
1292
|
printer: schemaPrinter
|
|
1356
1293
|
})] });
|
|
@@ -1481,7 +1418,7 @@ const typeGenerator = defineGenerator({
|
|
|
1481
1418
|
* casing/file-layout rules.
|
|
1482
1419
|
*
|
|
1483
1420
|
* The `default` method is supplied by `defineResolver`. It uses PascalCase for
|
|
1484
|
-
* type names and PascalCase
|
|
1421
|
+
* type names and PascalCase file paths (dotted names become `/`-joined) for files.
|
|
1485
1422
|
*
|
|
1486
1423
|
* @example Resolve a type and file name
|
|
1487
1424
|
* ```ts
|
|
@@ -1497,15 +1434,15 @@ const resolverTs = defineResolver(() => {
|
|
|
1497
1434
|
name: "default",
|
|
1498
1435
|
pluginName: "plugin-ts",
|
|
1499
1436
|
default(name, type) {
|
|
1500
|
-
|
|
1501
|
-
return
|
|
1437
|
+
if (type === "file") return toFilePath(name, pascalCase);
|
|
1438
|
+
return ensureValidVarName(pascalCase(name));
|
|
1502
1439
|
},
|
|
1503
1440
|
resolveTypeName(name) {
|
|
1504
1441
|
return ensureValidVarName(pascalCase(name));
|
|
1505
1442
|
},
|
|
1506
1443
|
resolvePathName(name, type) {
|
|
1507
|
-
|
|
1508
|
-
return
|
|
1444
|
+
if (type === "file") return toFilePath(name, pascalCase);
|
|
1445
|
+
return ensureValidVarName(pascalCase(name));
|
|
1509
1446
|
},
|
|
1510
1447
|
resolveParamName(node, param) {
|
|
1511
1448
|
return this.resolveTypeName(`${node.operationId} ${param.in} ${param.name}`);
|
|
@@ -1563,7 +1500,7 @@ const pluginTsName = "plugin-ts";
|
|
|
1563
1500
|
* plugins: [
|
|
1564
1501
|
* pluginTs({
|
|
1565
1502
|
* output: { path: './types' },
|
|
1566
|
-
*
|
|
1503
|
+
* enum: { type: 'asConst' },
|
|
1567
1504
|
* optionalType: 'questionTokenAndUndefined',
|
|
1568
1505
|
* }),
|
|
1569
1506
|
* ],
|
|
@@ -1573,9 +1510,15 @@ const pluginTsName = "plugin-ts";
|
|
|
1573
1510
|
const pluginTs = definePlugin((options) => {
|
|
1574
1511
|
const { output = {
|
|
1575
1512
|
path: "types",
|
|
1576
|
-
|
|
1577
|
-
}, group, exclude = [], include, override = [],
|
|
1578
|
-
const groupConfig = createGroupConfig(group
|
|
1513
|
+
barrel: { type: "named" }
|
|
1514
|
+
}, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", paramsCasing, printer, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
|
|
1515
|
+
const groupConfig = createGroupConfig(group);
|
|
1516
|
+
const resolvedEnum = {
|
|
1517
|
+
type: enumOptions.type ?? "asConst",
|
|
1518
|
+
constCasing: enumOptions.constCasing ?? "camelCase",
|
|
1519
|
+
typeSuffix: enumOptions.typeSuffix ?? "Key",
|
|
1520
|
+
keyCasing: enumOptions.keyCasing ?? "none"
|
|
1521
|
+
};
|
|
1579
1522
|
return {
|
|
1580
1523
|
name: pluginTsName,
|
|
1581
1524
|
options,
|
|
@@ -1588,9 +1531,7 @@ const pluginTs = definePlugin((options) => {
|
|
|
1588
1531
|
optionalType,
|
|
1589
1532
|
group: groupConfig,
|
|
1590
1533
|
arrayType,
|
|
1591
|
-
|
|
1592
|
-
enumTypeSuffix,
|
|
1593
|
-
enumKeyCasing,
|
|
1534
|
+
enum: resolvedEnum,
|
|
1594
1535
|
syntaxType,
|
|
1595
1536
|
paramsCasing,
|
|
1596
1537
|
printer
|