openapi-ts-mock-generator
Version:
typescript mock data generator based openapi
1 lines • 89.3 kB
Source Map (JSON)
{"version":3,"sources":["../src/defaults.ts","../src/writer.ts","../src/parser.ts","../src/types.ts","../src/generate.ts","../src/index.ts","../node_modules/cac/dist/index.mjs","../src/cli.ts"],"sourcesContent":["import { Options } from \"./types\"\nimport { Faker, ko } from \"@faker-js/faker\"\n\nexport const defaultOptions: Options = {\n path: \"\",\n baseDir: \"./\",\n arrayMinLength: 1,\n arrayMaxLength: 3,\n isStatic: true,\n handlerUrl: \"*\",\n fakerLocale: \"ko\",\n generateTarget: \"api,schema\",\n}\n\nexport const ARRAY_MIN_LENGTH = 1\nexport const ARRAY_MAX_LENGTH = 3\n\nexport const MIN_STRING_LENGTH = 3\nexport const MAX_STRING_LENGTH = 20\n\nexport const MIN_INTEGER = 1\nexport const MAX_INTEGER = 100000\n\nexport const MIN_NUMBER = 0\nexport const MAX_NUMBER = 100\n\nexport const MIN_WORD_LENGTH = 0\nexport const MAX_WORD_LENGTH = 3\n\nconst FAKER_SEED = 1\n\nexport const faker = new Faker({\n locale: [ko],\n})\nfaker.seed(FAKER_SEED)\n\n// script generated comment\nexport const GEN_COMMENT =\n \"/* Do not edit this file. */\\n/* This file generated by openapi-ts-mock-generator. */\\n\\n\"\n","import { getRandomLengthArray, parseSchema, refSchemaParser, specialFakerParser } from \"./parser\"\nimport { Options, ParseSchemaType, PathNormalizedType, SchemaOutputType } from \"./types\"\nimport SwaggerParser from \"@apidevtools/swagger-parser\"\nimport { camelCase, pascalCase } from \"change-case-all\"\nimport { existsSync, mkdirSync, writeFileSync, rmSync, readdirSync } from \"fs\"\nimport { isReference } from \"oazapfts/generate\"\nimport * as path from \"path\"\nimport { GEN_COMMENT } from \"./defaults\"\n\nexport const writeHandlers = (paths: PathNormalizedType[], options: Options) => {\n const firstTags = Array.from(new Set(paths.map((path) => path.tags[0])))\n // create records with tag as key\n const handlersPerTag = firstTags.reduce(\n (acc, tag) => {\n acc[tag] = []\n return acc\n },\n {} as Record<string, string[]>\n )\n\n paths.forEach((path) => {\n const codeBaseArray = [` http.${path.method}(\\`\\${handlerUrl}${path.pathname}\\`, () => {`]\n if (path.responses.length === 1) {\n // single response\n const res = path.responses[0]\n if (res.schema?.type === \"ref\") {\n const schemaName = pascalCase(res.schema.value.$ref.replace(\"#/components/schemas/\", \"\"))\n codeBaseArray.push(` // Schema is ${schemaName}`)\n }\n const outputResName = `get${pascalCase(path.operationId)}${res.statusCode}`\n codeBaseArray.push(` return HttpResponse.json(${outputResName}(), {`)\n codeBaseArray.push(` status: ${res.statusCode},`)\n codeBaseArray.push(` })`)\n } else if (path.responses.length > 1) {\n // multiple responses\n // random select response\n codeBaseArray.push(` const responses = [`)\n path.responses.forEach((res) => {\n const schemaName =\n res.schema?.type === \"ref\"\n ? pascalCase(res.schema.value.$ref.replace(\"#/components/schemas/\", \"\"))\n : \"\"\n const schemaComment = schemaName ? ` // Schema is ${schemaName}` : \"\"\n const outputResName = `get${pascalCase(path.operationId)}${res.statusCode}`\n codeBaseArray.push(\n ` [${outputResName}(), { status: ${res.statusCode} }],${schemaComment}`\n )\n return outputResName\n })\n codeBaseArray.push(` ]`)\n codeBaseArray.push(` const randomIndex = Math.floor(Math.random() * responses.length)`)\n codeBaseArray.push(` return HttpResponse.json(...responses[randomIndex])`)\n } else {\n // empty responses\n codeBaseArray.push(` return HttpResponse.json()`)\n }\n codeBaseArray.push(` }),`)\n const handler = codeBaseArray.join(\"\\n\")\n handlersPerTag[path.tags[0]].push(handler)\n })\n\n Object.entries(handlersPerTag).forEach(([tag, handlers]) => {\n const importMSW = `import { http, HttpResponse } from 'msw'`\n const responseNames = handlers\n .reduce((acc, handler) => {\n const matched = handler.match(/get[A-Z]\\w+/g)\n if (matched === null) return acc\n return [...acc, ...matched]\n }, [] as string[])\n .join(\", \")\n const importResponses =\n responseNames.length > 0 ? `import { ${responseNames} } from \"../response\"\\n` : \"\"\n\n const handlerUrl = `const handlerUrl = \"${options.handlerUrl}\"`\n\n const handlerName = camelCase(tag)\n const mockHandlers = [\n `${importMSW}`,\n `${importResponses}`,\n `${handlerUrl}`,\n ``,\n `export const ${handlerName}Handlers = [`,\n `${handlers.join(\"\\n\\n\")}`,\n `]`,\n ].join(\"\\n\")\n const directory = path.join(options.baseDir ?? \"\", \"handlers\")\n if (!existsSync(directory)) {\n mkdirSync(directory, { recursive: true })\n } else if (options.clear) {\n // clear directory\n readdirSync(directory).forEach((file) => {\n rmSync(path.join(directory, file))\n })\n }\n const fileName = path.join(directory, `${tag}.ts`)\n writeFileSync(fileName, GEN_COMMENT + mockHandlers)\n console.log(`Generated Handler ${fileName}`)\n })\n\n // make mockHandlers.ts for merge all handlers\n const handlersImport = Object.keys(handlersPerTag)\n .map((tag) => {\n const handlerName = `${camelCase(tag)}Handlers`\n return `import { ${handlerName} } from \"./handlers/${tag}\"`\n })\n .join(\"\\n\")\n const handlersArrayItem = Object.keys(handlersPerTag)\n .map((tag) => {\n const handlerName = `${camelCase(tag)}Handlers`\n return ` ...${handlerName},`\n })\n .join(\"\\n\")\n\n const mockHandlers = [\n `${handlersImport}`,\n ``,\n `export const handlers = [`,\n `${handlersArrayItem}`,\n `]`,\n ].join(\"\\n\")\n const fileName = path.join(options.baseDir ?? \"\", \"mockHandlers.ts\")\n writeFileSync(fileName, GEN_COMMENT + mockHandlers)\n console.log(`Generated mock handlers ${fileName}`)\n}\n\nexport const writeResponses = async (paths: PathNormalizedType[], options: Options) => {\n const parser = new SwaggerParser()\n const openapiPath = options.path.startsWith(\"http\")\n ? options.path\n : path.join(options.baseDir ?? \"\", options.path)\n await parser.dereference(openapiPath)\n const refs = parser.$refs\n\n const firstTags = Array.from(new Set(paths.map((path) => path.tags[0])))\n // create records with tag as key\n const codeBasePerTag = firstTags.reduce(\n (acc, tag) => {\n acc[tag] = []\n return acc\n },\n {} as Record<string, string[]>\n )\n const specialFakers = specialFakerParser(options)\n paths.forEach((path) => {\n const pathResponses = path.responses.map((res) => {\n const codeBaseArray = [\n `export const get${pascalCase(path.operationId)}${res.statusCode} = () => {`,\n ]\n if (res.schema?.type === \"ref\") {\n const { name, value } = refSchemaParser(res.schema.value.$ref, refs)\n const outputSchema = parseSchema(value, specialFakers, options)\n codeBaseArray.push(` // Schema is ${name}`)\n codeBaseArray.push(\n ` return ${toUnquotedJSON(outputSchema, {\n depth: 1,\n isStatic: options.isStatic,\n })}`\n )\n } else if (res.schema?.type === \"array\") {\n if (isReference(res.schema.value)) {\n const { name, value } = refSchemaParser(res.schema.value.$ref, refs)\n const outputSchema = getRandomLengthArray(\n options.arrayMinLength,\n options.arrayMaxLength\n ).map(() => parseSchema(value, specialFakers, options))\n codeBaseArray.push(` // Schema is ${name} array`)\n codeBaseArray.push(\n ` return ${toUnquotedJSON(outputSchema, {\n depth: 1,\n isStatic: options.isStatic,\n })}`\n )\n } else {\n const outputSchema = getRandomLengthArray(\n options.arrayMinLength,\n options.arrayMaxLength\n ).map(() => res.schema && parseSchema(res.schema.value, specialFakers, options))\n codeBaseArray.push(\n ` return ${toUnquotedJSON(outputSchema, {\n depth: 1,\n isStatic: options.isStatic,\n })}`\n )\n }\n } else if (res.schema?.type === \"anyOf\") {\n const firstSchema = res.schema.value.anyOf?.[0]\n if (isReference(firstSchema)) {\n const { name, value } = refSchemaParser(firstSchema.$ref, refs)\n const outputSchema = parseSchema(value, specialFakers, options)\n codeBaseArray.push(` // Schema is ${name}`)\n codeBaseArray.push(\n ` return ${toUnquotedJSON(outputSchema, {\n depth: 1,\n isStatic: options.isStatic,\n })}`\n )\n } else {\n codeBaseArray.push(` return ${res.schema.value}`)\n }\n } else {\n codeBaseArray.push(` return ${res.schema?.value}`)\n }\n\n return [...codeBaseArray, `}`].join(\"\\n\")\n })\n const pathResponsesWithComment = `// ${path.operationId}\\n` + pathResponses.join(\"\\n\\n\")\n codeBasePerTag[path.tags[0]].push(pathResponsesWithComment)\n })\n\n const directory = path.join(options.baseDir ?? \"\", \"response\")\n if (!existsSync(directory)) {\n mkdirSync(directory, { recursive: true })\n } else if (options.clear) {\n // clear directory\n readdirSync(directory).forEach((file) => {\n rmSync(path.join(directory, file))\n })\n }\n\n Object.entries(codeBasePerTag).forEach(([tag, responses]) => {\n const needImportFaker = responses.some((res) => res.includes(\"faker.\"))\n const importFaker =\n options.isStatic || !needImportFaker ? \"\" : 'import { faker } from \"../fakers\"\\n\\n'\n\n const fileName = `${directory}/${tag}.ts`\n writeFileSync(fileName, GEN_COMMENT + importFaker + responses.join(\"\\n\\n\"))\n console.log(`Generated ${fileName}`)\n })\n\n // make index.ts for merge all responses\n const importResponses = Object.entries(codeBasePerTag).map(([tag, responses]) => {\n const responseNames = responses\n .reduce((acc, handler) => {\n const matched = handler.match(/get[A-Z]\\w+/g)\n if (matched === null) return acc\n return [...acc, ...matched]\n }, [] as string[])\n .join(\",\\n \")\n return [\"export {\", \" \" + responseNames, '} from \"./' + tag + '\"'].join(\"\\n\")\n })\n const fileName = `${directory}/index.ts`\n writeFileSync(fileName, GEN_COMMENT + importResponses.join(\"\\n\"))\n console.log(`Generated ${fileName}`)\n}\n\nexport const writeSchema = (schemas: Record<string, SchemaOutputType>, options: Options) => {\n // key is schema name, value is generated schema value\n const generatedVars = Object.entries(schemas)\n .map(([varName, varValue]) => {\n return `export const ${varName}Mock = ${toUnquotedJSON(varValue, {\n isStatic: options.isStatic,\n })}`\n })\n .join(\"\\n\\n\")\n\n const importFaker = options.isStatic ? \"\" : 'import { faker } from \"./fakers\"\\n\\n'\n\n const outputFileName = path.join(`${options.baseDir}`, \"schemas.ts\")\n writeFileSync(outputFileName, GEN_COMMENT + importFaker + generatedVars)\n console.log(`Generated schema ${outputFileName}`)\n}\n\nexport const writeFaker = (options: Options) => {\n const directory = path.join(options.baseDir ?? \"\")\n if (!existsSync(directory)) {\n mkdirSync(directory, { recursive: true })\n }\n const localeOption = options.fakerLocale.replace(\",\", \", \")\n const importFaker = `import { Faker, ${localeOption} } from \"@faker-js/faker\"\\n\\n`\n const fakerDeclare = [\n \"export const faker = new Faker({\",\n ` locale: [${localeOption}]`,\n \"})\",\n ].join(\"\\n\")\n\n const outputFileName = path.join(`${options.baseDir}`, \"fakers.ts\")\n writeFileSync(outputFileName, GEN_COMMENT + importFaker + fakerDeclare)\n console.log(`Generated fakers ${outputFileName}`)\n}\n\nexport const toUnquotedJSON = (\n param: ParseSchemaType,\n options: {\n depth?: number\n isStatic?: boolean\n singleLine?: boolean\n }\n): string => {\n const { depth, isStatic, singleLine } = {\n depth: 0,\n isStatic: false,\n singleLine: false,\n ...options,\n }\n\n const prefixSpace = \" \".repeat(depth * 2) // for indent\n const lineBreak = singleLine ? \"\" : \"\\n\"\n\n if (param === null) {\n return \"null\"\n } else if (Array.isArray(param)) {\n const results = param.map((elem) => toUnquotedJSON(elem, { ...options, depth: depth + 1 }))\n const firstElementSpace = singleLine ? \"\" : \" \"\n return [\"[\", firstElementSpace + results.join(\", \"), \"]\"].join(lineBreak + prefixSpace)\n } else if (typeof param === \"object\") {\n const firstElementSpace = singleLine ? \" \" : \" \"\n const lastComma = singleLine ? \", \" : \",\"\n const results = Object.entries(param)\n .map(\n ([key, value]) =>\n `${firstElementSpace}${key}: ${toUnquotedJSON(value, {\n ...options,\n depth: depth + 1,\n })}${lastComma}`\n )\n .join(lineBreak + prefixSpace)\n return [\"{\", `${results}`, \"}\"].join(lineBreak + prefixSpace)\n } else if (\n typeof param === \"string\" &&\n isStatic === false &&\n (param.startsWith(\"faker\") || param.startsWith(\"Buffer.from(faker\"))\n ) {\n return param // dynamic mode, start with faker or Buffer.from(faker)\n } else if (typeof param === \"string\" && param.endsWith(\" as const\")) {\n // split \" as const\" from string\n return `\"${param.slice(0, -\" as const\".length)}\" as const`\n }\n return JSON.stringify(param)\n}\n\nexport const multiLineStr = (str: string) => {\n // line break to space\n // multiple space to single space\n // space + dot to dot\n return str.replace(/\\n/g, \" \").replace(/\\s+/g, \" \").replace(/\\s\\./g, \".\").trim()\n}\n","import {\n ARRAY_MAX_LENGTH,\n ARRAY_MIN_LENGTH,\n MAX_INTEGER,\n MAX_NUMBER,\n MAX_STRING_LENGTH,\n MAX_WORD_LENGTH,\n MIN_INTEGER,\n MIN_NUMBER,\n MIN_STRING_LENGTH,\n MIN_WORD_LENGTH,\n faker,\n} from \"./defaults\"\nimport { Options, ParseSchemaType, SchemaOutputType } from \"./types\"\nimport { multiLineStr, toUnquotedJSON } from \"./writer\"\nimport SwaggerParser from \"@apidevtools/swagger-parser\"\nimport { pascalCase } from \"change-case-all\"\nimport { existsSync, readFileSync } from \"fs\"\nimport { SchemaObject, isReference } from \"oazapfts/generate\"\nimport { OpenAPIV3_1 } from \"openapi-types\"\nimport { join } from \"path\"\n\nexport const parseSchema = (\n schemaValue: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject,\n specialSchema: ReturnType<typeof specialFakerParser>,\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(\" | \")}>(${toUnquotedJSON(schemaValue.enum, {\n depth: 0,\n isStatic: options.isStatic,\n singleLine: true,\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 : multiLineStr(`\n faker.helpers.arrayElement([\n ${anyOfValue.map((field) =>\n toUnquotedJSON(parseSchema(field, specialSchema, options, {}), {\n depth: 0,\n isStatic: options.isStatic,\n singleLine: true,\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 : multiLineStr(`\n faker.helpers.arrayElement([\n ${oneOfValue.map((field) =>\n toUnquotedJSON(parseSchema(field, specialSchema, options, {}), {\n depth: 0,\n isStatic: options.isStatic,\n singleLine: true,\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.isStatic)\n}\n\nconst uuidToB64 = (uuid: 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\nconst valueGenerator = (\n schemaValue: OpenAPIV3_1.SchemaObject,\n specialSchema: ReturnType<typeof specialFakerParser>,\n isStatic: boolean\n): ParseSchemaType => {\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 : multiLineStr(`\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 } 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 : multiLineStr(`\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 } 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 : multiLineStr(`\n Buffer.from(faker.string.uuid().replace(/-/g, \"\"), \"hex\")\n .toString(\"base64\")\n .replace(/\\\\+/g, \"-\")\n .replace(/\\\\//g, \"_\")\n .replace(/=/g, \"\")\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 : multiLineStr(`\n faker.string.alphanumeric({\n length: { min: ${minLength}, max: ${maxLength} },\n })\n `)\n } else if (schemaValue.type === \"integer\") {\n return isStatic\n ? faker.number.int({ min: MIN_INTEGER, max: MAX_INTEGER })\n : multiLineStr(`\n faker.number.int({ min: ${MIN_INTEGER}, max: ${MAX_INTEGER} })\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 : multiLineStr(`\n faker.number.float({\n min: ${minNumber},\n max: ${maxNumber},\n fractionDigits: 2,\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 : multiLineStr(`\n faker.word.words({\n count: {\n min: ${MIN_WORD_LENGTH},\n max: ${MAX_WORD_LENGTH},\n },\n })\n `)\n }\n\n return isStatic ? faker.word.adjective() : \"faker.word.adjective()\"\n}\n\nexport const getRandomLengthArray = (\n min: number = ARRAY_MIN_LENGTH,\n max: number = ARRAY_MAX_LENGTH\n) => {\n const length = faker.number.int({\n min,\n max,\n })\n return Array.from({ length }, (_, i) => i)\n}\n\nexport const refSchemaParser = (ref: string, refs: SwaggerParser.$Refs) => {\n const schemaName = pascalCase(ref.replace(\"#/components/schemas/\", \"\"))\n const schemaValue: OpenAPIV3_1.SchemaObject = refs.get(ref)\n return { name: schemaName, value: schemaValue }\n}\n\nconst getFakerValue = (value: object, options: { isStatic: boolean }): SchemaOutputType => {\n if (\"value\" in value) {\n // value type, use directly\n return value.value as SchemaOutputType\n }\n if (\"module\" in value && \"type\" in value) {\n // dynamic faker\n if (options.isStatic === false) {\n const fakerOption =\n \"options\" in value\n ? toUnquotedJSON(value.options, {\n depth: 0,\n isStatic: options.isStatic,\n singleLine: true,\n })\n : \"\"\n return `faker.${value.module}.${value.type}(${fakerOption})`\n }\n // faker type, make faker\n const fakerModule = faker[value.module as keyof typeof faker]\n if (fakerModule === undefined) {\n console.warn(\"can't find faker module\", fakerModule)\n return undefined\n }\n const fakerFunc = fakerModule[value.type as keyof typeof fakerModule] as Function\n if (fakerFunc === undefined || typeof fakerFunc !== \"function\") {\n console.warn(\"can't find faker function\", fakerFunc)\n return undefined\n }\n return \"options\" in value ? fakerFunc(value.options) : fakerFunc()\n }\n return undefined\n}\n\nexport const specialFakerParser = (options: Options) => {\n if (options.specialPath === undefined)\n return {\n titleSpecial: {},\n descriptionSpecial: {},\n }\n const titlePath = join(options.baseDir ?? \"\", options.specialPath, \"titles.json\")\n const descPath = join(options.baseDir ?? \"\", options.specialPath, \"descriptions.json\")\n const titleSpecialKey: Record<string, object> = existsSync(titlePath)\n ? JSON.parse(readFileSync(titlePath, \"utf-8\"))\n : {}\n const descriptionSpecialKey: Record<string, object> = existsSync(descPath)\n ? JSON.parse(readFileSync(descPath, \"utf-8\"))\n : {}\n\n const titleSpecial = Object.entries(titleSpecialKey).reduce((acc, [key, value]) => {\n const fakerValue = getFakerValue(value, {\n isStatic: options.isStatic,\n })\n acc[key] = fakerValue\n return acc\n }, {} as Record<string, SchemaOutputType>)\n\n const descriptionSpecial = Object.entries(descriptionSpecialKey).reduce((acc, [key, value]) => {\n const fakerValue = getFakerValue(value, {\n isStatic: options.isStatic,\n })\n acc[key] = fakerValue\n return acc\n }, {} as Record<string, SchemaOutputType>)\n\n return { titleSpecial, descriptionSpecial }\n}\n","import { OpenAPIV3_1 } from \"openapi-types\"\n\nexport type Options = {\n path: string\n arrayMinLength?: number\n arrayMaxLength?: number\n isStatic: boolean\n includeCodes?: number[]\n baseDir?: string\n specialPath?: string\n handlerUrl: string\n fakerLocale: string\n generateTarget: string\n clear?: boolean\n}\n\nexport type SchemaOutputType = string | number | boolean | null | undefined | Date\n\ntype NestedSchemaOutputType<T> =\n | {\n [K in keyof T]: T[K] extends object ? NestedSchemaOutputType<T[K]> : T[K]\n }\n | SchemaOutputType\n | {}\n\nexport type ParseSchemaType = NestedSchemaOutputType<string>\n\nexport enum HttpMethods {\n GET = \"get\",\n PUT = \"put\",\n POST = \"post\",\n DELETE = \"delete\",\n OPTIONS = \"options\",\n HEAD = \"head\",\n PATCH = \"patch\",\n TRACE = \"trace\",\n}\n\nexport type ResponseSchemaType =\n | {\n type: \"anyOf\" | \"oneOf\" | \"array\"\n value: OpenAPIV3_1.SchemaObject\n }\n | {\n type: \"ref\"\n value: OpenAPIV3_1.ReferenceObject\n }\n | undefined\n\nexport type PathNormalizedType = {\n pathname: string\n operationId: string\n summary: string\n method: HttpMethods\n responses: {\n statusCode: number\n description: string\n schema: ResponseSchemaType\n }[]\n tags: string[]\n}\n\nexport const isNotNullish = <TValue>(value: TValue | null | undefined): value is TValue => {\n return value !== null && value !== undefined\n}\n","import { parseSchema, specialFakerParser } from \"./parser\"\nimport {\n HttpMethods,\n Options,\n PathNormalizedType,\n ResponseSchemaType,\n SchemaOutputType,\n isNotNullish,\n} from \"./types\"\nimport SwaggerParser from \"@apidevtools/swagger-parser\"\nimport { isReference } from \"oazapfts/generate\"\nimport { OpenAPIV3_1 } from \"openapi-types\"\nimport * as path from \"path\"\n\nconst getOpenAPIDocsDeref = async (path: string) => {\n const doc = await SwaggerParser.dereference(path)\n const isOpenApiV3 = \"openapi\" in doc && doc.openapi.startsWith(\"3\")\n if (isOpenApiV3) return doc as OpenAPIV3_1.Document\n return\n}\n\nconst getOpenAPIDocsBundle = async (path: string) => {\n const doc = await SwaggerParser.bundle(path)\n const isOpenApiV3 = \"openapi\" in doc && doc.openapi.startsWith(\"3\")\n if (isOpenApiV3) return doc as OpenAPIV3_1.Document\n return\n}\n\nexport const generateSchema = async (options: Options) => {\n const openapiPath = options.path.startsWith(\"http\")\n ? options.path\n : path.join(options.baseDir ?? \"\", options.path)\n const doc = await getOpenAPIDocsDeref(openapiPath)\n const sampleSchemas = doc?.components?.schemas\n if (sampleSchemas === undefined) {\n console.warn(\"No schemas found\")\n return\n }\n\n const specialFakers = specialFakerParser(options)\n return Object.entries(sampleSchemas).reduce(\n (acc, [schemaName, schema]) => {\n acc[schemaName] = parseSchema(schema, specialFakers, options, {}) as SchemaOutputType\n return acc\n },\n {} as Record<string, SchemaOutputType>\n )\n}\n\nexport const generateAPI = async (options: Options) => {\n const openapiPath = options.path.startsWith(\"http\")\n ? options.path\n : path.join(options.baseDir ?? \"\", options.path)\n const doc = await getOpenAPIDocsBundle(openapiPath)\n\n const samplePaths = doc?.paths\n if (samplePaths === undefined) {\n console.warn(\"No paths found\")\n return\n }\n\n const specialFakers = specialFakerParser(options)\n const normalizedPaths = Object.entries(samplePaths).reduce((acc, [apiName, api]) => {\n if (api === undefined) return acc\n const paths = Object.values(HttpMethods)\n .map((method) => {\n if (api[method] === undefined) return\n const responses = Object.entries(api[method]?.responses ?? [])\n .map(([statusCode, response]) => {\n if (isReference(response)) return undefined\n if (options.includeCodes && !options.includeCodes.includes(parseInt(statusCode)))\n return undefined\n const schema = response.content?.[\"application/json\"]?.schema ?? {}\n const compositeSchema = (() => {\n if (\"oneOf\" in schema) {\n return {\n type: \"oneOf\",\n value: schema,\n } as ResponseSchemaType\n }\n if (\"anyOf\" in schema) {\n return {\n type: \"anyOf\",\n value: schema,\n } as ResponseSchemaType\n }\n if (\"type\" in schema && \"items\" in schema && schema.type === \"array\") {\n return {\n type: \"array\",\n value: schema.items,\n } as ResponseSchemaType\n }\n // Todo: can't find sample data\n // if (\"allOf\" in schema) {\n // return parseSchema(schema, {})\n // }\n if (isReference(schema)) return { type: \"ref\", value: schema } as ResponseSchemaType\n if (Object.keys(schema).length === 0) {\n // empty object return undefined\n return undefined\n }\n return parseSchema(schema ?? {}, specialFakers, options, {})\n })()\n\n return {\n statusCode: parseInt(statusCode),\n description: response.description,\n schema: compositeSchema,\n }\n })\n .filter(isNotNullish)\n\n return {\n pathname: apiName.replace(/{/g, \":\").replace(/}/g, \"\"),\n operationId: api[method]?.operationId ?? \"\",\n summary: api[method]?.summary ?? \"\",\n tags: api[method]?.tags ?? [\"default\"],\n method,\n responses,\n } as PathNormalizedType\n })\n .filter(isNotNullish)\n\n return [...acc, ...paths]\n }, [] as PathNormalizedType[])\n\n return normalizedPaths\n}\n","import { generateAPI, generateSchema } from \"./generate\"\nimport { Options } from \"./types\"\nimport { writeFaker, writeHandlers, writeResponses, writeSchema } from \"./writer\"\nimport { existsSync, mkdirSync } from \"fs\"\n\nexport const main = async (options: Options) => {\n if (options.baseDir && !existsSync(options.baseDir)) {\n mkdirSync(options.baseDir, { recursive: true })\n }\n\n if (options.generateTarget.includes(\"api\")) {\n const generatedAPI = await generateAPI(options)\n if (generatedAPI === undefined) {\n console.warn(\"generate api fail\")\n return\n }\n writeHandlers(generatedAPI, options)\n writeResponses(generatedAPI, options)\n }\n\n if (options.generateTarget.includes(\"schema\")) {\n const generatedSchema = await generateSchema(options)\n if (generatedSchema === undefined) {\n console.warn(\"generate schema fail\")\n return\n }\n writeSchema(generatedSchema, options)\n }\n\n if (options.isStatic === false) writeFaker(options)\n}\n","import { EventEmitter } from 'events';\n\nfunction toArr(any) {\n\treturn any == null ? [] : Array.isArray(any) ? any : [any];\n}\n\nfunction toVal(out, key, val, opts) {\n\tvar x, old=out[key], nxt=(\n\t\t!!~opts.string.indexOf(key) ? (val == null || val === true ? '' : String(val))\n\t\t: typeof val === 'boolean' ? val\n\t\t: !!~opts.boolean.indexOf(key) ? (val === 'false' ? false : val === 'true' || (out._.push((x = +val,x * 0 === 0) ? x : val),!!val))\n\t\t: (x = +val,x * 0 === 0) ? x : val\n\t);\n\tout[key] = old == null ? nxt : (Array.isArray(old) ? old.concat(nxt) : [old, nxt]);\n}\n\nfunction mri2 (args, opts) {\n\targs = args || [];\n\topts = opts || {};\n\n\tvar k, arr, arg, name, val, out={ _:[] };\n\tvar i=0, j=0, idx=0, len=args.length;\n\n\tconst alibi = opts.alias !== void 0;\n\tconst strict = opts.unknown !== void 0;\n\tconst defaults = opts.default !== void 0;\n\n\topts.alias = opts.alias || {};\n\topts.string = toArr(opts.string);\n\topts.boolean = toArr(opts.boolean);\n\n\tif (alibi) {\n\t\tfor (k in opts.alias) {\n\t\t\tarr = opts.alias[k] = toArr(opts.alias[k]);\n\t\t\tfor (i=0; i < arr.length; i++) {\n\t\t\t\t(opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (i=opts.boolean.length; i-- > 0;) {\n\t\tarr = opts.alias[opts.boolean[i]] || [];\n\t\tfor (j=arr.length; j-- > 0;) opts.boolean.push(arr[j]);\n\t}\n\n\tfor (i=opts.string.length; i-- > 0;) {\n\t\tarr = opts.alias[opts.string[i]] || [];\n\t\tfor (j=arr.length; j-- > 0;) opts.string.push(arr[j]);\n\t}\n\n\tif (defaults) {\n\t\tfor (k in opts.default) {\n\t\t\tname = typeof opts.default[k];\n\t\t\tarr = opts.alias[k] = opts.alias[k] || [];\n\t\t\tif (opts[name] !== void 0) {\n\t\t\t\topts[name].push(k);\n\t\t\t\tfor (i=0; i < arr.length; i++) {\n\t\t\t\t\topts[name].push(arr[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconst keys = strict ? Object.keys(opts.alias) : [];\n\n\tfor (i=0; i < len; i++) {\n\t\targ = args[i];\n\n\t\tif (arg === '--') {\n\t\t\tout._ = out._.concat(args.slice(++i));\n\t\t\tbreak;\n\t\t}\n\n\t\tfor (j=0; j < arg.length; j++) {\n\t\t\tif (arg.charCodeAt(j) !== 45) break; // \"-\"\n\t\t}\n\n\t\tif (j === 0) {\n\t\t\tout._.push(arg);\n\t\t} else if (arg.substring(j, j + 3) === 'no-') {\n\t\t\tname = arg.substring(j + 3);\n\t\t\tif (strict && !~keys.indexOf(name)) {\n\t\t\t\treturn opts.unknown(arg);\n\t\t\t}\n\t\t\tout[name] = false;\n\t\t} else {\n\t\t\tfor (idx=j+1; idx < arg.length; idx++) {\n\t\t\t\tif (arg.charCodeAt(idx) === 61) break; // \"=\"\n\t\t\t}\n\n\t\t\tname = arg.substring(j, idx);\n\t\t\tval = arg.substring(++idx) || (i+1 === len || (''+args[i+1]).charCodeAt(0) === 45 || args[++i]);\n\t\t\tarr = (j === 2 ? [name] : name);\n\n\t\t\tfor (idx=0; idx < arr.length; idx++) {\n\t\t\t\tname = arr[idx];\n\t\t\t\tif (strict && !~keys.indexOf(name)) return opts.unknown('-'.repeat(j) + name);\n\t\t\t\ttoVal(out, name, (idx + 1 < arr.length) || val, opts);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (defaults) {\n\t\tfor (k in opts.default) {\n\t\t\tif (out[k] === void 0) {\n\t\t\t\tout[k] = opts.default[k];\n\t\t\t}\n\t\t}\n\t}\n\n\tif (alibi) {\n\t\tfor (k in out) {\n\t\t\tarr = opts.alias[k] || [];\n\t\t\twhile (arr.length > 0) {\n\t\t\t\tout[arr.shift()] = out[k];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out;\n}\n\nconst removeBrackets = (v) => v.replace(/[<[].+/, \"\").trim();\nconst findAllBrackets = (v) => {\n const ANGLED_BRACKET_RE_GLOBAL = /<([^>]+)>/g;\n const SQUARE_BRACKET_RE_GLOBAL = /\\[([^\\]]+)\\]/g;\n const res = [];\n const parse = (match) => {\n let variadic = false;\n let value = match[1];\n if (value.startsWith(\"...\")) {\n value = value.slice(3);\n variadic = true;\n }\n return {\n required: match[0].startsWith(\"<\"),\n value,\n variadic\n };\n };\n let angledMatch;\n while (angledMatch = ANGLED_BRACKET_RE_GLOBAL.exec(v)) {\n res.push(parse(angledMatch));\n }\n let squareMatch;\n while (squareMatch = SQUARE_BRACKET_RE_GLOBAL.exec(v)) {\n res.push(parse(squareMatch));\n }\n return res;\n};\nconst getMriOptions = (options) => {\n const result = {alias: {}, boolean: []};\n for (const [index, option] of options.entries()) {\n if (option.names.length > 1) {\n result.alias[option.names[0]] = option.names.slice(1);\n }\n if (option.isBoolean) {\n if (option.negated) {\n const hasStringTypeOption = options.some((o, i) => {\n return i !== index && o.names.some((name) => option.names.includes(name)) && typeof o.required === \"boolean\";\n });\n if (!hasStringTypeOption) {\n result.boolean.push(option.names[0]);\n }\n } else {\n result.boolean.push(option.names[0]);\n }\n }\n }\n return result;\n};\nconst findLongest = (arr) => {\n return arr.sort((a, b) => {\n return a.length > b.length ? -1 : 1;\n })[0];\n};\nconst padRight = (str, length) => {\n return str.length >= length ? str : `${str}${\" \".repeat(length - str.length)}`;\n};\nconst camelcase = (input) => {\n return input.replace(/([a-z])-([a-z])/g, (_, p1, p2) => {\n return p1 + p2.toUpperCase();\n });\n};\nconst setDotProp = (obj, keys, val) => {\n let i = 0;\n let length = keys.length;\n let t = obj;\n let x;\n for (; i < length; ++i) {\n x = t[keys[i]];\n t = t[keys[i]] = i === length - 1 ? val : x != null ? x : !!~keys[i + 1].indexOf(\".\") || !(+keys[i + 1] > -1) ? {} : [];\n }\n};\nconst setByType = (obj, transforms) => {\n for (const key of Object.keys(transforms)) {\n const transform = transforms[key];\n if (transform.shouldTransform) {\n obj[key] = Array.prototype.concat.call([], obj[key]);\n if (typeof transform.transformFunction === \"function\") {\n obj[key] = obj[key].map(transform.transformFunction);\n }\n }\n }\n};\nconst getFileName = (input) => {\n const m = /([^\\\\\\/]+)$/.exec(input);\n return m ? m[1] : \"\";\n};\nconst camelcaseOptionName = (name) => {\n return name.split(\".\").map((v, i) => {\n return i === 0 ? camelcase(v) : v;\n }).join(\".\");\n};\nclass CACError extends Error {\n constructor(message) {\n super(message);\n this.name = this.constructor.name;\n if (typeof Error.captureStackTrace === \"function\") {\n Error.captureStackTrace(this, this.constructor);\n } else {\n this.stack = new Error(message).stack;\n }\n }\n}\n\nclass Option {\n constructor(rawName, description, config) {\n this.rawName = rawName;\n this.description = description;\n this.config = Object.assign({}, config);\n rawName = rawName.replace(/\\.\\*/g, \"\");\n this.negated = false;\n this.names = removeBrackets(rawName).split(\",\").map((v) => {\n let name = v.trim().replace(/^-{1,2}/, \"\");\n if (name.startsWith(\"no-\")) {\n this.negated = true;\n name = name.replace(/^no-/, \"\");\n }\n return camelcaseOptionName(name);\n }).sort((a, b) => a.length > b.length ? 1 : -1);\n this.name = this.names[this.names.length - 1];\n if (this.negated && this.config.default == null) {\n this.config.default = true;\n }\n if (rawName.includes(\"<\")) {\n this.required = true;\n } else if (rawName.includes(\"[\")) {\n this.required = false;\n } else {\n this.isBoolean = true;\n }\n }\n}\n\nconst processArgs = process.argv;\nconst platformInfo = `${process.platform}-${process.arch} node-${process.version}`;\n\nclass Command {\n constructor(rawName, description, config = {}, cli) {\n this.rawName = rawName;\n this.description = description;\n this.config = config;\n this.cli = cli;\n this.options = [];\n this.aliasNames = [];\n this.name = removeBrackets(rawName);\n this.args = findAllBrackets(rawName);\n this.examples = [];\n }\n usage(text) {\n this.usageText = text;\n return this;\n }\n allowUnknownOptions() {\n this.config.allowUnknownOptions = true;\n return this;\n }\n ignoreOptionDefaultValue() {\n this.config.ignoreOptionDefaultValue = true;\n return this;\n }\n version(version, customFlags = \"-v, --version\") {\n this.versionNumber = version;\n this.option(customFlags, \"Display version number\");\n return this;\n }\n example(example) {\n this.examples.push(example);\n return this;\n }\n option(rawName, description, config) {\n const option = new Option(rawName, description, config);\n this.options.push(option);\n return this;\n }\n alias(name) {\n this.aliasNames.push(name);\n return this;\n }\n action(callback) {\n this.commandAction = callback;\n return this;\n }\n isMatched(name) {\n return this.name === name || this.aliasNames.includes(name);\n }\n get isDefaultCommand() {\n return this.name === \"\" || this.aliasNames.includes(\"!\");\n }\n get isGlobalCommand() {\n return this instanceof GlobalCommand;\n }\n hasOption(name) {\n name = name.split(\".\")[0];\n return this.options.find((option) => {\n return option.names.includes(name);\n });\n }\n outputHelp() {\n const {name, commands} = this.cli;\n const {\n versionNumber,\n options: globalOptions,\n helpCallback\n } = this.cli.globalCommand;\n let sections = [\n {\n body: `${name}${versionNumber ? `/${versionNumber}` : \"\"}`\n }\n ];\n sections.push({\n title: \"Usage\",\n body: ` $ ${name} ${this.usageText || this.rawName}`\n });\n const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;\n if (showCommands) {\n const longestCommandName = findLongest(commands.map((command) => command.rawName));\n sections.push({\n title: \"Commands\",\n body: commands.map((command) => {\n return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;\n }).join(\"\\n\")\n });\n sections.push({\n title: `For more info, run any command with the \\`--help\\` flag`,\n body: commands.map((command) => ` $ ${name}${command.name === \"\" ? \"\" : ` ${command.name}`} --help`).join(\"\\n\")\n });\n }\n let options = this.isGlobalCommand ? globalOptions : [...this.options, ...globalOptions || []];\n if (!this.isGlobalCommand && !this.isDefaultCommand) {\n options = options.filter((option) => option.name !== \"version\");\n }\n if (options.length > 0) {\n const longestOptionName = findLongest(options.map((option) => option.rawName));\n sections.push({\n title: \"Options\",\n body: options.map((option) => {\n return ` ${padRight(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === void 0 ? \"\" : `(default: ${option.config.default})`}`;\n }).join(\"\\n\")\n });\n }\n if (this.examples.length > 0) {\n sections.push({\n title: \"Examples\",\n body: this.examples.map((example) => {\n if (typeof example === \"function\") {\n return example(name);\n }\n return example;\n }).join(\"\\n\")\n });\n }\n if (helpCallback) {\n sections = helpCallback(sections) || sections;\n }\n console.log(sections.map((section) => {\n return section.title ? `${section.title}:\n${section.body}` : section.body;\n }).join(\"\\n\\n\"));\n }\n outputVersion() {\n const {name} = this.cli;\n const {versionNumber} = this.cli.globalCommand;\n if (versionNumber) {\n console.log(`${name}/${versionNumber} ${platformInfo}`);\n }\n }\n checkRequiredArgs() {\n const minimalArgsCount = this.args.filter((arg) => arg.required).length;\n if (this.cli.args.length < minimalArgsCount) {\n throw new CACError(`missing required args for command \\`${this.rawName}\\``);\n }\n }\n checkUnknownOptions() {\n const {options, globalCommand} = this.cli;\n if (!this.config.allowUnknownOptions) {\n for (const name of Object.keys(options)) {\n if (name !== \"--\" && !this.hasOption(name) && !globalCommand.hasOption(name)) {\n throw new CACError(`Unknown option \\`${name.length > 1 ? `--${name}` : `-${name}`}\\``);\n }\n }\n }\n }\n checkOptionValue() {\n const {options: parsedOptions, globalCommand} = this.cli;\n const options = [...globalCommand.options, ...this.options];\n for (const option of options) {\n const value = parsedOptions[option.name.split(\".\")[0]];\n if (option.required) {\n const hasNegated = options.some((o) => o.negated && o.names.includes(option.name));\n if (value === true || value === false && !hasNegated) {\n throw new CACError(`option \\`${option.rawName}\\` value is missing`);\n }\n }\n }\n }\n}\nclass GlobalCommand extends Command {\n constructor(cli) {\n super(\"@@global@@\", \"\", {}, cli);\n }\n}\n\nvar __assign = Object.assign;\nclass CAC extends EventEmitter {\n constructor(name = \"\") {\n super();\n this.name = name;\n this.commands = [];\n this.rawArgs = [];\n this.args = [];\n this.options = {};\n this.globalCommand = new GlobalCommand(this);\n this.globalCommand.usage(\"<command> [options]\");\n }\n usage(text) {\n this.globalCommand.usage(text);\n return this;\n }\n command(rawName, description, config) {\n const command = new Command(rawName, description || \"\", config, this);\n command.globalCommand = this.globalCommand;\n this.commands.push(command);\n return command;\n }\n option(rawName, description, config) {\n this.globalCommand.option(rawName, description, config);\n return this;\n }\n help(callback) {\n this.globalCommand.option(\"-h, --help\", \"Display this message\");\n this.globalCommand.helpCallback = callback;\n this.showHelpOnExit = true;\n return this;\n }\n version(version, customFlags = \"-v, --version\") {\n this.globalCommand.version(version, customFlags);\n this.showVersionOnExit = true;\n return this;\n }\n example(example) {\n this.globalCommand.example(example);\n return this;\n }\n outputHelp() {\n if (this.matchedCommand) {\n this.matchedCommand.outputHelp();\n } else {\n this.globalCommand.outputHelp();\n }\n }\n outputVersion() {\n this.globalCommand.outputVersion();\n }\n setParsedInfo({args, options}, matchedCommand, matchedCommandName) {\n this.args = args;\n this.options = options;\n if (matchedCommand) {\n this.matchedCommand = matchedCommand;\n }\n if (matchedCommandName) {\n this.matchedCommandName = matchedCommandName;\n }\n return this;\n }\n unsetMatchedCommand() {\n this.matchedCommand = void 0;\n this.matchedCommandName = void 0;\n }\n parse(argv = processArgs, {\n run = true\n } = {}) {\n this.rawArgs = argv;\n if (!this.name) {\n this.name = argv[1] ? getFileName(argv[1]) : \"cli\";\n }\n let shouldParse = true;\n for (const command of this.commands) {\n const parsed = this.mri(argv.slice(2), command);\n const commandName = parsed.args[0];\n if (command.isMatched(commandName)) {\n shouldParse = false;\n const parsedInfo = __assign(__assign({}, parsed), {\n args: parsed.args.slice(1)\n });\n this.setParsedInfo(parsedInfo, command, commandName);\n this.emit(`command:${commandName}`, command);\n }\n }\n if (shouldParse) {\n for (const command of this.commands) {\n if (command.name === \"\") {\n shouldParse = false;\n const parsed = this.mri(argv.slice(2), command);\n this.setParsedInfo(parsed, command);\n this.emit(`command:!`, command);\n }\n }\n }\n if (shouldParse) {\n const parsed = this.mri(argv.slice(2));\n this.setParsedInfo(parsed);\n }\n if (this.options.help && this.showHelpOnExit) {\n this.outputHelp();\n run = false;\n this.unsetMatchedCommand();\n }\n if (this.options.version && this.showVersionOnExit && this.matchedCommandName == null) {\n this.outputVersion();\n run = false;\n this.unsetMatchedCommand();\n }\n const parsedArgv = {args: this.args, options: this.options};\n if (run) {\n this.runMatchedCommand();\n }\n if (!this.matchedCommand && this.args[0]) {\n this.emit(\"command:*\");\n }\n return parsedArgv;\n }\n mri(argv, command) {\n const cliOptions = [\n ...this.globalCommand.options,\n ...command ? command.options : []\n ];\n