UNPKG

api-morph

Version:

A modern TypeScript-first OpenAPI document generator that analyzes your code and JSDoc comments to automatically generate comprehensive and accurate API documentation.

1,932 lines (1,909 loc) 132 kB
import { SchemaRegistry, generateSwaggerUIHTML, getSwaggerUIAssetDir } from "./swagger-CArZTgxc.js"; import { assign, camel, cloneDeep, isObject, isPlainObject, pascal } from "radashi"; import z from "zod/v4"; import { Project } from "ts-morph"; import micromatch from "micromatch"; import { SyntaxKind } from "typescript"; import { basename, extname } from "node:path"; import deepmerge from "deepmerge"; import YAML from "yaml"; import * as mimeTypes from "mime-types"; import http from "node:http"; //#region src/analyzers/CodeAnalyzer.ts /** * 代码分析器抽象基类,用于分析代码结构并提取API信息 * * @category Analyzers */ var CodeAnalyzer = class { /** * 创建代码分析器实例 * @param context 解析上下文 */ constructor(context) { this.context = context; } }; //#endregion //#region src/analyzers/FrameworkAnalyzer.ts /** * 框架分析器抽象基类,用于分析特定框架的代码结构并提取API信息 * * @category Analyzers */ var FrameworkAnalyzer = class { /** * 创建框架分析器实例 * @param context 解析上下文 */ constructor(context) { this.context = context; } }; //#endregion //#region src/builders/CallbackBuilder.ts /** * 回调构建器,用于构建 OpenAPI CallbackObject * * @category Builders */ var CallbackBuilder = class { callback = {}; build() { return cloneDeep(this.callback); } /** * 添加回调表达式。 * @param expression 运行时计算的表达式,用于标识回调操作的URL。 * @param pathItem 描述一组请求和预期响应的路径项对象。 * @returns 回调构建器。 */ addExpression(expression, pathItem) { const callback = this.callback; if (!callback[expression]) callback[expression] = pathItem; return this; } /** * 添加扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 回调构建器。 */ addExtension(key, value) { const document = this.callback; if (!document[key]) document[key] = value; return this; } }; //#endregion //#region src/builders/ExternalDocsBuilder.ts /** * 外部文档构建器,用于构建 OpenAPI ExternalDocumentationObject * * @category Builders */ var ExternalDocsBuilder = class { externalDocs = { url: "" }; build() { return cloneDeep(this.externalDocs); } /** * 设置外部文档URL。 * @param url 外部文档URL(必需)。 * @returns 外部文档构建器。 */ setUrl(url) { this.externalDocs.url = url; return this; } /** * 设置外部文档描述。 * @param description 外部文档描述。 * @returns 外部文档构建器。 */ setDescription(description) { this.externalDocs.description = description; return this; } /** * 添加扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 响应构建器。 */ addExtension(key, value) { const externalDocs = this.externalDocs; if (!externalDocs[key]) externalDocs[key] = value; return this; } }; //#endregion //#region src/utils/typeGuards.ts /** * 判断参数对象是否为 ParameterObject。 * @param parameter - 要判断的参数对象。 * @returns 如果参数对象是 ParameterObject 则返回 true,否则返回 false。 */ function isParameterObject(parameter) { return "name" in parameter && "in" in parameter; } /** * 判断参数是否为 `x-` 开头的字符串。 * @param key 要判断的参数。 * @returns 如果参数是 `x-` 开头的字符串则返回 `true`,否则返回 `false`。 */ function isExtensionKey(key) { return key.startsWith("x-"); } /** * 判断给定的 value 是否为 Zod schema。 * @param value 要判断的值。 * @returns 如果是 Zod schema 则返回 true,否则返回 false。 */ function isZodSchema(value) { return isObject(value) && "_zod" in value; } //#endregion //#region src/builders/MediaTypeBuilder.ts /** * 媒体类型构建器,用于构建 OpenAPI MediaTypeObject * * @category Builders */ var MediaTypeBuilder = class { mediaType = {}; build() { return cloneDeep(this.mediaType); } /** * 设置媒体类型的 Schema。 * @param schema Schema 对象、布尔值或 Zod schema。 * @returns 媒体类型构建器。 */ setSchema(schema) { if (isZodSchema(schema)) this.mediaType.schema = z.toJSONSchema(schema); else this.mediaType.schema = schema; return this; } /** * 设置媒体类型的单个示例。 * @param example 示例值。 * @returns 媒体类型构建器。 */ setExample(example) { this.mediaType.example = example; return this; } /** * 设置媒体类型的多个示例。 * @param examples 示例对象集合。 * @returns 媒体类型构建器。 */ setExamples(examples) { this.mediaType.examples = examples; return this; } /** * 添加单个属性的编码信息。 * @param propertyName 属性名称。 * @param encodingObject 编码对象。 * @returns 媒体类型构建器。 */ addEncoding(propertyName, encodingObject) { if (!this.mediaType.encoding) this.mediaType.encoding = {}; if (!this.mediaType.encoding[propertyName]) this.mediaType.encoding[propertyName] = encodingObject; return this; } /** * 添加扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 媒体类型构建器。 */ addExtension(key, value) { if (!this.mediaType[key]) this.mediaType[key] = value; return this; } }; //#endregion //#region src/builders/OpenAPIBuilder.ts /** * 文档构建器,用于构建完整的 OpenAPI 文档 * * @category Builders */ var OpenAPIBuilder = class { document; globalResponses = {}; globalParameters = []; constructor(document) { const defaultDocument = { openapi: "3.1.0", info: { title: "", version: "1.0.0" } }; const finalDocument = assign(defaultDocument, document ?? {}); this.document = cloneDeep(finalDocument); } build() { return cloneDeep(this.document); } /** * 获取全局响应配置。 * @returns 全局响应对象。 */ getGlobalResponses() { return cloneDeep(this.globalResponses); } /** * 添加全局响应,该响应将应用于所有操作。 * @param statusCode HTTP 状态码或 "default"。 * @param response 响应对象或引用对象。 * @returns 文档构建器。 */ addGlobalResponse(statusCode, response) { this.globalResponses[statusCode] = response; return this; } /** * 获取全局参数配置。 * @returns 全局参数数组。 */ getGlobalParameters() { return cloneDeep(this.globalParameters); } /** * 添加全局参数,该参数将应用于所有操作。 * @param parameter 参数对象或引用对象。 * @returns 文档构建器。 */ addGlobalParameter(parameter) { this.globalParameters.push(parameter); return this; } /** * 设置 OpenAPI 版本。 * @param version OpenAPI 版本。 * @returns 文档构建器。 */ setOpenAPIVersion(version) { this.document.openapi = version; return this; } /** * 设置 API 标题。 * @param title API 标题。 * @returns 文档构建器。 */ setTitle(title) { this.document.info.title = title; return this; } /** * 设置 API 摘要。 * @param summary API 摘要。 * @returns 文档构建器。 */ setSummary(summary) { this.document.info.summary = summary; return this; } /** * 设置 API 描述。 * @param description API 描述。 * @returns 文档构建器。 */ setDescription(description) { this.document.info.description = description; return this; } /** * 设置服务条款链接。 * @param termsOfService 服务条款链接。 * @returns 文档构建器。 */ setTermsOfService(termsOfService) { this.document.info.termsOfService = termsOfService; return this; } /** * 设置联系信息。 * @param contact 联系信息对象。 * @returns 文档构建器。 */ setContact(contact) { this.document.info.contact = contact; return this; } /** * 设置许可证信息。 * @param license 许可证信息对象。 * @returns 文档构建器。 */ setLicense(license) { this.document.info.license = license; return this; } /** * 设置 API 版本。 * @param version API 版本。 * @returns 文档构建器。 */ setVersion(version) { this.document.info.version = version; return this; } /** * 添加 info 扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 文档构建器。 */ addInfoExtension(key, value) { const document = this.document; if (!document.info[key]) document.info[key] = value; return this; } /** * 设置 JSON Schema Dialect。 * @param dialect JSON Schema Dialect URI。 * @returns 文档构建器。 */ setJsonSchemaDialect(dialect) { this.document.jsonSchemaDialect = dialect; return this; } /** * 添加服务器信息到文档中。 * @param server 要添加的服务器对象。 * @returns 文档构建器。 */ addServer(server) { const document = this.document; if (!document.servers) document.servers = []; document.servers.push(server); return this; } /** * 添加路径项。 * @param path API 路径。 * @param pathItem 路径项对象。 * @returns 文档构建器。 */ addPathItem(path$1, pathItem) { const document = this.document; if (!document.paths) document.paths = {}; if (!document.paths[path$1]) document.paths[path$1] = pathItem; return this; } /** * 使用 PathItemBuilder 添加路径项。 * @param pathItemBuilder 路径项构建器实例。 * @returns 文档构建器。 */ addPathItemFromBuilder(path$1, pathItemBuilder) { const pathItem = pathItemBuilder.build(); return this.addPathItem(path$1, pathItem); } /** * 添加 paths 扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 文档构建器。 */ addPathsExtension(key, value) { const document = this.document; if (!document.paths) document.paths = {}; if (!document.paths[key]) document.paths[key] = value; return this; } /** * 添加 Webhook。 * @param name Webhook 名称。 * @param webhook Webhook 的 PathItemObject。 * @returns 文档构建器。 */ addWebhook(name, webhook) { const document = this.document; if (!document.webhooks) document.webhooks = {}; if (!document.webhooks[name]) document.webhooks[name] = webhook; return this; } /** * 添加安全要求。 * @param securityRequirement 单个安全要求对象。 * @returns 文档构建器。 */ addSecurity(securityRequirement) { const document = this.document; if (!document.security) document.security = []; document.security.push(securityRequirement); return this; } /** * 添加标签定义到文档中。 * @param tag 要添加的标签对象。 * @returns 文档构建器。 */ addTag(tag) { const document = this.document; if (!document.tags) document.tags = []; if (!document.tags.find((t) => t.name === tag.name)) document.tags.push(tag); return this; } /** * 设置外部文档。 * @param externalDocs 外部文档对象。 * @returns 文档构建器。 */ setExternalDocs(externalDocs) { this.document.externalDocs = externalDocs; return this; } /** * 添加扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 响应构建器。 */ addExtension(key, value) { const document = this.document; if (!document[key]) document[key] = value; return this; } /** * 添加 Schema 组件到 components。 * @param name Schema 名称。 * @param schema Schema 对象、布尔值或 Zod schema。 * @returns 文档构建器。 */ addSchemaToComponents(name, schema) { const document = this.document; if (!document.components) document.components = {}; if (!document.components.schemas) document.components.schemas = {}; if (!document.components.schemas[name]) if (isZodSchema(schema)) document.components.schemas[name] = z.toJSONSchema(schema); else document.components.schemas[name] = schema; return this; } /** * 添加响应组件到 components。 * @param name 响应名称。 * @param response 响应对象或引用对象。 * @returns 文档构建器。 */ addResponseToComponents(name, response) { const document = this.document; if (!document.components) document.components = {}; if (!document.components.responses) document.components.responses = {}; if (!document.components.responses[name]) document.components.responses[name] = response; return this; } /** * 添加参数组件到 components。 * @param name 参数名称。 * @param parameter 参数对象或引用对象。 * @returns 文档构建器。 */ addParameterToComponents(name, parameter) { const document = this.document; if (!document.components) document.components = {}; if (!document.components.parameters) document.components.parameters = {}; if (!document.components.parameters[name]) document.components.parameters[name] = parameter; return this; } /** * 添加示例组件到 components。 * @param name 示例名称。 * @param example 示例对象或引用对象。 * @returns 文档构建器。 */ addExampleToComponents(name, example) { const document = this.document; if (!document.components) document.components = {}; if (!document.components.examples) document.components.examples = {}; if (!document.components.examples[name]) document.components.examples[name] = example; return this; } /** * 添加请求体组件到 components。 * @param name 请求体名称。 * @param requestBody 请求体对象或引用对象。 * @returns 文档构建器。 */ addRequestBodyToComponents(name, requestBody) { const document = this.document; if (!document.components) document.components = {}; if (!document.components.requestBodies) document.components.requestBodies = {}; if (!document.components.requestBodies[name]) document.components.requestBodies[name] = requestBody; return this; } /** * 添加头部组件到 components。 * @param name 头部名称。 * @param header 头部对象或引用对象。 * @returns 文档构建器。 */ addHeaderToComponents(name, header) { const document = this.document; if (!document.components) document.components = {}; if (!document.components.headers) document.components.headers = {}; if (!document.components.headers[name]) document.components.headers[name] = header; return this; } /** * 添加安全方案组件到 components。 * @param name 安全方案名称。 * @param securityScheme 安全方案对象或引用对象。 * @returns 文档构建器。 */ addSecuritySchemeToComponents(name, securityScheme) { const document = this.document; if (!document.components) document.components = {}; if (!document.components.securitySchemes) document.components.securitySchemes = {}; if (!document.components.securitySchemes[name]) document.components.securitySchemes[name] = securityScheme; return this; } /** * 添加链接组件到 components。 * @param name 链接名称。 * @param link 链接对象或引用对象。 * @returns 文档构建器。 */ addLinkToComponents(name, link) { const document = this.document; if (!document.components) document.components = {}; if (!document.components.links) document.components.links = {}; if (!document.components.links[name]) document.components.links[name] = link; return this; } /** * 添加回调组件到 components。 * @param name 回调名称。 * @param callback 回调对象或引用对象。 * @returns 文档构建器。 */ addCallbackToComponents(name, callback) { const document = this.document; if (!document.components) document.components = {}; if (!document.components.callbacks) document.components.callbacks = {}; if (!document.components.callbacks[name]) document.components.callbacks[name] = callback; return this; } /** * 添加路径项组件到 components。 * @param name 路径项名称。 * @param pathItem 路径项对象或引用对象。 * @returns 文档构建器。 */ addPathItemToComponents(name, pathItem) { const document = this.document; if (!document.components) document.components = {}; if (!document.components.pathItems) document.components.pathItems = {}; if (!document.components.pathItems[name]) document.components.pathItems[name] = pathItem; return this; } /** * 添加组件扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 文档构建器。 */ addComponentsExtension(key, value) { const document = this.document; if (!document.components) document.components = {}; if (!document.components[key]) document.components[key] = value; return this; } }; //#endregion //#region src/builders/OperationBuilder.ts /** * 操作构建器,用于构建 OpenAPI OperationObject * * @category Builders */ var OperationBuilder = class { operation = { responses: {} }; build() { return cloneDeep(this.operation); } /** * 添加标签。 * @param tag 标签 * @returns 操作构建器。 */ addTag(tag) { const operation = this.operation; if (!operation.tags) operation.tags = []; if (!operation.tags.includes(tag)) operation.tags.push(tag); return this; } /** * 设置操作摘要。 * @param summary 操作摘要 * @returns 操作构建器。 */ setSummary(summary) { this.operation.summary = summary; return this; } /** * 设置操作描述。 * @param description 操作描述。 * @returns 操作构建器。 */ setDescription(description) { this.operation.description = description; return this; } /** * 设置外部文档。 * @param externalDocs 外部文档对象。 * @returns 操作构建器。 */ setExternalDocs(externalDocs) { this.operation.externalDocs = externalDocs; return this; } /** * 设置操作 ID。 * @param operationId 操作 ID。 * @returns 操作构建器。 */ setOperationId(operationId) { this.operation.operationId = operationId; return this; } /** * 添加参数(ParameterObject)。 * @param parameter 参数对象(ParameterObject)。 * @returns 操作构建器。 */ addParameterFromObject(parameter) { const operation = this.operation; if (!operation.parameters) operation.parameters = []; const exists = operation.parameters.some((p) => isParameterObject(p) && p.name === parameter.name && p.in === parameter.in); if (!exists) operation.parameters.push(parameter); return this; } /** * 添加参数引用(ReferenceObject)。 * @param parameter 参数引用对象(ReferenceObject)。 * @returns 操作构建器。 */ addParameterFromReference(parameter) { const operation = this.operation; if (!operation.parameters) operation.parameters = []; const exists = operation.parameters.some((p) => !isParameterObject(p) && p.$ref === parameter.$ref); if (!exists) operation.parameters.push(parameter); return this; } /** * 使用 ParameterBuilder 添加参数。 * @param parameterBuilder 参数构建器实例。 * @returns 操作构建器。 */ addParameterFromBuilder(parameterBuilder) { const parameter = parameterBuilder.build(); return this.addParameterFromObject(parameter); } /** * 设置请求体。 * @param requestBody 请求体对象。 * @returns 操作构建器。 */ setRequestBody(requestBody) { this.operation.requestBody = requestBody; return this; } /** * 使用 RequestBodyBuilder 设置请求体。 * @param requestBodyBuilder 请求体构建器实例。 * @returns 操作构建器。 */ setRequestBodyFromBuilder(requestBodyBuilder) { const requestBody = requestBodyBuilder.build(); return this.setRequestBody(requestBody); } /** * 添加响应。 * @param statusCode HTTP 状态码。 * @param response 响应对象。 * @returns 操作构建器。 */ addResponse(statusCode, response) { const operation = this.operation; if (!operation.responses[statusCode]) operation.responses[statusCode] = response; return this; } /** * 使用 ResponseBuilder 添加响应。 * @param responseBuilder 响应构建器实例。 * @returns 操作构建器。 */ addResponseFromBuilder(statusCode, responseBuilder) { const response = responseBuilder.build(); return this.addResponse(statusCode, response); } /** * 添加回调。 * @param name 回调事件的名称 (event name)。 * @param callback 回调对象或引用对象。 * @returns 操作构建器。 */ addCallback(name, callback) { const operation = this.operation; if (!operation.callbacks) operation.callbacks = {}; if (!operation.callbacks[name]) operation.callbacks[name] = callback; return this; } /** * 设置已废弃标志。 * @param deprecated 是否已废弃。 * @returns 操作构建器。 */ setDeprecated(deprecated) { this.operation.deprecated = deprecated; return this; } /** * 添加此操作特定的安全要求。 * @param securityRequirement 单个安全要求对象。 * @returns 操作构建器。 */ addSecurity(securityRequirement) { const operation = this.operation; if (!operation.security) operation.security = []; operation.security.push(securityRequirement); return this; } /** * 添加此操作特定的服务器。 * @param server 服务器对象。 * @returns 操作构建器。 */ addServer(server) { const operation = this.operation; if (!operation.servers) operation.servers = []; operation.servers.push(server); return this; } /** * 添加扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 操作构建器。 */ addExtension(key, value) { const operation = this.operation; if (!operation[key]) operation[key] = value; return this; } /** * 添加响应扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 操作构建器。 */ addResponsesExtension(key, value) { const operation = this.operation; if (!operation.responses[key]) operation.responses[key] = value; return this; } }; //#endregion //#region src/builders/ParameterBuilder.ts /** * 参数构建器,用于构建 OpenAPI ParameterObject * * @category Builders */ var ParameterBuilder = class { parameter; /** * 创建 ParameterBuilder 实例。 * @param name 参数名称。 * @param paramIn 参数位置。 */ constructor(name, paramIn) { this.parameter = { name, in: paramIn, required: paramIn === "path" ? true : void 0 }; } build() { return cloneDeep(this.parameter); } /** * 获取参数名称。 * @returns 参数名称。 */ getName() { return this.parameter.name; } /** * 获取参数位置。 * @returns 参数位置。 */ getIn() { return this.parameter.in; } /** * 设置参数描述。 * @param description 参数描述。 * @returns 参数构建器。 */ setDescription(description) { this.parameter.description = description; return this; } /** * 设置参数是否必需。 * @param required 是否必需。 * @returns 参数构建器。 */ setRequired(required) { this.parameter.required = required; return this; } /** * 设置参数是否已废弃。 * @param deprecated 是否已废弃。 * @returns 参数构建器。 */ setDeprecated(deprecated) { this.parameter.deprecated = deprecated; return this; } /** * 设置是否允许空值。 * @param allowEmptyValue 是否允许空值。 * @returns 参数构建器。 */ setAllowEmptyValue(allowEmptyValue) { this.parameter.allowEmptyValue = allowEmptyValue; return this; } /** * 设置参数样式。 * @param style 参数样式。 * @returns 参数构建器。 */ setStyle(style) { this.parameter.style = style; return this; } /** * 设置是否展开对象。 * @param explode 是否展开对象。 * @returns 参数构建器。 */ setExplode(explode) { this.parameter.explode = explode; return this; } /** * 设置是否允许保留字符。 * @param allowReserved 是否允许保留字符。 * @returns 参数构建器。 */ setAllowReserved(allowReserved) { this.parameter.allowReserved = allowReserved; return this; } /** * 设置参数 Schema。 * @param schema Schema 对象或引用对象。 * @returns 参数构建器。 */ setSchema(schema) { this.parameter.schema = schema; return this; } /** * 设置参数的单个示例。 * @param example 示例对象。 * @returns 参数构建器。 */ setExample(example) { this.parameter.example = example; return this; } /** * 设置参数的多个示例。 * @param examples 示例对象。 * @returns 参数构建器。 */ setExamples(examples) { this.parameter.examples = examples; return this; } /** * 添加参数的单个内容类型定义。 * @param mediaType 媒体类型 (例如 'application/json')。 * @param mediaTypeObject 媒体类型对象。 * @returns 参数构建器。 */ addContent(mediaType, mediaTypeObject) { const parameter = this.parameter; if (!parameter.content) parameter.content = {}; if (!parameter.content[mediaType]) parameter.content[mediaType] = mediaTypeObject; return this; } /** * 添加扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 参数构建器。 */ addExtension(key, value) { const parameter = this.parameter; if (!parameter[key]) parameter[key] = value; return this; } }; //#endregion //#region src/builders/PathItemBuilder.ts /** * 路径项构建器,用于构建 OpenAPI PathItemObject * * @category Builders */ var PathItemBuilder = class { pathItem = {}; build() { return cloneDeep(this.pathItem); } /** * 设置路径项的引用。 * @param ref 引用路径。 * @returns 路径项构建器。 */ setRef(ref) { this.pathItem.$ref = ref; return this; } /** * 设置路径摘要。 * @param summary 路径摘要。 * @returns 路径项构建器。 */ setSummary(summary) { this.pathItem.summary = summary; return this; } /** * 设置路径描述。 * @param description 路径描述。 * @returns 路径项构建器。 */ setDescription(description) { this.pathItem.description = description; return this; } /** * 添加操作。 * @param method HTTP 方法。 * @param operation 操作对象。 * @returns 路径项构建器。 */ addOperation(method, operation) { const pathItem = this.pathItem; if (!pathItem[method]) pathItem[method] = operation; return this; } /** * 使用 OperationBuilder 添加操作。 * @param operationBuilder 操作构建器实例。 * @returns 路径项构建器。 */ addOperationFromBuilder(method, operationBuilder) { const operation = operationBuilder.build(); return this.addOperation(method, operation); } /** * 添加服务器信息到路径项中。 * @param server 要添加的服务器对象。 * @returns 路径项构建器。 */ addServer(server) { const pathItem = this.pathItem; if (!pathItem.servers) pathItem.servers = []; pathItem.servers.push(server); return this; } /** * 添加参数(ParameterObject)。 * @param parameter 参数对象(ParameterObject)。 * @returns 操作构建器。 */ addParameterFromObject(parameter) { const pathItem = this.pathItem; if (!pathItem.parameters) pathItem.parameters = []; const exists = pathItem.parameters.some((p) => isParameterObject(p) && p.name === parameter.name && p.in === parameter.in); if (!exists) pathItem.parameters.push(parameter); return this; } /** * 添加参数引用(ReferenceObject)。 * @param parameter 参数引用对象(ReferenceObject)。 * @returns 操作构建器。 */ addParameterFromReference(parameter) { const pathItem = this.pathItem; if (!pathItem.parameters) pathItem.parameters = []; const exists = pathItem.parameters.some((p) => !isParameterObject(p) && p.$ref === parameter.$ref); if (!exists) pathItem.parameters.push(parameter); return this; } /** * 使用 ParameterBuilder 添加参数。 * @param parameterBuilder 参数构建器实例。 * @returns 操作构建器。 */ addParameterFromBuilder(parameterBuilder) { const parameter = parameterBuilder.build(); return this.addParameterFromObject(parameter); } /** * 添加扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 操作构建器。 */ addExtension(key, value) { const pathItem = this.pathItem; if (!pathItem[key]) pathItem[key] = value; return this; } }; //#endregion //#region src/builders/RequestBodyBuilder.ts /** * 请求体构建器,用于构建 OpenAPI RequestBodyObject * * @category Builders */ var RequestBodyBuilder = class { requestBody = { content: {} }; build() { return cloneDeep(this.requestBody); } /** * 设置请求体描述。 * @param description 请求体描述。 * @returns 请求体构建器。 */ setDescription(description) { this.requestBody.description = description; return this; } /** * 添加请求体的单个内容类型定义。 * @param mediaType 媒体类型 (例如 'application/json') * @param mediaTypeObject 媒体类型对象。 * @returns 请求体构建器。 */ addContent(mediaType, mediaTypeObject) { const requestBody = this.requestBody; if (!requestBody.content[mediaType]) requestBody.content[mediaType] = mediaTypeObject; return this; } /** * 设置请求体是否必需。 * @param required 是否必需 * @returns 请求体构建器。 */ setRequired(required) { this.requestBody.required = required; return this; } /** * 添加扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 请求体构建器。 */ addExtension(key, value) { const requestBody = this.requestBody; if (!requestBody[key]) requestBody[key] = value; return this; } }; //#endregion //#region src/builders/ResponseBuilder.ts /** * 响应构建器,用于构建 OpenAPI ResponseObject * * @category Builders */ var ResponseBuilder = class { response = { description: "" }; build() { return cloneDeep(this.response); } /** * 设置响应描述。 * @param description 响应描述。 * @returns 响应构建器。 */ setDescription(description) { this.response.description = description; return this; } /** * 添加响应的单个头信息定义。头部名称会被转换为小写, * 并且会过滤掉 `content-type` 头(OpenAPI 规范要求响应 headers 不应包含 "content-type")。 * @param name 要设置的头信息的名称。 * @param header 头对象或引用对象。 * @returns 响应构建器。 */ addHeader(name, header) { const response = this.response; if (!response.headers) response.headers = {}; const lowerName = name.toLowerCase(); if (lowerName === "content-type") return this; if (!response.headers[lowerName]) response.headers[lowerName] = header; return this; } /** * 添加响应的单个内容类型定义。 * @param mediaType 媒体类型 (例如 'application/json')。 * @param mediaTypeObject 媒体类型对象。 * @returns 响应构建器。 */ addContent(mediaType, mediaTypeObject) { const response = this.response; if (!response.content) response.content = {}; if (!response.content[mediaType]) response.content[mediaType] = mediaTypeObject; return this; } /** * 添加响应的单个链接定义。 * @param name 要设置的链接的名称。 * @param link 链接对象或引用对象。 * @returns 响应构建器。 */ addLink(name, link) { const response = this.response; if (!response.links) response.links = {}; if (!response.links[name]) response.links[name] = link; return this; } /** * 添加扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 响应构建器。 */ addExtension(key, value) { const response = this.response; if (!response[key]) response[key] = value; return this; } }; //#endregion //#region src/builders/SecurityBuilder.ts /** * 安全需求构建器,用于构建 OpenAPI SecurityRequirementObject * * @category Builders */ var SecurityBuilder = class { security = {}; build() { return cloneDeep(this.security); } /** * 设置指定安全方案的作用域。 * @param schemeName 安全方案名称(必须对应在 Components Object 中声明的安全方案)。 * @param scopes 作用域数组(对于 oauth2 或 openIdConnect 类型,为所需的作用域名称;对于其他类型,为角色名称)。 * @returns 安全需求构建器。 */ addScopes(schemeName, scopes = []) { const security = this.security; if (!security[schemeName]) security[schemeName] = scopes; return this; } }; //#endregion //#region src/builders/ServerBuilder.ts /** * 服务器构建器,用于构建 OpenAPI ServerObject * * @category Builders */ var ServerBuilder = class { server = { url: "" }; build() { return cloneDeep(this.server); } /** * 设置服务器URL。 * @param url 服务器URL(必需)。 * @returns 服务器构建器。 */ setUrl(url) { this.server.url = url; return this; } /** * 设置服务器描述。 * @param description 服务器描述。 * @returns 服务器构建器。 */ setDescription(description) { this.server.description = description; return this; } /** * 添加服务器变量。 * @param name 变量名称。 * @param variable 服务器变量对象。 * @returns 服务器构建器。 */ addVariable(name, variable) { const server = this.server; if (!server.variables) server.variables = {}; if (!server.variables[name]) server.variables[name] = variable; return this; } /** * 添加扩展字段。 * @param key 扩展字段键(必须以 'x-' 开头)。 * @param value 扩展字段值。 * @returns 响应构建器。 */ addExtension(key, value) { const server = this.server; if (!server[key]) server[key] = value; return this; } }; //#endregion //#region src/constants.ts const VALID_HTTP_METHODS = [ "get", "post", "put", "delete", "patch", "options", "head", "trace" ]; const VALID_PARAMETER_IN = [ "query", "header", "path", "cookie" ]; const VALID_PARAMETER_STYLE = [ "matrix", "label", "simple", "form", "spaceDelimited", "pipeDelimited", "deepObject" ]; //#endregion //#region src/registry/CodeAnalyzerRegistry.ts /** * 代码分析器注册表,用于管理和查找代码分析器 */ var CodeAnalyzerRegistry = class { /** 所有注册的代码分析器 */ analyzers = /* @__PURE__ */ new Set(); /** * 注册代码分析器 * @param analyzer 代码分析器实例 */ register(analyzer) { if (this.analyzers.has(analyzer)) throw new Error(`代码分析器名称冲突:分析器 "${analyzer.constructor.name}" 已经被注册。`); this.analyzers.add(analyzer); } /** * 获取所有注册的分析器 * @returns 所有分析器数组 */ getAllAnalyzers() { return Array.from(this.analyzers); } }; //#endregion //#region src/analyzers/ExpressRouteCodeAnalyzer.ts /** * 路由代码分析器,负责从Express路由调用中分析HTTP方法、路径和operationId */ var ExpressRouteCodeAnalyzer = class extends CodeAnalyzer { /** * 分析路由信息(方法、路径和operationId) */ async analyze(node) { const expression = node.getFirstChildByKindOrThrow(SyntaxKind.CallExpression); const propertyAccess = expression.getFirstChildByKindOrThrow(SyntaxKind.PropertyAccessExpression); const method = propertyAccess.getName(); const args = expression.getArguments(); let path$1 = this.calculateRoutePath(expression) || "/"; path$1 = this.convertExpressPathToOpenAPI(path$1); let operationId; const functionName = this.extractHandlerName(args); if (this.context.options.generateOperationId) { const filePath = node.getSourceFile().getFilePath(); const fileName = basename(filePath, extname(filePath)); operationId = this.context.options.generateOperationId(method, path$1, fileName, functionName) || void 0; } else operationId = functionName ?? this.generateOperationIdFromRoute(method, path$1); return { method, path: path$1, operationId }; } /** * 计算route的最终路径 * @param callExpression route方法调用表达式 * @returns 路由最终路径,如果没有找到则返回空字符串 */ calculateRoutePath(callExpression) { const args = callExpression.getArguments(); const propertyAccess = callExpression.getFirstChildByKind(SyntaxKind.PropertyAccessExpression); if (!propertyAccess) return ""; const path$1 = this.getStringValueFromNode(args[0]) || ""; const mountPath = this.normalizePath(path$1); let parentBasePath = ""; const identifier = propertyAccess.getExpression().asKindOrThrow(SyntaxKind.Identifier); const references = identifier.findReferencesAsNodes(); for (const reference of references) { const parent = reference.getParentIfKind(SyntaxKind.CallExpression); if (!parent) continue; parentBasePath = this.calculateRoutePath(parent); break; } return parentBasePath + mountPath; } /** * 规范化路径,确保路径以 / 开头,且不以 / 结尾 * @param path 路径 * @returns 规范化后的路径 */ normalizePath(path$1) { if (path$1 && !path$1.startsWith("/")) path$1 = `/${path$1}`; if (path$1.endsWith("/")) path$1 = path$1.slice(0, -1); return path$1; } /** * 从 AST 节点中提取字符串字面量值 * @param node 要提取字符串值的节点 * @returns 字符串值,如果提取失败则返回undefined */ getStringValueFromNode(node) { const nodeType = node.getType(); if (nodeType.isString() || nodeType.isStringLiteral()) return nodeType.getLiteralValue(); return void 0; } /** * 将 Express 路径参数格式转换为 OpenAPI 格式: `/users/:id` -> `/users/{id}` */ convertExpressPathToOpenAPI(path$1) { return path$1.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "{$1}"); } /** * 从路由处理函数中提取函数名 * @param args 参数 * @returns 函数名,如果提取失败则返回 undefined */ extractHandlerName(args) { const handlerArg = args[args.length - 1]; if (handlerArg.isKind(SyntaxKind.FunctionExpression)) { const functionName = handlerArg.getName(); if (functionName) return functionName; } if (handlerArg.isKind(SyntaxKind.Identifier)) return handlerArg.getText(); if (handlerArg.isKind(SyntaxKind.PropertyAccessExpression)) { const propertyName = handlerArg.getName(); if (propertyName) return propertyName; } return void 0; } /** * 根据HTTP方法和路径生成operationId * @param method HTTP方法 * @param path 路径 * @returns operationId */ generateOperationIdFromRoute(method, path$1) { const cleanPath = path$1.slice(1); const segments = cleanPath.split("/").filter((segment) => segment.length > 0); const pathParts = segments.map((segment) => { if (segment.startsWith("{") && segment.endsWith("}")) { const paramName = segment.slice(1, -1); return `By${pascal(paramName)}`; } return pascal(segment); }); const methodName = method.toLowerCase(); const pathName = pathParts.join(""); return methodName + pathName; } }; //#endregion //#region src/analyzers/ExpressZodValidatorCodeAnalyzer.ts /** * Express Zod 验证中间件代码分析器,负责从 Express 路由中的 zodValidator 中间件调用中提取 Zod schema * 并转换为 OpenAPI 的参数和请求体定义 */ var ExpressZodValidatorCodeAnalyzer = class extends CodeAnalyzer { /** * 分析节点中的 zodValidator 调用,提取 Zod schema * @param node 节点 * @returns 提取的 Zod schemas */ async analyze(node) { const zodValidatorCall = this.findZodValidatorCall(node); if (!zodValidatorCall) return {}; const sourceFile = zodValidatorCall.getSourceFile(); const filePath = sourceFile.getFilePath(); const lineNumber = zodValidatorCall.getStartLineNumber(); const location = `${filePath}:${lineNumber}`; const registry = SchemaRegistry.getInstance(); const schemaInfo = registry.get(location); if (!schemaInfo) return {}; let schemaNames = {}; const optionsArg = zodValidatorCall.getArguments()[0]; if (optionsArg?.isKind(SyntaxKind.ObjectLiteralExpression)) schemaNames = this.extractZodSchemaNames(optionsArg); return this.convertRuntimeSchemaToOperationData(schemaInfo, schemaNames); } /** * 从 Zod schema 创建 OpenAPI RequestBody * @param schema JSON schema * @param schemaName schema 名称 * @returns 创建的 OpenAPI RequestBody */ createRequestBodyFromSchema(schema, schemaName) { const mediaType = this.context.options.defaultRequestBodyMediaType; if (schemaName && !this.context.schemas.has(schemaName)) this.context.schemas.set(schemaName, schema); const schemaObject = schemaName ? { $ref: `#/components/schemas/${schemaName}` } : schema; return { content: { [mediaType]: { schema: schemaObject } } }; } /** * 从 JSON Schema 创建 OpenAPI Parameters * @param schema JSON schema * @param paramIn 参数位置 * @returns 创建的 OpenAPI Parameters */ createParametersFromSchema(schema, paramIn) { const parameters = []; if (schema.type === "object" && schema.properties) for (const [propName, propSchema] of Object.entries(schema.properties)) { const isRequired = schema.required?.includes(propName) || false; const cleanSchema = { ...propSchema }; const parameterProps = {}; if (cleanSchema.description) { parameterProps.description = cleanSchema.description; delete cleanSchema.description; } if (cleanSchema.deprecated) { parameterProps.deprecated = cleanSchema.deprecated; delete cleanSchema.deprecated; } if (cleanSchema.allowEmptyValue) { parameterProps.allowEmptyValue = cleanSchema.allowEmptyValue; delete cleanSchema.allowEmptyValue; } if (cleanSchema.example) { parameterProps.example = cleanSchema.example; delete cleanSchema.example; } if (cleanSchema.examples) { parameterProps.examples = cleanSchema.examples; delete cleanSchema.examples; } const parameter = { name: propName, in: paramIn, required: paramIn === "path" ? true : isRequired, schema: cleanSchema, ...parameterProps }; parameters.push(parameter); } return parameters; } /** * 查找节点中的 zodValidator 调用 * @param node 节点 * @returns zodValidator 调用节点 */ findZodValidatorCall(node) { const expression = node.getFirstChildByKindOrThrow(SyntaxKind.CallExpression); const args = expression.getArguments(); for (const arg of args) if (arg.isKind(SyntaxKind.CallExpression)) { const expression$1 = arg.getExpression(); if (expression$1.isKind(SyntaxKind.Identifier) && expression$1.getText() === "zodValidator") return arg; } return null; } /** * 从 zodValidator 的选项对象中提取 Zod schema 名称 * @param optionsObject zodValidator 的选项对象 * @returns 提取的 Zod schema 名称. e.g. { body: 'UserSchema', query: 'ListQuerySchema' } */ extractZodSchemaNames(optionsObject) { const schemaNames = {}; const properties = optionsObject.getChildrenOfKind(SyntaxKind.PropertyAssignment); for (const property of properties) { const nameNode = property.getNameNode(); const initializer = property.getInitializer(); if (initializer?.isKind(SyntaxKind.Identifier)) schemaNames[nameNode.getText()] = initializer.getText(); } return schemaNames; } /** * 将运行时 Schema 信息转换为 OperationData * @param schemaInfo 运行时 Schema 信息 * @param schemaNames 从代码中提取的 Schema 名称 * @returns 转换后的 OperationData */ convertRuntimeSchemaToOperationData(schemaInfo, schemaNames) { const operationData = {}; if (schemaInfo.schemas.body) { const schemaName = schemaNames.body; operationData.requestBody = this.createRequestBodyFromSchema(schemaInfo.schemas.body, schemaName); } if (schemaInfo.schemas.query) operationData.parameters = [...operationData.parameters || [], ...this.createParametersFromSchema(schemaInfo.schemas.query, "query")]; if (schemaInfo.schemas.params) operationData.parameters = [...operationData.parameters || [], ...this.createParametersFromSchema(schemaInfo.schemas.params, "path")]; if (schemaInfo.schemas.headers) operationData.parameters = [...operationData.parameters || [], ...this.createParametersFromSchema(schemaInfo.schemas.headers, "header")]; return operationData; } }; //#endregion //#region src/analyzers/ExpressFrameworkAnalyzer.ts /** * Express框架分析器,用于分析Express应用的各种节点类型。 */ var ExpressFrameworkAnalyzer = class extends FrameworkAnalyzer { frameworkName = "Express"; codeAnalyzerRegistry; constructor(context) { super(context); this.codeAnalyzerRegistry = new CodeAnalyzerRegistry(); const defaultAnalyzers = [ExpressRouteCodeAnalyzer, ExpressZodValidatorCodeAnalyzer]; const analyzers = [...defaultAnalyzers, ...context.options.customExpressCodeAnalyzers ?? []]; for (const analyzer of analyzers) this.codeAnalyzerRegistry.register(new analyzer(this.context)); } /** * 判断节点是否属于Express框架 * @param node 代码节点 * @returns 如果属于Express框架返回true */ canAnalyze(node) { if (!node.isKind(SyntaxKind.ExpressionStatement)) return false; const expression = node.getFirstChildByKind(SyntaxKind.CallExpression); if (!expression) return false; const propertyAccess = expression.getFirstChildByKind(SyntaxKind.PropertyAccessExpression); if (!propertyAccess) return false; const methodName = propertyAccess.getName(); if (!VALID_HTTP_METHODS.includes(methodName)) return false; const objectExpression = propertyAccess.getExpression(); const isExpressAppType = this.isExpressAppType(objectExpression) || this.isExpressRouterType(objectExpression); if (!isExpressAppType) return false; const args = expression.getArguments(); if (args.length < 2) return false; const pathArg = args[0]; const pathArgType = pathArg.getType(); if (!pathArgType.isString() && !pathArgType.isStringLiteral()) return false; return true; } /** * 分析Express节点,使用代码分析器注册表来获取不同的信息 * @param node 代码节点 * @returns 解析后的操作数据 */ async analyze(node) { const analyzers = this.codeAnalyzerRegistry.getAllAnalyzers(); const results = await Promise.all(analyzers.map((analyzer) => analyzer.analyze(node))); const operationData = {}; results.forEach((result) => { Object.assign(operationData, result); }); return operationData; } /** * 检查节点是否为Express类型 * @param node 要检查的节点 * @returns 如果是Express类型返回true */ isExpressAppType(node) { const nodeType = node.getType(); const typeSymbol = nodeType.getSymbol(); return nodeType.getText().includes("@types/express") && typeSymbol?.getName() === "Express"; } /** * 检查节点是否为Express Router类型 * @param node 要检查的节点 * @returns 如果是Express Router类型返回true */ isExpressRouterType(node) { const nodeType = node.getType(); const typeSymbol = nodeType.getSymbol(); return nodeType.getText().includes("@types/express") && typeSymbol?.getName() === "Router"; } }; //#endregion //#region src/analyzers/HonoRouteCodeAnalyzer.ts /** * Hono路由代码分析器,负责从Hono路由调用中分析HTTP方法、路径和operationId */ var HonoRouteCodeAnalyzer = class extends CodeAnalyzer { /** * 分析Hono路由信息(方法、路径和operationId) */ async analyze(node) { const expression = node.getFirstChildByKindOrThrow(SyntaxKind.CallExpression); const propertyAccess = expression.getFirstChildByKindOrThrow(SyntaxKind.PropertyAccessExpression); const method = propertyAccess.getName(); const args = expression.getArguments(); let path$1 = this.calculateRoutePath(expression) || "/"; path$1 = this.convertHonoPathToOpenAPI(path$1); let operationId; const functionName = this.extractHandlerName(args); if (this.context.options.generateOperationId) { const filePath = node.getSourceFile().getFilePath(); const fileName = basename(filePath, extname(filePath)); operationId = this.context.options.generateOperationId(method, path$1, fileName, functionName) || void 0; } else operationId = functionName ?? this.generateOperationIdFromRoute(method, path$1); return { method, path: path$1, operationId }; } /** * 计算route的最终路径 * @param callExpression route方法调用表达式 * @returns 路由最终路径,如果没有找到则返回空字符串 */ calculateRoutePath(callExpression) { const args = callExpression.getArguments(); const propertyAccess = callExpression.getFirstChildByKind(SyntaxKind.PropertyAccessExpression); if (!propertyAccess) return ""; const identifier = propertyAccess.getExpression().asKindOrThrow(SyntaxKind.Identifier); const path$1 = this.getStringValueFromNode(args[0]) || ""; const basePath = identifier.getType().getTypeArguments()[2].getLiteralValue(); const mountPath = this.normalizePath(basePath) + this.normalizePath(path$1); let parentBasePath = ""; const references = identifier.findReferencesAsNodes(); for (const reference of references) { const parent = reference.getParentIfKind(SyntaxKind.CallExpression); if (!parent) continue; parentBasePath = this.calculateRoutePath(parent); break; } return parentBasePath + mountPath; } /** * 规范化路径,确保路径以 / 开头,且不以 / 结尾 * @param path 路径 * @returns 规范化后的路径 */ normalizePath(path$1) { if (path$1 && !path$1.startsWith("/")) path$1 = `/${path$1}`; if (path$1.endsWith("/")) path$1 = path$1.slice(0, -1); return path$1; } /** * 从 AST 节点中提取字符串字面量值 * @param node 要提取字符串值的节点 * @returns 字符串值,如果提取失败则返回undefined */ getStringValueFromNode(node) { const nodeType = node.getType(); if (nodeType.isString() || nodeType.isStringLiteral()) return nodeType.getLiteralValue(); return void 0; } /** * 将 Hono 路径参数格式转换为 OpenAPI 格式: `/users/:id` -> `/users/{id}` */ convertHonoPathToOpenAPI(path$1) { return path$1.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "{$1}"); } /** * 从路由处理函数中提取函数名 * @param args 参数 * @returns 函数名,如果提取失败则返回 undefined */ extractHandlerName(args) { const handlerArg = args[args.length - 1]; if (handlerArg.isKind(SyntaxKind.FunctionExpression)) { const functionName = handlerArg.getName(); if (functionName) return functionName; } if (handlerArg.isKind(SyntaxKind.Identifier)) return handlerArg.getText(); if (handlerArg.isKind(SyntaxKind.PropertyAccessExpression)) { const propertyName = handlerArg.getName(); if (propertyName) return propertyName; } return void 0; } /** * 根据HTTP方法和路径生成operationId * @param method HTTP方法 * @param path 路径 * @returns 生成的operationId */ generateOperationIdFromRoute(method, path$1) { const pathParts = path$1.split("/").filter((part) => part.length > 0).map((part) => { if (part.startsWith("{") && part.endsWith("}")) { const paramName = part.slice(1, -1); return `By${pascal(paramName)}`; } return pascal(part); }); const pathString = pathParts.join(""); return `${method}${pathString}`; } }; //#endregion //#region src/analyzers/HonoZodValidatorCodeAnalyzer.ts /** * Hono Zod 验证中间件代码分析器,负责从 Hono 路由中的 zodValidator 中间件调用中提取 Zod schema * 并转换为 OpenAPI 的参数和请求体定义 */ var HonoZodValidatorCodeAnalyzer = class extends CodeAnalyzer { /** * 分析节点中的 zodValidator 调用,提取 Zod schema * @param node 节点 * @returns 提取的 Zod schemas */ async analyze(node) { const zodValidatorCalls = this.findAllZodValidatorCalls(node); if (zodValidatorCalls.length === 0) return {}; const operationData = {}; for (const zodValidatorCall of zodValidatorCalls) { const singleResult = await this.analyzeSingleZodValidatorCall(zodValidatorCall); if (singleResult.requestBody) operationData.requestBody = singleResult.requestBody; if (singleResult.parameters) operationData.parameters = [...operationData.parameters || [], ...singleResult.parameters]; } return operationData; } /** * 分析单个 zodValidator 调用 * @param zodValidatorCall zodValidator 调用节点 * @returns 提取的 Zod schemas */ async analyzeSingleZodValidatorCall(zodValidatorCall) { const sourceFile = zodValidatorCall.getSourceFile(); const filePath = sourceFile.getFilePath(); const lineNumber = zodValidatorCall.getStartLineNumber(); const location = `${filePath}:${lineNumber}`; const registry = SchemaRegistry.getInstance(); const schemaInfo = registry.get(location); if (!schemaInfo) return {}; const args = zodValidatorCall.getArguments(); const targetArg = args[0]; const target = targetArg.getLiteralText(); const schemaArg = args[1]; let schemaName; if (schemaArg.isKind(SyntaxKind.Identifier)) schemaName = schemaArg.getText(); return this.convertSingleTargetSchemaToOperationData(schemaInfo, target, schemaName); } /** * 从 Zod schema 创建 OpenAPI RequestBody * @param schema JSON schema * @param schemaName schema 名称 * @returns 创建的 OpenAPI RequestBody */ createRequestBodyFromSchema(schema, schemaName) { const mediaType = this.context.options.defaultRequestBodyMediaType; if (schemaName && !this.context.schemas.has(schemaName)) this.context.schemas.set(schemaName, schema); const schemaObject = schemaName ? { $ref: `#/components/schemas/${schemaName}` } : schema; return { content: { [mediaType]: { schema: schemaObject } } }; } /** * 从 JSON Schema 创建 OpenAPI Parameters * @param schema JSON schema * @param paramIn 参数位置 * @returns 创建的 OpenAPI Parameters */ createParametersFromSchema(schema, paramIn) { const parameters = []; if (schema.type === "object" && schema.properties) for (const [propName, propSchema] of Object.entries(schema.properties)) { const isRequired = schema.required?.includes(propName) || false; const cleanSchema = { ...propSchema }; const parameterProps = {}; if (cleanSchema.description) { parameterProps.description = cleanSchema.description; delete cleanSchema.description; } if (cleanSchema.deprecated) { parameterProps.deprecated = cleanSchema.deprecated; delete cleanSchema.deprecated; } if (cleanSchema.allowEmptyValue) { parameterProps.allowEmptyValue = cleanSchema.allowEmptyValue; delete cleanSchema.allowEmptyValue; } if (cleanSchema.example) { parameterProps.example = cleanSchema.example; delete cleanSchema.example; } if (cleanSchema.examples) { parameterProps.examples = cleanSchema.examples; delete cleanSchema.examples; } const parameter = { name: propName, in: paramIn, required: paramIn === "path" ? true : isRequired, schema: cleanSchema, ...parameterProps }; parameters.push(parameter); } return parameters; } /** * 查找节点中的所有 zodValidator 调用 * @param node 节点 * @returns 所有 zodValidator 调用节点的数组 */ findAllZodValidatorCalls(node) { const zodValidatorCalls = []; const expression = node.getFirstChildByKindOrThrow(SyntaxKind.CallExpression); const args = expression.getArguments(); for (const arg of args) if (arg.isKind(SyntaxKind.CallExpression)) { const expression$1 = arg.getExpression(); if (expression$1.isKind(SyntaxKind.Identifier) && expression$1.getText() === "zodValidator") zodValidatorCalls.push(arg); } return zodValidatorCalls; } /** * 将单个 target 的 Schema 信息转换为 OperationData * @param schemaInfo 运行时 Schema 信息 * @param target zodValidator 的 target * @param schemaName 从代码中提取的 Schema 名称 * @returns 转换后的 OperationData */ convertSingleTargetSchemaToOperationData(schemaInfo, target, schemaName) { const operationData = {}; let schema; let paramIn; switch (target) { case "json": case "form": schema = schemaInfo.schemas.body; if (schema) operationData.requestBody = this.createRequestBodyFromSchema(schema, schemaName); break; case "query": schema = schemaInfo.schemas.query; paramIn = "query"; break; case "param": schema = schemaInfo.schemas.params; paramIn = "path"; break; case "header": schema = schemaInfo.schemas.headers; paramIn = "header"; break; } if (schema && paramIn) operationData.parameters = this.createParametersFromSchema(schema, paramIn); return operationData; } }; //#endregion //#region src/analyzers/HonoFrameworkAnalyzer.ts /** * Hono框架分析器,用于分析Hono应用的各种节点类型。 */ var HonoFrameworkAnalyzer = class extends FrameworkAnalyzer { frameworkName = "Hono"; codeAnalyzerRegistry; constructor(context) { super(context); this.codeAnalyzerRegistry = new CodeAnalyzerRegistry(); const defaultAnalyzers = [HonoRouteCodeAnalyzer, HonoZodValidatorCodeAnalyzer]; const analyzers = [...defaultAnalyzers, ...context.options.customHonoCodeAnalyzers ?? []];