@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/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
- const require_Faker = require("./Faker-D39THFJ-.cjs");
6
- const require_fakerGenerator = require("./fakerGenerator-B-XuVREg.cjs");
5
+ //#endregion
7
6
  let node_path = require("node:path");
8
- node_path = require_Faker.__toESM(node_path, 1);
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 node_crypto = require("node:crypto");
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
- if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
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') // 'helloWorld'
45
- * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
46
- */
47
- function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
48
- if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
49
- prefix,
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/resolvers/resolverFaker.ts
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
- * Naming convention resolver for Faker plugin.
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
- * Provides default naming helpers using camelCase with a `create` prefix for factory functions and files.
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
- * `resolverFaker.default('list pets', 'function') // → 'createListPets'`
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
- const resolverFaker = (0, _kubb_core.defineResolver)(() => {
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
- name: "default",
170
- pluginName: "plugin-faker",
171
- default(name, type) {
172
- const resolvedName = camelCase(name, {
173
- isFile: type === "file",
174
- prefix: "create"
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
- if (type === "file" || isValidVarName(resolvedName)) return resolvedName;
177
- return `_${resolvedName}`;
178
- },
179
- resolveName(name, type) {
180
- return this.default(name, type);
181
- },
182
- resolvePathName(name, type) {
183
- return this.default(name, type);
184
- },
185
- resolveFile({ name, extname, tag, path: groupPath }, context) {
186
- const pathMode = _kubb_core.PluginDriver.getMode(node_path.default.resolve(context.root, context.output.path));
187
- const baseName = `${pathMode === "single" ? "" : this.resolveName(name, "file")}${extname}`;
188
- const filePath = this.resolvePath({
189
- baseName,
190
- pathMode,
191
- tag,
192
- path: groupPath
193
- }, context);
194
- return {
195
- kind: "File",
196
- id: (0, node_crypto.createHash)("sha256").update(filePath).digest("hex"),
197
- name: node_path.default.basename(filePath, extname),
198
- path: filePath,
199
- baseName,
200
- extname,
201
- meta: { pluginName: this.pluginName },
202
- sources: [],
203
- imports: [],
204
- exports: []
205
- };
206
- },
207
- resolveParamName(node, param) {
208
- return this.resolveName(`${node.operationId} ${param.in} ${param.name}`);
209
- },
210
- resolveDataName(node) {
211
- return this.resolveName(`${node.operationId} Data`);
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
- resolveResponseStatusName(node, statusCode) {
214
- return this.resolveName(`${node.operationId} Status ${statusCode}`);
736
+ query(node) {
737
+ return this.name(`${node.operationId} Query`);
215
738
  },
216
- resolveResponseName(node) {
217
- return this.resolveName(`${node.operationId} Response`);
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
- resolveResponsesName(node) {
220
- return this.resolveName(`${node.operationId} Responses`);
759
+ body(node) {
760
+ return this.name(`${node.operationId} Body`);
221
761
  },
222
- resolvePathParamsName(node, param) {
223
- return this.resolveParamName(node, param);
762
+ responses(node) {
763
+ return this.name(`${node.operationId} Responses`);
224
764
  },
225
- resolveQueryParamsName(node, param) {
226
- return this.resolveParamName(node, param);
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
- resolveHeaderParamsName(node, param) {
229
- return this.resolveParamName(node, param);
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`, used in driver lookups and warnings.
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 Faker mock data factories from OpenAPI/AST specification.
241
- *
242
- * Creates randomized test data and mock helpers from schema definitions.
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
- * `import pluginFaker from '@kubb/plugin-faker'; export default defineConfig({ plugins: [pluginFaker({ output: { path: 'mocks' } })], })`
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, _kubb_core.definePlugin)((options) => {
1446
+ const pluginFaker = (0, kubb_kit.definePlugin)((options) => {
248
1447
  const { output = {
249
1448
  path: "mocks",
250
- barrelType: "named"
251
- }, seed, locale, group, exclude = [], include, override = [], mapper = {}, dateParser = "faker", generators: userGenerators = [], regexGenerator = "faker", paramsCasing, printer, resolver: userResolver, transformer: userTransformer } = options;
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
- ...resolverFaker,
280
- ...userResolver
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 = require_Faker.Faker;
1476
+ exports.Faker = Faker;
290
1477
  exports.default = pluginFaker;
291
- exports.fakerGenerator = require_fakerGenerator.fakerGenerator;
1478
+ exports.fakerGenerator = fakerGenerator;
292
1479
  exports.pluginFaker = pluginFaker;
293
1480
  exports.pluginFakerName = pluginFakerName;
294
- exports.printerFaker = require_fakerGenerator.printerFaker;
1481
+ exports.printerFaker = printerFaker;
295
1482
  exports.resolverFaker = resolverFaker;
296
1483
 
297
1484
  //# sourceMappingURL=index.cjs.map