UNPKG

openapi-axios

Version:

OpenAPI(2.0/3.0/3.1) Schema → Type-safe Axios

1,459 lines 79.7 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 __typeError = (msg) => { throw TypeError(msg); }; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; 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 __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); var _depSets, _Parser_instances, prepareVarName_fn, depNames_get, mergeResultDeps_fn, mergeParserDeps_fn, _Parser_static, parseInner_fn, parseAsUnknown_fn, parseArray_fn, parseObject_fn, parseObjectProp_fn, parsePropBoolean_fn, _slices, _mainContent, _typeContent, _zodContent, _mockContent, _pathZodNames, _respZodNames, _Printer_instances, parseRefComponent_fn, tryRegisterAnchors_fn, printAlert_fn, printInfo_fn, printImports_fn, printHeader_fn, printFooter_fn, printSchemas_fn, printSchema_fn, printPaths_fn, printPathItem_fn, printOperation_fn, parseContents_fn, parseContent_fn, parseParameter_fn, parseRequestBody_fn, parseResponses_fn, parseResponse_fn, _Generator_instances, generateOpenAPI_fn, writePrintResult_fn; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const fs = require("node:fs"); const path = require("node:path"); const process = require("node:process"); const commander = require("commander"); const tryFlatten = require("try-flatten"); const strictEventEmitter = require("strict-event-emitter"); const localPkg = require("local-pkg"); const openapiCore = require("@redocly/openapi-core"); const chalk = require("chalk"); const pkgName = "openapi-axios"; const pkgVersion = "0.34.0"; const configFileBaseName = "openapi-axios.config"; var OpenAPIVersion = /* @__PURE__ */ ((OpenAPIVersion2) => { OpenAPIVersion2["V2_0"] = "2.0.0"; OpenAPIVersion2["V3_0"] = "3.0.3"; OpenAPIVersion2["V3_1"] = "3.1.0"; return OpenAPIVersion2; })(OpenAPIVersion || {}); function isRelative(toFile) { return /^\.{1,2}[/\\]/.test(toFile); } function toRelative(toFile, fromFile) { if (!fromFile) return toFile; if (!path.isAbsolute(toFile)) return toFile; const relative = path.relative(path.dirname(fromFile), toFile); return /^\.{1,2}\//.test(relative) ? relative : `./${relative}`; } function toImportPath(toFile, cwd, fromFile) { if (path.isAbsolute(toFile)) { return fromFile ? toRelative(toFile, fromFile) : toFile; } if (isRelative(toFile)) { return toImportPath(path.join(cwd, toFile), cwd, fromFile); } return toFile; } function fixVarName(origin, bigger = false, fallback = "var") { const name = origin.replace(/\W/g, "_").replace(/(^_+|_+$)/g, "").replace(/_(.)/g, ($0, $1) => $1.toUpperCase()).replace(/^\d+/, "") || fallback; return (bigger ? name[0].toUpperCase() : name[0].toLowerCase()) + name.slice(1); } function nextUniqueName(refName, nameCountMap) { const baseName = refName.replace(/_[1-9]\d*$/, ""); const count = nameCountMap.get(baseName) || 0; const nextCount = count + 1; nameCountMap.set(baseName, nextCount); return nextCount === 1 ? baseName : `${baseName}_${nextCount}`; } async function formatTsCode(tsCode, userOptions, cwd = process.cwd()) { try { const prettier = await import("prettier"); const cwdOptions = await prettier.resolveConfig(cwd) || await prettier.resolveConfig(path.join(cwd, "1")); return prettier.format(tsCode, { ...cwdOptions, ...userOptions, parser: "typescript" }); } catch (cause) { return tsCode; } } function isString(any) { return typeof any === "string"; } function isBoolean(any) { return typeof any === "boolean"; } function isNumber(any) { return typeof any === "number"; } function isArray(any) { return Array.isArray(any); } function isUndefined(any) { return any === void 0; } const KEYWORD_VARS = [ // 保留字 "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do", "else", "enum", "export", "extends", "false", "finally", "for", "function", "if", "import", "in", "instanceof", "new", "null", "return", "super", "switch", "this", "throw", "true", "try", "typeof", "var", "void", "while", "with", // 严格保留字 "as", "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield", "namespace", "async", "await" // // 上下文关键字 // 'any', // 'boolean', // 'constructor', // 'declare', // 'get', // 'module', // 'require', // 'number', // 'set', // 'string', // 'symbol', // 'type', // 'from', // 'of', ]; const AXIOS_IMPORT_NAME = "axios"; const ZOD_IMPORT_NAME = "z"; const FAKER_IMPORT_NAME = "faker"; const AXIOS_IMPORT_FILE = "axios"; const FAKER_IMPORT_FILE = "@faker-js/faker"; const AXIOS_PARAM_CONFIG_NAME = "config"; const AXIOS_RESPONSE_NAME = "resp"; const ENABLE_MOCK_NAME = "enableMock"; const AXIOS_PARAM_TRANSFORM_RESPONSE_NAME = "transformResponse"; const AXIOS_REQUEST_TYPE_NAME = "AxiosRequestConfig"; const INTERNAL_VARS = [ AXIOS_IMPORT_NAME, ZOD_IMPORT_NAME, FAKER_IMPORT_NAME, AXIOS_PARAM_CONFIG_NAME, AXIOS_RESPONSE_NAME, ENABLE_MOCK_NAME ]; const TYPE_FILE_EXPORT_NAME = "Type"; const INTERNAL_TYPES = [ // native "Blob", "Array", "Object", // typeScript "string", "number", "boolean", "any", "unknown", "void", "never", "null", "undefined", "object", "symbol", "bigint", "Record", // type TYPE_FILE_EXPORT_NAME, AXIOS_REQUEST_TYPE_NAME ]; const DEFAULT_ENABLE_CONDITION = 'process.env.NODE_ENV !== "production"'; const DEFAULT_RESPONSE_DATA_PROPS = ["data"]; function isRefSchema(schema) { return "$ref" in schema && isString(schema.$ref); } function isRefParameter(parameter) { return "$ref" in parameter && isString(parameter.$ref); } function isRefRequest(request) { return "$ref" in request && isString(request.$ref); } function isRefMedia(request) { return "$ref" in request && isString(request.$ref); } function isRefPathItem(pathItem) { return "$ref" in pathItem && isString(pathItem.$ref); } function isRefOperation(operation) { return "$ref" in operation && isString(operation.$ref); } function isRefResponse(response) { return "$ref" in response && isString(response.$ref); } function requiredTypeStringify(required) { return required ? ":" : "?:"; } function requiredKeyStringify(key, required) { return required ? key : `[${key}]`; } function toImportString(id, name, path2, isType = false) { const isDefault = name === ""; const type = isType ? isDefault ? " type " : "type " : isDefault ? " " : ""; return isDefault ? `import${type}${id} from "${path2}";` : `import {${type}${name} as ${id}} from "${path2}";`; } function toZodName(typeName) { return `z-${typeName}`; } function sortingByDeps(depList) { return depList.sort((a, b) => { if (a.deps.includes(b.name)) { return 1; } else if (b.deps.includes(a.name)) { return -1; } else { return 0; } }); } function withGroup(texts, options) { const { sep = ",", wrap = ["(", ")"], always = false } = options || {}; const [start, end] = wrap; return !always && texts.length < 2 ? texts.at(0) || "" : start + texts.join(sep) + end; } function formatLine(key, val) { val = key === "externalDocs" ? JsDoc.printExternalDoc(val) : val; key = key === "externalDocs" ? "see" : key; if (isBoolean(val) && val) return `@${key}`; if (isString(val) || isNumber(val)) return `@${key} ${val}`; } const supportTypes = ["string", "number", "boolean", "object", "array", "null"]; class JsDoc { constructor(tags = []) { __publicField(this, "lines", []); this.tags = tags; } addComments(comments) { if ("tags" in comments) { const tags = comments.tags || []; comments.tags = void 0; tags.forEach((tag) => { const info = this.tags.find((t) => t.name === tag); if (!info) return; comments[`see ${tag}`] = [info.description, JsDoc.printExternalDoc(info.externalDocs)].filter(Boolean).join(" "); }); } this.addLines(JsDoc.toLines(comments)); } addLines(lines) { this.lines.push(...lines); } print() { if (this.lines.length === 0) return ""; return [ // "/**", ...this.lines.map((line) => { const slices = line.split("\n"); return slices.map((s) => ` * ${s}`).join("\n"); }), " */" ].join("\n"); } static toLines(comments) { return Object.entries(comments).map(([key, val]) => { if (isArray(val)) return val.map((v) => formatLine(key, v)); return formatLine(key, val); }).flat().filter(Boolean); } static fromRef(ref) { const { description, summary } = ref; return { description, summary }; } static fromSchema(schema) { const { deprecated, description, default: defaultValue, format, example, examples, title, externalDocs, type } = schema; const types = isArray(type) ? type : isString(type) ? [type] : void 0; return { summary: title, description, deprecated, default: defaultValue, format: format || (types == null ? void 0 : types.filter((type2) => !supportTypes.includes(type2)).join(" | ")) || void 0, example, examples, externalDocs }; } static fromParameter(parameter) { const { deprecated, description, example, examples } = parameter; return { deprecated, description, example }; } static fromOperation(operation) { const { deprecated, description, summary, tags } = operation; return { deprecated, description, summary, tags }; } static printExternalDoc(externalDoc) { const { url, description } = externalDoc || {}; if (url && description) return `{@link ${url} ${description}}`; if (url) return `{@link ${url}}`; if (description) return description; return false; } } const _Parser = class _Parser { constructor(named, schema) { __privateAdd(this, _Parser_instances); __privateAdd(this, _depSets, /* @__PURE__ */ new Set()); this.named = named; this.schema = schema; } parse() { var _a, _b, _c, _d, _e, _f, _g, _h, _i; const { schema } = this; if (isRefSchema(schema)) { const typeName = this.named.getRefType(schema.$ref); const zodName = typeName ? __privateMethod(this, _Parser_instances, prepareVarName_fn).call(this, schema.$ref) : `${ZOD_IMPORT_NAME}.unknown()`; if (!typeName) { throw new Error(`未找到 refId: ${schema.$ref} 的类型`); } return { type: typeName, zod: zodName, comments: JsDoc.fromRef(schema), required: false, deps: [...__privateGet(this, _depSets).values()] }; } const { type, allOf, oneOf, anyOf, required } = schema; const requiredBool = isBoolean(required) ? required : false; const comments = JsDoc.fromSchema(schema); if (allOf && allOf.length > 0) { const group = allOf.map((a) => { var _a2; return __privateMethod(_a2 = _Parser, _Parser_static, parseInner_fn).call(_a2, this, a); }); return { comments, required: false, deps: __privateGet(this, _Parser_instances, depNames_get), type: withGroup(group.map((g) => g.type), { sep: "&" }), zod: withGroup( group.map((g) => g.zod), { sep: ",", wrap: [`${ZOD_IMPORT_NAME}.intersection(`, ")"] } ) }; } if (oneOf && oneOf.length > 0) { const group = oneOf.map((o) => { var _a2; return __privateMethod(_a2 = _Parser, _Parser_static, parseInner_fn).call(_a2, this, o); }); return { comments, required: false, deps: __privateGet(this, _Parser_instances, depNames_get), type: withGroup(group.map((g) => g.type), { sep: "|" }), zod: withGroup( group.map((g) => g.zod), { wrap: [`${ZOD_IMPORT_NAME}.union([`, "])"] } ) }; } if (anyOf && anyOf.length > 0) { const group = anyOf.map((a) => { var _a2; return __privateMethod(_a2 = _Parser, _Parser_static, parseInner_fn).call(_a2, this, a); }); return { comments, required: false, deps: __privateGet(this, _Parser_instances, depNames_get), type: withGroup(group.map((g) => g.type), { sep: "|" }), zod: withGroup( group.map((g) => g.zod), { sep: ",", wrap: [`${ZOD_IMPORT_NAME}.union([`, "])"] } ) }; } if (isArray(type)) { if (type.length === 0) { return __privateMethod(_a = _Parser, _Parser_static, parseAsUnknown_fn).call(_a, schema, requiredBool); } if (type.length === 1) { return __privateMethod(_b = _Parser, _Parser_static, parseInner_fn).call(_b, this, { ...schema, // eslint-disable-next-line ts/ban-ts-comment // @ts-ignore type: type[0] }); } const group = type.map( (type2) => { var _a2; return __privateMethod(_a2 = _Parser, _Parser_static, parseInner_fn).call(_a2, this, type2 === "null" ? { type: type2 } : { ...schema, type: type2 }); } ); return { comments, required: false, deps: __privateGet(this, _Parser_instances, depNames_get), type: withGroup(group.map((g) => g.type), { sep: "|" }), zod: withGroup( group.map((g) => g.zod), { wrap: [`${ZOD_IMPORT_NAME}.union([`, "])"] } ) }; } switch (type) { case "string": { const { enum: enumValues = [], format, minLength, maxLength, pattern } = schema; const isBlob = format === "binary"; const required2 = Boolean(schema.required); return { comments: { ...comments, minLength, maxLength, pattern }, required: required2, deps: __privateGet(this, _Parser_instances, depNames_get), type: enumValues.length > 0 ? withGroup( enumValues.map((e) => isString(e) ? JSON.stringify(e) : this.named.getRefType(e.$ref) || "unknown"), { sep: "|" } ) : isBlob ? "Blob" : "string", zod: enumValues.length > 0 ? withGroup( enumValues.map((e) => isString(e) ? `${ZOD_IMPORT_NAME}.literal(${JSON.stringify(e)})` : __privateMethod(this, _Parser_instances, prepareVarName_fn).call(this, e.$ref)), { wrap: [`${ZOD_IMPORT_NAME}.union([`, "])"] } ) : isBlob ? `${ZOD_IMPORT_NAME}.instanceof(Blob)` : `${ZOD_IMPORT_NAME}.string()` }; } case "number": case "integer": { const { enum: enumValues = [], const: const_, minimum, maximum } = schema; const required2 = Boolean(schema.required); if (!isUndefined(const_)) enumValues.push(const_); return { comments: { ...comments, minimum, maximum }, required: required2, deps: __privateGet(this, _Parser_instances, depNames_get), type: enumValues.length > 0 ? withGroup( enumValues.map((e) => isNumber(e) ? String(e) : this.named.getRefType(e.$ref) || "unknown"), { sep: "|" } ) : "number", zod: enumValues.length > 0 ? withGroup( enumValues.map((e) => isNumber(e) ? `${ZOD_IMPORT_NAME}.literal(${e})` : __privateMethod(this, _Parser_instances, prepareVarName_fn).call(this, e.$ref)), { wrap: [`${ZOD_IMPORT_NAME}.union([`, "])"] } ) : `${ZOD_IMPORT_NAME}.number()` }; } case "boolean": { const { enum: enumValues = [] } = schema; const required2 = Boolean(schema.required); return { comments, required: required2, deps: __privateGet(this, _Parser_instances, depNames_get), type: enumValues.length > 0 ? withGroup( enumValues.map((e) => isBoolean(e) ? String(e) : this.named.getRefType(e.$ref) || "unknown"), { sep: "|" } ) : type, zod: enumValues.length > 0 ? withGroup( enumValues.map((e) => isBoolean(e) ? `${ZOD_IMPORT_NAME}.literal(${e})` : __privateMethod(this, _Parser_instances, prepareVarName_fn).call(this, e.$ref)), { sep: ",", wrap: [`${ZOD_IMPORT_NAME}.union([`, "])"] } ) : `${ZOD_IMPORT_NAME}.boolean()` }; } case "null": { const required2 = Boolean(schema.required); return { comments, required: required2, deps: __privateGet(this, _Parser_instances, depNames_get), type, zod: `${ZOD_IMPORT_NAME}.null()` }; } case "array": return __privateMethod(_c = _Parser, _Parser_static, parseArray_fn).call(_c, this, schema); case "object": return __privateMethod(_d = _Parser, _Parser_static, parseObject_fn).call(_d, this, schema); case void 0: { if ("properties" in schema) { return __privateMethod(_e = _Parser, _Parser_static, parseObject_fn).call(_e, this, schema); } else if ("additionalProperties" in schema) { return __privateMethod(_f = _Parser, _Parser_static, parseObject_fn).call(_f, this, schema); } else if ("items" in schema) { return __privateMethod(_g = _Parser, _Parser_static, parseArray_fn).call(_g, this, schema); } else { return __privateMethod(_h = _Parser, _Parser_static, parseAsUnknown_fn).call(_h, schema, requiredBool); } } default: return __privateMethod(_i = _Parser, _Parser_static, parseAsUnknown_fn).call(_i, schema, requiredBool); } } static parse(named, schema) { return new _Parser(named, schema).parse(); } }; _depSets = new WeakMap(); _Parser_instances = new WeakSet(); prepareVarName_fn = function(refId) { const typeName = this.named.getRefType(refId); if (!typeName) { throw new Error(`未找到 refId: ${refId} 的类型`); } const varName = this.named.prepareVarName(toZodName(typeName)); __privateGet(this, _depSets).add(varName); return varName; }; depNames_get = function() { return [...__privateGet(this, _depSets).values()]; }; mergeResultDeps_fn = function(parseResult) { parseResult.deps.forEach((d) => { __privateGet(this, _depSets).add(d); }); }; mergeParserDeps_fn = function(parser) { __privateGet(parser, _depSets).forEach((d) => { __privateGet(this, _depSets).add(d); }); }; _Parser_static = new WeakSet(); parseInner_fn = function(parent, schema) { var _a; const result = new _Parser(parent.named, schema).parse(); __privateMethod(_a = parent, _Parser_instances, mergeResultDeps_fn).call(_a, result); return result; }; parseAsUnknown_fn = function(schema, required = false, spec) { const comments = JsDoc.fromSchema(schema); return { comments, required, deps: [], type: (spec == null ? void 0 : spec.type) || "unknown", zod: (spec == null ? void 0 : spec.zod) || `${ZOD_IMPORT_NAME}.unknown()` }; }; parseArray_fn = function(parent, schema) { var _a; const comments = JsDoc.fromSchema(schema); const { minItems, maxItems, items } = schema; const result = __privateMethod(_a = _Parser, _Parser_static, parseInner_fn).call(_a, parent, items); return { comments: { ...comments, minItems, maxItems }, required: false, deps: result.deps, type: `Array<${result.type}>`, zod: `${ZOD_IMPORT_NAME}.array(${result.zod})` }; }; parseObject_fn = function(parent, schema) { var _a, _b, _c; const parser = new _Parser(parent.named, schema); const required = isBoolean(schema.required) ? schema.required : false; const comments = JsDoc.fromSchema(schema); const explicitProps = "properties" in schema ? schema.properties : void 0; const genericProps = "additionalProperties" in schema ? schema.additionalProperties : void 0; const explicitEntries = Object.entries(explicitProps || {}); const noExplicitProps = explicitEntries.length === 0; const noGenericProps = isUndefined(genericProps) || genericProps === false || Object.keys(genericProps).length === 0; const typeList = []; const zodList = []; if (!noExplicitProps) { const propTypeList = []; const propZodList = []; explicitEntries.forEach(([name, propSchema]) => { var _a2, _b2; const { type, zod } = __privateMethod(_b2 = _Parser, _Parser_static, parseObjectProp_fn).call(_b2, parser, name, propSchema, isArray(schema.required) ? (_a2 = schema.required) == null ? void 0 : _a2.includes(name) : false); propTypeList.push(type); propZodList.push(zod); }); typeList.push(withGroup(propTypeList, { sep: "\n", wrap: ["{\n", "\n}"], always: true })); zodList.push(withGroup(propZodList, { sep: "\n", wrap: [`${ZOD_IMPORT_NAME}.object({ `, "\n})"], always: true })); } if (!noGenericProps) { const { type, zod } = __privateMethod(_a = _Parser, _Parser_static, parseInner_fn).call(_a, parser, genericProps); typeList.push(`Record<string, ${type}>`); zodList.push(`${ZOD_IMPORT_NAME}.record(${ZOD_IMPORT_NAME}.string(), ${zod})`); } if (typeList.length === 0) { return __privateMethod(_b = _Parser, _Parser_static, parseAsUnknown_fn).call(_b, schema, required, { type: "Record<string, unknown>", zod: `${ZOD_IMPORT_NAME}.record(${ZOD_IMPORT_NAME}.string(), ${ZOD_IMPORT_NAME}.unknown())` }); } __privateMethod(_c = parent, _Parser_instances, mergeParserDeps_fn).call(_c, parser); return { comments, required: isBoolean(schema.required) ? schema.required : false, deps: __privateGet(parser, _Parser_instances, depNames_get), type: withGroup(typeList, { sep: "&" }), zod: withGroup(zodList, { wrap: [`${ZOD_IMPORT_NAME}.intersection(`, ")"] }) }; }; parseObjectProp_fn = function(parent, propName, propSchema, propRequired1) { var _a, _b; const { required: propRequired2, comments, type, zod } = isBoolean(propSchema) ? __privateMethod(_a = _Parser, _Parser_static, parsePropBoolean_fn).call(_a, propSchema) : __privateMethod(_b = _Parser, _Parser_static, parseInner_fn).call(_b, parent, propSchema); const jsDoc = new JsDoc(); jsDoc.addComments(comments); const required = propRequired1 || propRequired2 || false; return { type: [jsDoc.print(), `${JSON.stringify(propName)}${requiredTypeStringify(required)}${type};`].filter(Boolean).join("\n"), zod: `${JSON.stringify(propName)}: ${required ? zod : `${ZOD_IMPORT_NAME}.optional(${zod})`},` }; }; parsePropBoolean_fn = function(bool) { return { comments: {}, deps: [], required: true, type: bool ? "any" : "never", zod: bool ? `${ZOD_IMPORT_NAME}.any()` : `${ZOD_IMPORT_NAME}.never()` }; }; __privateAdd(_Parser, _Parser_static); let Parser = _Parser; class VarPath { constructor(path2) { __privateAdd(this, _slices, []); __publicField(this, "props", []); this.path = path2; __privateSet(this, _slices, path2.split("/").map((s) => { const type = s.startsWith("{") && s.endsWith("}") ? "param" : "segment"; const value = type === "segment" ? s : s.slice(1, -1); if (type === "param") this.props.push(value); return { type, value }; })); } toString(vars) { if (!this.props.length) { return JSON.stringify(this.path); } const path2 = __privateGet(this, _slices).map(({ type, value }) => { if (type === "segment") return value; const varName = vars[value]; if (varName === void 0) { throw new Error(`路径参数 ${value} 未定义`); } return `\${${vars[value]}}`; }).join("/"); return `\`${path2}\``; } toPattern() { if (!this.props.length) { return JSON.stringify(this.path); } const main = __privateGet(this, _slices).map(({ type, value }) => { return type === "segment" ? value : "[^/]+"; }).join("\\/"); return `/^${main}$/`; } } _slices = new WeakMap(); class Arg { constructor(kind, operationName, docNamed, argNamed, printOptions, isSingle = false) { __publicField(this, "parameters", []); __publicField(this, "originName", ""); /** * 作为属性的名称 */ __publicField(this, "propName", ""); /** * 作为参数的注释名称 */ __publicField(this, "docName", ""); /** * 作为参数的变量名称 */ __publicField(this, "argName", ""); /** * 作为 zod 的变量名称 */ __publicField(this, "zodName", ""); /** * 是否必填 */ __publicField(this, "required", false); /** * 类型名称 */ __publicField(this, "typeName", ""); /** * 类型值 */ __publicField(this, "typeValue", ""); /** * zod 值 */ __publicField(this, "zodValue", ""); __publicField(this, "comments", {}); __publicField(this, "props", []); __publicField(this, "varPath", new VarPath("")); this.kind = kind; this.operationName = operationName; this.docNamed = docNamed; this.argNamed = argNamed; this.printOptions = printOptions; this.isSingle = isSingle; this.originName = kind; this.propName = kind === "path" ? "url" : kind; this.docName = kind; this.argName = ""; this.typeName = docNamed.nextTypeName(`${operationName}-${kind}`); this.zodName = docNamed.prepareVarName(toZodName(this.typeName)); } add(parameter) { if (!parameter) return; this.parameters.push(parameter); } parse() { const fixedParameters = this.parameters.filter((p) => !isRefParameter(p) && "schema" in p && p.schema); const propLength = fixedParameters.length; const requiredNames = []; fixedParameters.forEach((parameter) => { const { required, schema, name } = parameter; if (!schema) return; if (required || parameter.in === "path") { requiredNames.push(name); parameter.required = true; } this.props.push({ parameter, name, schema }); }); switch (propLength) { case 0: { switch (this.kind) { case "path": this.required = true; this.typeValue = ""; this.argName = this.argNamed.nextVarName(this.docName); return this; case "config": this.typeValue = AXIOS_REQUEST_TYPE_NAME; this.argName = AXIOS_PARAM_CONFIG_NAME; this.comments = { [`param [${this.argName}]`]: `request ${this.propName}` }; return this; } return null; } case 1: { const [firstArg] = this.props; const { parameter, schema } = firstArg; const result = Parser.parse(this.docNamed, schema); const isResponse = this.kind === "response"; const required = parameter.required || result.required || false; this.originName = firstArg.name; this.argName = this.argNamed.nextVarName(firstArg.name); this.required = required; this.typeValue = result.type; this.zodValue = result.zod; this.comments = isResponse ? { returns: parameter.description || schema.description || false } : { [`param ${requiredKeyStringify(this.argName, required)}`]: parameter.description || schema.description || (this.kind === "data" ? "request data" : `request ${this.docName} ${JSON.stringify(firstArg.name)}`) }; return this; } default: { const rootSchema = { type: "object", properties: this.props.reduce( (acc, { parameter, schema, name: originName }) => { acc[originName] = { ...schema, description: parameter.description || schema.description, deprecated: parameter.deprecated || schema.deprecated }; return acc; }, {} ), required: requiredNames }; const result = Parser.parse(this.docNamed, rootSchema); const required = requiredNames.length > 0; this.required = required; this.typeValue = result.type; this.zodValue = result.zod; this.argName = this.argNamed.nextVarName(this.docName); this.comments = this.kind === "response" ? { returns: result.comments.description } : { [`param ${requiredKeyStringify(this.docName, required)}`]: `request ${this.docName}` }; return this; } } } } class Args { constructor(args) { __publicField(this, "fixedArgs"); this.args = args; this.fixedArgs = this._sort(); } _sort() { const fixedArgs = this.args.filter(Boolean); return fixedArgs.sort((a, b) => Number(b.required) - Number(a.required)); } toComments() { return this.fixedArgs.reduce( (acc, arg) => { return { ...acc, ...arg.comments }; }, {} ); } printFormalParams() { return this.fixedArgs.filter((fixArg) => fixArg.typeValue !== "").map((fixArg) => { const typeValue = fixArg.kind === "config" ? fixArg.typeValue : `${TYPE_FILE_EXPORT_NAME}.${fixArg.typeName}`; return `${fixArg.argName}${requiredTypeStringify(fixArg.required)}${typeValue}`; }).join(","); } filterValidateAble() { return this.fixedArgs.filter((fixArg) => fixArg.typeValue !== "" && fixArg.kind !== "config"); } printSchemaTypes() { return this.filterValidateAble().map((fixArg) => `export type ${fixArg.typeName} = ${fixArg.typeValue};`); } printActualParams() { return this.fixedArgs.map((fixedArg) => { const { originName, argName: varName, propName, kind, props, varPath, isSingle } = fixedArg; switch (kind) { case "config": return `...${varName}`; case "path": { const singleProp = varPath.props.length === 1; const resolvedURL = varPath.toString(props.reduce((acc, cur) => { acc[cur.name] = singleProp ? varName : `${varName}[${JSON.stringify(cur.name)}]`; return acc; }, {})); return `url: ${resolvedURL}`; } default: { const value = props.length === 1 && !isSingle ? `{${JSON.stringify(originName)}: ${varName}}` : varName; return `${propName}: ${value}`; } } }).join(",\n"); } } const contentTypes = [ "header", "alert", "info", "import", "block", "footer" ]; class Content { constructor() { __publicField(this, "parts", /* @__PURE__ */ new Map()); __publicField(this, "orderlyCodes", []); __publicField(this, "errors", []); } push(type, code) { const part = this.parts.get(type) || []; part.push(...isArray(code) ? code : [code]); this.parts.set(type, part); } unshift(type, code) { const part = this.parts.get(type) || []; part.unshift(...isArray(code) ? code : [code]); this.parts.set(type, part); } add(orderlyCode) { this.orderlyCodes.push(orderlyCode); } print() { this.unshift("block", sortingByDeps(this.orderlyCodes).map((i) => i.code)); this.orderlyCodes.length = 0; const parts = [...contentTypes].map((t) => this.parts.get(t)).filter(Boolean); return parts.map((p) => p.join("\n")).join("\n\n"); } pushError(message) { this.errors.push(message); } } class Named { constructor({ keywordVars, internalTypes, internalVars } = {}) { // eslint-disable-next-line style/type-generic-spacing __publicField(this, "varNameCountMap", /* @__PURE__ */ new Map()); // eslint-disable-next-line style/type-generic-spacing __publicField(this, "typeNameCountMap", /* @__PURE__ */ new Map()); // eslint-disable-next-line style/type-generic-spacing __publicField(this, "refIdTypeMap", /* @__PURE__ */ new Map()); __publicField(this, "prepareVars", /* @__PURE__ */ new Map()); if (keywordVars) KEYWORD_VARS.forEach(this.internalVarName.bind(this)); if (internalVars) INTERNAL_VARS.forEach(this.internalVarName.bind(this)); if (internalTypes) INTERNAL_TYPES.forEach(this.internalTypeName.bind(this)); } /** * 注册内部变量 * @param {string} varName */ internalVarName(varName) { this.varNameCountMap.set(varName, 1); } /** * 注册内部类型 * @param {string} typeName */ internalTypeName(typeName) { this.typeNameCountMap.set(typeName, 1); } nextVarName(name) { return nextUniqueName(fixVarName(name), this.varNameCountMap); } /** * 预设变量名,如果存在则返回,否则创建 * @param {string} name */ prepareVarName(name) { const next = this.prepareVars.get(name) || this.nextVarName(name); this.prepareVars.set(name, next); return next; } nextOperationId(method, url, operationId) { operationId = operationId || fixVarName( [ method, url.replace(/\{.*?\}/g, "").split("/").filter(Boolean) ].join("_") ); return nextUniqueName(operationId, this.varNameCountMap); } nextTypeName(typeName) { const fixedTypeName = fixVarName(typeName, true, "Type"); return nextUniqueName(fixedTypeName, this.typeNameCountMap); } nextRefType(refType, refId) { const uniqueTypeName = this.nextTypeName(refType); this.setRefType(refId, uniqueTypeName); return uniqueTypeName; } setRefType(refId, refType) { this.refIdTypeMap.set(refId, refType); } getRefType(refId) { return this.refIdTypeMap.get(refId); } } const allowMethods = ["get", "put", "post", "delete", "options", "head", "patch", "trace"]; const parameterTypes = ["query", "header", "path", "cookie"]; class Printer { constructor(document, options) { __privateAdd(this, _Printer_instances); __publicField(this, "named", new Named({ internalVars: true, internalTypes: true })); __privateAdd(this, _mainContent, new Content()); __privateAdd(this, _typeContent, new Content()); __privateAdd(this, _zodContent, new Content()); __privateAdd(this, _mockContent, new Content()); __publicField(this, "configs", {}); __publicField(this, "schemas", {}); __publicField(this, "requestBodies", {}); __publicField(this, "parameters", {}); __publicField(this, "responses", {}); __publicField(this, "pathItems", {}); __privateAdd(this, _pathZodNames, /* @__PURE__ */ new Set()); __privateAdd(this, _respZodNames, /* @__PURE__ */ new Set()); this.document = document; this.options = options; const { openapi } = document; if (!openapi) throw new Error("未找到 openapi 版本号"); if (!openapi.startsWith(OpenAPIVersion.V3_1)) { throw new Error(`当前仅支持 openapi ${OpenAPIVersion.V3_1},当前版本为 ${openapi}`); } this.registerComponents(); } registerComponents() { const { schemas = {}, requestBodies = {}, parameters = {}, responses = {}, pathItems = {} } = this.document.components || {}; for (const [name, schema] of Object.entries(schemas)) { __privateMethod(this, _Printer_instances, parseRefComponent_fn).call(this, { kind: "schemas", name, obj: schema }, (nodeId, namedId) => { if (this.schemas[nodeId]) { throw new Error(`重复的 schema 引用 id:${nodeId}`); } const typeName = this.named.nextRefType(name, nodeId); this.schemas[nodeId] = { position: "root", typeName, schema, nodeId, namedId, nodeName: name }; __privateMethod(this, _Printer_instances, tryRegisterAnchors_fn).call(this, { namedId, nodeId, typeName }, schema); if (namedId) { this.named.setRefType(namedId, typeName); } }); } for (const [name, requestBody] of Object.entries(requestBodies)) { __privateMethod(this, _Printer_instances, parseRefComponent_fn).call(this, { kind: "requestBodies", name, obj: requestBody }, (nodeId, namedId) => { if (this.requestBodies[nodeId]) { throw new Error(`重复的 requestBody 引用 id:${nodeId}`); } this.requestBodies[nodeId] = { nodeId, namedId, requestBody }; }); } for (const [name, parameter] of Object.entries(parameters)) { __privateMethod(this, _Printer_instances, parseRefComponent_fn).call(this, { kind: "parameters", name, obj: parameter }, (nodeId, namedId) => { if (this.parameters[nodeId]) { throw new Error(`重复的 parameter 引用 id:${nodeId}`); } this.parameters[nodeId] = { nodeId, namedId, parameter }; }); } for (const [name, response] of Object.entries(responses)) { __privateMethod(this, _Printer_instances, parseRefComponent_fn).call(this, { kind: "responses", name, obj: response }, (nodeId, namedId) => { if (this.responses[nodeId]) { throw new Error(`重复的 response 引用 id:${nodeId}`); } this.responses[nodeId] = { nodeId, namedId, response }; }); } for (const [name, pathItem] of Object.entries(pathItems)) { __privateMethod(this, _Printer_instances, parseRefComponent_fn).call(this, { kind: "pathItems", name, obj: pathItem }, (nodeId, namedId) => { if (this.pathItems[nodeId]) { throw new Error(`重复的 pathItem 引用 id:${nodeId}`); } this.pathItems[nodeId] = { nodeId, namedId, pathItem }; }); } } print(configs) { Object.assign(this.configs, configs); const { hideHeaders, hideFooters, hideAlert, hideInfo, hideSchemas, hideImports, hidePaths } = this.configs; !hideInfo && __privateMethod(this, _Printer_instances, printInfo_fn).call(this); !hideAlert && __privateMethod(this, _Printer_instances, printAlert_fn).call(this); !hideSchemas && __privateMethod(this, _Printer_instances, printSchemas_fn).call(this); !hidePaths && __privateMethod(this, _Printer_instances, printPaths_fn).call(this); !hideHeaders && __privateMethod(this, _Printer_instances, printHeader_fn).call(this); !hideFooters && __privateMethod(this, _Printer_instances, printFooter_fn).call(this); !hideImports && __privateMethod(this, _Printer_instances, printImports_fn).call(this); return { main: { lang: "ts", code: __privateGet(this, _mainContent).print(), errors: __privateGet(this, _mainContent).errors }, type: { lang: "ts", code: __privateGet(this, _typeContent).print(), errors: __privateGet(this, _typeContent).errors }, zod: { lang: "ts", code: __privateGet(this, _zodContent).print(), errors: __privateGet(this, _zodContent).errors }, mock: { lang: "ts", code: __privateGet(this, _mockContent).print(), errors: __privateGet(this, _mockContent).errors } }; } } _mainContent = new WeakMap(); _typeContent = new WeakMap(); _zodContent = new WeakMap(); _mockContent = new WeakMap(); _pathZodNames = new WeakMap(); _respZodNames = new WeakMap(); _Printer_instances = new WeakSet(); parseRefComponent_fn = function({ kind, name, obj }, processor) { const nodeId = `#/components/${kind}/${name}`; const refId = "$ref" in obj ? obj.$ref : ""; const namedId = "$ref" in obj ? "" : obj.$id; if (refId === nodeId) { throw new Error(`${kind}/${name} 引用了自身`); } processor(nodeId, namedId); }; tryRegisterAnchors_fn = function(info, schema) { const { nodeId, namedId, typeName } = info; if (isRefSchema(schema)) return; if (schema.$anchor) { const anchorId = `${nodeId}#${schema.$anchor}`; if (this.schemas[anchorId]) { throw new Error(`重复的 anchor 引用 id:${anchorId}`); } const anchorNamedId = namedId && `${namedId}#${schema.$anchor}`; const anchorTypeName = this.named.nextTypeName(`${typeName}-${schema.$anchor}`); this.schemas[anchorId] = { position: "anchor", nodeId: anchorId, namedId: anchorNamedId, typeName: anchorTypeName, schema, nodeName: anchorId }; this.named.setRefType(anchorId, anchorTypeName); anchorNamedId && this.named.setRefType(anchorNamedId, anchorTypeName); } if ("items" in schema && schema.items) { __privateMethod(this, _Printer_instances, tryRegisterAnchors_fn).call(this, info, schema.items); } else if ("properties" in schema && schema.properties) { for (const [prop, property] of Object.entries(schema.properties)) { __privateMethod(this, _Printer_instances, tryRegisterAnchors_fn).call(this, info, property); } } }; printAlert_fn = function() { const alert = [ `/**`, ` * 由 ${pkgName}@${pkgVersion} 生成,建议忽略此文件的格式校验`, ` */` ]; __privateGet(this, _mainContent).push("alert", alert); __privateGet(this, _typeContent).push("alert", alert); __privateGet(this, _zodContent).push("alert", alert); __privateGet(this, _mockContent).push("alert", alert); }; printInfo_fn = function() { const { contact, description, license, summary, termsOfService, title, version } = this.document.info; const { externalDocs } = this.document; const { name, email, url } = contact || {}; const jsDoc = new JsDoc(); const { document } = this.configs; document && jsDoc.addComments({ document }); const extDoc = JsDoc.printExternalDoc(externalDocs); jsDoc.addComments({ title, version, contact: name || url || email ? [name, email ? `<${email}>` : "", url ? `(${url})` : ""].filter(Boolean).join(" ") : void 0, description, summary, see: extDoc }); const code = jsDoc.print(); __privateGet(this, _mainContent).push("info", code); __privateGet(this, _typeContent).push("info", code); __privateGet(this, _zodContent).push("info", code); __privateGet(this, _mockContent).push("info", code); }; printImports_fn = function() { const { axiosImportName = "", axiosImportFile, runtimeValidate, runtimeMock } = this.options || {}; const { cwd = "/", mainFile, typeFile = ".", zodFile = ".", mockFile = "." } = this.configs; const axiosImportFile2 = axiosImportFile || AXIOS_IMPORT_FILE; const importPath = toImportPath(axiosImportFile2, cwd, mainFile); __privateGet(this, _mainContent).push("import", [ toImportString(AXIOS_IMPORT_NAME, axiosImportName, importPath), `import type * as ${TYPE_FILE_EXPORT_NAME} from "${toRelative(typeFile, mainFile)}";` ]); if (!axiosImportFile || axiosImportFile === AXIOS_IMPORT_FILE) { if (!localPkg.isPackageExists(AXIOS_IMPORT_FILE)) { __privateGet(this, _mockContent).pushError(`需要安装 ${AXIOS_IMPORT_FILE}`); } } if (runtimeValidate || runtimeMock) { if (!localPkg.isPackageExists("zod")) { __privateGet(this, _zodContent).errors.push(`需要安装 zod`); } } if (runtimeValidate) { const zodNames = [...__privateGet(this, _pathZodNames).values()].join(","); __privateGet(this, _mainContent).push("import", [ `import {${zodNames}} from "${toRelative(zodFile, mainFile)}";` ]); } if (runtimeMock) { const { fakerImportName = FAKER_IMPORT_NAME, fakerImportFile = FAKER_IMPORT_FILE } = isBoolean(runtimeMock) ? {} : runtimeMock; const fakerImportPath = toImportPath(fakerImportFile, cwd, mockFile); const zodNames = [...__privateGet(this, _respZodNames).values()].join(","); __privateGet(this, _mainContent).push("import", [ `import ${ENABLE_MOCK_NAME} from "${toRelative(mockFile, mainFile)}";` ]); __privateGet(this, _mockContent).push("import", [ `import { generateMock } from "@anatine/zod-mock";`, `import AxiosMockAdapter from "axios-mock-adapter";`, toImportString(AXIOS_IMPORT_NAME, axiosImportName, importPath), toImportString(FAKER_IMPORT_NAME, fakerImportName, fakerImportPath), `import {${zodNames}} from "${toRelative(zodFile, mainFile)}";` ]); if (!fakerImportFile || fakerImportFile === FAKER_IMPORT_FILE) { if (!localPkg.isPackageExists(FAKER_IMPORT_FILE)) { __privateGet(this, _mockContent).pushError(`需要安装 ${FAKER_IMPORT_FILE}`); } } if (!localPkg.isPackageExists("@anatine/zod-mock")) { __privateGet(this, _mockContent).pushError(`需要安装 @anatine/zod-mock`); } if (!localPkg.isPackageExists("axios-mock-adapter")) { __privateGet(this, _mockContent).pushError(`需要安装 axios-mock-adapter`); } } __privateGet(this, _zodContent).push("import", `import { ${ZOD_IMPORT_NAME} } from "zod";`); }; printHeader_fn = function() { const { header } = this.options || {}; header && __privateGet(this, _mainContent).push("header", header); header && __privateGet(this, _typeContent).push("header", header); header && __privateGet(this, _zodContent).push("header", header); }; printFooter_fn = function() { const { footer } = this.options || {}; footer && __privateGet(this, _mainContent).push("footer", footer); footer && __privateGet(this, _typeContent).push("footer", footer); footer && __privateGet(this, _zodContent).push("footer", footer); }; printSchemas_fn = function() { Object.entries(this.schemas).forEach(([nodeId, schemaInfo]) => { __privateMethod(this, _Printer_instances, printSchema_fn).call(this, schemaInfo); }); }; printSchema_fn = function({ schema, nodeId, nodeName, typeName }) { if (isUndefined(typeName)) { throw new Error(`未发现 schema 引用:${nodeId}`); } const zodName = this.named.prepareVarName(toZodName(typeName)); const { comments, deps, type, zod } = Parser.parse(this.named, schema); const jsDoc = new JsDoc(); jsDoc.addComments({ name: nodeName }); jsDoc.addComments(comments); __privateGet(this, _typeContent).push("block", [ jsDoc.print(), `export type ${typeName} = ${type};` ]); __privateGet(this, _zodContent).add({ name: zodName, deps, code: `export const ${zodName} = ${zod};` }); }; printPaths_fn = function() { const { runtimeMock } = this.options || {}; if (runtimeMock) { const enableCondition = isBoolean(runtimeMock) ? DEFAULT_ENABLE_CONDITION : runtimeMock.enableCondition || DEFAULT_ENABLE_CONDITION; __privateGet(this, _mainContent).push("block", [ `if (${enableCondition}) {`, `${ENABLE_MOCK_NAME}();`, "}" ]); __privateGet(this, _mockContent).push("block", [ `export default function ${ENABLE_MOCK_NAME}() {`, `const mock = new AxiosMockAdapter(axios);` ]); } __privateGet(this, _mainContent).push("block", ["", `type ${AXIOS_REQUEST_TYPE_NAME} = Parameters<typeof axios.request>[0];`, ""]); Object.entries(this.document.paths || {}).forEach(([url, pathItem]) => { __privateMethod(this, _Printer_instances, printPathItem_fn).call(this, url, pathItem); }); __privateGet(this, _mockContent).push("block", `}`); }; printPathItem_fn = function(url, pathItem) { if (isRefPathItem(pathItem)) { const refPathItem = this.pathItems[pathItem.$ref]; if (isUndefined(refPathItem)) { throw new Error(`未发现 pathItem 引用:${pathItem.$ref}`); } __privateMethod(this, _Printer_instances, printPathItem_fn).call(this, url, refPathItem.pathItem); return; } Object.entries(pathItem).forEach(([method, _operation]) => { const isOperation = allowMethods.includes(method); if (!isOperation) return; if (isUndefined(_operation)) return; const operation = _operation; __privateMethod(this, _Printer_instances, printOperation_fn).call(this, method, url, operation); }); }; printOperation_fn = function(method, url, operation) { if (isRefOperation(operation)) return