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