@kubb/plugin-faker
Version:
Faker.js data generator plugin for Kubb, creating realistic mock data from OpenAPI specifications for development and testing.
358 lines (355 loc) • 13.2 kB
JavaScript
import transformers from "@kubb/core/transformers";
import { createParser, findSchemaKeyword, 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 = []) => `{...${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);
}
}
const parse = createParser({
mapper: fakerKeywordMapper,
handlers: {
union(tree, options) {
const { current, schema, name, siblings } = tree;
if (!isKeyword(current, schemaKeywords.union)) return void 0;
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(Boolean));
},
and(tree, options) {
const { current, schema, siblings } = tree;
if (!isKeyword(current, schemaKeywords.and)) return void 0;
return fakerKeywordMapper.and(current.args.map((it) => this.parse({
schema,
parent: current,
current: it,
siblings
}, {
...options,
canOverride: false
})).filter(Boolean));
},
array(tree, options) {
const { current, schema } = tree;
if (!isKeyword(current, schemaKeywords.array)) return void 0;
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(Boolean), current.args.min, current.args.max);
},
enum(tree, options) {
const { current, parent, name } = tree;
if (!isKeyword(current, schemaKeywords.enum)) return void 0;
if (parent ? isKeyword(parent, 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 transformers.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 transformers.stringify(schema.value);
}), name ? options.typeName : void 0);
},
ref(tree, options) {
const { current } = tree;
if (!isKeyword(current, schemaKeywords.ref)) return void 0;
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}()`;
},
object(tree, options) {
const { current, schema } = tree;
if (!isKeyword(current, schemaKeywords.object)) return void 0;
return `{${Object.entries(current.args?.properties || {}).filter((item) => {
const schema$1 = item[1];
return schema$1 && typeof schema$1.map === "function";
}).map(([name, schemas]) => {
const mappedName = schemas.find((schema$1) => schema$1.keyword === schemaKeywords.name)?.args || name;
if (options.mapper?.[mappedName]) return `"${name}": ${options.mapper?.[mappedName]}`;
return `"${name}": ${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(Boolean))}`;
}).join(",")}}`;
},
tuple(tree, options) {
const { current, schema, siblings } = tree;
if (!isKeyword(current, schemaKeywords.tuple)) return void 0;
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(Boolean));
return this.parse({
schema,
parent: current,
current: current.args.items,
siblings
}, {
...options,
canOverride: false
});
},
const(tree, _options) {
const { current } = tree;
if (!isKeyword(current, schemaKeywords.const)) return void 0;
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));
},
matches(tree, options) {
const { current } = tree;
if (!isKeyword(current, schemaKeywords.matches)) return void 0;
if (current.args) return fakerKeywordMapper.matches(current.args, options.regexGenerator);
},
null(tree) {
const { current } = tree;
if (!isKeyword(current, schemaKeywords.null)) return void 0;
return fakerKeywordMapper.null();
},
undefined(tree) {
const { current } = tree;
if (!isKeyword(current, schemaKeywords.undefined)) return void 0;
return fakerKeywordMapper.undefined();
},
any(tree) {
const { current } = tree;
if (!isKeyword(current, schemaKeywords.any)) return void 0;
return fakerKeywordMapper.any();
},
string(tree, _options) {
const { current, siblings } = tree;
if (!isKeyword(current, schemaKeywords.string)) return void 0;
if (siblings) {
const minSchema = findSchemaKeyword(siblings, "min");
const maxSchema = findSchemaKeyword(siblings, "max");
return fakerKeywordMapper.string(minSchema?.args, maxSchema?.args);
}
return fakerKeywordMapper.string();
},
number(tree, _options) {
const { current, siblings } = tree;
if (!isKeyword(current, schemaKeywords.number)) return void 0;
if (siblings) {
const minSchema = findSchemaKeyword(siblings, "min");
const maxSchema = findSchemaKeyword(siblings, "max");
return fakerKeywordMapper.number(minSchema?.args, maxSchema?.args);
}
return fakerKeywordMapper.number();
},
integer(tree, _options) {
const { current, siblings } = tree;
if (!isKeyword(current, schemaKeywords.integer)) return void 0;
if (siblings) {
const minSchema = findSchemaKeyword(siblings, "min");
const maxSchema = findSchemaKeyword(siblings, "max");
return fakerKeywordMapper.integer(minSchema?.args, maxSchema?.args);
}
return fakerKeywordMapper.integer();
},
datetime(tree) {
const { current } = tree;
if (!isKeyword(current, schemaKeywords.datetime)) return void 0;
return fakerKeywordMapper.datetime();
},
date(tree, options) {
const { current } = tree;
if (!isKeyword(current, schemaKeywords.date)) return void 0;
return fakerKeywordMapper.date(current.args.type, options.dateParser);
},
time(tree, options) {
const { current } = tree;
if (!isKeyword(current, schemaKeywords.time)) return void 0;
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,
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-omV0ciEl.js.map