UNPKG

interface-faker

Version:

Generate mock data for TypeScript interfaces using faker.js

218 lines (214 loc) 8.15 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { createFakeGenerator: () => createFakeGenerator, default: () => index_default }); module.exports = __toCommonJS(index_exports); var import_ts_morph = require("ts-morph"); var import_faker = require("@faker-js/faker"); var path = __toESM(require("path")); var fs = __toESM(require("fs")); var MockInterfaceGenerator = class { constructor() { this.project = null; this.sourceFiles = /* @__PURE__ */ new Map(); } /** * Initialize TypeScript project with tsconfig */ initProject(filePath) { if (this.project) return this.project; const tsConfigPath = this.findTsConfig(filePath); this.project = new import_ts_morph.Project({ tsConfigFilePath: tsConfigPath }); return this.project; } /** * Search for tsconfig.json starting from the given file path */ findTsConfig(startPath) { let currentDir = path.dirname(startPath); while (currentDir !== path.dirname(currentDir)) { const tsConfigPath = path.join(currentDir, "tsconfig.json"); if (fs.existsSync(tsConfigPath)) { return tsConfigPath; } currentDir = path.dirname(currentDir); } console.warn("\u26A0\uFE0F tsconfig.json not found, using default configuration"); return void 0; } /** * Get or load a source file */ getSourceFile(filePath) { if (this.sourceFiles.has(filePath)) { return this.sourceFiles.get(filePath); } const project = this.initProject(filePath); let sourceFile = project.getSourceFile(filePath); if (!sourceFile) { sourceFile = project.addSourceFileAtPath(filePath); } this.sourceFiles.set(filePath, sourceFile); return sourceFile; } /** * Generate mock value for a TypeScript type */ generateMockValue(type, sourceFile, options, depth = 0, visited = /* @__PURE__ */ new Set(), propName) { const indent = " ".repeat(depth); if (type.isArray()) { const elementMock = this.generateMockValue( type.getArrayElementTypeOrThrow(), sourceFile, options, depth, visited ); const length = options.arrayLength || 2; return `Array.from({ length: ${length} }, () => (${elementMock}))`; } if (type.isStringLiteral()) { return `"${type.getLiteralValue()}"`; } if (type.isLiteral()) { return JSON.stringify(type.getLiteralValue()); } if (type.isEnumLiteral()) { return type.getText(); } const text = type.getText(); if (text === "string") { if (propName) { const lowerProp = propName.toLowerCase(); if (lowerProp.includes("email")) return "faker.internet.email()"; if (lowerProp.includes("currency")) return "faker.finance.currencyCode()"; if (lowerProp.includes("name")) return "faker.person.fullName()"; if (lowerProp.includes("phone")) return "faker.phone.number()"; if (lowerProp.includes("address")) return "faker.location.streetAddress()"; if (lowerProp.includes("city")) return "faker.location.city()"; if (lowerProp.includes("country")) return "faker.location.country()"; if (lowerProp.includes("url") || lowerProp.includes("link")) return "faker.internet.url()"; if (lowerProp.includes("id")) return "faker.string.uuid()"; } return "faker.lorem.word()"; } if (text === "number") { return "faker.number.int({ min: 1, max: 100 })"; } if (text === "boolean") { return "faker.datatype.boolean()"; } if (text === "Date") { return "faker.date.recent()"; } if (text === "null") { return "null"; } if (text === "undefined") { return "undefined"; } if (text === "any") { return "faker.lorem.word()"; } if (type.isUnion()) { const unionTypes = type.getUnionTypes(); const mockValues = unionTypes.map((t) => this.generateMockValue(t, sourceFile, options, depth, visited)).join(", "); return `faker.helpers.arrayElement([${mockValues}])`; } const apparent = type.getApparentType(); const key = apparent.getText(); if (visited.has(key)) return "{}"; visited.add(key); if (type.getAliasSymbol()?.getName() === "Record") { const [keyType, valueType] = type.getAliasTypeArguments(); if (!keyType || !valueType) { visited.delete(key); return "{}"; } const keyMock = this.generateMockValue(keyType, sourceFile, options, depth + 1, visited); const mockValue = this.generateMockValue(valueType, sourceFile, options, depth + 1, visited); const { min, max } = options.recordLength || { min: 1, max: 5 }; return `Object.fromEntries(Array.from({ length: faker.number.int({ min: ${min}, max: ${max} }) }, () => [${keyMock}, ${mockValue}]))`; } const props = apparent.getProperties(); if (props.length === 0) { visited.delete(key); return "{}"; } const lines = props.filter((prop) => !/^__@.+@\d+$/.test(prop.getName())).map((prop) => { const propName2 = prop.getName(); const propType = prop.getTypeAtLocation(sourceFile); const optional = prop.isOptional?.() ?? false; if (optional && Math.random() > (options.optionalPropertyChance || 0.7)) { return null; } const mockValue = this.generateMockValue(propType, sourceFile, options, depth + 1, visited, propName2); return `${indent} ${JSON.stringify(propName2)}: ${mockValue},`; }).filter(Boolean); visited.delete(key); return `{ ${lines.join("\n")} ${indent}}`; } /** * Generate a mock function for a TypeScript interface */ generateMockFunction(interfaceName, filePath, options = {}) { const sourceFile = this.getSourceFile(filePath); const iface = sourceFile.getInterface(interfaceName); if (!iface) { throw new Error(`Interface '${interfaceName}' not found in ${path.basename(filePath)}`); } const mockGenerator = ` function create${interfaceName}Mock() { return ${this.generateMockValue(iface.getType(), sourceFile, options)}; } function create${interfaceName}Mocks(count = 1) { return Array.from({ length: count }, () => create${interfaceName}Mock()); } return create${interfaceName}Mocks; `; return new Function("faker", mockGenerator)(import_faker.faker); } }; var generator = new MockInterfaceGenerator(); function createFakeGenerator(interfaceName, filePath, options = {}) { return generator.generateMockFunction(interfaceName, filePath, options); } var index_default = createFakeGenerator; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { createFakeGenerator });