UNPKG

@bluejeans/flexdoc-backend

Version:

FlexDoc backend integration for NestJS and other frameworks

866 lines (855 loc) 36.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.generateFlexDocHTML = generateFlexDocHTML; const nunjucks = __importStar(require("nunjucks")); const path = __importStar(require("path")); const fs = __importStar(require("fs")); const templatesPath = path.join(__dirname, 'templates'); if (!fs.existsSync(templatesPath)) { fs.mkdirSync(templatesPath, { recursive: true }); } const nunjucksEnv = nunjucks.configure(templatesPath, { autoescape: true, trimBlocks: true, lstripBlocks: true, }); nunjucksEnv.addFilter('json', function (obj) { return JSON.stringify(obj); }); nunjucksEnv.addFilter('upper', function (str) { return str.toUpperCase(); }); nunjucksEnv.addFilter('lower', function (str) { return str.toLowerCase(); }); function generateFlexDocHTML(spec, options = {}) { const { title = 'API Documentation', description = 'Interactive API documentation powered by FlexDoc', altDescription = '', theme = 'light', customCss = '', customJs = '', favicon = '', logo = '', specUrl, tagGroups, } = options; const themeConfig = typeof theme === 'object' ? theme : {}; const themeMode = typeof theme === 'string' ? theme : 'light'; if (spec && tagGroups && spec.paths) { const includedTags = new Set(); tagGroups.forEach((group) => { group.tags.forEach((tag) => includedTags.add(tag)); }); const filteredPaths = {}; for (const path in spec.paths) { const pathItem = spec.paths[path]; const filteredPathItem = {}; let hasIncludedOperation = false; for (const method in pathItem) { if (['get', 'post', 'put', 'delete', 'patch', 'options', 'head'].includes(method)) { const operation = pathItem[method]; if (operation.tags && operation.tags.some((tag) => includedTags.has(tag))) { filteredPathItem[method] = operation; hasIncludedOperation = true; } } else { filteredPathItem[method] = pathItem[method]; } } if (hasIncludedOperation) { filteredPaths[path] = filteredPathItem; } } spec.paths = filteredPaths; if (spec.components?.schemas) { const usedSchemaRefs = new Set(); const collectSchemaRefs = (obj) => { if (!obj || typeof obj !== 'object') return; if (obj.$ref && typeof obj.$ref === 'string' && obj.$ref.startsWith('#/components/schemas/')) { const schemaName = obj.$ref.replace('#/components/schemas/', ''); usedSchemaRefs.add(schemaName); if (spec.components?.schemas?.[schemaName]) { collectSchemaRefs(spec.components.schemas[schemaName]); } } if (obj.items) { collectSchemaRefs(obj.items); } if (obj.properties) { Object.values(obj.properties).forEach((prop) => { collectSchemaRefs(prop); }); } if (Array.isArray(obj)) { obj.forEach((item) => collectSchemaRefs(item)); } Object.values(obj).forEach((val) => { if (val && typeof val === 'object') { collectSchemaRefs(val); } }); }; Object.values(filteredPaths).forEach((pathItem) => { collectSchemaRefs(pathItem); }); const filteredSchemas = {}; for (const schemaName of usedSchemaRefs) { if (spec.components.schemas[schemaName]) { filteredSchemas[schemaName] = spec.components.schemas[schemaName]; } } spec.components.schemas = filteredSchemas; } } const specJsonString = spec ? JSON.stringify(spec, null, 2) : ''; let logoUrl = ''; let logoStyle = ''; let logoContainerStyle = ''; let logoContainerClass = ''; let logoAlt = 'Logo'; let logoClickable = false; if (logo) { if (typeof logo === 'string') { logoUrl = logo; } else { logoUrl = logo.url; logoAlt = logo.alt || 'Logo'; logoClickable = logo.clickable || false; const logoStyles = []; if (logo.maxHeight) { logoStyles.push(`max-height: ${typeof logo.maxHeight === 'number' ? `${logo.maxHeight}px` : logo.maxHeight}`); } if (logo.maxWidth) { logoStyles.push(`max-width: ${typeof logo.maxWidth === 'number' ? `${logo.maxWidth}px` : logo.maxWidth}`); } logoStyle = logoStyles.length > 0 ? ` style="${logoStyles.join('; ')}"` : ''; const containerStyles = []; if (logo.backgroundColor) { containerStyles.push(`background-color: ${logo.backgroundColor}`); } if (logo.padding) { if (typeof logo.padding === 'string') { containerStyles.push(`padding: ${logo.padding}`); } else { const vertical = logo.padding.vertical ? typeof logo.padding.vertical === 'number' ? `${logo.padding.vertical}px` : logo.padding.vertical : '0'; const horizontal = logo.padding.horizontal ? typeof logo.padding.horizontal === 'number' ? `${logo.padding.horizontal}px` : logo.padding.horizontal : '0'; containerStyles.push(`padding: ${vertical} ${horizontal}`); } } logoContainerStyle = containerStyles.length > 0 ? ` style="${containerStyles.join('; ')}"` : ''; logoContainerClass = logo.containerClass ? ` ${logo.containerClass}` : ''; } } const specData = spec ? JSON.stringify(spec, null, 2) : null; const specSource = specUrl ? `fetch('${specUrl}').then(r => r.json())` : `Promise.resolve(${specData})`; const themeColors = themeConfig?.colors || {}; const isDarkMode = themeMode === 'dark'; const cssVars = []; if (themeColors.primary) { if (themeColors.primary.main) cssVars.push(`--flexdoc-primary: ${themeColors.primary.main}`); if (themeColors.primary.light) cssVars.push(`--flexdoc-primary-light: ${themeColors.primary.light}`); if (themeColors.primary.dark) cssVars.push(`--flexdoc-primary-dark: ${themeColors.primary.dark}`); } if (themeColors.success) { if (themeColors.success.main) cssVars.push(`--flexdoc-success: ${themeColors.success.main}`); if (themeColors.success.light) cssVars.push(`--flexdoc-success-light: ${themeColors.success.light}`); if (themeColors.success.dark) cssVars.push(`--flexdoc-success-dark: ${themeColors.success.dark}`); } if (themeColors.error) { if (themeColors.error.main) cssVars.push(`--flexdoc-error: ${themeColors.error.main}`); if (themeColors.error.light) cssVars.push(`--flexdoc-error-light: ${themeColors.error.light}`); if (themeColors.error.dark) cssVars.push(`--flexdoc-error-dark: ${themeColors.error.dark}`); } if (themeColors.text) { if (themeColors.text.primary) cssVars.push(`--flexdoc-text-primary: ${themeColors.text.primary}`); if (themeColors.text.secondary) cssVars.push(`--flexdoc-text-secondary: ${themeColors.text.secondary}`); } if (themeColors.gray) { if (themeColors.gray[50]) cssVars.push(`--flexdoc-gray-50: ${themeColors.gray[50]}`); if (themeColors.gray[100]) cssVars.push(`--flexdoc-gray-100: ${themeColors.gray[100]}`); } if (themeColors.border) { if (themeColors.border.dark) cssVars.push(`--flexdoc-border-dark: ${themeColors.border.dark}`); if (themeColors.border.light) cssVars.push(`--flexdoc-border-light: ${themeColors.border.light}`); } if (themeConfig.typography) { const typography = themeConfig.typography; if (typography.fontSize) cssVars.push(`--flexdoc-font-size: ${typography.fontSize}`); if (typography.lineHeight) cssVars.push(`--flexdoc-line-height: ${typography.lineHeight}`); if (typography.fontFamily) cssVars.push(`--flexdoc-font-family: ${typography.fontFamily}`); if (typography.headings) { if (typography.headings.fontFamily) cssVars.push(`--flexdoc-headings-font-family: ${typography.headings.fontFamily}`); if (typography.headings.fontWeight) cssVars.push(`--flexdoc-headings-font-weight: ${typography.headings.fontWeight}`); } if (typography.code) { if (typography.code.fontSize) cssVars.push(`--flexdoc-code-font-size: ${typography.code.fontSize}`); if (typography.code.fontFamily) cssVars.push(`--flexdoc-code-font-family: ${typography.code.fontFamily}`); if (typography.code.lineHeight) cssVars.push(`--flexdoc-code-line-height: ${typography.code.lineHeight}`); if (typography.code.color) cssVars.push(`--flexdoc-code-color: ${typography.code.color}`); if (typography.code.backgroundColor) cssVars.push(`--flexdoc-code-bg: ${typography.code.backgroundColor}`); } } if (themeConfig.sidebar) { const sidebar = themeConfig.sidebar; if (sidebar.backgroundColor) cssVars.push(`--flexdoc-sidebar-bg: ${sidebar.backgroundColor}`); if (sidebar.textColor) cssVars.push(`--flexdoc-sidebar-text: ${sidebar.textColor}`); if (sidebar.activeTextColor) cssVars.push(`--flexdoc-sidebar-active: ${sidebar.activeTextColor}`); } const cssVarsString = cssVars.length > 0 ? cssVars.join(';') + ';' : ''; const customThemeCSS = Object.entries(themeColors) .map(([key, value]) => `--flexdoc-${key}: ${value};`) .join('\n '); const taggedPaths = {}; const tagNameMap = {}; if (options.tagGroups) { options.tagGroups.forEach((group) => { group.tags.forEach((tag) => { tagNameMap[tag] = group.name; }); }); } if (spec) { const paths = spec['paths'] || {}; for (const path in paths) { const pathItem = paths[path]; for (const method in pathItem) { if (['get', 'post', 'put', 'delete', 'patch', 'options', 'head'].includes(method)) { const operation = pathItem[method]; const tags = operation.tags || ['default']; for (const tag of tags) { const tagName = tagNameMap[tag] || tag; if (!taggedPaths[tagName]) { taggedPaths[tagName] = {}; } if (!taggedPaths[tagName][path]) { taggedPaths[tagName][path] = {}; } taggedPaths[tagName][path][method] = operation; } } } } } const tagNameMapForTemplate = Object.fromEntries(Object.entries(tagNameMap).map(([tag, name]) => [name, tag])); const methodColors = { get: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300', post: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300', put: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300', delete: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300', patch: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300', options: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300', head: 'bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-300', }; nunjucksEnv.addGlobal('getOperationId', (path, method, operation) => { return (operation.operationId || `${method}-${path.replace(/[^a-zA-Z0-9]/g, '-')}`); }); nunjucksEnv.addGlobal('getStatusCodeClass', (statusCode) => { const code = parseInt(statusCode, 10); if (code >= 200 && code < 300) { return 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300'; } else if (code >= 300 && code < 400) { return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300'; } else if (code >= 400 && code < 500) { return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300'; } else { return 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300'; } }); nunjucksEnv.addGlobal('getStatusText', (statusCode) => { const statusTexts = { '200': 'OK', '201': 'Created', '204': 'No Content', '400': 'Bad Request', '401': 'Unauthorized', '403': 'Forbidden', '404': 'Not Found', '500': 'Internal Server Error', }; return statusTexts[statusCode] || ''; }); nunjucksEnv.addGlobal('renderSchema', (schema) => { function renderSchemaAsHTML(schema, indent = 0) { if (!schema) return '<span class="text-gray-400">null</span>'; if (schema.$ref) { const refSchema = resolveSchemaRef(schema.$ref); if (refSchema) { return renderSchemaAsHTML(refSchema, indent); } return `<span class="text-red-500">Could not resolve reference: ${schema.$ref}</span>`; } if (schema.type === 'object' || (!schema.type && schema.properties)) { let html = '<div class="pl-4">'; if (schema.properties) { const properties = Object.entries(schema.properties); properties.forEach(([propName, propSchema], index) => { const isRequired = schema.required && schema.required.includes(propName); const requiredBadge = isRequired ? '<span class="text-red-500 ml-1">*</span>' : ''; html += `<div class="mb-2"> <span class="font-semibold text-blue-600 dark:text-blue-400">${propName}</span>${requiredBadge}: `; html += renderSchemaAsHTML(propSchema, indent + 2); if (propSchema.description) { html += `<span class="text-gray-500 ml-2">// ${propSchema.description}</span>`; } html += '</div>'; }); } html += '</div>'; return html; } else if (schema.type === 'array') { return `<span class="text-purple-600 dark:text-purple-400">Array of:</span> ${renderSchemaAsHTML(schema.items, indent)}`; } else if (schema.enum) { return `<span class="text-green-600 dark:text-green-400">${schema.type || 'enum'}</span> <span class="text-gray-500">(${schema.enum .map((e) => JSON.stringify(e)) .join(' | ')})</span>`; } else { let typeText = schema.type || 'any'; let formatText = schema.format ? ` (${schema.format})` : ''; return `<span class="text-green-600 dark:text-green-400">${typeText}${formatText}</span>`; } } return renderSchemaAsHTML(schema); }); nunjucksEnv.addGlobal('renderExample', (example) => { return JSON.stringify(example, null, 2); }); const baseUrl = spec?.servers?.[0]?.url || 'https://api.example.com'; function renderCodeExamples(path, method, operation) { return { curl: renderCurlExample(path, method, operation, baseUrl), python: renderPythonExample(path, method, operation, baseUrl), javascript: renderJavaScriptExample(path, method, operation, baseUrl), go: renderGoExample(path, method, operation, baseUrl), }; } function renderCurlExample(path, method, operation, baseUrl) { let url = `${baseUrl}${path}`; const params = operation.parameters?.filter((p) => p.in === 'path') || []; params.forEach((param) => { const paramName = param.name; url = url.replace(`{${paramName}}`, `${paramName}_value`); }); const queryParams = operation.parameters?.filter((p) => p.in === 'query') || []; if (method.toLowerCase() === 'get' && queryParams.length > 0) { url += '?' + queryParams.map((p) => `${p.name}=${p.name}_value`).join('&'); } let headers = 'Content-Type: application/json'; let body = ''; if (method.toLowerCase() !== 'get' && operation.requestBody?.content?.['application/json']) { try { const schema = operation.requestBody.content['application/json'].schema; if (!schema) { body = ` \\ -d '{ "example": "No schema available" }'`; return `curl -X ${method.toUpperCase()} \\ "${url}" \\ -H "${headers}"${body}`; } const exampleBody = generateExampleFromSchema(schema); const jsonString = JSON.stringify(exampleBody, null, 2); const escapedJson = jsonString .replace(/"/g, '\\"') .replace(/\n/g, '\\n'); body = ` \\ -d '${escapedJson}'`; } catch (error) { console.error('Error generating request body:', error); body = ` \\ -d '{ "error": "Failed to generate example" }'`; } } return `curl -X ${method.toUpperCase()} \\ "${url}" \\ -H "${headers}"${body}`; } function resolveSchemaRef(ref) { if (!ref.startsWith('#/')) { return null; } const path = ref.substring(2).split('/'); let current = spec; for (const segment of path) { if (current && current[segment]) { current = current[segment]; } else { console.log('Could not resolve reference:', ref, 'at segment:', segment); return null; } } return current; } function generateExampleFromSchema(schema) { if (!schema) return null; if (schema.$ref) { const refSchema = resolveSchemaRef(schema.$ref); if (refSchema) { return generateExampleFromSchema(refSchema); } return { example: 'value' }; } switch (schema.type) { case 'object': const obj = {}; if (schema.properties) { for (const propName in schema.properties) { obj[propName] = generateExampleFromSchema(schema.properties[propName]); } } return obj; case 'array': if (schema.items) { return [generateExampleFromSchema(schema.items)]; } return []; case 'string': if (schema.enum && schema.enum.length > 0) { return schema.enum[0]; } if (schema.format === 'date-time') return '2023-01-01T00:00:00Z'; if (schema.format === 'date') return '2023-01-01'; if (schema.format === 'email') return 'user@example.com'; if (schema.format === 'uuid') return '00000000-0000-0000-0000-000000000000'; return 'string'; case 'number': case 'integer': if (schema.enum && schema.enum.length > 0) { return schema.enum[0]; } return 0; case 'boolean': return false; default: return null; } } function renderPythonExample(path, method, operation, baseUrl) { let url = `${baseUrl}${path}`; const params = operation.parameters?.filter((p) => p.in === 'path') || []; params.forEach((param) => { const paramName = param.name; url = url.replace(`{${paramName}}`, `${paramName}_value`); }); const queryParams = operation.parameters?.filter((p) => p.in === 'query') || []; let queryParamsCode = ''; if (queryParams.length > 0) { queryParamsCode = `params = {\n ${queryParams .map((p) => `"${p.name}": "${p.name}_value"`) .join(',\n ')}\n}`; } let bodyCode = ''; if (method.toLowerCase() !== 'get' && operation.requestBody?.content?.['application/json']) { const schema = operation.requestBody.content['application/json'].schema; const exampleBody = generateExampleFromSchema(schema); const jsonStr = JSON.stringify(exampleBody, null, 4) .replace(/"/g, '\\"') .replace(/\n/g, '\n '); bodyCode = `payload = ${jsonStr}`; } return `import requests ${queryParamsCode ? queryParamsCode + '\n\n' : ''}${bodyCode ? bodyCode + '\n\n' : ''}headers = {\n "Content-Type": "application/json"\n} response = requests.${method.toLowerCase()}("${url}"${queryParams.length > 0 ? ', params=params' : ''}${bodyCode ? ', json=payload' : ''}, headers=headers) print(response.json())`; } function renderJavaScriptExample(path, method, operation, baseUrl) { let url = `${baseUrl}${path}`; const params = operation.parameters?.filter((p) => p.in === 'path') || []; params.forEach((param) => { const paramName = param.name; url = url.replace(`{${paramName}}`, `${paramName}_value`); }); const queryParams = operation.parameters?.filter((p) => p.in === 'query') || []; let queryParamsCode = ''; if (queryParams.length > 0) { const queryString = queryParams .map((p) => `${p.name}=\${${p.name}Value}`) .join('&'); queryParamsCode = `const ${queryParams .map((p) => `${p.name}Value = "${p.name}_value"`) .join(';\n')}; \nconst queryString = "${queryString}"; url = \`${url}?\${queryString}\`;`; } let bodyCode = ''; if (method.toLowerCase() !== 'get' && operation.requestBody?.content?.['application/json']) { const schema = operation.requestBody.content['application/json'].schema; const exampleBody = generateExampleFromSchema(schema); const jsonStr = JSON.stringify(exampleBody, null, 2) .replace(/"/g, '\\"') .replace(/\n/g, '\n '); bodyCode = `const payload = ${jsonStr};`; } return `// Using fetch API\nasync function callApi() {\n let url = "${url}"; ${queryParamsCode ? queryParamsCode + '\n\n ' : ''}${bodyCode ? bodyCode + '\n\n ' : ''}const options = {\n method: "${method.toUpperCase()}",\n headers: {\n "Content-Type": "application/json"\n }${bodyCode ? ',\n body: JSON.stringify(payload)' : ''}\n };\n\n try {\n const response = await fetch(url, options);\n const data = await response.json();\n console.log(data);\n } catch (error) {\n console.error("Error:", error);\n }\n}\n\ncallApi();`; } function renderGoExample(path, method, operation, baseUrl) { let url = `${baseUrl}${path}`; const params = operation.parameters?.filter((p) => p.in === 'path') || []; params.forEach((param) => { const paramName = param.name; url = url.replace(`{${paramName}}`, `${paramName}_value`); }); const queryParams = operation.parameters?.filter((p) => p.in === 'query') || []; let queryParamsCode = ''; if (queryParams.length > 0) { queryParamsCode = ` // Add query parameters q := req.URL.Query() ${queryParams.map((p) => `q.Add("${p.name}", "${p.name}_value")`).join('\n\t')} req.URL.RawQuery = q.Encode()`; } let bodyCode = ''; let bodyImport = ''; if (method.toLowerCase() !== 'get' && operation.requestBody?.content?.['application/json']) { const schema = operation.requestBody.content['application/json'].schema; const exampleBody = generateExampleFromSchema(schema); function convertToGoMap(obj, indent = '\t') { if (obj === null) return 'nil'; if (Array.isArray(obj)) { if (obj.length === 0) return '[]interface{}{}'; const items = obj .map((item) => { if (typeof item === 'object' && item !== null) { return convertToGoMap(item, indent + '\t'); } else if (typeof item === 'string') { return `"${item}"`; } else { return `${item}`; } }) .join(',\n' + indent + '\t'); return `[]interface{}{\n${indent}\t${items}\n${indent}}`; } if (typeof obj === 'object') { const entries = Object.entries(obj) .map(([key, value]) => { if (typeof value === 'object' && value !== null) { return `"${key}": ${convertToGoMap(value, indent + '\t')}`; } else if (typeof value === 'string') { return `"${key}": "${value}"`; } else { return `"${key}": ${value}`; } }) .join(',\n' + indent + '\t'); return `map[string]interface{}{\n${indent}\t${entries}\n${indent}}`; } return String(obj); } const goMapStr = convertToGoMap(exampleBody); bodyImport = '\n\t"bytes"'; bodyCode = ` // Create request body payload := ${goMapStr} body, err := json.Marshal(payload) if err != nil { log.Fatalf("Error creating request body: %v", err) } req, err = http.NewRequest("${method.toUpperCase()}", "${url}", bytes.NewBuffer(body))`; } else { bodyCode = ` req, err := http.NewRequest("${method.toUpperCase()}", "${url}", nil)`; } return `package main import ( "encoding/json" "fmt" "io/ioutil" "log" "net/http"${bodyImport} ) func main() {${bodyCode} if err != nil { log.Fatalf("Error creating request: %v", err) }${queryParamsCode} // Add headers req.Header.Set("Content-Type", "application/json") // Send request client := &http.Client{} resp, err := client.Do(req) if err != nil { log.Fatalf("Error sending request: %v", err) } defer resp.Body.Close() // Read response body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatalf("Error reading response: %v", err) } fmt.Println(string(body)) }`; } function getOperationId(path, method, operation) { return (operation.operationId || `${method}-${path.replace(/[{}]/g, '').replace(/\//g, '-')}`); } const methodCounts = { get: 0, post: 0, put: 0, delete: 0, patch: 0, options: 0, head: 0, }; for (const tag in taggedPaths) { const paths = taggedPaths[tag]; for (const path in paths) { const methods = paths[path]; for (const method in methods) { if (['get', 'post', 'put', 'delete', 'patch', 'options', 'head'].includes(method)) { methodCounts[method]++; } } } } function renderSchema(schema) { if (!schema) return '<span class="text-gray-500">null</span>'; function resolveRef(ref) { if (!ref.startsWith('#/components/schemas/')) { return { $ref: ref }; } const schemaName = ref.replace('#/components/schemas/', ''); if (spec?.components?.schemas && spec.components.schemas[schemaName]) { return spec.components.schemas[schemaName]; } return { $ref: ref }; } const clonedSchema = JSON.parse(JSON.stringify(schema)); function processSchema(obj) { if (!obj || typeof obj !== 'object') return obj; if (obj.$ref) { const resolved = resolveRef(obj.$ref); return { ...processSchema(resolved), originalRef: obj.$ref }; } if (obj.type === 'array' && obj.items) { obj.items = processSchema(obj.items); return obj; } if (obj.properties) { Object.keys(obj.properties).forEach((key) => { obj.properties[key] = processSchema(obj.properties[key]); }); } if (obj.additionalProperties && typeof obj.additionalProperties === 'object') { obj.additionalProperties = processSchema(obj.additionalProperties); } return obj; } const processed = processSchema(clonedSchema); function generateSchemaHTML(schema, level = 0, isArrayItem = false) { if (!schema) return '<span class="text-gray-500">null</span>'; const indent = ' '.repeat(level); let html = ''; if (schema.type === 'object') { if (schema.properties) { html += '<div class="schema-object">'; if (!isArrayItem) { html += `<div class="text-blue-600 font-semibold mb-1">Object</div>`; } html += '<ul class="pl-4 space-y-2">'; for (const propName in schema.properties) { const prop = schema.properties[propName]; const required = schema.required && schema.required.includes(propName); html += '<li>'; html += `<div><span class="font-mono font-medium">${propName}</span>`; if (required) { html += ' <span class="text-red-500 text-xs font-semibold">required</span>'; } html += `</div>`; html += `<div class="text-sm">`; if (prop.type) { html += `<span class="text-green-600">${prop.type}</span>`; if (prop.format) { html += ` <span class="text-gray-500">(${prop.format})</span>`; } } html += '</div>'; if (prop.description) { html += `<div class="text-sm text-gray-600">${prop.description}</div>`; } if ((prop.type === 'object' && prop.properties) || (prop.type === 'array' && prop.items)) { html += generateSchemaHTML(prop, level + 1); } html += '</li>'; } html += '</ul></div>'; } else { html += '<div class="text-blue-600 font-semibold">Object</div>'; } } else if (schema.type === 'array') { html += '<div class="schema-array">'; html += `<div class="text-blue-600 font-semibold mb-1">Array of:</div>`; html += generateSchemaHTML(schema.items, level + 1, true); html += '</div>'; } else { html += `<div class="text-green-600">${schema.type || 'unknown'}</div>`; if (schema.format) { html += ` <span class="text-gray-500">(${schema.format})</span>`; } if (schema.description) { html += `<div class="text-sm text-gray-600">${schema.description}</div>`; } } return html; } const jsonString = JSON.stringify(processed, null, 2); return generateSchemaHTML(processed); } function renderExample(example) { if (!example) return 'null'; return JSON.stringify(example, null, 2); } return nunjucksEnv.render('content.njk', { title, description, altDescription, favicon, logo: logoUrl, logoAlt, logoStyle, logoContainerStyle, logoContainerClass, logoClickable, cssVars, cssVarsString, customCss, customJs, customThemeCSS, isDarkMode, currentSpec: spec, taggedPaths, tagNameMapForTemplate, methodColors, specUrl, baseUrl, methodCounts, getOperationId, renderCodeExamples, renderCurlExample, renderPythonExample, renderJavaScriptExample, renderGoExample, renderSchema, renderExample, specJsonString, footer: options.footer, }); } //# sourceMappingURL=template.js.map