@kubb/plugin-faker 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 +1303 -116
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +277 -16
- package/dist/index.js +1300 -113
- package/dist/index.js.map +1 -1
- package/package.json +9 -29
- package/dist/Faker-BMgoFj8b.d.ts +0 -27
- package/dist/Faker-CWtonujy.js +0 -334
- package/dist/Faker-CWtonujy.js.map +0 -1
- package/dist/Faker-D39THFJ-.cjs +0 -418
- package/dist/Faker-D39THFJ-.cjs.map +0 -1
- package/dist/components.cjs +0 -3
- package/dist/components.d.ts +0 -2
- package/dist/components.js +0 -2
- package/dist/fakerGenerator-B-QnVz9o.d.ts +0 -9
- package/dist/fakerGenerator-B-XuVREg.cjs +0 -570
- package/dist/fakerGenerator-B-XuVREg.cjs.map +0 -1
- package/dist/fakerGenerator-DH6hN3yb.js +0 -560
- package/dist/fakerGenerator-DH6hN3yb.js.map +0 -1
- package/dist/generators.cjs +0 -3
- package/dist/generators.d.ts +0 -2
- package/dist/generators.js +0 -2
- package/dist/printerFaker-W0pLunAj.d.ts +0 -213
- package/extension.yaml +0 -357
- package/src/components/Faker.tsx +0 -153
- package/src/components/index.ts +0 -1
- package/src/generators/fakerGenerator.tsx +0 -269
- package/src/generators/index.ts +0 -1
- package/src/index.ts +0 -7
- package/src/plugin.ts +0 -88
- package/src/printers/printerFaker.ts +0 -341
- package/src/resolvers/resolverFaker.ts +0 -85
- package/src/types.ts +0 -179
- package/src/utils.ts +0 -268
- /package/dist/{chunk--u3MIqq1.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -2,13 +2,12 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
const require_fakerGenerator = require("./fakerGenerator-B-XuVREg.cjs");
|
|
5
|
+
//#endregion
|
|
7
6
|
let node_path = require("node:path");
|
|
8
|
-
|
|
9
|
-
let _kubb_core = require("@kubb/core");
|
|
7
|
+
let kubb_kit = require("kubb/kit");
|
|
10
8
|
let _kubb_plugin_ts = require("@kubb/plugin-ts");
|
|
11
|
-
let
|
|
9
|
+
let kubb_jsx = require("kubb/jsx");
|
|
10
|
+
let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
|
|
12
11
|
//#region ../../internals/utils/src/casing.ts
|
|
13
12
|
/**
|
|
14
13
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
@@ -20,35 +19,19 @@ let node_crypto = require("node:crypto");
|
|
|
20
19
|
function toCamelOrPascal(text, pascal) {
|
|
21
20
|
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) => {
|
|
22
21
|
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
23
|
-
|
|
24
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
22
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
25
23
|
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
26
24
|
}
|
|
27
25
|
/**
|
|
28
|
-
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
29
|
-
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
30
|
-
* Segments are joined with `/` to form a file path.
|
|
31
|
-
*
|
|
32
|
-
* Only splits on dots followed by a letter so that version numbers
|
|
33
|
-
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
34
|
-
*/
|
|
35
|
-
function applyToFileParts(text, transformPart) {
|
|
36
|
-
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
37
|
-
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
26
|
* Converts `text` to camelCase.
|
|
41
|
-
* When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
|
|
42
27
|
*
|
|
43
|
-
* @example
|
|
44
|
-
* camelCase('hello-world')
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
suffix
|
|
51
|
-
} : {}));
|
|
28
|
+
* @example Word boundaries
|
|
29
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
30
|
+
*
|
|
31
|
+
* @example With a prefix
|
|
32
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
33
|
+
*/
|
|
34
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
52
35
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
53
36
|
}
|
|
54
37
|
//#endregion
|
|
@@ -57,7 +40,7 @@ function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
|
57
40
|
* JavaScript and Java reserved words.
|
|
58
41
|
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
59
42
|
*/
|
|
60
|
-
const reservedWords = new Set([
|
|
43
|
+
const reservedWords = /* @__PURE__ */ new Set([
|
|
61
44
|
"abstract",
|
|
62
45
|
"arguments",
|
|
63
46
|
"boolean",
|
|
@@ -152,110 +135,1320 @@ const reservedWords = new Set([
|
|
|
152
135
|
*/
|
|
153
136
|
function isValidVarName(name) {
|
|
154
137
|
if (!name || reservedWords.has(name)) return false;
|
|
138
|
+
return isIdentifier(name);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Returns `name` when it's a syntactically valid JavaScript variable name,
|
|
142
|
+
* otherwise prefixes it with `_` so the result is a valid identifier.
|
|
143
|
+
*
|
|
144
|
+
* Useful for sanitizing OpenAPI schema names or operation IDs that start with
|
|
145
|
+
* a digit (e.g. `409`, `504AccountCancel`) before using them as exported
|
|
146
|
+
* variable, type, or function names.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```ts
|
|
150
|
+
* ensureValidVarName('409') // '_409'
|
|
151
|
+
* ensureValidVarName('504AccountCancel') // '_504AccountCancel'
|
|
152
|
+
* ensureValidVarName('Pet') // 'Pet'
|
|
153
|
+
* ensureValidVarName('class') // '_class'
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
function ensureValidVarName(name) {
|
|
157
|
+
if (!name || isValidVarName(name)) return name;
|
|
158
|
+
return `_${name}`;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
|
|
162
|
+
*
|
|
163
|
+
* Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
|
|
164
|
+
* even though they are not valid variable names, so use this (not {@link isValidVarName}) when
|
|
165
|
+
* deciding whether an object key needs quoting.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```ts
|
|
169
|
+
* isIdentifier('name') // true
|
|
170
|
+
* isIdentifier('x-total')// false
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
function isIdentifier(name) {
|
|
155
174
|
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
156
175
|
}
|
|
157
176
|
//#endregion
|
|
158
|
-
//#region src/
|
|
177
|
+
//#region ../../internals/utils/src/strings.ts
|
|
178
|
+
/**
|
|
179
|
+
* Wraps a value in single quotes for emitting a single-quoted JavaScript string literal, escaping
|
|
180
|
+
* any backslash or single quote in the content.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* singleQuote('foo') // "'foo'"
|
|
185
|
+
* singleQuote("o'clock") // "'o\\'clock'"
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
function singleQuote(value) {
|
|
189
|
+
if (value === void 0 || value === null) return "''";
|
|
190
|
+
return `'${String(value).replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
|
|
194
|
+
* Returns the string unchanged when no balanced quote pair is found.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* trimQuotes('"hello"') // 'hello'
|
|
199
|
+
* trimQuotes('hello') // 'hello'
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
function trimQuotes(text) {
|
|
203
|
+
if (text.length >= 2) {
|
|
204
|
+
const first = text[0];
|
|
205
|
+
const last = text[text.length - 1];
|
|
206
|
+
if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
|
|
207
|
+
}
|
|
208
|
+
return text;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Serializes a primitive to a single-quoted string literal, stripping any surrounding quotes first.
|
|
212
|
+
*
|
|
213
|
+
* Escaping runs through `JSON.stringify`, then the result switches to single quotes so the generated
|
|
214
|
+
* code matches the repo style without a formatter.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```ts
|
|
218
|
+
* stringify('hello') // "'hello'"
|
|
219
|
+
* stringify('"hello"') // "'hello'"
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
function stringify(value) {
|
|
223
|
+
if (value === void 0 || value === null) return "''";
|
|
224
|
+
return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Escapes characters that are not allowed inside JS string literals, covering quotes, backslashes,
|
|
228
|
+
* and the Unicode line terminators U+2028 and U+2029.
|
|
229
|
+
*
|
|
230
|
+
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```ts
|
|
234
|
+
* jsStringEscape('say "hi"\nbye') // 'say \\"hi\\"\\nbye'
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
function jsStringEscape(input) {
|
|
238
|
+
return `${input}`.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
|
|
239
|
+
switch (character) {
|
|
240
|
+
case "\"":
|
|
241
|
+
case "'":
|
|
242
|
+
case "\\": return `\\${character}`;
|
|
243
|
+
case "\n": return "\\n";
|
|
244
|
+
case "\r": return "\\r";
|
|
245
|
+
case "\u2028": return "\\u2028";
|
|
246
|
+
case "\u2029": return "\\u2029";
|
|
247
|
+
default: return "";
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
|
|
253
|
+
* Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
|
|
254
|
+
* Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```ts
|
|
258
|
+
* toRegExpString('^(?im)foo') // 'new RegExp("^foo", "im")'
|
|
259
|
+
* toRegExpString('^(?im)foo', null) // '/^foo/im'
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
function toRegExpString(text, func = "RegExp") {
|
|
263
|
+
const raw = trimQuotes(text);
|
|
264
|
+
const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
|
|
265
|
+
const replacementTarget = match?.[1] ?? "";
|
|
266
|
+
const matchedFlags = match?.[2];
|
|
267
|
+
const cleaned = raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, "");
|
|
268
|
+
const { source, flags } = new RegExp(cleaned, matchedFlags);
|
|
269
|
+
if (func === null) return `/${source}/${flags}`;
|
|
270
|
+
return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
|
|
271
|
+
}
|
|
272
|
+
//#endregion
|
|
273
|
+
//#region ../../internals/utils/src/codegen.ts
|
|
274
|
+
const INDENT = " ";
|
|
159
275
|
/**
|
|
160
|
-
*
|
|
276
|
+
* Indents every non-empty line of `text` by one indent level, leaving blank lines empty.
|
|
277
|
+
*/
|
|
278
|
+
function indentLines(text) {
|
|
279
|
+
if (!text) return "";
|
|
280
|
+
return text.split("\n").map((line) => line.trim() ? `${INDENT}${line}` : "").join("\n");
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Renders an object key, quoting it with single quotes only when it is not a valid identifier.
|
|
284
|
+
* Reserved words and globals (`name`, `class`, …) are valid bare keys and stay unquoted.
|
|
161
285
|
*
|
|
162
|
-
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```ts
|
|
288
|
+
* objectKey('name') // 'name'
|
|
289
|
+
* objectKey('x-total') // "'x-total'"
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
function objectKey(name) {
|
|
293
|
+
return isIdentifier(name) ? name : singleQuote(name);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one
|
|
297
|
+
* level and closing the brace at column zero. Entries that are themselves multi-line objects indent
|
|
298
|
+
* cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.
|
|
163
299
|
*
|
|
164
300
|
* @example
|
|
165
|
-
*
|
|
301
|
+
* ```ts
|
|
302
|
+
* buildObject(['id: z.number()', 'name: z.string()'])
|
|
303
|
+
* // '{\n id: z.number(),\n name: z.string(),\n}'
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
306
|
+
function buildObject(entries) {
|
|
307
|
+
if (entries.length === 0) return "{}";
|
|
308
|
+
return `{\n${entries.map((entry) => `${indentLines(entry)},`).join("\n")}\n}`;
|
|
309
|
+
}
|
|
310
|
+
//#endregion
|
|
311
|
+
//#region ../../internals/utils/src/fs.ts
|
|
312
|
+
/**
|
|
313
|
+
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
314
|
+
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
315
|
+
* every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
|
|
316
|
+
*
|
|
317
|
+
* Empty segments are dropped before joining. They arise when the name starts with a dot
|
|
318
|
+
* followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
|
|
319
|
+
* an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
|
|
320
|
+
* absolute path, letting generated files escape the configured output directory.
|
|
321
|
+
*
|
|
322
|
+
* @example Nested path from a dotted name
|
|
323
|
+
* `toFilePath('pet.petId') // 'pet/petId'`
|
|
324
|
+
*
|
|
325
|
+
* @example PascalCase the final segment
|
|
326
|
+
* `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
|
|
327
|
+
*
|
|
328
|
+
* @example Suffix applied to the final segment only
|
|
329
|
+
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
166
330
|
*/
|
|
167
|
-
|
|
331
|
+
function toFilePath(name, caseLast = camelCase) {
|
|
332
|
+
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
333
|
+
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
334
|
+
}
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region ../../internals/utils/src/imports.ts
|
|
337
|
+
function escapeRegExp(value) {
|
|
338
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
339
|
+
}
|
|
340
|
+
function getImportNames(entry) {
|
|
341
|
+
return (Array.isArray(entry.name) ? entry.name : [entry.name]).map((name) => {
|
|
342
|
+
if (typeof name === "string") return name;
|
|
343
|
+
return name.name ?? name.propertyName;
|
|
344
|
+
}).filter((name) => Boolean(name));
|
|
345
|
+
}
|
|
346
|
+
function filterUsedImports(imports, text, skipImportNames = []) {
|
|
347
|
+
const skip = new Set(skipImportNames);
|
|
348
|
+
return imports.filter((entry) => {
|
|
349
|
+
return getImportNames(entry).some((name) => {
|
|
350
|
+
if (skip.has(name)) return false;
|
|
351
|
+
return new RegExp(`\\b${escapeRegExp(name)}\\b(?=\\s*\\()`).test(text);
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
function aliasConflictingImports(imports, reservedNames) {
|
|
356
|
+
const reservedNameSet = new Set(reservedNames);
|
|
357
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
168
358
|
return {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
359
|
+
imports: imports.map((entry) => {
|
|
360
|
+
const aliasedNames = (Array.isArray(entry.name) ? entry.name : [entry.name]).map((item) => {
|
|
361
|
+
if (typeof item !== "string" || !reservedNameSet.has(item)) return item;
|
|
362
|
+
const alias = `${item}Schema`;
|
|
363
|
+
aliases.set(item, alias);
|
|
364
|
+
return {
|
|
365
|
+
propertyName: item,
|
|
366
|
+
name: alias
|
|
367
|
+
};
|
|
175
368
|
});
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
369
|
+
return aliasedNames.some((item) => typeof item === "object" && item.name) ? {
|
|
370
|
+
...entry,
|
|
371
|
+
name: aliasedNames
|
|
372
|
+
} : entry;
|
|
373
|
+
}),
|
|
374
|
+
aliases
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function rewriteAliasedImports(text, aliases) {
|
|
378
|
+
return Array.from(aliases).reduce((acc, [name, alias]) => acc.replace(new RegExp(`\\b${escapeRegExp(name)}\\b`, "g"), alias), text);
|
|
379
|
+
}
|
|
380
|
+
//#endregion
|
|
381
|
+
//#region src/utils.ts
|
|
382
|
+
/**
|
|
383
|
+
* Returns the `@faker-js/faker` named export for a locale code.
|
|
384
|
+
*
|
|
385
|
+
* Without a locale, returns `'faker'` for the default English instance.
|
|
386
|
+
* With a locale, the language code is converted to upper case and joined with any region suffix.
|
|
387
|
+
*
|
|
388
|
+
* @example Default
|
|
389
|
+
* `localeToFakerImport() // 'faker'`
|
|
390
|
+
*
|
|
391
|
+
* @example Simple locale
|
|
392
|
+
* `localeToFakerImport('de') // 'fakerDE'`
|
|
393
|
+
*
|
|
394
|
+
* @example Compound locale
|
|
395
|
+
* `localeToFakerImport('de_AT') // 'fakerDE_AT'`
|
|
396
|
+
*/
|
|
397
|
+
function localeToFakerImport(locale) {
|
|
398
|
+
if (!locale) return "faker";
|
|
399
|
+
const parts = locale.split("_");
|
|
400
|
+
parts[0] = parts[0].toUpperCase();
|
|
401
|
+
return `faker${parts.join("_")}`;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Determines if a schema node can be overridden during faker generation.
|
|
405
|
+
*/
|
|
406
|
+
function canOverrideSchema(node) {
|
|
407
|
+
return (/* @__PURE__ */ new Set([
|
|
408
|
+
"array",
|
|
409
|
+
"tuple",
|
|
410
|
+
"object",
|
|
411
|
+
"intersection",
|
|
412
|
+
"union",
|
|
413
|
+
"enum",
|
|
414
|
+
"ref",
|
|
415
|
+
"string",
|
|
416
|
+
"email",
|
|
417
|
+
"url",
|
|
418
|
+
"uuid",
|
|
419
|
+
"number",
|
|
420
|
+
"integer",
|
|
421
|
+
"bigint",
|
|
422
|
+
"boolean",
|
|
423
|
+
"date",
|
|
424
|
+
"time",
|
|
425
|
+
"datetime",
|
|
426
|
+
"blob"
|
|
427
|
+
])).has(node.type);
|
|
428
|
+
}
|
|
429
|
+
function shouldInlineSingleResponseSchema(schema) {
|
|
430
|
+
return (/* @__PURE__ */ new Set([
|
|
431
|
+
"any",
|
|
432
|
+
"unknown",
|
|
433
|
+
"void",
|
|
434
|
+
"null",
|
|
435
|
+
"array",
|
|
436
|
+
"tuple",
|
|
437
|
+
"string",
|
|
438
|
+
"email",
|
|
439
|
+
"url",
|
|
440
|
+
"uuid",
|
|
441
|
+
"number",
|
|
442
|
+
"integer",
|
|
443
|
+
"bigint",
|
|
444
|
+
"boolean",
|
|
445
|
+
"date",
|
|
446
|
+
"time",
|
|
447
|
+
"datetime",
|
|
448
|
+
"blob",
|
|
449
|
+
"enum",
|
|
450
|
+
"union"
|
|
451
|
+
])).has(schema.type);
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Builds a response schema as a union of all response statuses.
|
|
455
|
+
* Returns null if no responses are provided, or embeds single simple responses inline.
|
|
456
|
+
*/
|
|
457
|
+
function buildResponseUnionSchema(node, resolver) {
|
|
458
|
+
const responses = node.responses.filter((response) => response.content?.[0]?.schema);
|
|
459
|
+
if (!responses.length) return null;
|
|
460
|
+
if (responses.length === 1) {
|
|
461
|
+
const schema = responses[0].content?.[0]?.schema;
|
|
462
|
+
if (schema && shouldInlineSingleResponseSchema(schema)) return schema;
|
|
463
|
+
return kubb_kit.ast.factory.createSchema({
|
|
464
|
+
type: "ref",
|
|
465
|
+
name: resolver.response.status(node, responses[0].statusCode)
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
return kubb_kit.ast.factory.createSchema({
|
|
469
|
+
type: "union",
|
|
470
|
+
members: responses.map((response) => kubb_kit.ast.factory.createSchema({
|
|
471
|
+
type: "ref",
|
|
472
|
+
name: resolver.response.status(node, response.statusCode)
|
|
473
|
+
}))
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
const SCALAR_TYPES$1 = /* @__PURE__ */ new Set([
|
|
477
|
+
"string",
|
|
478
|
+
"email",
|
|
479
|
+
"url",
|
|
480
|
+
"uuid",
|
|
481
|
+
"number",
|
|
482
|
+
"integer",
|
|
483
|
+
"bigint",
|
|
484
|
+
"boolean",
|
|
485
|
+
"date",
|
|
486
|
+
"time",
|
|
487
|
+
"datetime",
|
|
488
|
+
"blob",
|
|
489
|
+
"enum"
|
|
490
|
+
]);
|
|
491
|
+
function toRelativeImportPath(from, to) {
|
|
492
|
+
const relativePath = node_path.posix.relative(node_path.posix.dirname(from), to);
|
|
493
|
+
return relativePath.startsWith("../") ? relativePath : `./${relativePath}`;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Resolves a type reference, determining if it needs an import statement or inline type reference.
|
|
497
|
+
* Takes into account whether the type can be overridden and the file paths.
|
|
498
|
+
*/
|
|
499
|
+
function resolveTypeReference({ node, canOverride, name, typeName, filePath, typeFilePath }) {
|
|
500
|
+
const { usesTypeName } = resolveFakerTypeUsage(node, typeName, canOverride);
|
|
501
|
+
if (!usesTypeName) return { typeName };
|
|
502
|
+
if (name === typeName) return { typeName: `import('${toRelativeImportPath(filePath, typeFilePath)}').${typeName}` };
|
|
503
|
+
return {
|
|
504
|
+
importPath: typeFilePath,
|
|
505
|
+
typeName
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Maps a schema node type to its corresponding scalar type representation.
|
|
510
|
+
* Returns the type name for enums or the base type (string, number, etc.) for primitives.
|
|
511
|
+
*/
|
|
512
|
+
function getScalarType(node, typeName) {
|
|
513
|
+
switch (node.type) {
|
|
514
|
+
case "string":
|
|
515
|
+
case "email":
|
|
516
|
+
case "url":
|
|
517
|
+
case "uuid": return "string";
|
|
518
|
+
case "number":
|
|
519
|
+
case "integer": return "number";
|
|
520
|
+
case "bigint": return "bigint";
|
|
521
|
+
case "boolean": return "boolean";
|
|
522
|
+
case "date":
|
|
523
|
+
case "time": return node.representation === "date" ? "Date" : "string";
|
|
524
|
+
case "datetime": return "string";
|
|
525
|
+
case "blob": return "Blob";
|
|
526
|
+
case "enum": return typeName;
|
|
527
|
+
default: return typeName;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Resolves faker type usage information for a schema.
|
|
532
|
+
* Determines the data type, return type, and whether it uses the type name.
|
|
533
|
+
*/
|
|
534
|
+
function resolveFakerTypeUsage(node, typeName, canOverride) {
|
|
535
|
+
const isArray = node.type === "array";
|
|
536
|
+
const isTuple = node.type === "tuple";
|
|
537
|
+
const isScalar = SCALAR_TYPES$1.has(node.type);
|
|
538
|
+
let dataType = `Partial<${typeName}>`;
|
|
539
|
+
if (isArray || isTuple || node.type === "union" || node.type === "enum") dataType = typeName;
|
|
540
|
+
if (isScalar) dataType = getScalarType(node, typeName);
|
|
541
|
+
let returnType = canOverride ? typeName : null;
|
|
542
|
+
if (isScalar) returnType = getScalarType(node, typeName);
|
|
543
|
+
return {
|
|
544
|
+
dataType,
|
|
545
|
+
returnType,
|
|
546
|
+
usesTypeName: dataType.includes(typeName) || Boolean(returnType?.includes(typeName))
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
//#endregion
|
|
550
|
+
//#region src/components/Faker.tsx
|
|
551
|
+
const OBJECT_TYPES = /* @__PURE__ */ new Set(["object", "intersection"]);
|
|
552
|
+
const SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
553
|
+
"string",
|
|
554
|
+
"email",
|
|
555
|
+
"url",
|
|
556
|
+
"uuid",
|
|
557
|
+
"number",
|
|
558
|
+
"integer",
|
|
559
|
+
"bigint",
|
|
560
|
+
"boolean",
|
|
561
|
+
"date",
|
|
562
|
+
"time",
|
|
563
|
+
"datetime",
|
|
564
|
+
"blob",
|
|
565
|
+
"enum"
|
|
566
|
+
]);
|
|
567
|
+
const declarationPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
|
|
568
|
+
function Faker({ node, description, name, typeName, printer, seed, canOverride }) {
|
|
569
|
+
const fakerText = printer.print(node) ?? "undefined";
|
|
570
|
+
const isArray = node.type === "array";
|
|
571
|
+
const isObject = OBJECT_TYPES.has(node.type);
|
|
572
|
+
const isTuple = node.type === "tuple";
|
|
573
|
+
const isScalar = SCALAR_TYPES.has(node.type);
|
|
574
|
+
const useGenericOverride = canOverride && isObject;
|
|
575
|
+
const fakerTextWithOverride = (() => {
|
|
576
|
+
if (canOverride && isTuple) return `data || ${fakerText}`;
|
|
577
|
+
if (canOverride && isArray) return `[\n ...${fakerText},\n ...(data || [])\n]`;
|
|
578
|
+
if (canOverride && isScalar) return `data ?? ${fakerText}`;
|
|
579
|
+
return fakerText;
|
|
580
|
+
})();
|
|
581
|
+
const { dataType, returnType: resolvedReturnType } = resolveFakerTypeUsage(node, typeName, canOverride);
|
|
582
|
+
if (!useGenericOverride) {
|
|
583
|
+
const params = (0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
|
|
584
|
+
name: /\bdata\b/.test(fakerTextWithOverride) ? "data" : "_data",
|
|
585
|
+
type: dataType,
|
|
586
|
+
optional: true
|
|
587
|
+
})] });
|
|
588
|
+
const paramsSignature = declarationPrinter.print(params) ?? "";
|
|
589
|
+
const returnType = resolvedReturnType;
|
|
590
|
+
const returnExpression = node.type === "ref" && canOverride && returnType ? `${fakerTextWithOverride} as ${returnType}` : fakerTextWithOverride;
|
|
591
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
|
|
592
|
+
name,
|
|
593
|
+
isExportable: true,
|
|
594
|
+
isIndexable: true,
|
|
595
|
+
children: /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.Function, {
|
|
596
|
+
export: true,
|
|
597
|
+
name,
|
|
598
|
+
JSDoc: { comments: description ? [`@description ${jsStringEscape(description)}`] : [] },
|
|
599
|
+
params: canOverride ? paramsSignature : void 0,
|
|
600
|
+
returnType: returnType ?? void 0,
|
|
601
|
+
children: [seed ? /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [`faker.seed(${JSON.stringify(seed)})`, /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)("br", {})] }) : void 0, `return ${returnExpression}`]
|
|
602
|
+
})
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
const functionSignature = `${description ? `/**\n * @description ${jsStringEscape(description)}\n */\n ` : ""}export function ${name}<TData extends Partial<${typeName}> = object>(data?: TData)`;
|
|
606
|
+
const seedCode = seed ? `faker.seed(${JSON.stringify(seed)})\n ` : "";
|
|
607
|
+
const { cyclicSchemas, schemaName } = printer.options;
|
|
608
|
+
const functionBody = node.type === "object" && !!cyclicSchemas && (node.properties ?? []).some((p) => (0, kubb_kit.containsCircularRef)(p.schema, {
|
|
609
|
+
circularSchemas: cyclicSchemas,
|
|
610
|
+
excludeName: schemaName
|
|
611
|
+
})) ? `{
|
|
612
|
+
${seedCode}const defaultFakeData = ${fakerText}
|
|
613
|
+
if (data) {
|
|
614
|
+
for (const [key, value] of Object.entries(data)) {
|
|
615
|
+
Object.defineProperty(defaultFakeData, key, { value, configurable: true, writable: true, enumerable: true })
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return defaultFakeData as Omit<typeof defaultFakeData, keyof TData> & TData
|
|
619
|
+
}` : `{
|
|
620
|
+
${seedCode}const defaultFakeData = ${fakerText}
|
|
621
|
+
return {
|
|
622
|
+
...defaultFakeData,
|
|
623
|
+
...(data || {}),
|
|
624
|
+
} as Omit<typeof defaultFakeData, keyof TData> & TData
|
|
625
|
+
}`;
|
|
626
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.File.Source, {
|
|
627
|
+
name,
|
|
628
|
+
isExportable: true,
|
|
629
|
+
isIndexable: true,
|
|
630
|
+
children: [functionSignature, functionBody]
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
//#endregion
|
|
634
|
+
//#region ../../internals/shared/src/params.ts
|
|
635
|
+
/**
|
|
636
|
+
* Drops parameters that share the same name, keeping the first.
|
|
637
|
+
*
|
|
638
|
+
* A malformed spec can declare the same parameter name twice within one `in` location. Both would
|
|
639
|
+
* resolve to the same output property, so emitting both would yield an object type with a duplicate
|
|
640
|
+
* member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
|
|
641
|
+
* parameter names flow through unchanged, so no two distinct names ever collide here anymore.
|
|
642
|
+
*/
|
|
643
|
+
function dedupeParams(params) {
|
|
644
|
+
const seen = /* @__PURE__ */ new Set();
|
|
645
|
+
return params.filter((param) => {
|
|
646
|
+
if (seen.has(param.name)) return false;
|
|
647
|
+
seen.add(param.name);
|
|
648
|
+
return true;
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
//#endregion
|
|
652
|
+
//#region ../../internals/shared/src/operation.ts
|
|
653
|
+
/**
|
|
654
|
+
* Maps a content type to the PascalCase suffix used to name per-content-type variants
|
|
655
|
+
* (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
|
|
656
|
+
*/
|
|
657
|
+
function getContentTypeSuffix(contentType) {
|
|
658
|
+
const baseType = contentType.split(";")[0].trim();
|
|
659
|
+
if (baseType === "application/json") return "Json";
|
|
660
|
+
if (baseType === "multipart/form-data") return "FormData";
|
|
661
|
+
if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
|
|
662
|
+
const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
663
|
+
if (parts.length === 0) return "Unknown";
|
|
664
|
+
return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
|
|
668
|
+
* (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
|
|
669
|
+
*/
|
|
670
|
+
function getPerContentTypeName(baseName, suffix) {
|
|
671
|
+
if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
|
|
672
|
+
return baseName + suffix;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Resolves per-content-type variant names for a set of content entries, deduplicating suffix
|
|
676
|
+
* collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is
|
|
677
|
+
* the final (possibly counter-augmented) value, so callers can derive parallel names in another
|
|
678
|
+
* namespace (e.g. plugin-faker deriving the matching plugin-ts type name).
|
|
679
|
+
*/
|
|
680
|
+
function resolveContentTypeVariants(entries, baseName) {
|
|
681
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
682
|
+
return entries.filter((entry) => entry.schema).map((entry) => {
|
|
683
|
+
const baseSuffix = getContentTypeSuffix(entry.contentType);
|
|
684
|
+
let suffix = baseSuffix;
|
|
685
|
+
let name = getPerContentTypeName(baseName, suffix);
|
|
686
|
+
let counter = 2;
|
|
687
|
+
while (usedNames.has(name)) {
|
|
688
|
+
suffix = `${baseSuffix}${counter++}`;
|
|
689
|
+
name = getPerContentTypeName(baseName, suffix);
|
|
690
|
+
}
|
|
691
|
+
usedNames.add(name);
|
|
692
|
+
return {
|
|
693
|
+
name,
|
|
694
|
+
suffix,
|
|
695
|
+
schema: entry.schema,
|
|
696
|
+
keysToOmit: entry.keysToOmit,
|
|
697
|
+
contentType: entry.contentType
|
|
698
|
+
};
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
function getOperationParameters(node) {
|
|
702
|
+
return {
|
|
703
|
+
path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
|
|
704
|
+
query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
|
|
705
|
+
header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
|
|
706
|
+
cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
//#endregion
|
|
710
|
+
//#region ../../internals/shared/src/resolver.ts
|
|
711
|
+
/**
|
|
712
|
+
* Resolves a single operation parameter name with the
|
|
713
|
+
* `<operationId> <in> <name>` template.
|
|
714
|
+
*
|
|
715
|
+
* @example
|
|
716
|
+
* `operationParamName.call(resolver, node, param) // → 'DeletePetPathPetId'`
|
|
717
|
+
*/
|
|
718
|
+
function operationParamName(node, param) {
|
|
719
|
+
return this.name(`${node.operationId} ${param.in} ${param.name}`);
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Builds the shared `param` namespace. Spread the result into `createResolver`
|
|
723
|
+
* and override individual methods next to it when a plugin deviates.
|
|
724
|
+
*
|
|
725
|
+
* @example
|
|
726
|
+
* ```ts
|
|
727
|
+
* createResolver<PluginTs>({ param: createOperationParamResolver(), ... })
|
|
728
|
+
* ```
|
|
729
|
+
*/
|
|
730
|
+
function createOperationParamResolver() {
|
|
731
|
+
return {
|
|
732
|
+
name: operationParamName,
|
|
733
|
+
path(node) {
|
|
734
|
+
return this.name(`${node.operationId} Path`);
|
|
212
735
|
},
|
|
213
|
-
|
|
214
|
-
return this.
|
|
736
|
+
query(node) {
|
|
737
|
+
return this.name(`${node.operationId} Query`);
|
|
215
738
|
},
|
|
216
|
-
|
|
217
|
-
return this.
|
|
739
|
+
headers(node) {
|
|
740
|
+
return this.name(`${node.operationId} Headers`);
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Builds the shared `response` namespace. Spread the result into
|
|
746
|
+
* `createResolver` and add plugin-specific methods (`options`, `error`) next
|
|
747
|
+
* to it.
|
|
748
|
+
*
|
|
749
|
+
* @example
|
|
750
|
+
* ```ts
|
|
751
|
+
* createResolver<PluginTs>({ response: { ...createOperationResponseResolver(), options(node) {...} }, ... })
|
|
752
|
+
* ```
|
|
753
|
+
*/
|
|
754
|
+
function createOperationResponseResolver() {
|
|
755
|
+
return {
|
|
756
|
+
status(node, statusCode) {
|
|
757
|
+
return this.name(`${node.operationId} Status ${statusCode}`);
|
|
218
758
|
},
|
|
219
|
-
|
|
220
|
-
return this.
|
|
759
|
+
body(node) {
|
|
760
|
+
return this.name(`${node.operationId} Body`);
|
|
221
761
|
},
|
|
222
|
-
|
|
223
|
-
return this.
|
|
762
|
+
responses(node) {
|
|
763
|
+
return this.name(`${node.operationId} Responses`);
|
|
224
764
|
},
|
|
225
|
-
|
|
226
|
-
return this.
|
|
765
|
+
response(node) {
|
|
766
|
+
return this.name(`${node.operationId} Response`);
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Builds a resolver `file` override whose base name runs every path segment
|
|
772
|
+
* through `toFilePath`, casing the final segment with `caseLast`.
|
|
773
|
+
*
|
|
774
|
+
* @example
|
|
775
|
+
* ```ts
|
|
776
|
+
* createResolver<PluginTs>({ file: createCasedFile(pascalCase), ... })
|
|
777
|
+
* ```
|
|
778
|
+
*/
|
|
779
|
+
function createCasedFile(caseLast) {
|
|
780
|
+
return { baseName({ name, extname }) {
|
|
781
|
+
return `${toFilePath(name, caseLast)}${extname}`;
|
|
782
|
+
} };
|
|
783
|
+
}
|
|
784
|
+
//#endregion
|
|
785
|
+
//#region ../../internals/shared/src/group.ts
|
|
786
|
+
/**
|
|
787
|
+
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
788
|
+
* shared default naming so every plugin groups output consistently:
|
|
789
|
+
*
|
|
790
|
+
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
791
|
+
* - other groups use the camelCased group (`pet store` → `petStore`).
|
|
792
|
+
*
|
|
793
|
+
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
794
|
+
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
795
|
+
* per-plugin convention.
|
|
796
|
+
*
|
|
797
|
+
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
798
|
+
*
|
|
799
|
+
* @example
|
|
800
|
+
* ```ts
|
|
801
|
+
* createGroupConfig(group) // shared across every plugin
|
|
802
|
+
* ```
|
|
803
|
+
*/
|
|
804
|
+
function createGroupConfig(group) {
|
|
805
|
+
if (!group) return null;
|
|
806
|
+
const defaultName = (ctx) => {
|
|
807
|
+
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
808
|
+
return camelCase(ctx.group);
|
|
809
|
+
};
|
|
810
|
+
return {
|
|
811
|
+
...group,
|
|
812
|
+
name: group.name ? group.name : defaultName
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
//#endregion
|
|
816
|
+
//#region ../../internals/shared/src/schemaTraversal.ts
|
|
817
|
+
/**
|
|
818
|
+
* Maps each member of a union or intersection schema to its transformed output, pairing every
|
|
819
|
+
* result with the original member.
|
|
820
|
+
*/
|
|
821
|
+
function mapSchemaMembers(node, transform) {
|
|
822
|
+
return (node.members ?? []).map((schema) => ({
|
|
823
|
+
schema,
|
|
824
|
+
output: transform(schema)
|
|
825
|
+
}));
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Maps each item of an array or tuple schema to its transformed output, pairing every result with
|
|
829
|
+
* the original item.
|
|
830
|
+
*/
|
|
831
|
+
function mapSchemaItems(node, transform) {
|
|
832
|
+
return (node.items ?? []).map((schema) => ({
|
|
833
|
+
schema,
|
|
834
|
+
output: transform(schema)
|
|
835
|
+
}));
|
|
836
|
+
}
|
|
837
|
+
//#endregion
|
|
838
|
+
//#region src/printers/printerFaker.ts
|
|
839
|
+
const fakerKeywordMapper = {
|
|
840
|
+
any: () => "undefined",
|
|
841
|
+
unknown: () => "undefined",
|
|
842
|
+
void: () => "undefined",
|
|
843
|
+
number: (min, max) => {
|
|
844
|
+
if (max !== void 0 && min !== void 0) return `faker.number.float({ min: ${min}, max: ${max} })`;
|
|
845
|
+
if (max !== void 0) return `faker.number.float({ max: ${max} })`;
|
|
846
|
+
if (min !== void 0) return `faker.number.float({ min: ${min} })`;
|
|
847
|
+
return "faker.number.float()";
|
|
848
|
+
},
|
|
849
|
+
integer: (min, max) => {
|
|
850
|
+
if (max !== void 0 && min !== void 0) return `faker.number.int({ min: ${min}, max: ${max} })`;
|
|
851
|
+
if (max !== void 0) return `faker.number.int({ max: ${max} })`;
|
|
852
|
+
if (min !== void 0) return `faker.number.int({ min: ${min} })`;
|
|
853
|
+
return "faker.number.int()";
|
|
854
|
+
},
|
|
855
|
+
bigint: () => "faker.number.bigInt()",
|
|
856
|
+
string: (min, max) => {
|
|
857
|
+
if (max !== void 0 && min !== void 0) return `faker.string.alpha({ length: { min: ${min}, max: ${max} } })`;
|
|
858
|
+
if (max !== void 0) return `faker.string.alpha({ length: ${max} })`;
|
|
859
|
+
if (min !== void 0) return `faker.string.alpha({ length: ${min} })`;
|
|
860
|
+
return "faker.string.alpha()";
|
|
861
|
+
},
|
|
862
|
+
boolean: () => "faker.datatype.boolean()",
|
|
863
|
+
null: () => "null",
|
|
864
|
+
array: (items = [], min, max) => {
|
|
865
|
+
if (items.length > 1) return `faker.helpers.arrayElements([${items.join(", ")}])`;
|
|
866
|
+
const item = items.at(0);
|
|
867
|
+
if (min !== void 0 && max !== void 0) return `faker.helpers.multiple(() => (${item}), { count: { min: ${min}, max: ${max} }})`;
|
|
868
|
+
if (min !== void 0) return `faker.helpers.multiple(() => (${item}), { count: ${min} })`;
|
|
869
|
+
if (max !== void 0) return `faker.helpers.multiple(() => (${item}), { count: { min: 0, max: ${max} }})`;
|
|
870
|
+
return `faker.helpers.multiple(() => (${item}))`;
|
|
871
|
+
},
|
|
872
|
+
tuple: (items = []) => `[${items.join(", ")}]`,
|
|
873
|
+
enum: (items = [], type) => `faker.helpers.arrayElement${type ? `<${type}>` : ""}([${items.join(", ")}])`,
|
|
874
|
+
union: (items = []) => `faker.helpers.arrayElement([${items.join(", ")}])`,
|
|
875
|
+
datetime: () => "faker.date.anytime().toISOString()",
|
|
876
|
+
date: (representation = "string", parser = "faker") => {
|
|
877
|
+
if (representation === "string") {
|
|
878
|
+
if (parser !== "faker") return `${parser}(faker.date.anytime()).format("YYYY-MM-DD")`;
|
|
879
|
+
return "faker.date.anytime().toISOString().substring(0, 10)";
|
|
880
|
+
}
|
|
881
|
+
if (parser !== "faker") throw new Error(`type '${representation}' and parser '${parser}' can not work together`);
|
|
882
|
+
return "faker.date.anytime()";
|
|
883
|
+
},
|
|
884
|
+
time: (representation = "string", parser = "faker") => {
|
|
885
|
+
if (representation === "string") {
|
|
886
|
+
if (parser !== "faker") return `${parser}(faker.date.anytime()).format("HH:mm:ss")`;
|
|
887
|
+
return "faker.date.anytime().toISOString().substring(11, 19)";
|
|
888
|
+
}
|
|
889
|
+
if (parser !== "faker") throw new Error(`type '${representation}' and parser '${parser}' can not work together`);
|
|
890
|
+
return "faker.date.anytime()";
|
|
891
|
+
},
|
|
892
|
+
uuid: () => "faker.string.uuid()",
|
|
893
|
+
url: () => "faker.internet.url()",
|
|
894
|
+
and: (items = []) => {
|
|
895
|
+
if (items.length === 0) return "{}";
|
|
896
|
+
if (items.length === 1) return items[0] ?? "{}";
|
|
897
|
+
return `{...${items.join(", ...")}}`;
|
|
898
|
+
},
|
|
899
|
+
matches: (value = "", regexGenerator = "faker") => {
|
|
900
|
+
if (regexGenerator === "randexp") return `${toRegExpString(value, "RandExp")}.gen()`;
|
|
901
|
+
return `faker.helpers.fromRegExp("${value}")`;
|
|
902
|
+
},
|
|
903
|
+
email: () => "faker.internet.email()",
|
|
904
|
+
blob: () => "faker.image.url() as unknown as Blob"
|
|
905
|
+
};
|
|
906
|
+
function getEnumValues(node) {
|
|
907
|
+
if (node.namedEnumValues?.length) return node.namedEnumValues.map((item) => item.value);
|
|
908
|
+
return node.enumValues ?? [];
|
|
909
|
+
}
|
|
910
|
+
function parseEnumValue(value) {
|
|
911
|
+
if (typeof value === "string") return stringify(value);
|
|
912
|
+
return value;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Reads the discriminator literal off a variant, or `undefined` when it can't be determined.
|
|
916
|
+
*/
|
|
917
|
+
function getDiscriminatorValue(member, discriminatorPropertyName) {
|
|
918
|
+
const prop = kubb_kit.ast.narrowSchema(member, "object")?.properties?.find((p) => p.name === discriminatorPropertyName);
|
|
919
|
+
const enumNode = prop ? kubb_kit.ast.narrowSchema(prop.schema, "enum") : null;
|
|
920
|
+
return enumNode ? getEnumValues(enumNode)[0] : void 0;
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Type expression for an object property's value, indexed off the parent `typeName`.
|
|
924
|
+
*
|
|
925
|
+
* In a union (`oneOf`), a key that only some branches declare turns a plain `NonNullable<T>[K]`
|
|
926
|
+
* into a TS2339 error, so union members guard the access. The breakdown is below.
|
|
927
|
+
*/
|
|
928
|
+
function indexedTypeName(typeName, propertyName, nestedInUnion) {
|
|
929
|
+
const key = JSON.stringify(propertyName);
|
|
930
|
+
return nestedInUnion ? `(NonNullable<${typeName}> & Record<${key}, unknown>)[${key}]` : `NonNullable<${typeName}>[${key}]`;
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Creates a Faker printer that generates mock data generation code from schema nodes.
|
|
934
|
+
* Handles circular references gracefully by emitting memoizing getters for cyclic properties.
|
|
935
|
+
*/
|
|
936
|
+
const printerFaker = kubb_kit.ast.createPrinter((options) => {
|
|
937
|
+
const printNested = (node, overrideOptions = {}) => {
|
|
938
|
+
return printerFaker({
|
|
939
|
+
...options,
|
|
940
|
+
...overrideOptions,
|
|
941
|
+
nodes: options.nodes
|
|
942
|
+
}).print(node) ?? "undefined";
|
|
943
|
+
};
|
|
944
|
+
return {
|
|
945
|
+
name: "faker",
|
|
946
|
+
options,
|
|
947
|
+
nodes: {
|
|
948
|
+
any: () => fakerKeywordMapper.any(),
|
|
949
|
+
unknown: () => fakerKeywordMapper.unknown(),
|
|
950
|
+
void: () => fakerKeywordMapper.void(),
|
|
951
|
+
boolean: () => fakerKeywordMapper.boolean(),
|
|
952
|
+
null: () => fakerKeywordMapper.null(),
|
|
953
|
+
string(node) {
|
|
954
|
+
if (node.pattern) return fakerKeywordMapper.matches(node.pattern, this.options.regexGenerator);
|
|
955
|
+
return fakerKeywordMapper.string(node.min, node.max);
|
|
956
|
+
},
|
|
957
|
+
email: () => fakerKeywordMapper.email(),
|
|
958
|
+
url: () => fakerKeywordMapper.url(),
|
|
959
|
+
uuid: () => fakerKeywordMapper.uuid(),
|
|
960
|
+
number(node) {
|
|
961
|
+
return fakerKeywordMapper.number(node.min, node.max);
|
|
962
|
+
},
|
|
963
|
+
integer(node) {
|
|
964
|
+
return fakerKeywordMapper.integer(node.min, node.max);
|
|
965
|
+
},
|
|
966
|
+
bigint: () => fakerKeywordMapper.bigint(),
|
|
967
|
+
blob: () => fakerKeywordMapper.blob(),
|
|
968
|
+
datetime: () => fakerKeywordMapper.datetime(),
|
|
969
|
+
date(node) {
|
|
970
|
+
return fakerKeywordMapper.date(node.representation ?? "string", this.options.dateParser);
|
|
971
|
+
},
|
|
972
|
+
time(node) {
|
|
973
|
+
return fakerKeywordMapper.time(node.representation ?? "string", this.options.dateParser);
|
|
974
|
+
},
|
|
975
|
+
ref(node) {
|
|
976
|
+
const refName = kubb_kit.ast.resolveRefName(node);
|
|
977
|
+
if (!refName) throw new Error("Name not defined for ref node");
|
|
978
|
+
if (this.options.schemaName && refName === this.options.schemaName) return this.options.typeName ? `undefined as unknown as ${this.options.typeName}` : "undefined as unknown";
|
|
979
|
+
const resolvedName = node.ref ? this.options.resolver.name(refName) : refName;
|
|
980
|
+
if (!this.options.nestedInObject) return `${resolvedName}(data)`;
|
|
981
|
+
return `${resolvedName}()`;
|
|
982
|
+
},
|
|
983
|
+
enum(node) {
|
|
984
|
+
return fakerKeywordMapper.enum(getEnumValues(node).map(parseEnumValue), this.options.typeName);
|
|
985
|
+
},
|
|
986
|
+
union(node) {
|
|
987
|
+
const { discriminatorPropertyName } = node;
|
|
988
|
+
const baseTypeName = this.options.typeName;
|
|
989
|
+
const items = mapSchemaMembers(node, (member) => {
|
|
990
|
+
const value = discriminatorPropertyName ? getDiscriminatorValue(member, discriminatorPropertyName) : void 0;
|
|
991
|
+
if (baseTypeName && value !== void 0) {
|
|
992
|
+
const typeName = `Extract<NonNullable<${baseTypeName}>, { ${JSON.stringify(discriminatorPropertyName)}: ${parseEnumValue(value)} }>`;
|
|
993
|
+
return printNested(member, {
|
|
994
|
+
typeName,
|
|
995
|
+
nestedInObject: true
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
return printNested(member, {
|
|
999
|
+
typeName: baseTypeName,
|
|
1000
|
+
nestedInObject: true,
|
|
1001
|
+
nestedInUnion: true
|
|
1002
|
+
});
|
|
1003
|
+
}).map(({ output }) => output).filter((item) => Boolean(item));
|
|
1004
|
+
return fakerKeywordMapper.union(items);
|
|
1005
|
+
},
|
|
1006
|
+
intersection(node) {
|
|
1007
|
+
const items = mapSchemaMembers(node, (member) => printNested(member, { nestedInObject: true })).map(({ output }) => output).filter((item) => Boolean(item) && item !== "undefined");
|
|
1008
|
+
return fakerKeywordMapper.and(items);
|
|
1009
|
+
},
|
|
1010
|
+
array(node) {
|
|
1011
|
+
const items = mapSchemaItems(node, (member) => printNested(member, {
|
|
1012
|
+
typeName: this.options.typeName ? `NonNullable<${this.options.typeName}>[number]` : void 0,
|
|
1013
|
+
nestedInObject: true
|
|
1014
|
+
})).map(({ output }) => output).filter((item) => Boolean(item));
|
|
1015
|
+
return fakerKeywordMapper.array(items, node.min, node.max);
|
|
1016
|
+
},
|
|
1017
|
+
tuple(node) {
|
|
1018
|
+
const items = (node.items ?? []).map((member, index) => printNested(member, {
|
|
1019
|
+
typeName: this.options.typeName ? `NonNullable<${this.options.typeName}>[${index}]` : void 0,
|
|
1020
|
+
nestedInObject: true
|
|
1021
|
+
})).filter((item) => Boolean(item));
|
|
1022
|
+
return fakerKeywordMapper.tuple(items);
|
|
1023
|
+
},
|
|
1024
|
+
object(node) {
|
|
1025
|
+
const cyclicSchemas = this.options.cyclicSchemas;
|
|
1026
|
+
return buildObject((node.properties ?? []).map((property) => {
|
|
1027
|
+
const value = printNested(property.schema, {
|
|
1028
|
+
typeName: this.options.typeName ? indexedTypeName(this.options.typeName, property.name, this.options.nestedInUnion) : void 0,
|
|
1029
|
+
nestedInObject: true
|
|
1030
|
+
}) ?? "undefined";
|
|
1031
|
+
if (cyclicSchemas && (0, kubb_kit.containsCircularRef)(property.schema, {
|
|
1032
|
+
circularSchemas: cyclicSchemas,
|
|
1033
|
+
excludeName: this.options.schemaName
|
|
1034
|
+
})) return `get ${objectKey(property.name)}() { const _value = ${value}; Object.defineProperty(this, ${JSON.stringify(property.name)}, { value: _value, configurable: true, writable: true, enumerable: true }); return _value }`;
|
|
1035
|
+
return `${objectKey(property.name)}: ${value}`;
|
|
1036
|
+
}));
|
|
1037
|
+
},
|
|
1038
|
+
...options.nodes
|
|
227
1039
|
},
|
|
228
|
-
|
|
229
|
-
return this.
|
|
1040
|
+
print(node) {
|
|
1041
|
+
return this.transform(node) ?? null;
|
|
230
1042
|
}
|
|
231
1043
|
};
|
|
232
1044
|
});
|
|
233
1045
|
//#endregion
|
|
1046
|
+
//#region src/generators/fakerGenerator.tsx
|
|
1047
|
+
/**
|
|
1048
|
+
* Built-in generator for `@kubb/plugin-faker`. Emits one `createX` factory
|
|
1049
|
+
* per schema in the spec plus per-operation request/response factories. Each
|
|
1050
|
+
* factory returns a value matching the corresponding TypeScript type from
|
|
1051
|
+
* `@kubb/plugin-ts`.
|
|
1052
|
+
*/
|
|
1053
|
+
const fakerGenerator = (0, kubb_kit.defineGenerator)({
|
|
1054
|
+
name: "faker",
|
|
1055
|
+
renderer: kubb_jsx.jsxRenderer,
|
|
1056
|
+
schema(node, ctx) {
|
|
1057
|
+
const { config, resolver, root } = ctx;
|
|
1058
|
+
const { output, group, dateParser, regexGenerator, seed, locale, printer } = ctx.options;
|
|
1059
|
+
const pluginTs = ctx.driver.getPlugin(_kubb_plugin_ts.pluginTsName);
|
|
1060
|
+
if (!node.name || !pluginTs) return;
|
|
1061
|
+
const tsResolver = ctx.driver.getResolver(_kubb_plugin_ts.pluginTsName);
|
|
1062
|
+
const schemaName = node.name;
|
|
1063
|
+
const isEnumSchema = !!kubb_kit.ast.narrowSchema(node, kubb_kit.ast.schemaTypes.enum);
|
|
1064
|
+
const tsEnumType = pluginTs.options?.enum?.type;
|
|
1065
|
+
const tsEnumTypeSuffix = pluginTs.options?.enum?.typeSuffix ?? "Key";
|
|
1066
|
+
const schemaTypeName = isEnumSchema && tsEnumType === "asConst" ? tsResolver.enum.keyName({ name: schemaName }, tsEnumTypeSuffix) : tsResolver.name(schemaName);
|
|
1067
|
+
const meta = {
|
|
1068
|
+
name: resolver.name(schemaName),
|
|
1069
|
+
file: resolver.file({
|
|
1070
|
+
name: schemaName,
|
|
1071
|
+
extname: ".ts",
|
|
1072
|
+
root,
|
|
1073
|
+
output,
|
|
1074
|
+
group: group ?? void 0
|
|
1075
|
+
}),
|
|
1076
|
+
typeName: schemaTypeName,
|
|
1077
|
+
typeFile: tsResolver.file({
|
|
1078
|
+
name: schemaName,
|
|
1079
|
+
extname: ".ts",
|
|
1080
|
+
root,
|
|
1081
|
+
output: pluginTs.options?.output ?? output,
|
|
1082
|
+
group: pluginTs.options?.group ?? void 0
|
|
1083
|
+
})
|
|
1084
|
+
};
|
|
1085
|
+
const canOverride = canOverrideSchema(node);
|
|
1086
|
+
const cyclicSchemas = new Set(ctx.meta.circularNames);
|
|
1087
|
+
const printerInstance = printerFaker({
|
|
1088
|
+
resolver,
|
|
1089
|
+
schemaName,
|
|
1090
|
+
typeName: meta.typeName,
|
|
1091
|
+
dateParser,
|
|
1092
|
+
regexGenerator,
|
|
1093
|
+
nodes: printer?.nodes,
|
|
1094
|
+
cyclicSchemas
|
|
1095
|
+
});
|
|
1096
|
+
const fakerText = printerInstance.print(node) ?? "undefined";
|
|
1097
|
+
const typeReference = resolveTypeReference({
|
|
1098
|
+
node,
|
|
1099
|
+
canOverride,
|
|
1100
|
+
name: meta.name,
|
|
1101
|
+
typeName: meta.typeName,
|
|
1102
|
+
filePath: meta.file.path,
|
|
1103
|
+
typeFilePath: meta.typeFile.path
|
|
1104
|
+
});
|
|
1105
|
+
const usedImports = filterUsedImports(resolver.imports({
|
|
1106
|
+
node,
|
|
1107
|
+
root,
|
|
1108
|
+
output,
|
|
1109
|
+
group: group ?? void 0
|
|
1110
|
+
}), fakerText);
|
|
1111
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.File, {
|
|
1112
|
+
baseName: meta.file.baseName,
|
|
1113
|
+
path: meta.file.path,
|
|
1114
|
+
meta: meta.file.meta,
|
|
1115
|
+
banner: resolver.default.banner(ctx.meta, {
|
|
1116
|
+
output,
|
|
1117
|
+
config,
|
|
1118
|
+
file: {
|
|
1119
|
+
path: meta.file.path,
|
|
1120
|
+
baseName: meta.file.baseName
|
|
1121
|
+
}
|
|
1122
|
+
}),
|
|
1123
|
+
footer: resolver.default.footer(ctx.meta, {
|
|
1124
|
+
output,
|
|
1125
|
+
config,
|
|
1126
|
+
file: {
|
|
1127
|
+
path: meta.file.path,
|
|
1128
|
+
baseName: meta.file.baseName
|
|
1129
|
+
}
|
|
1130
|
+
}),
|
|
1131
|
+
children: [
|
|
1132
|
+
/* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
1133
|
+
name: locale ? [{
|
|
1134
|
+
propertyName: localeToFakerImport(locale),
|
|
1135
|
+
name: "faker"
|
|
1136
|
+
}] : ["faker"],
|
|
1137
|
+
path: "@faker-js/faker"
|
|
1138
|
+
}),
|
|
1139
|
+
regexGenerator === "randexp" && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
1140
|
+
name: "RandExp",
|
|
1141
|
+
path: "randexp"
|
|
1142
|
+
}),
|
|
1143
|
+
dateParser !== "faker" && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
1144
|
+
path: dateParser,
|
|
1145
|
+
name: dateParser
|
|
1146
|
+
}),
|
|
1147
|
+
typeReference.importPath && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
1148
|
+
isTypeOnly: true,
|
|
1149
|
+
root: meta.file.path,
|
|
1150
|
+
path: typeReference.importPath,
|
|
1151
|
+
name: [meta.typeName]
|
|
1152
|
+
}),
|
|
1153
|
+
usedImports.map((imp) => /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
1154
|
+
root: meta.file.path,
|
|
1155
|
+
path: imp.path,
|
|
1156
|
+
name: imp.name
|
|
1157
|
+
}, [
|
|
1158
|
+
schemaName,
|
|
1159
|
+
imp.path,
|
|
1160
|
+
imp.name
|
|
1161
|
+
].join("-"))),
|
|
1162
|
+
/* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Faker, {
|
|
1163
|
+
name: meta.name,
|
|
1164
|
+
typeName: typeReference.typeName,
|
|
1165
|
+
description: node.description,
|
|
1166
|
+
node,
|
|
1167
|
+
printer: printerInstance,
|
|
1168
|
+
seed,
|
|
1169
|
+
canOverride
|
|
1170
|
+
})
|
|
1171
|
+
]
|
|
1172
|
+
});
|
|
1173
|
+
},
|
|
1174
|
+
operation(node, ctx) {
|
|
1175
|
+
const { config, resolver, root } = ctx;
|
|
1176
|
+
const { output, group, dateParser, regexGenerator, seed, locale, printer } = ctx.options;
|
|
1177
|
+
const pluginTs = ctx.driver.getPlugin(_kubb_plugin_ts.pluginTsName);
|
|
1178
|
+
if (!pluginTs) return;
|
|
1179
|
+
const tsResolver = ctx.driver.getResolver(_kubb_plugin_ts.pluginTsName);
|
|
1180
|
+
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
|
|
1181
|
+
const paramGroups = [
|
|
1182
|
+
{
|
|
1183
|
+
params: pathParams,
|
|
1184
|
+
name: resolver.param.path,
|
|
1185
|
+
typeName: tsResolver.param.path
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
params: queryParams,
|
|
1189
|
+
name: resolver.param.query,
|
|
1190
|
+
typeName: tsResolver.param.query
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
params: headerParams,
|
|
1194
|
+
name: resolver.param.headers,
|
|
1195
|
+
typeName: tsResolver.param.headers
|
|
1196
|
+
}
|
|
1197
|
+
].filter((group) => group.params.length > 0).map((group) => ({
|
|
1198
|
+
schema: (0, _kubb_plugin_ts.buildParams)({ params: group.params }),
|
|
1199
|
+
name: group.name(node, group.params[0]),
|
|
1200
|
+
typeName: group.typeName(node, group.params[0])
|
|
1201
|
+
}));
|
|
1202
|
+
function expandContentUnits(entries, baseName, tsBaseName, description, decorate) {
|
|
1203
|
+
const withSchema = entries.filter((entry) => entry.schema);
|
|
1204
|
+
if (withSchema.length <= 1) {
|
|
1205
|
+
const primary = withSchema[0] ?? entries[0];
|
|
1206
|
+
if (!primary?.schema) return [];
|
|
1207
|
+
return [{
|
|
1208
|
+
schema: decorate ? decorate(primary.schema) : primary.schema,
|
|
1209
|
+
name: baseName,
|
|
1210
|
+
typeName: tsBaseName,
|
|
1211
|
+
description,
|
|
1212
|
+
skipImportNames: []
|
|
1213
|
+
}];
|
|
1214
|
+
}
|
|
1215
|
+
const variants = resolveContentTypeVariants(entries, baseName);
|
|
1216
|
+
const unionSchema = kubb_kit.ast.factory.createSchema({
|
|
1217
|
+
type: "union",
|
|
1218
|
+
members: variants.map((variant) => kubb_kit.ast.factory.createSchema({
|
|
1219
|
+
type: "ref",
|
|
1220
|
+
name: variant.name
|
|
1221
|
+
}))
|
|
1222
|
+
});
|
|
1223
|
+
return [...variants.map((variant) => ({
|
|
1224
|
+
schema: decorate ? decorate(variant.schema) : variant.schema,
|
|
1225
|
+
name: variant.name,
|
|
1226
|
+
typeName: getPerContentTypeName(tsBaseName, variant.suffix),
|
|
1227
|
+
description,
|
|
1228
|
+
skipImportNames: []
|
|
1229
|
+
})), {
|
|
1230
|
+
schema: unionSchema,
|
|
1231
|
+
name: baseName,
|
|
1232
|
+
typeName: tsBaseName,
|
|
1233
|
+
description,
|
|
1234
|
+
skipImportNames: variants.map((variant) => variant.name)
|
|
1235
|
+
}];
|
|
1236
|
+
}
|
|
1237
|
+
const responseUnits = node.responses.flatMap((response) => expandContentUnits(response.content ?? [], resolver.response.status(node, response.statusCode), tsResolver.response.status(node, response.statusCode), response.description));
|
|
1238
|
+
const dataUnits = expandContentUnits(node.requestBody?.content ?? [], resolver.response.body(node), tsResolver.response.body(node), node.requestBody?.description, (schema) => ({
|
|
1239
|
+
...schema,
|
|
1240
|
+
description: node.requestBody?.description ?? schema.description
|
|
1241
|
+
}));
|
|
1242
|
+
const responseName = resolver.response.response(node);
|
|
1243
|
+
const localHelperNames = /* @__PURE__ */ new Set([
|
|
1244
|
+
...paramGroups.map((group) => group.name),
|
|
1245
|
+
...responseUnits.map((unit) => unit.name),
|
|
1246
|
+
...dataUnits.map((unit) => unit.name),
|
|
1247
|
+
responseName
|
|
1248
|
+
]);
|
|
1249
|
+
const cyclicSchemas = new Set(ctx.meta.circularNames);
|
|
1250
|
+
const meta = {
|
|
1251
|
+
file: resolver.file({
|
|
1252
|
+
name: node.operationId,
|
|
1253
|
+
extname: ".ts",
|
|
1254
|
+
tag: node.tags[0] ?? "default",
|
|
1255
|
+
path: node.path,
|
|
1256
|
+
root,
|
|
1257
|
+
output,
|
|
1258
|
+
group: group ?? void 0
|
|
1259
|
+
}),
|
|
1260
|
+
typeFile: tsResolver.file({
|
|
1261
|
+
name: node.operationId,
|
|
1262
|
+
extname: ".ts",
|
|
1263
|
+
tag: node.tags[0] ?? "default",
|
|
1264
|
+
path: node.path,
|
|
1265
|
+
root,
|
|
1266
|
+
output: pluginTs.options?.output ?? output,
|
|
1267
|
+
group: pluginTs.options?.group ?? void 0
|
|
1268
|
+
})
|
|
1269
|
+
};
|
|
1270
|
+
function resolveMockImports(schema) {
|
|
1271
|
+
return resolver.imports({
|
|
1272
|
+
node: schema,
|
|
1273
|
+
root,
|
|
1274
|
+
output,
|
|
1275
|
+
group: group ?? void 0
|
|
1276
|
+
}).filter((entry) => entry.path !== meta.file.path);
|
|
1277
|
+
}
|
|
1278
|
+
function renderEntry({ schema, name, typeName, description, skipImportNames = [] }) {
|
|
1279
|
+
if (!schema) return null;
|
|
1280
|
+
const canOverride = canOverrideSchema(schema);
|
|
1281
|
+
const printerInstance = printerFaker({
|
|
1282
|
+
resolver,
|
|
1283
|
+
schemaName: name,
|
|
1284
|
+
typeName,
|
|
1285
|
+
dateParser,
|
|
1286
|
+
regexGenerator,
|
|
1287
|
+
nodes: printer?.nodes,
|
|
1288
|
+
cyclicSchemas
|
|
1289
|
+
});
|
|
1290
|
+
const fakerText = printerInstance.print(schema) ?? "undefined";
|
|
1291
|
+
const { imports, aliases } = aliasConflictingImports(filterUsedImports(resolveMockImports(schema), fakerText, skipImportNames), localHelperNames);
|
|
1292
|
+
const rewrittenFakerText = rewriteAliasedImports(fakerText, aliases);
|
|
1293
|
+
const typeReference = resolveTypeReference({
|
|
1294
|
+
node: schema,
|
|
1295
|
+
canOverride,
|
|
1296
|
+
name,
|
|
1297
|
+
typeName,
|
|
1298
|
+
filePath: meta.file.path,
|
|
1299
|
+
typeFilePath: meta.typeFile.path
|
|
1300
|
+
});
|
|
1301
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx_jsx_runtime.Fragment, { children: [
|
|
1302
|
+
typeReference.importPath && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
1303
|
+
isTypeOnly: true,
|
|
1304
|
+
root: meta.file.path,
|
|
1305
|
+
path: typeReference.importPath,
|
|
1306
|
+
name: [typeName]
|
|
1307
|
+
}),
|
|
1308
|
+
imports.map((imp) => /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
1309
|
+
root: meta.file.path,
|
|
1310
|
+
path: imp.path,
|
|
1311
|
+
name: imp.name
|
|
1312
|
+
}, [
|
|
1313
|
+
name,
|
|
1314
|
+
imp.path,
|
|
1315
|
+
imp.name
|
|
1316
|
+
].join("-"))),
|
|
1317
|
+
/* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Faker, {
|
|
1318
|
+
name,
|
|
1319
|
+
typeName: typeReference.typeName,
|
|
1320
|
+
description,
|
|
1321
|
+
node: schema,
|
|
1322
|
+
printer: {
|
|
1323
|
+
...printerInstance,
|
|
1324
|
+
print: () => rewrittenFakerText
|
|
1325
|
+
},
|
|
1326
|
+
seed,
|
|
1327
|
+
canOverride
|
|
1328
|
+
})
|
|
1329
|
+
] });
|
|
1330
|
+
}
|
|
1331
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.File, {
|
|
1332
|
+
baseName: meta.file.baseName,
|
|
1333
|
+
path: meta.file.path,
|
|
1334
|
+
meta: meta.file.meta,
|
|
1335
|
+
banner: resolver.default.banner(ctx.meta, {
|
|
1336
|
+
output,
|
|
1337
|
+
config,
|
|
1338
|
+
file: {
|
|
1339
|
+
path: meta.file.path,
|
|
1340
|
+
baseName: meta.file.baseName
|
|
1341
|
+
}
|
|
1342
|
+
}),
|
|
1343
|
+
footer: resolver.default.footer(ctx.meta, {
|
|
1344
|
+
output,
|
|
1345
|
+
config,
|
|
1346
|
+
file: {
|
|
1347
|
+
path: meta.file.path,
|
|
1348
|
+
baseName: meta.file.baseName
|
|
1349
|
+
}
|
|
1350
|
+
}),
|
|
1351
|
+
children: [
|
|
1352
|
+
/* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
1353
|
+
name: locale ? [{
|
|
1354
|
+
propertyName: localeToFakerImport(locale),
|
|
1355
|
+
name: "faker"
|
|
1356
|
+
}] : ["faker"],
|
|
1357
|
+
path: "@faker-js/faker"
|
|
1358
|
+
}),
|
|
1359
|
+
regexGenerator === "randexp" && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
1360
|
+
name: "RandExp",
|
|
1361
|
+
path: "randexp"
|
|
1362
|
+
}),
|
|
1363
|
+
dateParser !== "faker" && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
1364
|
+
path: dateParser,
|
|
1365
|
+
name: dateParser
|
|
1366
|
+
}),
|
|
1367
|
+
paramGroups.map((group) => renderEntry(group)),
|
|
1368
|
+
responseUnits.map((unit) => renderEntry({
|
|
1369
|
+
schema: unit.schema,
|
|
1370
|
+
name: unit.name,
|
|
1371
|
+
typeName: unit.typeName,
|
|
1372
|
+
description: unit.description,
|
|
1373
|
+
skipImportNames: unit.skipImportNames
|
|
1374
|
+
})),
|
|
1375
|
+
dataUnits.map((unit) => renderEntry({
|
|
1376
|
+
schema: unit.schema,
|
|
1377
|
+
name: unit.name,
|
|
1378
|
+
typeName: unit.typeName,
|
|
1379
|
+
description: unit.description,
|
|
1380
|
+
skipImportNames: unit.skipImportNames
|
|
1381
|
+
})),
|
|
1382
|
+
renderEntry({
|
|
1383
|
+
schema: buildResponseUnionSchema(node, resolver),
|
|
1384
|
+
name: responseName,
|
|
1385
|
+
typeName: tsResolver.response.response(node),
|
|
1386
|
+
skipImportNames: responseUnits.map((unit) => unit.name)
|
|
1387
|
+
})
|
|
1388
|
+
]
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
//#endregion
|
|
1393
|
+
//#region src/resolvers/resolverFaker.ts
|
|
1394
|
+
/**
|
|
1395
|
+
* Default resolver used by `@kubb/plugin-faker`. Decides the names and file
|
|
1396
|
+
* paths for every generated mock factory. Functions and files are prefixed
|
|
1397
|
+
* with `create` so `Pet` becomes `createPet`.
|
|
1398
|
+
*
|
|
1399
|
+
* @example Resolve a factory name
|
|
1400
|
+
* ```ts
|
|
1401
|
+
* import { resolverFaker } from '@kubb/plugin-faker'
|
|
1402
|
+
*
|
|
1403
|
+
* resolverFaker.name('list pets') // 'createListPets'
|
|
1404
|
+
* ```
|
|
1405
|
+
*/
|
|
1406
|
+
const resolverFaker = (0, kubb_kit.createResolver)({
|
|
1407
|
+
pluginName: "plugin-faker",
|
|
1408
|
+
name(name) {
|
|
1409
|
+
return ensureValidVarName(camelCase(name, { prefix: "create" }));
|
|
1410
|
+
},
|
|
1411
|
+
file: createCasedFile((part) => camelCase(part, { prefix: "create" })),
|
|
1412
|
+
param: createOperationParamResolver(),
|
|
1413
|
+
response: createOperationResponseResolver()
|
|
1414
|
+
});
|
|
1415
|
+
//#endregion
|
|
234
1416
|
//#region src/plugin.ts
|
|
235
1417
|
/**
|
|
236
|
-
* Canonical plugin name for `@kubb/plugin-faker
|
|
1418
|
+
* Canonical plugin name for `@kubb/plugin-faker`. Used for driver lookups and
|
|
1419
|
+
* cross-plugin dependency references.
|
|
237
1420
|
*/
|
|
238
1421
|
const pluginFakerName = "plugin-faker";
|
|
239
1422
|
/**
|
|
240
|
-
* Generates
|
|
241
|
-
*
|
|
242
|
-
*
|
|
1423
|
+
* Generates one mock-data factory per OpenAPI schema using Faker.js. Call
|
|
1424
|
+
* `createPet()` to get a realistic `Pet` object. Useful for tests, Storybook,
|
|
1425
|
+
* and local development without a running backend.
|
|
243
1426
|
*
|
|
244
1427
|
* @example
|
|
245
|
-
*
|
|
1428
|
+
* ```ts
|
|
1429
|
+
* import { defineConfig } from 'kubb/config'
|
|
1430
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1431
|
+
* import { pluginFaker } from '@kubb/plugin-faker'
|
|
1432
|
+
*
|
|
1433
|
+
* export default defineConfig({
|
|
1434
|
+
* input: './petStore.yaml',
|
|
1435
|
+
* output: { path: './src/gen' },
|
|
1436
|
+
* plugins: [
|
|
1437
|
+
* pluginTs(),
|
|
1438
|
+
* pluginFaker({
|
|
1439
|
+
* output: { path: './mocks' },
|
|
1440
|
+
* seed: [100],
|
|
1441
|
+
* }),
|
|
1442
|
+
* ],
|
|
1443
|
+
* })
|
|
1444
|
+
* ```
|
|
246
1445
|
*/
|
|
247
|
-
const pluginFaker = (0,
|
|
1446
|
+
const pluginFaker = (0, kubb_kit.definePlugin)((options) => {
|
|
248
1447
|
const { output = {
|
|
249
1448
|
path: "mocks",
|
|
250
|
-
|
|
251
|
-
}, seed, locale, group, exclude = [], include, override = [],
|
|
252
|
-
const groupConfig = group
|
|
253
|
-
...group,
|
|
254
|
-
name: group.name ? group.name : (ctx) => {
|
|
255
|
-
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
256
|
-
return `${camelCase(ctx.group)}Controller`;
|
|
257
|
-
}
|
|
258
|
-
} : void 0;
|
|
1449
|
+
barrel: { type: "named" }
|
|
1450
|
+
}, seed, locale = "en", group, exclude = [], include, override = [], dateParser = "faker", regexGenerator = "faker", printer, resolver: userResolver, macros: userMacros } = options;
|
|
1451
|
+
const groupConfig = createGroupConfig(group);
|
|
259
1452
|
return {
|
|
260
1453
|
name: pluginFakerName,
|
|
261
1454
|
options,
|
|
@@ -269,29 +1462,23 @@ const pluginFaker = (0, _kubb_core.definePlugin)((options) => {
|
|
|
269
1462
|
include,
|
|
270
1463
|
override,
|
|
271
1464
|
group: groupConfig,
|
|
272
|
-
mapper,
|
|
273
1465
|
dateParser,
|
|
274
1466
|
regexGenerator,
|
|
275
|
-
paramsCasing,
|
|
276
1467
|
printer
|
|
277
1468
|
});
|
|
278
|
-
ctx.setResolver(userResolver ?
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
} : resolverFaker);
|
|
282
|
-
if (userTransformer) ctx.setTransformer(userTransformer);
|
|
283
|
-
ctx.addGenerator(require_fakerGenerator.fakerGenerator);
|
|
284
|
-
for (const generator of userGenerators) ctx.addGenerator(generator);
|
|
1469
|
+
ctx.setResolver(userResolver ? kubb_kit.Resolver.merge(resolverFaker, userResolver) : resolverFaker);
|
|
1470
|
+
if (userMacros?.length) ctx.setMacros(userMacros);
|
|
1471
|
+
ctx.addGenerator(fakerGenerator);
|
|
285
1472
|
} }
|
|
286
1473
|
};
|
|
287
1474
|
});
|
|
288
1475
|
//#endregion
|
|
289
|
-
exports.Faker =
|
|
1476
|
+
exports.Faker = Faker;
|
|
290
1477
|
exports.default = pluginFaker;
|
|
291
|
-
exports.fakerGenerator =
|
|
1478
|
+
exports.fakerGenerator = fakerGenerator;
|
|
292
1479
|
exports.pluginFaker = pluginFaker;
|
|
293
1480
|
exports.pluginFakerName = pluginFakerName;
|
|
294
|
-
exports.printerFaker =
|
|
1481
|
+
exports.printerFaker = printerFaker;
|
|
295
1482
|
exports.resolverFaker = resolverFaker;
|
|
296
1483
|
|
|
297
1484
|
//# sourceMappingURL=index.cjs.map
|