UNPKG

api-morph

Version:

A TypeScript-first OpenAPI document generator that analyzes your code and generates comprehensive OpenAPI documentation.

1,947 lines (1,917 loc) 106 kB
import { createRequire } from "node:module"; import { SyntaxKind } from "typescript"; import { assign, camel, cloneDeep, isPlainObject, pascal } from "radashi"; import z$1, { z } from "zod/v4"; import YAML from "yaml"; import { Project, SyntaxKind as SyntaxKind$1 } from "ts-morph"; import * as mimeTypes from "mime-types"; import http from "node:http"; import deepmerge from "deepmerge"; import path, { basename, extname } from "node:path"; //#region src/constants.ts let JSDocTagName = /* @__PURE__ */ function(JSDocTagName$1) { JSDocTagName$1["OPERATION"] = "operation"; JSDocTagName$1["TAGS"] = "tags"; JSDocTagName$1["SUMMARY"] = "summary"; JSDocTagName$1["DESCRIPTION"] = "description"; JSDocTagName$1["EXTERNAL_DOCS"] = "externalDocs"; JSDocTagName$1["OPERATION_ID"] = "operationId"; JSDocTagName$1["PARAMETER"] = "parameter"; JSDocTagName$1["REQUEST_BODY"] = "requestBody"; JSDocTagName$1["RESPONSE"] = "response"; JSDocTagName$1["CALLBACK"] = "callback"; JSDocTagName$1["DEPRECATED"] = "deprecated"; JSDocTagName$1["SECURITY"] = "security"; JSDocTagName$1["SERVER"] = "server"; JSDocTagName$1["EXTENSIONS"] = "extensions"; JSDocTagName$1["RESPONSES_EXTENSIONS"] = "responsesExtensions"; JSDocTagName$1["HIDDEN"] = "hidden"; return JSDocTagName$1; }({}); const VALID_HTTP_METHODS = [ "get", "post", "put", "delete", "patch", "options", "head", "trace" ]; const VALID_PARAMETER_IN = [ "query", "header", "path", "cookie" ]; //#endregion //#region src/core/ASTAnalyzer.ts /** * AST分析器抽象基类,用于分析代码结构并提取API信息 */ var ASTAnalyzer = class { /** * 创建AST分析器实例 * @param context 解析上下文 */ constructor(context) { this.context = context; } }; //#endregion //#region src/core/ASTAnalyzerRegistry.ts /** * AST分析器注册表,用于管理和查找AST分析器 */ var ASTAnalyzerRegistry = class { /** 分析器名称到实例的映射 */ nameToAnalyzer = /* @__PURE__ */ new Map(); /** * 注册AST分析器 * @param analyzer AST分析器实例 */ register(analyzer) { if (this.nameToAnalyzer.has(analyzer.name)) throw new Error(`AST分析器名称冲突:分析器 "${analyzer.name}" 已经被注册。`); this.nameToAnalyzer.set(analyzer.name, analyzer); } /** * 获取所有注册的分析器 * @returns 所有分析器数组 */ getAllAnalyzers() { return Array.from(this.nameToAnalyzer.values()); } }; //#endregion //#region src/core/FrameworkAnalyzer.ts /** * 框架分析器抽象基类,用于分析特定框架的代码结构并提取API信息 */ var FrameworkAnalyzer = class { /** * 创建框架分析器实例 * @param context 解析上下文 */ constructor(context) { this.context = context; } }; //#endregion //#region src/core/FrameworkAnalyzerRegistry.ts /** * 框架分析器注册表,用于管理和查找框架分析器 */ var FrameworkAnalyzerRegistry = class { /** 框架名称到分析器实例的映射 */ nameToAnalyzer = /* @__PURE__ */ new Map(); /** * 注册框架分析器 * @param analyzer 框架分析器实例 */ register(analyzer) { if (this.nameToAnalyzer.has(analyzer.frameworkName)) throw new Error(`框架分析器名称冲突:框架 "${analyzer.frameworkName}" 已经被注册。`); this.nameToAnalyzer.set(analyzer.frameworkName, analyzer); } /** * 按注册顺序尝试每个框架分析器,返回第一个能够处理的结果 * @param node AST节点 * @returns 第一个能够处理该节点的框架分析器,如果没有找到返回null */ getFirstMatchingAnalyzer(node) { for (const analyzer of this.nameToAnalyzer.values()) if (analyzer.canAnalyze(node)) return analyzer; return null; } /** * 获取所有注册的框架分析器 * @returns 所有框架分析器的数组 */ getAllAnalyzers() { return Array.from(this.nameToAnalyzer.values()); } }; //#endregion //#region src/builders/CallbackBuilder.ts /** * 回调构建器,用于构建 OpenAPI CallbackObject */ 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 */ 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/builders/OpenAPIBuilder.ts /** * 文档构建器,用于构建完整的 OpenAPI 文档 */ var OpenAPIBuilder = class { document; 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); } /** * 设置 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 对象。 * @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]) 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/utils/string.ts /** * 移除字符串两端的引号(单引号或双引号)。 * @param text 可能包含引号的文本。 * @returns 移除引号后的文本。 */ function removeQuotes(text) { const trimmed = text.trim(); if (trimmed.startsWith("'") && trimmed.endsWith("'") && trimmed.length >= 2) return trimmed.slice(1, -1); if (trimmed.startsWith("\"") && trimmed.endsWith("\"") && trimmed.length >= 2) return trimmed.slice(1, -1); return trimmed; } /** * 对字符串进行反转义处理,将 `\"` 转为 `"`,`\'` 转为 `'`,`\\` 转为 `\`,`\{` 转为 `{`,`\}` 转为 `}`。 * @param text 可能包含转义字符的文本。 * @returns 反转义后的文本。 */ function unescapeString(text) { return text.replace(/\\("|'|\\|\{|\})/g, "$1"); } /** * 将字符串按空白和引号分割为词元,支持单双引号、大括号和转义。 * @param text 输入的字符串文本 * @returns 词元数组 */ function tokenizeString(text) { const trimmedLine = text.trim(); if (!trimmedLine) return []; const values = []; const valueRegex = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\{(?:[^{}\\]|\\.)*\}|[^\s'"]+/g; let currentMatch; while (true) { currentMatch = valueRegex.exec(trimmedLine); if (currentMatch === null) break; const rawValue = currentMatch[0]; let cleanedValue; if (rawValue.startsWith("{") && rawValue.endsWith("}") && rawValue.length >= 2) cleanedValue = unescapeString(rawValue.slice(1, -1)).trim(); else cleanedValue = unescapeString(removeQuotes(rawValue)).trim(); if (cleanedValue.length > 0) values.push(cleanedValue); } return values; } //#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-"); } //#endregion //#region src/builders/OperationBuilder.ts /** * 操作构建器,用于构建 OpenAPI OperationObject */ 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; /* v8 ignore next */ if (!operation.responses) operation.responses = {}; 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 */ 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 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 */ 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 */ 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; /* v8 ignore next */ if (!requestBody.content) requestBody.content = {}; 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 */ 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 */ 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 */ 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/helpers/jsdoc.ts /** * 查找标签中的 JSDocLink 节点 * @param tag JSDoc 标签对象 * @returns JSDocLink 节点数组 */ function findSchemaJSDocLinks(tag) { const comment = tag.getComment(); const schemaLinks = []; if (!Array.isArray(comment)) return schemaLinks; for (let i = 0; i < comment.length; i++) { const item = comment[i]; if (item?.isKind(SyntaxKind$1.JSDocText)) { const nextItem = comment[i + 1]; if (nextItem?.isKind(SyntaxKind$1.JSDocLink)) schemaLinks.push(nextItem); } } return schemaLinks; } //#endregion //#region src/helpers/mediaType.ts /** * 标准化 media type,支持简写转换和验证 * @param value 要检查的字符串(如 'json', 'application/json') * @returns 标准化的 media type 或 null * * @example * ```typescript * normalizeMediaType('json') // 'application/json' * normalizeMediaType('application/json') // 'application/json' * normalizeMediaType('application/vnd.api+json') // 'application/vnd.api+json' * normalizeMediaType('invalid') // null * ``` */ function normalizeMediaType(value) { const result = mimeTypes.contentType(value); if (result) return result.split(";")[0].trim(); const mediaTypePattern = /^[a-zA-Z][a-zA-Z0-9][a-zA-Z0-9!#$&\-^]*\/[a-zA-Z0-9][a-zA-Z0-9!#$&\-^+]*$/; if (mediaTypePattern.test(value)) return value; return null; } //#endregion //#region src/helpers/zod.ts /** * 判断节点是否为 ZodType。 * @param node 要判断的节点。 * @returns 如果类型是 ZodType 则返回 true,否则返回 false。 */ function isZodType(node) { const nodeType = node.getType(); return nodeType.getText().includes("Zod") && !!nodeType.getProperties().find((p) => p.getName() === "_zod"); } /** * 获取 ZodError 的错误信息。 * @param error ZodError 实例。 * @returns 错误信息。 */ function getZodErrorMessage(error) { const flattenedError = z.flattenError(error); const formErrors = flattenedError.formErrors; const fieldErrors = Object.values(flattenedError.fieldErrors).flat(); const message = formErrors[0] ?? fieldErrors[0]; return message; } //#endregion //#region src/core/TagParser.ts /** * 标签解析器接口,所有标签解析器必须实现此接口 */ var TagParser = class { /** * 创建标签解析器实例。 * @param context 解析上下文。 */ constructor(context) { this.context = context; } /** * 获取解析器支持的标签名称。 * @returns 解析器支持的标签名称数组。 */ getTags() { return this.tags; } /** * 获取标签的完整多行内容。 * @param tag JSDoc 标签对象。 * @returns 标签的所有行文本内容,保留缩进格式,去掉星号和星号前空格,去掉尾部连续的空行。 */ extractTagContentLines(tag) { const tagName = tag.getTagName(); const tagNamePattern = new RegExp(`@${tagName}`); const lines = tag.getFullText().replace(tagNamePattern, "").split("\n").map((line, index) => index === 0 ? line.trim() : line.replace(/^\s*\*\s?/, "").trimEnd()); while (lines.length > 0 && lines[lines.length - 1].trim() === "") lines.pop(); return lines; } /** * 解析标签 inline 和 YAML 参数。 * @param tag JSDoc 标签对象。 * @param options 解析选项。 * @returns 返回一个对象,包含: * - `inline`: inline 参数数组(通常为标签行的参数部分) * - `yaml`: YAML 参数对象(如果存在 YAML 参数,否则为 null) * - `rawText`: 标签的原始注释文本内容 */ async parseTagParamsWithYaml(tag, options = { preprocessJSDocLinks: true }) { let processedTag = tag; if (options.preprocessJSDocLinks) processedTag = await this.preprocessJSDocLinks(processedTag); const rawText = tag.getCommentText()?.trim() ?? ""; const lines = this.extractTagContentLines(processedTag); if (lines.length === 0) return { inline: [], rawText }; const firstLine = lines[0]; const inline = firstLine ? tokenizeString(firstLine) : []; let parsedYaml; if (lines.length > 1) { const yamlLines = lines.slice(1); const yamlContent = yamlLines.join("\n"); if (yamlContent) try { const parsed = YAML.parse(yamlContent); if (isPlainObject(parsed)) parsedYaml = parsed; } catch {} } return { inline, yaml: parsedYaml, rawText }; } /** * 预处理JSDoc链接,将 {@link xxx} 替换为实际的JSON schema引用 * @param tag JSDoc 标签对象。 */ async preprocessJSDocLinks(tag) { let replacedText = tag.getFullText(); const jsDocLinks = findSchemaJSDocLinks(tag); if (jsDocLinks.length === 0) return tag; for (const jsDocLink of jsDocLinks) { const identifier = jsDocLink.getFirstDescendantByKind(SyntaxKind.Identifier); if (!identifier) continue; const definitionNode = identifier.getDefinitionNodes()[0]; if (!definitionNode) continue; if (!isZodType(definitionNode)) continue; const filePath = definitionNode.getSourceFile().getFilePath(); const module = await import(filePath); const identifierName = identifier.getText(); const schema = z$1.toJSONSchema(module[identifierName]); if (!this.context.schemas.has(identifierName)) this.context.schemas.set(identifierName, schema); const schemaRef = `{ $ref: "#/components/schemas/${identifierName}" }`; replacedText = replacedText.replace(jsDocLink.getText(), schemaRef); } return tag.replaceWithText(replacedText); } }; //#endregion //#region src/parsers/CallbackTagParser.ts /** * 回调标签解析器,处理 `@callback` 标签 */ var CallbackTagParser = class extends TagParser { tags = [JSDocTagName.CALLBACK]; /** * 解析 JSDoc 标签。 * @param tag JSDoc 标签对象。 * @returns 解析结果。 */ async parse(tag) { const params = await this.parseTagParamsWithYaml(tag); const transformedParams = this.transformParams(params); const validatedParams = this.validateParams(transformedParams); return this.buildCallback(validatedParams); } /** * 转换参数的钩子方法,子类可以重写此方法来完全控制参数转换。 * @param params 参数对象。 * @returns 转换后的参数对象。 */ transformParams(params) { const { inline, yaml } = params; const [callbackName] = inline; return { callbackName, yaml }; } /** * 验证回调标签的参数。 * @param params 参数对象。 * @returns 验证后的参数对象。 */ validateParams(params) { const message = `\n正确格式:\n @${JSDocTagName.CALLBACK} <callbackName>\n [expression: string]: PathItemObject\n [key: \`x-\${string}\`]: any\n`; const schema = z.object({ callbackName: z.string(`@${JSDocTagName.CALLBACK} 标签 callbackName 不能为空`).regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, { error: (iss) => `@${JSDocTagName.CALLBACK} 标签 callbackName 格式不正确:"${iss.input}",必须是有效的标识符(以字母或下划线开头,只能包含字母、数字和下划线)` }), yaml: z.record(z.string(), z.unknown(), `@${JSDocTagName.CALLBACK} 标签必须包含 YAML 参数`) }); const { success, data, error } = schema.safeParse(params); if (!success) throw new Error(getZodErrorMessage(error) + message); return data; } /** * 构建回调对象。 * @param params 回调参数。 * @returns 构建的回调对象。 */ buildCallback(params) { const { callbackName, yaml } = params; const callbackBuilder = new CallbackBuilder(); for (const [key, value] of Object.entries(yaml)) if (isExtensionKey(key)) callbackBuilder.addExtension(key, value); else callbackBuilder.addExpression(key, value); return { callback: { name: callbackName, callback: callbackBuilder.build() } }; } }; //#endregion //#region src/parsers/DeprecatedTagParser.ts /** * 废弃标记解析器,处理 `@deprecated` 标签 */ var DeprecatedTagParser = class extends TagParser { tags = [JSDocTagName.DEPRECATED]; /** * 解析 JSDoc 标签。 * @param tag JSDoc 标签对象。 * @returns 解析结果。 */ async parse(tag) { const params = await this.parseTagParamsWithYaml(tag); const transformedParams = this.transformParams(params); this.validateParams(transformedParams); return this.buildResult(); } /** * 转换参数的钩子方法,子类可以重写此方法来完全控制参数转换。 * @param params 参数对象。 * @returns 转换后的参数对象。 */ transformParams(params) { const { inline, yaml } = params; return { inline, yaml }; } /** * 验证废弃标签的参数。 * @param params 参数对象。 */ validateParams(params) { const message = `\n正确格式:\n @${JSDocTagName.DEPRECATED}\n`; if (params.inline.length > 0 || params.yaml) throw new Error(`@${JSDocTagName.DEPRECATED} 标签不应包含任何参数${message}`); } /** * 构建解析结果。 * @returns 构建的解析结果。 */ buildResult() { return { deprecated: true }; } }; //#endregion //#region src/parsers/DescriptionTagParser.ts /** * 描述解析器,处理 `@description` 标签 */ var DescriptionTagParser = class extends TagParser { tags = [JSDocTagName.DESCRIPTION]; /** * 解析 JSDoc 标签。 * @param tag JSDoc 标签对象。 * @returns 解析结果。 */ async parse(tag) { const params = await this.parseTagParamsWithYaml(tag, { preprocessJSDocLinks: false }); const transformedParams = this.transformParams(params); const validatedParams = this.validateParams(transformedParams); return this.buildResult(validatedParams); } /** * 转换参数的钩子方法,子类可以重写此方法来完全控制参数转换。 * @param params 参数对象。 * @returns 转换后的参数对象。 */ transformParams(params) { const { rawText } = params; return { description: rawText }; } /** * 验证描述标签的参数。 * @param params 参数对象。 * @returns 验证后的参数对象。 */ validateParams(params) { const message = `\n正确格式:\n @${JSDocTagName.DESCRIPTION} <description>\n`; const schema = z$1.object({ description: z$1.string().min(1, `@${JSDocTagName.DESCRIPTION} 标签 description 不能为空`) }); const { success, data, error } = schema.safeParse(params); if (!success) throw new Error(getZodErrorMessage(error) + message); return data; } /** * 构建解析结果。 * @param params 验证后的参数。 * @returns 构建的解析结果。 */ buildResult(params) { return params; } }; //#endregion //#region src/parsers/ExtensionsTagParser.ts /** * 扩展解析器,处理 `@extensions` 标签 */ var ExtensionsTagParser = class extends TagParser { tags = [JSDocTagName.EXTENSIONS]; /** * 解析 JSDoc 标签。 * @param tag JSDoc 标签对象。 * @returns 解析结果。 */ async parse(tag) { const params = await this.parseTagParamsWithYaml(tag, { preprocessJSDocLinks: false }); const transformedParams = this.transformParams(params); const validatedParams = this.validateParams(transformedParams); return this.buildResult(validatedParams); } /** * 转换参数的钩子方法,子类可以重写此方法来完全控制参数转换。 * @param params 参数对象。 * @returns 转换后的参数对象。 */ transformParams(params) { const { inline, yaml } = params; return { inline, yaml }; } /** * 验证扩展标签的参数。 * @param params 参数对象。 * @returns 验证后的参数对象。 */ validateParams(params) { const message = `\n正确格式:\n @${JSDocTagName.EXTENSIONS}\n [key: \`x-\${string}\`]: any\n`; if (params.inline.length > 0) throw new Error(`@${JSDocTagName.EXTENSIONS} 标签不应包含任何 inline 参数${message}`); const schema = z$1.record(z$1.templateLiteral(["x-", z$1.string()]), z$1.unknown(), { error: (iss) => iss.code === "invalid_key" ? `@${JSDocTagName.EXTENSIONS} 标签的扩展名必须以 "x-" 开头` : `@${JSDocTagName.EXTENSIONS} 标签必须包含 YAML 参数` }); const { success, data, error } = schema.safeParse(params.yaml); if (!success) throw new Error(getZodErrorMessage(error) + message); return data; } /** * 构建解析结果。 * @param extensions 验证后的扩展数据。 * @returns 构建的解析结果。 */ buildResult(extensions) { return { extensions }; } }; //#endregion //#region src/parsers/ExternalDocsTagParser.ts /** * 外部文档标签解析器,处理 `@externalDocs` 标签 */ var ExternalDocsTagParser = class extends TagParser { tags = [JSDocTagName.EXTERNAL_DOCS]; /** * 解析 JSDoc 标签。 * @param tag JSDoc 标签对象。 * @returns 解析结果。 */ async parse(tag) { const params = await this.parseTagParamsWithYaml(tag); const transformedParams = this.transformParams(params); const validatedParams = this.validateParams(transformedParams); return this.buildExternalDocs(validatedParams); } /** * 转换参数的钩子方法,子类可以重写此方法来完全控制参数转换。 * @param params 参数对象。 * @returns 转换后的参数对象。 */ transformParams(params) { const { inline, yaml } = params; const [url, description] = inline; return { url, description, yaml }; } /** * 验证外部文档标签的参数。 * @param inline 内联参数数组。 * @param params 参数对象。 * @returns 验证后的参数对象。 */ validateParams(params) { const message = `\n正确格式:\n @${JSDocTagName.EXTERNAL_DOCS} <url> [description]\n description?: string\n [key: \`x-\${string}\`]: any\n`; const schema = z.object({ url: z.url({ error: (iss) => iss.input === void 0 ? `@${JSDocTagName.EXTERNAL_DOCS} 标签 url 不能为空` : `@${JSDocTagName.EXTERNAL_DOCS} 标签提供的 url 格式无效: "${iss.input}"` }), description: z.string().optional(), yaml: z.record(z.string(), z.unknown()).nullable().optional() }); const { success, data, error } = schema.safeParse(params); if (!success) throw new Error(getZodErrorMessage(error) + message); return data; } /** * 构建外部文档对象。 * @param params 外部文档参数。 * @returns 构建的外部文档对象。 */ buildExternalDocs(params) { const { url, yaml, description } = params; const externalDocsBuilder = new ExternalDocsBuilder(); externalDocsBuilder.setUrl(url); if (description !== void 0) externalDocsBuilder.setDescription(description); if (yaml) { for (const [key, value] of Object.entries(yaml)) if (isExtensionKey(key)) externalDocsBuilder.addExtension(key, value); } return { externalDocs: externalDocsBuilder.build() }; } }; //#endregion //#region src/parsers/OperationIdTagParser.ts /** * 操作ID解析器,处理 `@operationId` 标签 */ var OperationIdTagParser = class extends TagParser { tags = [JSDocTagName.OPERATION_ID]; /** * 解析 JSDoc 标签。 * @param tag JSDoc 标签对象。 * @returns 解析结果。 */ async parse(tag) { const params = await this.parseTagParamsWithYaml(tag); const transformedParams = this.transformParams(params); const validatedParams = this.validateParams(transformedParams); return this.buildResult(validatedParams); } /** * 转换参数的钩子方法,子类可以重写此方法来完全控制参数转换。 * @param params 参数对象。 * @returns 转换后的参数对象。 */ transformParams(params) { const { inline } = params; const [operationId] = inline; return { operationId }; } /** * 验证操作ID标签的参数。 * @param params 参数对象。 * @returns 验证后的参数对象。 */ validateParams(params) { const message = `\n正确格式:\n @${JSDocTagName.OPERATION_ID} <operationId>\n`; const schema = z$1.object({ operationId: z$1.string(`@${JSDocTagName.OPERATION_ID} 标签 operationId 不能为空`).regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, { error: (iss) => `@${JSDocTagName.OPERATION_ID} 标签 operationId 格式不正确:"${iss.input}",操作ID必须是有效的标识符(以字母或下划线开头,只能包含字母、数字和下划线)` }) }); const { success, data, error } = schema.safeParse(params); if (!success) throw new Error(getZodErrorMessage(error) + message); return data; } /** * 构建解析结果。 * @param params 验证后的参数。 * @returns 构建的解析结果。 */ buildResult(params) { return params; } }; //#endregion //#region src/parsers/OperationTagParser.ts /**∏ * 操作标签解析器,处理 `@operation` 标签 */ var OperationTagParser = class extends TagParser { tags = [JSDocTagName.OPERATION]; /** * 解析 JSDoc 标签。 * @param tag JSDoc 标签对象。 * @returns 解析结果。 */ async parse(tag) { const params = await this.parseTagParamsWithYaml(tag); const transformedParams = this.transformParams(params); const validatedParams = this.validateParams(transformedParams); return this.buildResult(validatedParams); } /** * 转换参数的钩子方法,子类可以重写此方法来完全控制参数转换。 * @param params 参数对象。 * @returns 转换后的参数对象。 */ transformParams(params) { const { inline } = params; if (inline.length === 0) return {}; if (inline.length === 1) { const param = inline[0]; if (param.startsWith("/")) return { path: param }; if (VALID_HTTP_METHODS.includes(param.toLowerCase())) return { method: param.toLowerCase() }; return { method: param }; } const [method, path$1] = inline; return { method, path: path$1 }; } /** * 验证操作标签的参数。 * @param params 参数对象。 * @returns 验证后的参数对象。 */ validateParams(params) { const message = `\n正确格式:\n @${JSDocTagName.OPERATION} <METHOD> <path>\n`; if (this.context.options.enableASTAnalysis) { const schema = z$1.object({ method: z$1.enum(VALID_HTTP_METHODS, { error: (iss) => iss.input === void 0 ? `@${JSDocTagName.OPERATION} 标签 method 不能为空` : `@${JSDocTagName.OPERATION} 标签包含不支持的 HTTP 方法:"${iss.input}",支持的方法有:${VALID_HTTP_METHODS.join(", ")}` }).optional(), path: z$1.string(`@${JSDocTagName.OPERATION} 标签 path 不能为空`).startsWith("/", { error: (iss) => `@${JSDocTagName.OPERATION} 标签 path 格式不正确:"${iss.input}",路径必须以 "/" 开头` }).optional() }); const { success, data, error } = schema.safeParse(params); if (!success) throw new Error(getZodErrorMessage(error) + message); return data; } else { const schema = z$1.object({ method: z$1.enum(VALID_HTTP_METHODS, { error: (iss) => iss.input === void 0 ? `@${JSDocTagName.OPERATION} 标签 method 不能为空` : `@${JSDocTagName.OPERATION} 标签包含不支持的 HTTP 方法:"${iss.input}",支持的方法有:${VALID_HTTP_METHODS.join(", ")}` }), path: z$1.string(`@${JSDocTagName.OPERATION} 标签 path 不能为空`).startsWith("/", { error: (iss) => `@${JSDocTagName.OPERATION} 标签 path 格式不正确:"${iss.input}",路径必须以 "/" 开头` }) }); const { success, data, error } = schema.safeParse(params); if (!success) throw new Error(getZodErrorMessage(error) + message); return data; } } /** * 构建解析结果。 * @param params 验证后的参数。 * @returns 构建的解析结果。 */ buildResult(params) { return params; } }; //#endregion //#region src/parsers/ParameterTagParser.ts /** * 参数标签解析器,处理 `@parameter` 标签 */ var ParameterTagParser = class extends TagParser { tags = [JSDocTagName.PARAMETER]; /** * 解析 JSDoc 标签。 * @param tag JSDoc 标签对象。 * @returns 解析结果。 */ async parse(tag) { const params = await this.parseTagParamsWithYaml(tag); const transformedParams = this.transformParams(params); const validatedParams = this.validateParams(transformedParams); return this.buildParameter(validatedParams); } /** * 转换参数的钩子方法,子类可以重写此方法来完全控制参数转换。 * @param params 参数对象。 * @returns 转换后的参数对象。 */ transformParams(params) { const { inline, yaml } = params; const [name, paramIn, description] = inline; return { name, paramIn, description, yaml }; } /** * 验证参数标签的参数和YAML参数。 * @param params 参数对象。 * @returns 验证后的数据对象。 */ validateParams(params) { const message = `\n正确格式:\n @${JSDocTagName.PARAMETER} <name> <in> [description]\n description?: string\n required?: boolean\n deprecated?: boolean\n allowEmptyValue?: boolean\n style?: string\n explode?: boolean\n allowReserved?: boolean\n schema?: SchemaObject | ReferenceObject\n content?: Record<string, MediaTypeObject>\n [key: \`x-\${string}\`]: any\n`; const schema = z$1.object({ name: z$1.string(`@${JSDocTagName.PARAMETER} 标签 name 不能为空`).regex(/^[a-zA-Z_][a-zA-Z0-9_.-]*$/, { error: (iss) => `@${JSDocTagName.PARAMETER} 标签 name 格式不正确:"${iss.input}",必须是有效的标识符(以字母或下划线开头,只能包含字母、数字、下划线、点和连字符)` }), paramIn: z$1.enum(VALID_PARAMETER_IN, { error: (iss) => iss.input === void 0 ? `@${JSDocTagName.PARAMETER} 标签 in 不能为空` : `@${JSDocTagName.PARAMETER} 标签 in 值不正确:"${iss.input}",支持的值有:${VALID_PARAMETER_IN.join(", ")}` }), description: z$1.string().optional(), yaml: z$1.record(z$1.string(), z$1.unknown()).nullable().optional() }); const { success, data, error } = schema.safeParse(params); if (!success) throw new Error(getZodErrorMessage(error) + message); return data; } /** * 构建参数对象。 * @param params 参数对象。 * @returns 构建的参数对象。 */ buildParameter(params) { const { name, paramIn, description, yaml } = params; const parameterBuilder = new ParameterBuilder(name, paramIn); let finalDescription = description; if (yaml) { if (yaml.description) finalDescription = yaml.description; if (yaml.required !== void 0) parameterBuilder.setRequired(yaml.required); if (yaml.deprecated !== void 0) parameterBuilder.setDeprecated(yaml.deprecated); if (yaml.allowEmptyValue !== void 0) parameterBuilder.setAllowEmptyValue(yaml.allowEmptyValue); if (yaml.style) parameterBuilder.setStyle(yaml.style); if (yaml.explode !== void 0) parameterBuilder.setExplode(yaml.explode); if (yaml.allowReserved !== void 0) parameterBuilder.setAllowReserved(yaml.allowReserved); if (yaml.schema) parameterBuilder.setSchema(yaml.schema); if (yaml.content) Object.entries(yaml.content).forEach(([mediaType, mediaTypeObject]) => { parameterBuilder.addContent(mediaType, mediaTypeObject); }); Object.entries(yaml).forEach(([key, value]) => { if (isExtensionKey(key)) parameterBuilder.addExtension(key, value); }); } if (finalDescription) parameterBuilder.setDescription(finalDescription); return { parameters: [parameterBuilder.build()] }; } }; //#endregion //#region src/parsers/RequestBodyTagParser.ts /** * 请求体标签解析器,处理 `@requestBody` 标签。 * 支持简化语法: `@requestBody [mediaType] [schema] [description]`。 * * 当省略 mediaType 但提供了 schema 时,会自动使用默认的请求体媒体类型。 */ var RequestBodyTagParser = class extends TagParser { tags = [JSDocTagName.REQUEST_BODY]; /** * 解析 JSDoc 标签。 * @param tag JSDoc 标签对象。 * @returns 解析结果。 */ async parse(tag) { const params = await this.parseTagParamsWithYaml(tag); const transformedParams = this.transformParams(params); const validatedParams = this.validateParams(transformedParams); return this.buildRequestBody(validatedParams); } /** * 转换参数的钩子方法,子类可以重写此方法来完全控制参数转换。 * @param params 参数对象。 * @returns 转换后的参数对象。 */ transformParams(params) { const { inline, yaml } = params; const simplified = this.parseSimplifiedSyntax(inline); if (simplified) return {