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