@kubb/plugin-faker
Version:
Faker.js data generator plugin for Kubb, creating realistic mock data from OpenAPI specifications for development and testing.
304 lines (301 loc) • 11.9 kB
JavaScript
import transformers from "@kubb/core/transformers";
import { SchemaGenerator, isKeyword, schemaKeywords } from "@kubb/plugin-oas";
import { File, Function, FunctionParams } from "@kubb/react-fabric";
import { jsx, jsxs } from "@kubb/react-fabric/jsx-runtime";
//#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()";
},
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 = []) => `Object.assign({}, ${items.join(", ")})`,
object: () => "object",
ref: () => "ref",
matches: (value = "", regexGenerator = "faker") => {
if (regexGenerator === "randexp") return `${transformers.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);
}
}
function parse({ schema, current, parent, name, siblings }, options) {
const value = fakerKeywordMapper[current.keyword];
if (!value) return;
if (isKeyword(current, schemaKeywords.union)) {
if (Array.isArray(current.args) && !current.args.length) return "";
return fakerKeywordMapper.union(current.args.map((it) => parse({
schema,
parent: current,
name,
current: it,
siblings
}, {
...options,
canOverride: false
})).filter(Boolean));
}
if (isKeyword(current, schemaKeywords.and)) return fakerKeywordMapper.and(current.args.map((it) => parse({
schema,
parent: current,
current: it,
siblings
}, {
...options,
canOverride: false
})).filter(Boolean));
if (isKeyword(current, schemaKeywords.array)) return fakerKeywordMapper.array(current.args.items.map((it) => parse({
schema,
parent: current,
current: it,
siblings
}, {
...options,
typeName: `NonNullable<${options.typeName}>[number]`,
canOverride: false
})).filter(Boolean), current.args.min, current.args.max);
if (isKeyword(current, schemaKeywords.enum)) {
if (parent ? isKeyword(parent, schemaKeywords.tuple) : false) return fakerKeywordMapper.enum(current.args.items.map((schema$1) => {
if (schema$1.format === "number") return schema$1.value;
if (schema$1.format === "boolean") return schema$1.value;
return transformers.stringify(schema$1.value);
}));
return fakerKeywordMapper.enum(current.args.items.map((schema$1) => {
if (schema$1.format === "number") return schema$1.value;
if (schema$1.format === "boolean") return schema$1.value;
return transformers.stringify(schema$1.value);
}), name ? options.typeName : void 0);
}
if (isKeyword(current, schemaKeywords.ref)) {
if (!current.args?.name) throw new Error(`Name not defined for keyword ${current.keyword}`);
if (options.canOverride) return `${current.args.name}(data)`;
return `${current.args.name}()`;
}
if (isKeyword(current, schemaKeywords.object)) return `{${Object.entries(current.args?.properties || {}).filter((item) => {
const schema$1 = item[1];
return schema$1 && typeof schema$1.map === "function";
}).map(([name$1, schemas]) => {
const mappedName = schemas.find((schema$1) => schema$1.keyword === schemaKeywords.name)?.args || name$1;
if (options.mapper?.[mappedName]) return `"${name$1}": ${options.mapper?.[mappedName]}`;
return `"${name$1}": ${joinItems(schemas.sort(schemaKeywordSorter).map((it) => parse({
schema,
name: name$1,
parent: current,
current: it,
siblings: schemas
}, {
...options,
typeName: `NonNullable<${options.typeName}>[${JSON.stringify(name$1)}]`,
canOverride: false
})).filter(Boolean))}`;
}).join(",")}}`;
if (isKeyword(current, schemaKeywords.tuple)) {
if (Array.isArray(current.args.items)) return fakerKeywordMapper.tuple(current.args.items.map((it) => parse({
schema,
parent: current,
current: it,
siblings
}, {
...options,
canOverride: false
})).filter(Boolean));
return parse({
schema,
parent: current,
current: current.args.items,
siblings
}, {
...options,
canOverride: false
});
}
if (isKeyword(current, schemaKeywords.const)) {
if (current.args.format === "number" && current.args.name !== void 0) return fakerKeywordMapper.const(current.args.name?.toString());
return fakerKeywordMapper.const(transformers.stringify(current.args.value));
}
if (isKeyword(current, schemaKeywords.matches) && current.args) return fakerKeywordMapper.matches(current.args, options.regexGenerator);
if (isKeyword(current, schemaKeywords.null) || isKeyword(current, schemaKeywords.undefined) || isKeyword(current, schemaKeywords.any)) return value() || "";
if (isKeyword(current, schemaKeywords.string)) {
if (siblings) {
const minSchema = SchemaGenerator.find(siblings, schemaKeywords.min);
const maxSchema = SchemaGenerator.find(siblings, schemaKeywords.max);
return fakerKeywordMapper.string(minSchema?.args, maxSchema?.args);
}
return fakerKeywordMapper.string();
}
if (isKeyword(current, schemaKeywords.number)) {
if (siblings) {
const minSchema = SchemaGenerator.find(siblings, schemaKeywords.min);
const maxSchema = SchemaGenerator.find(siblings, schemaKeywords.max);
return fakerKeywordMapper.number(minSchema?.args, maxSchema?.args);
}
return fakerKeywordMapper.number();
}
if (isKeyword(current, schemaKeywords.integer)) {
if (siblings) {
const minSchema = SchemaGenerator.find(siblings, schemaKeywords.min);
const maxSchema = SchemaGenerator.find(siblings, schemaKeywords.max);
return fakerKeywordMapper.integer(minSchema?.args, maxSchema?.args);
}
return fakerKeywordMapper.integer();
}
if (isKeyword(current, schemaKeywords.datetime)) return fakerKeywordMapper.datetime();
if (isKeyword(current, schemaKeywords.date)) return fakerKeywordMapper.date(current.args.type, options.dateParser);
if (isKeyword(current, schemaKeywords.time)) return fakerKeywordMapper.time(current.args.type, options.dateParser);
if (current.keyword in fakerKeywordMapper && "args" in current) {
const value$1 = fakerKeywordMapper[current.keyword];
return value$1(JSON.stringify(current.args));
}
if (current.keyword in fakerKeywordMapper) return value();
}
//#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,
regexGenerator,
mapper,
canOverride,
dateParser
})).filter(Boolean));
const isArray = fakerText.startsWith("faker.helpers.arrayElements") || fakerText.startsWith("faker.helpers.multiple");
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) 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;
else if (isSimpleString) type = name;
else if (isSimpleInt || isSimpleFloat) type = "number";
const params = FunctionParams.factory({ data: {
type,
optional: true
} });
let returnType = canOverride ? typeName : void 0;
if (isSimpleString || isSimpleInt || isSimpleFloat) returnType = type;
return /* @__PURE__ */ jsx(File.Source, {
name,
isExportable: true,
isIndexable: true,
children: /* @__PURE__ */ jsxs(Function, {
export: true,
name,
JSDoc: { comments: [description ? `@description ${transformers.jsStringEscape(description)}` : void 0].filter(Boolean) },
params: canOverride ? params.toConstructor() : void 0,
returnType,
children: [
seed ? `faker.seed(${JSON.stringify(seed)})` : void 0,
/* @__PURE__ */ jsx("br", {}),
`return ${fakerTextWithOverride}`
]
})
});
}
//#endregion
export { Faker as t };
//# sourceMappingURL=components-DUQvu8JV.js.map