openapi-ts-mock-generator
Version:
typescript mock data generator based openapi
1 lines • 23.6 kB
Source Map (JSON)
{"version":3,"sources":["../../src/core/config.ts","../../src/utils/string-utils.ts","../../src/utils/code-utils.ts","../../src/utils/array-utils.ts","../../src/parsers/schema-parser.ts"],"sourcesContent":["import { Faker, ko } from \"@faker-js/faker\"\nimport { Options } from \"./types\"\n\n/**\n * 기본 옵션 설정\n */\nexport const defaultOptions: Options = {\n path: \"\",\n arrayMinLength: 1,\n arrayMaxLength: 3,\n includeCodes: undefined,\n baseDir: \"./\",\n specialPath: undefined,\n handlerUrl: \"*\",\n fakerLocale: \"ko\",\n generateTarget: \"api,schema\",\n clear: false,\n // TypeScriptCodeOptions\n isStatic: false,\n isOptional: false,\n}\n\n/**\n * 배열 길이 관련 상수\n */\nexport const ARRAY_MIN_LENGTH = 1\nexport const ARRAY_MAX_LENGTH = 3\n\n/**\n * 문자열 길이 관련 상수\n */\nexport const MIN_STRING_LENGTH = 3\nexport const MAX_STRING_LENGTH = 20\n\n/**\n * 정수 범위 관련 상수\n */\nexport const MIN_INTEGER = 1\nexport const MAX_INTEGER = 100000\n\n/**\n * 소수 범위 관련 상수\n */\nexport const MIN_NUMBER = 0\nexport const MAX_NUMBER = 100\n\n/**\n * 단어 길이 관련 상수\n */\nexport const MIN_WORD_LENGTH = 0\nexport const MAX_WORD_LENGTH = 3\n\n/**\n * Faker 시드값 (일관된 결과를 위함)\n */\nconst FAKER_SEED = 1\n\n/**\n * 전역 Faker 인스턴스\n * 한국어 로케일을 기본으로 설정하고 시드를 고정하여 일관된 결과 제공\n */\nexport const faker = new Faker({\n locale: [ko],\n})\nfaker.seed(FAKER_SEED)\n\n/**\n * 생성된 파일의 상단에 추가되는 주석\n */\nexport const GEN_COMMENT =\n \"/* Do not edit this file. */\\n/* This file generated by openapi-ts-mock-generator. */\\n\\n\"\n","/**\n * 문자열 처리 관련 유틸리티 함수들\n */\n\n/**\n * UUID를 Base64 형태로 변환\n * URL-safe Base64 인코딩을 사용하여 패딩 제거\n */\nexport const uuidToB64 = (uuid: string): string => {\n const uuidBuffer = Buffer.from(uuid.replace(/-/g, \"\"), \"hex\")\n const base64Uuid = uuidBuffer\n .toString(\"base64\")\n .replace(/\\+/g, \"-\") // + 를 - 로\n .replace(/\\//g, \"_\") // / 를 _ 로\n .replace(/=/g, \"\") // 패딩 제거\n return base64Uuid\n}\n\n/**\n * camelCase 문자열을 kebab-case로 변환\n */\nexport const camelToKebab = (str: string): string => {\n return str.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase()\n}\n\n/**\n * 문자열이 URL인지 확인\n */\nexport const isUrl = (str: string): boolean => {\n return str.startsWith(\"http://\") || str.startsWith(\"https://\")\n}\n\n/**\n * 파일 확장자 추출\n */\nexport const getFileExtension = (filename: string): string => {\n return filename.split(\".\").pop() || \"\"\n}\n","/**\n * 코드 생성 관련 유틸리티 함수들\n */\n\nimport { ParseSchemaType, TypeScriptCodeOptions, CodeFormatOptions } from \"../core\"\n\n/**\n * 객체를 TypeScript 코드로 변환 (nullable 타입 확장 지원)\n * 동적 모킹에서 optional 필드를 지원하는 코드를 생성\n */\nexport const toTypeScriptCode = (\n param: ParseSchemaType,\n options: TypeScriptCodeOptions\n): string => {\n const { depth = 0, isStatic } = options\n\n const prefixSpace = \" \".repeat(depth * 2) // 들여쓰기용\n\n if (param === null) {\n return \"null\"\n }\n\n if (Array.isArray(param)) {\n const results = param\n .map((elem) => toTypeScriptCode(elem, { ...options, depth: depth + 1 }))\n .join(\",\\n\" + prefixSpace)\n return [\"[\", results, \"]\"].join(\"\\n\" + prefixSpace)\n }\n\n if (typeof param === \"object\") {\n const results = Object.entries(param)\n .map(([key, value]) => {\n return generateObjectProperty(key, value, options, prefixSpace)\n })\n .join(\"\\n\" + prefixSpace)\n\n return [\"{\", `${results}`, \"}\"].join(\"\\n\" + prefixSpace)\n }\n\n // 문자열 처리\n if (typeof param === \"string\") {\n // 동적 faker 호출은 그대로 유지\n if (\n isStatic === false &&\n (param.startsWith(\"faker\") || param.startsWith(\"Buffer.from(faker\"))\n ) {\n return param\n }\n\n // \" as const\" 분리하여 처리\n if (param.endsWith(\" as const\")) {\n return `\"${param.slice(0, -\" as const\".length)}\" as const`\n }\n }\n\n return JSON.stringify(param)\n}\n\n/**\n * nullable 타입 확장 여부 확인\n */\nconst shouldApplyNullableExtension = (value: any, isOptional: boolean): boolean => {\n if (!isOptional) return false\n\n // 값이 null인 경우\n if (value === null) return true\n\n // 문자열 및 null을 포함한 경우\n if (typeof value === \"string\" && value.includes(\",null\")) {\n return true\n }\n\n return false\n}\n\n/**\n * 객체 프로퍼티를 생성 (nullable 타입 확장 포함)\n */\nconst generateObjectProperty = (\n key: string,\n value: any,\n options: TypeScriptCodeOptions,\n prefixSpace: string\n): string => {\n const { isOptional, depth = 0 } = options\n\n // nullable 타입 확장 로직\n const shouldApplyNullable = shouldApplyNullableExtension(value, isOptional)\n const nullableTypeExtensionStart = shouldApplyNullable\n ? `...(faker.datatype.boolean() ? {\\n${prefixSpace}`\n : \"\"\n const nullableTypeExtensionEnd = shouldApplyNullable ? `\\n${prefixSpace}} : {})` : \"\"\n\n const propertyValue = toTypeScriptCode(value, {\n ...options,\n depth: depth + 1,\n })\n\n return `${nullableTypeExtensionStart}${prefixSpace}${key}: ${propertyValue}${nullableTypeExtensionEnd},`\n}\n\n/**\n * 멀티라인 코드를 한 줄로 압축\n */\nexport const compressCode = (code: string): string => {\n return code\n .replace(/\\n/g, \" \") // 줄바꿈을 공백으로\n .replace(/\\s+/g, \" \") // 연속된 공백을 하나로\n .replace(/\\s\\./g, \".\") // 공백 + 점을 점으로\n .trim()\n}\n\n/**\n * TypeScript 인터페이스 코드 생성\n */\nexport const generateInterface = (name: string, properties: Record<string, string>): string => {\n const props = Object.entries(properties)\n .map(([key, type]) => ` ${key}: ${type}`)\n .join(\"\\n\")\n\n return `interface ${name} {\\n${props}\\n}`\n}\n\n/**\n * TypeScript 타입 별칭 코드 생성\n */\nexport const generateTypeAlias = (name: string, type: string): string => {\n return `type ${name} = ${type}`\n}\n","/**\n * 배열 처리 관련 유틸리티 함수들\n */\n\nimport { faker } from \"../core\"\n\n/**\n * 지정된 범위 내에서 랜덤한 길이의 배열을 생성\n * 배열의 각 요소는 인덱스 값으로 초기화됨\n */\nexport const getRandomLengthArray = (min: number = 1, max: number = 3): number[] => {\n const length = faker.number.int({ min, max })\n return Array.from({ length }, (_, i) => i)\n}\n\n/**\n * 배열을 지정된 크기로 청크 단위로 분할\n */\nexport const chunkArray = <T>(array: T[], size: number): T[][] => {\n const chunks: T[][] = []\n for (let i = 0; i < array.length; i += size) {\n chunks.push(array.slice(i, i + size))\n }\n return chunks\n}\n\n/**\n * 배열에서 중복 제거 (primitive 타입용)\n */\nexport const unique = <T>(array: T[]): T[] => {\n return [...new Set(array)]\n}\n\n/**\n * 배열에서 중복 제거 (객체용, 키 기준)\n */\nexport const uniqueBy = <T>(array: T[], keyFn: (item: T) => any): T[] => {\n const seen = new Set()\n return array.filter((item) => {\n const key = keyFn(item)\n if (seen.has(key)) {\n return false\n }\n seen.add(key)\n return true\n })\n}\n","/**\n * OpenAPI 스키마 파싱 및 값 생성 로직\n */\n\nimport {\n Options,\n ParseSchemaType,\n SchemaOutputType,\n faker,\n MIN_STRING_LENGTH,\n MAX_STRING_LENGTH,\n MIN_INTEGER,\n MAX_INTEGER,\n MIN_NUMBER,\n MAX_NUMBER,\n MIN_WORD_LENGTH,\n MAX_WORD_LENGTH,\n TypeScriptCodeOptions,\n} from \"../core\"\nimport { compressCode, uuidToB64, getRandomLengthArray, toTypeScriptCode } from \"../utils\"\nimport SwaggerParser from \"@apidevtools/swagger-parser\"\nimport { pascalCase } from \"change-case-all\"\nimport { SchemaObject, isReference } from \"oazapfts/generate\"\nimport { OpenAPIV3_1 } from \"openapi-types\"\n\n/**\n * OpenAPI 스키마를 파싱하여 실제 값을 생성\n */\nexport const parseSchema = (\n schemaValue: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject,\n specialSchema: {\n titleSpecial: Record<string, SchemaOutputType>\n descriptionSpecial: Record<string, SchemaOutputType>\n },\n options: Options,\n outputSchema: ParseSchemaType = {}\n): ParseSchemaType => {\n if (isReference(schemaValue)) {\n console.warn(\"can't parse reference schema\", schemaValue, schemaValue.$ref)\n return\n }\n\n if (schemaValue.type === \"object\") {\n if (schemaValue.properties === undefined) return {}\n return Object.entries(schemaValue.properties).reduce((acc, [key, field]) => {\n acc[key] = parseSchema(field, specialSchema, options, outputSchema) as SchemaOutputType\n return acc\n }, {} as Record<string, SchemaOutputType>)\n } else if (schemaValue.enum !== undefined) {\n // enum value\n const enumValue = options.isStatic\n ? faker.helpers.arrayElement(schemaValue.enum)\n : `faker.helpers.arrayElement<${schemaValue.enum\n .map((item) => `\"${item}\"`)\n .join(\" | \")}>(${toTypeScriptCode(schemaValue.enum, {\n depth: 0,\n ...options,\n })})`\n if (options.isStatic && typeof enumValue === \"string\") return enumValue + \" as const\"\n return enumValue\n } else if (schemaValue.allOf !== undefined) {\n // allOf value, sub model\n const allOfValue = schemaValue.allOf\n return faker.helpers.arrayElement(\n allOfValue.map((field) => {\n return parseSchema(field, specialSchema, options, outputSchema)\n })\n )\n } else if (schemaValue.anyOf !== undefined) {\n // anyOf value, select one or more. ex) string or null\n const anyOfValue = schemaValue.anyOf\n return options.isStatic\n ? faker.helpers.arrayElement(\n anyOfValue.map((field) => {\n return parseSchema(field, specialSchema, options, outputSchema)\n })\n )\n : compressCode(\n `\n faker.helpers.arrayElement([\n ${anyOfValue.map((field) =>\n toTypeScriptCode(parseSchema(field, specialSchema, options, {}), {\n depth: 0,\n ...options,\n })\n )}\n ])\n `\n )\n } else if (schemaValue.oneOf !== undefined) {\n // oneOf value, exactly one. Can't find example\n const oneOfValue = schemaValue.oneOf\n return options.isStatic\n ? faker.helpers.arrayElement(\n oneOfValue.map((field) => {\n return parseSchema(field, specialSchema, options, outputSchema)\n })\n )\n : compressCode(\n `\n faker.helpers.arrayElement([\n ${oneOfValue.map((field) =>\n toTypeScriptCode(parseSchema(field, specialSchema, options, {}), {\n depth: 0,\n ...options,\n })\n )}\n ])\n `\n )\n } else if (schemaValue.type === \"array\") {\n if (\"prefixItems\" in schemaValue) {\n const length = faker.number.int({\n min: schemaValue.minItems,\n max: schemaValue.maxItems,\n })\n\n return (schemaValue.prefixItems as Array<SchemaObject>)\n .slice(0, length)\n .map((field) => parseSchema(field, specialSchema, options, outputSchema)) as (\n | SchemaOutputType\n | Record<string, SchemaOutputType>\n )[]\n }\n // array\n const arrayValue = schemaValue.items\n return getRandomLengthArray(options.arrayMinLength, options.arrayMaxLength).map(() =>\n parseSchema(arrayValue, specialSchema, options, outputSchema)\n ) as (SchemaOutputType | Record<string, SchemaOutputType>)[]\n }\n return valueGenerator(schemaValue, specialSchema, options)\n}\n\n/**\n * 참조 스키마를 파싱하여 이름과 값을 반환\n */\nexport const refSchemaParser = (ref: string, refs: SwaggerParser[\"$refs\"]) => {\n const schemaName = pascalCase(ref.replace(\"#/components/schemas/\", \"\"))\n const schemaValue = refs.get(ref) as OpenAPIV3_1.SchemaObject\n return { name: schemaName, value: schemaValue }\n}\n\n/**\n * 스키마 타입별로 실제 값을 생성하는 함수\n */\nexport const valueGenerator = (\n schemaValue: OpenAPIV3_1.SchemaObject,\n specialSchema: {\n titleSpecial: Record<string, SchemaOutputType>\n descriptionSpecial: Record<string, SchemaOutputType>\n },\n options: TypeScriptCodeOptions\n): ParseSchemaType => {\n const { isStatic } = options\n // if title or description in special keys\n // return special faker data\n const { titleSpecial, descriptionSpecial } = specialSchema\n if (schemaValue.title && titleSpecial[schemaValue.title]) {\n return titleSpecial[schemaValue.title]\n } else if (schemaValue.description && descriptionSpecial[schemaValue.description]) {\n return descriptionSpecial[schemaValue.description]\n }\n\n if (schemaValue.type === \"string\" && schemaValue.format === \"date-time\") {\n // date-time, 2017-07-21T17:32:28Z\n return isStatic\n ? faker.date\n .between({\n from: \"2020-01-01T00:00:00.000Z\",\n to: \"2030-12-31T23:59:59.999Z\",\n })\n .toISOString()\n : compressCode(\n `\n faker.date.between({\n from: \"2020-01-01T00:00:00.000Z\",\n to: \"2030-12-31T23:59:59.999Z\",\n })\n .toISOString()\n `\n )\n } else if (schemaValue.type === \"string\" && schemaValue.format === \"date\") {\n // date, 2017-07-21\n return isStatic\n ? faker.date\n .between({\n from: \"2020-01-01T00:00:00.000Z\",\n to: \"2030-12-31T23:59:59.999Z\",\n })\n .toISOString()\n .split(\"T\")[0]\n : compressCode(\n `\n faker.date.between({\n from: \"2020-01-01T00:00:00.000Z\",\n to: \"2030-12-31T23:59:59.999Z\",\n })\n .toISOString()\n .split(\"T\")[0]\n `\n )\n } else if (schemaValue.type === \"string\" && schemaValue.pattern) {\n return isStatic\n ? faker.helpers.fromRegExp(schemaValue.pattern)\n : `faker.helpers.fromRegExp(/${schemaValue.pattern}/)`\n } else if (schemaValue.type === \"string\" && schemaValue.title?.toLowerCase() === \"b64uuid\") {\n // generate base 64 uuid\n const baseUuid = faker.string.uuid()\n return isStatic\n ? uuidToB64(baseUuid)\n : compressCode(\n `\n Buffer.from(faker.string.uuid().replace(/-/g, \"\"), \"hex\")\n .toString(\"base64\")\n .replace(/\\\\+/g, \"-\")\n .replace(/\\\\//g, \"_\")\n .replace(/=/g, \"\")\n `\n )\n } else if (schemaValue.type === \"string\") {\n const minLength =\n schemaValue.minLength ??\n Math.min(MIN_STRING_LENGTH, schemaValue.maxLength ?? MAX_STRING_LENGTH)\n const maxLength =\n schemaValue.maxLength ??\n Math.max(MAX_STRING_LENGTH, schemaValue.minLength ?? MIN_STRING_LENGTH)\n\n return isStatic\n ? faker.string.alphanumeric({\n length: { min: minLength, max: maxLength },\n })\n : compressCode(\n `\n faker.string.alphanumeric({\n length: { min: ${minLength}, max: ${maxLength} },\n })\n `\n )\n } else if (schemaValue.type === \"integer\") {\n return isStatic\n ? faker.number.int({ min: MIN_INTEGER, max: MAX_INTEGER })\n : compressCode(\n `\n faker.number.int({ min: ${MIN_INTEGER}, max: ${MAX_INTEGER} })\n `\n )\n } else if (schemaValue.type === \"number\") {\n const minNumber = schemaValue.minimum ?? Math.min(MIN_NUMBER, schemaValue.maximum ?? MAX_NUMBER)\n const maxNumber = schemaValue.maximum ?? Math.max(MAX_NUMBER, schemaValue.minimum ?? MIN_NUMBER)\n return isStatic\n ? faker.number.float({\n min: minNumber,\n max: maxNumber,\n fractionDigits: 2,\n })\n : compressCode(\n `\n faker.number.float({\n min: ${minNumber},\n max: ${maxNumber},\n fractionDigits: 2,\n })\n `\n )\n } else if (schemaValue.type === \"boolean\") {\n return isStatic ? faker.datatype.boolean() : \"faker.datatype.boolean()\"\n } else if (schemaValue.type === \"null\") {\n return null\n } else if (Object.keys(schemaValue).length === 0) {\n // array any. ex) blank=True list\n return isStatic\n ? faker.word.words({\n count: {\n min: MIN_WORD_LENGTH,\n max: MAX_WORD_LENGTH,\n },\n })\n : compressCode(\n `\n faker.word.words({\n count: {\n min: ${MIN_WORD_LENGTH},\n max: ${MAX_WORD_LENGTH},\n },\n })\n `\n )\n }\n\n return isStatic ? faker.word.adjective() : \"faker.word.adjective()\"\n}\n"],"mappings":"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","names":[]}