@kubb/plugin-faker
Version:
Faker.js data generator plugin for Kubb, creating realistic mock data from OpenAPI specifications for development and testing.
496 lines (495 loc) • 18.9 kB
JavaScript
//#region \0rolldown/runtime.js
var __create = Object.create;
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", {
value,
configurable: true
});
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
let _kubb_plugin_oas = require("@kubb/plugin-oas");
let _kubb_react_fabric = require("@kubb/react-fabric");
let _kubb_react_fabric_jsx_runtime = require("@kubb/react-fabric/jsx-runtime");
//#region ../../internals/utils/src/string.ts
/**
* Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
* Returns the string unchanged when no balanced quote pair is found.
*
* @example
* trimQuotes('"hello"') // 'hello'
* trimQuotes('hello') // 'hello'
*/
function trimQuotes(text) {
if (text.length >= 2) {
const first = text[0];
const last = text[text.length - 1];
if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
}
return text;
}
/**
* Escapes characters that are not allowed inside JS string literals.
* Handles quotes, backslashes, and Unicode line terminators (U+2028 / U+2029).
*
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
*/
function jsStringEscape(input) {
return `${input}`.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
switch (character) {
case "\"":
case "'":
case "\\": return `\\${character}`;
case "\n": return "\\n";
case "\r": return "\\r";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
default: return "";
}
});
}
//#endregion
//#region ../../internals/utils/src/object.ts
/**
* Serializes a primitive value to a JSON string literal, stripping any surrounding quote characters first.
*
* @example
* stringify('hello') // '"hello"'
* stringify('"hello"') // '"hello"'
*/
function stringify(value) {
if (value === void 0 || value === null) return "\"\"";
return JSON.stringify(trimQuotes(value.toString()));
}
//#endregion
//#region ../../internals/utils/src/regexp.ts
/**
* Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
* Inline flags expressed as `^(?im)` prefixes are extracted and applied to the resulting expression.
* Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
*
* @example
* toRegExpString('^(?im)foo') // → 'new RegExp("foo", "im")'
* toRegExpString('^(?im)foo', null) // → '/foo/im'
*/
function toRegExpString(text, func = "RegExp") {
const raw = trimQuotes(text);
const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
const replacementTarget = match?.[1] ?? "";
const matchedFlags = match?.[2];
const cleaned = raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, "");
const { source, flags } = new RegExp(cleaned, matchedFlags);
if (func === null) return `/${source}/${flags}`;
return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
}
//#endregion
//#region src/parser.ts
const fakerKeywordMapper = {
any: () => "undefined",
unknown: () => "undefined",
void: () => "undefined",
number: (min, max) => {
if (max !== void 0 && min !== void 0) return `faker.number.float({ min: ${min}, max: ${max} })`;
if (max !== void 0) return `faker.number.float({ max: ${max} })`;
if (min !== void 0) return `faker.number.float({ min: ${min} })`;
return "faker.number.float()";
},
integer: (min, max) => {
if (max !== void 0 && min !== void 0) return `faker.number.int({ min: ${min}, max: ${max} })`;
if (max !== void 0) return `faker.number.int({ max: ${max} })`;
if (min !== void 0) return `faker.number.int({ min: ${min} })`;
return "faker.number.int()";
},
bigint: () => "faker.number.bigInt()",
string: (min, max) => {
if (max !== void 0 && min !== void 0) return `faker.string.alpha({ length: { min: ${min}, max: ${max} } })`;
if (max !== void 0) return `faker.string.alpha({ length: ${max} })`;
if (min !== void 0) return `faker.string.alpha({ length: ${min} })`;
return "faker.string.alpha()";
},
boolean: () => "faker.datatype.boolean()",
undefined: () => "undefined",
null: () => "null",
array: (items = [], min, max) => {
if (items.length > 1) return `faker.helpers.arrayElements([${items.join(", ")}])`;
const item = items.at(0);
if (min !== void 0 && max !== void 0) return `faker.helpers.multiple(() => (${item}), { count: { min: ${min}, max: ${max} }})`;
if (min !== void 0) return `faker.helpers.multiple(() => (${item}), { count: ${min} })`;
if (max !== void 0) return `faker.helpers.multiple(() => (${item}), { count: { min: 0, max: ${max} }})`;
return `faker.helpers.multiple(() => (${item}))`;
},
tuple: (items = []) => `[${items.join(", ")}]`,
enum: (items = [], type = "any") => `faker.helpers.arrayElement<${type}>([${items.join(", ")}])`,
union: (items = []) => `faker.helpers.arrayElement<any>([${items.join(", ")}])`,
datetime: () => "faker.date.anytime().toISOString()",
date: (type = "string", parser = "faker") => {
if (type === "string") {
if (parser !== "faker") return `${parser}(faker.date.anytime()).format("YYYY-MM-DD")`;
return "faker.date.anytime().toISOString().substring(0, 10)";
}
if (parser !== "faker") throw new Error(`type '${type}' and parser '${parser}' can not work together`);
return "faker.date.anytime()";
},
time: (type = "string", parser = "faker") => {
if (type === "string") {
if (parser !== "faker") return `${parser}(faker.date.anytime()).format("HH:mm:ss")`;
return "faker.date.anytime().toISOString().substring(11, 19)";
}
if (parser !== "faker") throw new Error(`type '${type}' and parser '${parser}' can not work together`);
return "faker.date.anytime()";
},
uuid: () => "faker.string.uuid()",
url: () => "faker.internet.url()",
and: (items = []) => {
if (items.length === 0) return "{}";
if (items.length === 1) return items[0] ?? "{}";
return `{...${items.join(", ...")}}`;
},
object: () => "object",
ref: () => "ref",
matches: (value = "", regexGenerator = "faker") => {
if (regexGenerator === "randexp") return `${toRegExpString(value, "RandExp")}.gen()`;
return `faker.helpers.fromRegExp("${value}")`;
},
email: () => "faker.internet.email()",
firstName: () => "faker.person.firstName()",
lastName: () => "faker.person.lastName()",
password: () => "faker.internet.password()",
phone: () => "faker.phone.number()",
blob: () => "faker.image.url() as unknown as Blob",
default: void 0,
describe: void 0,
const: (value) => value ?? "",
max: void 0,
min: void 0,
nullable: void 0,
nullish: void 0,
optional: void 0,
readOnly: void 0,
writeOnly: void 0,
deprecated: void 0,
example: void 0,
schema: void 0,
catchall: void 0,
name: void 0,
interface: void 0,
exclusiveMaximum: void 0,
exclusiveMinimum: void 0
};
/**
* @link based on https://github.com/cellular/oazapfts/blob/7ba226ebb15374e8483cc53e7532f1663179a22c/src/codegen/generate.ts#L398
*/
function schemaKeywordSorter(_a, b) {
if (b.keyword === "null") return -1;
return 0;
}
function joinItems(items) {
switch (items.length) {
case 0: return "undefined";
case 1: return items[0];
default: return fakerKeywordMapper.union(items);
}
}
/**
* Returns true if any schema in the list contains a $ref that points to a type other
* than rootTypeName (i.e. an indirect / mutual ref rather than a direct self-ref).
* Recurses through array.items, union/and args, tuple items, and nested object properties.
*/
function hasIndirectRef(schemas, rootTypeName) {
return hasIndirectRefInner(schemas, rootTypeName, /* @__PURE__ */ new WeakSet());
}
function hasIndirectRefInner(schemas, rootTypeName, visited) {
return schemas.some((schema) => {
if (!("args" in schema)) return false;
if (visited.has(schema)) return false;
visited.add(schema);
switch (schema.keyword) {
case _kubb_plugin_oas.schemaKeywords.ref: return schema.args?.name !== rootTypeName;
case _kubb_plugin_oas.schemaKeywords.array: return hasIndirectRefInner(schema.args?.items ?? [], rootTypeName, visited);
case _kubb_plugin_oas.schemaKeywords.union:
case _kubb_plugin_oas.schemaKeywords.and: return Array.isArray(schema.args) && hasIndirectRefInner(schema.args, rootTypeName, visited);
case _kubb_plugin_oas.schemaKeywords.tuple: return hasIndirectRefInner(Array.isArray(schema.args?.items) ? schema.args.items : schema.args?.items ? [schema.args.items] : [], rootTypeName, visited);
case _kubb_plugin_oas.schemaKeywords.object: {
const props = schema.args?.properties;
if (!props) return false;
return Object.values(props).some((propSchemas) => hasIndirectRefInner(propSchemas, rootTypeName, visited));
}
}
return false;
});
}
const parse = (0, _kubb_plugin_oas.createParser)({
mapper: fakerKeywordMapper,
handlers: {
union(tree, options) {
const { current, schema, name, siblings } = tree;
if (Array.isArray(current.args) && !current.args.length) return "";
return fakerKeywordMapper.union(current.args.map((it) => this.parse({
schema,
parent: current,
name,
current: it,
siblings
}, {
...options,
canOverride: false
})).filter((x) => Boolean(x)));
},
and(tree, options) {
const { current, schema, siblings } = tree;
return fakerKeywordMapper.and(current.args.map((it) => this.parse({
schema,
parent: current,
current: it,
siblings
}, {
...options,
canOverride: false
})).filter((x) => Boolean(x)));
},
array(tree, options) {
const { current, schema } = tree;
return fakerKeywordMapper.array(current.args.items.map((it) => this.parse({
schema,
parent: current,
current: it,
siblings: current.args.items
}, {
...options,
typeName: `NonNullable<${options.typeName}>[number]`,
canOverride: false
})).filter((x) => Boolean(x)), current.args.min, current.args.max);
},
enum(tree, options) {
const { current, parent, name } = tree;
if (parent ? (0, _kubb_plugin_oas.isKeyword)(parent, _kubb_plugin_oas.schemaKeywords.tuple) : false) return fakerKeywordMapper.enum(current.args.items.map((schema) => {
if (schema.format === "number") return schema.value;
if (schema.format === "boolean") return schema.value;
return stringify(schema.value);
}));
return fakerKeywordMapper.enum(current.args.items.map((schema) => {
if (schema.format === "number") return schema.value;
if (schema.format === "boolean") return schema.value;
return stringify(schema.value);
}), name ? options.typeName : void 0);
},
ref(tree, options) {
const { current, parent } = tree;
if (!current.args?.name) throw new Error(`Name not defined for keyword ${current.keyword}`);
if (options.rootTypeName && current.args.name === options.rootTypeName) return "undefined as any";
const isNestedInObjectOrAnd = parent && ((0, _kubb_plugin_oas.isKeyword)(parent, _kubb_plugin_oas.schemaKeywords.object) || (0, _kubb_plugin_oas.isKeyword)(parent, _kubb_plugin_oas.schemaKeywords.and));
if (options.canOverride && !isNestedInObjectOrAnd) return `${current.args.name}(data)`;
return `${current.args.name}()`;
},
object(tree, options) {
const { current, schema } = tree;
const filteredEntries = Object.entries(current.args?.properties || {}).filter((item) => {
const schema = item[1];
return schema && typeof schema.map === "function";
});
const anyIndirectRef = filteredEntries.some(([, schemas]) => hasIndirectRef(schemas, options.rootTypeName));
const argsObject = filteredEntries.map(([name, schemas]) => {
const mappedName = schemas.find((schema) => schema.keyword === _kubb_plugin_oas.schemaKeywords.name)?.args || name;
if (options.mapper && Object.hasOwn(options.mapper, mappedName)) return `"${name}": ${options.mapper?.[mappedName]}`;
const parsed = joinItems(schemas.sort(schemaKeywordSorter).map((it) => this.parse({
schema,
name,
parent: current,
current: it,
siblings: schemas
}, {
...options,
typeName: `NonNullable<${options.typeName}>[${JSON.stringify(name)}]`,
canOverride: false
})).filter((x) => Boolean(x)));
if (anyIndirectRef && hasIndirectRef(schemas, options.rootTypeName)) return `get "${name}"() { return ${parsed} }`;
return `"${name}": ${parsed}`;
}).join(",");
if (anyIndirectRef && options.canOverride) return `{${argsObject}${argsObject.length > 0 ? "," : ""}...(data || {})}`;
return `{${argsObject}}`;
},
tuple(tree, options) {
const { current, schema, siblings } = tree;
if (Array.isArray(current.args.items)) return fakerKeywordMapper.tuple(current.args.items.map((it) => this.parse({
schema,
parent: current,
current: it,
siblings
}, {
...options,
canOverride: false
})).filter((x) => Boolean(x)));
return this.parse({
schema,
parent: current,
current: current.args.items,
siblings
}, {
...options,
canOverride: false
});
},
const(tree, _options) {
const { current } = tree;
if (current.args.format === "number" && current.args.name !== void 0) return fakerKeywordMapper.const(current.args.name?.toString());
return fakerKeywordMapper.const(stringify(current.args.value));
},
matches(tree, options) {
const { current } = tree;
if (current.args) return fakerKeywordMapper.matches(current.args, options.regexGenerator);
},
null() {
return fakerKeywordMapper.null();
},
undefined() {
return fakerKeywordMapper.undefined();
},
any() {
return fakerKeywordMapper.any();
},
string(tree, _options) {
const { siblings } = tree;
if (siblings) {
const minSchema = (0, _kubb_plugin_oas.findSchemaKeyword)(siblings, "min");
const maxSchema = (0, _kubb_plugin_oas.findSchemaKeyword)(siblings, "max");
return fakerKeywordMapper.string(minSchema?.args, maxSchema?.args);
}
return fakerKeywordMapper.string();
},
number(tree, _options) {
const { siblings } = tree;
if (siblings) {
const minSchema = (0, _kubb_plugin_oas.findSchemaKeyword)(siblings, "min");
const maxSchema = (0, _kubb_plugin_oas.findSchemaKeyword)(siblings, "max");
return fakerKeywordMapper.number(minSchema?.args, maxSchema?.args);
}
return fakerKeywordMapper.number();
},
integer(tree, _options) {
const { siblings } = tree;
if (siblings) {
const minSchema = (0, _kubb_plugin_oas.findSchemaKeyword)(siblings, "min");
const maxSchema = (0, _kubb_plugin_oas.findSchemaKeyword)(siblings, "max");
return fakerKeywordMapper.integer(minSchema?.args, maxSchema?.args);
}
return fakerKeywordMapper.integer();
},
bigint(_tree, _options) {
return fakerKeywordMapper.bigint();
},
datetime() {
return fakerKeywordMapper.datetime();
},
date(tree, options) {
const { current } = tree;
return fakerKeywordMapper.date(current.args.type, options.dateParser);
},
time(tree, options) {
const { current } = tree;
return fakerKeywordMapper.time(current.args.type, options.dateParser);
}
}
});
//#endregion
//#region src/components/Faker.tsx
function Faker({ tree, description, name, typeName, seed, regexGenerator, canOverride, mapper, dateParser }) {
const fakerText = joinItems(tree.map((schema, _index, siblings) => parse({
name,
schema,
parent: void 0,
current: schema,
siblings
}, {
typeName,
rootTypeName: name,
regexGenerator,
mapper,
canOverride,
dateParser
})).filter((x) => Boolean(x)));
const isArray = fakerText.startsWith("faker.helpers.arrayElements") || fakerText.startsWith("faker.helpers.multiple");
const isRefToArray = tree.some((s) => (0, _kubb_plugin_oas.isKeyword)(s, _kubb_plugin_oas.schemaKeywords.schema) && s.args.type === "array");
const isObject = fakerText.startsWith("{");
const isTuple = fakerText.startsWith("faker.helpers.arrayElement");
const isSimpleString = name === "string";
const isSimpleInt = name === "integer";
const isSimpleFloat = name === "float";
let fakerTextWithOverride = fakerText;
if (canOverride && isObject) if (fakerText.includes("...(data || {})")) fakerTextWithOverride = `{ ...${fakerText} }`;
else fakerTextWithOverride = `{
...${fakerText},
...data || {}
}`;
if (canOverride && isTuple) fakerTextWithOverride = `data || ${fakerText}`;
if (canOverride && isArray) fakerTextWithOverride = `[
...${fakerText},
...data || []
]`;
if (canOverride && isSimpleString) fakerTextWithOverride = "data ?? faker.string.alpha()";
if (canOverride && isSimpleInt) fakerTextWithOverride = "data ?? faker.number.int()";
if (canOverride && isSimpleFloat) fakerTextWithOverride = "data ?? faker.number.float()";
let type = `Partial<${typeName}>`;
if (isArray) type = typeName;
if (isRefToArray) type = typeName;
if (isSimpleString) type = name;
if (isSimpleInt || isSimpleFloat) type = "number";
const params = _kubb_react_fabric.FunctionParams.factory({ data: {
type,
optional: true
} });
let returnType = canOverride ? typeName : void 0;
if (isSimpleString || isSimpleInt || isSimpleFloat) returnType = type;
return /* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)(_kubb_react_fabric.File.Source, {
name,
isExportable: true,
isIndexable: true,
children: /* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsxs)(_kubb_react_fabric.Function, {
export: true,
name,
JSDoc: { comments: [description ? `@description ${jsStringEscape(description)}` : void 0].filter(Boolean) },
params: canOverride ? params.toConstructor() : void 0,
returnType,
children: [
seed ? `faker.seed(${JSON.stringify(seed)})` : void 0,
/* @__PURE__ */ (0, _kubb_react_fabric_jsx_runtime.jsx)("br", {}),
`return ${fakerTextWithOverride}`
]
})
});
}
//#endregion
Object.defineProperty(exports, "Faker", {
enumerable: true,
get: function() {
return Faker;
}
});
Object.defineProperty(exports, "__name", {
enumerable: true,
get: function() {
return __name;
}
});
Object.defineProperty(exports, "__toESM", {
enumerable: true,
get: function() {
return __toESM;
}
});
//# sourceMappingURL=components-cpULzmDp.cjs.map