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
JavaScript
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 {