UNPKG

api-morph

Version:

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

137 lines (133 loc) 3.97 kB
import { createRequire } from "node:module"; import z from "zod/v4"; import path from "node:path"; //#region src/registry/SchemaRegistry.ts /** * Schema 注册中心,管理运行时收集的 Zod Schema 信息 */ var SchemaRegistry = class SchemaRegistry { static instance; schemas = /* @__PURE__ */ new Map(); /** * 获取单例实例 */ static getInstance() { if (!SchemaRegistry.instance) SchemaRegistry.instance = new SchemaRegistry(); return SchemaRegistry.instance; } /** * 注册 Schema 信息 * @param location 调用位置(文件路径:行号) * @param schemas Schema 对象 */ register(location, schemas) { const schemaInfo = { location, schemas: {} }; if (schemas.body) schemaInfo.schemas.body = z.toJSONSchema(schemas.body); if (schemas.query) schemaInfo.schemas.query = z.toJSONSchema(schemas.query); if (schemas.params) schemaInfo.schemas.params = z.toJSONSchema(schemas.params); if (schemas.headers) schemaInfo.schemas.headers = z.toJSONSchema(schemas.headers); this.schemas.set(location, schemaInfo); } /** * 根据位置获取 Schema 信息 * @param location 文件位置(文件路径:行号) * @returns Schema 信息 */ get(location) { return this.schemas.get(location); } /** * 清空所有已注册的 Schema */ clear() { this.schemas.clear(); } }; /** * 获取调用栈中的调用位置信息 * @param skipFrames 跳过的栈帧数量 * @returns 调用位置字符串(文件路径:行号) */ function getCallLocation(skipFrames = 3) { const stack = (/* @__PURE__ */ new Error()).stack; if (!stack) return "unknown"; const lines = stack.split("\n"); for (let i = skipFrames; i < lines.length; i++) { const line = lines[i]; if (line.includes("at ") && !line.includes("zodValidator") && !line.includes("SchemaRegistry")) { const patterns = [/at .* \((.+):(\d+):(\d+)\)/, /at (.+):(\d+):(\d+)/]; for (const pattern of patterns) { const match = line.match(pattern); if (match?.[1] && match[2]) return `${match[1]}:${match[2]}`; } } } return "unknown"; } //#endregion //#region src/core/swagger.ts /** * 获取 Swagger UI 静态资源目录。 * @returns Swagger UI 静态资源目录。 * * @category Core */ function getSwaggerUIAssetDir() { const require = createRequire(import.meta.url); const packageJsonPath = require.resolve("swagger-ui-dist/package.json"); const assetDir = path.dirname(packageJsonPath); return assetDir; } /** * 生成 Swagger UI 的 HTML 字符串。 * @param options 选项。 * @returns 完整的 Swagger UI HTML 字符串。 * * @category Core */ function generateSwaggerUIHTML(options = {}) { const { url = "/openapi.json", title = "Swagger UI", customCss = "", customJs = "", persistAuthorization = false } = options; const html = `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>${title}</title> <link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32"> <link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16"> <link rel="stylesheet" href="./swagger-ui.css"> <link rel="stylesheet" href="./index.css"> ${customCss ? `<style>\n${customCss}\n</style>` : ""} </head> <body> <div id="swagger-ui"></div> <script src="./swagger-ui-bundle.js"><\/script> <script src="./swagger-ui-standalone-preset.js"><\/script> <script> window.onload = () => { const ui = SwaggerUIBundle({ url: "${url}", dom_id: "#swagger-ui", deepLinking: true, presets: [ SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset ], plugins: [ SwaggerUIBundle.plugins.DownloadUrl ], layout: "StandaloneLayout", persistAuthorization: ${persistAuthorization} }); window.ui = ui; }; ${customJs} <\/script> </body> </html>`; return html; } //#endregion export { SchemaRegistry, generateSwaggerUIHTML, getCallLocation, getSwaggerUIAssetDir };