import type { AttributeInterface, ClassInterface, DocstringSection, FunctionInterface, ModuleInterface, ParameterInterface, } from '@/generated'; export type OutputFile = { path: string; frontmatter: Record; content: string; }; export interface ConvertOptions { baseUrl?: string; } export function convert(mod: ModuleInterface, options: ConvertOptions = {}): OutputFile[] { const files: OutputFile[] = []; const content: string[] = []; const tabs: string[] = []; const tabContents: string[] = []; if (mod.description) content.push(encodeText(mod.description)); for (const attr of mod.attributes) { content.push(convertAttribute(attr)); } if (Object.keys(mod.classes).length > 0) { tabs.push('Class'); const lines: string[] = []; for (const cls of Object.values(mod.classes)) { files.push(...convertClass(cls)); lines.push( element('Card', { title: cls.name, href: getHref(cls, options), }), ); } tabContents.push(element('Cards', undefined, lines.join('\n'))); } if (Object.keys(mod.functions).length > 0) { tabs.push('Functions'); const lines: string[] = []; for (const func of Object.values(mod.functions)) { lines.push(convertFunction(func)); } tabContents.push(lines.join('\n')); } if (Object.keys(mod.modules).length > 0) { tabs.push('Modules'); const lines: string[] = []; for (const submod of Object.values(mod.modules)) { files.push(...convert(submod, options)); lines.push( element('Card', { href: getHref(submod, options), title: submod.name, }), ); } tabContents.push(element('Cards', undefined, lines.join('\n'))); } if (tabs.length > 0) { content.push( element( 'Tabs', { items: tabs, }, tabContents.map((content, i) => element('Tab', { value: tabs[i] }, content)).join('\n'), ), ); } files.push({ path: [...mod.path.split('.'), 'index.mdx'].join('/'), frontmatter: { title: mod.name, }, content: content.join('\n\n'), }); return files; } function convertClass(cls: ClassInterface): OutputFile[] { const content: string[] = []; const files: OutputFile[] = []; if (cls.description) content.push(encodeText(cls.description)); if (cls.attributes.length > 0) { content.push(heading(2, 'Attributes')); } for (const attr of cls.attributes) { content.push(convertAttribute(attr)); } if (Object.keys(cls.functions).length > 0) { content.push(heading(2, 'Functions')); for (const func of Object.values(cls.functions)) { content.push(convertFunction(func)); } } files.push({ path: cls.path.replaceAll('.', '/') + '.mdx', frontmatter: { title: cls.name, }, content: content.join('\n\n'), }); return files; } function convertFunction(func: FunctionInterface) { return element( 'PyFunction', { name: func.name, type: func.signature, }, [ func.description ? encodeText(func.description) : null, convertDoc(func.docstring ?? []), func.source.length > 0 ? element('PySourceCode', undefined, codeblock('python', func.source)) : null, func.parameters.length > 0 ? element('div', undefined, func.parameters.map(convertParameter).join('\n')) : null, element( 'PyFunctionReturn', { type: func.returns.annotation, }, [func.returns.description ? encodeText(func.returns.description) : null] .filter(Boolean) .join('\n'), ), ] .filter(Boolean) .join('\n\n'), ); } function convertParameter(param: ParameterInterface) { const lines: string[] = []; if (param.description) lines.push( typeof param.description === 'string' ? param.description : convertDoc(param.description), ); return element( 'PyParameter', { name: param.name, type: param.annotation, value: param.value }, lines.join('\n'), ); } function convertAttribute(attribute: AttributeInterface) { return element( 'PyAttribute', { name: attribute.name, type: attribute.annotation, value: attribute.value, }, [attribute.description ? convertDoc(attribute.description) : null].filter(Boolean).join('\n'), ); } function convertDoc(docstring: DocstringSection[]) { const lines: string[] = []; for (const item of docstring) { if (item.kind === 'text') { lines.push(encodeText(item.value as string)); } if (item.kind === 'admonition') { const value = item.value as { annotation: string; description: string }; lines.push( element( 'Callout', { title: item.title, type: value.annotation, }, encodeText(item.value.description), ), ); } if (item.kind === 'code') { console.log(item.value); } } return lines.join('\n\n'); } function heading(depth: number, content: string) { return ['#'.repeat(depth), content].join(' '); } function codeblock(meta: string, code: string) { const delimit = '```'; return `${delimit}${meta}\n${code.replaceAll(delimit, '\\' + delimit)}\n${delimit}`; } function element(name: string, props: Record = {}, children?: string) { const propsStr: string[] = []; for (const key in props) { propsStr.push(`${key}={${JSON.stringify(props[key])}}`); } if (children) return `<${name} ${propsStr.join(' ')}> ${children} `; return `<${name} ${propsStr.join(' ')} />`; } function getHref( ele: ModuleInterface | FunctionInterface | ClassInterface, options: ConvertOptions, ) { const { baseUrl = '/' } = options; return ( '/' + [...baseUrl.split('/'), ...ele.path.split('.')].filter((v) => v.length > 0).join('/') ); } function encodeText(v: string) { return v.replaceAll('<', '\\<').replaceAll('{', '\\{').replaceAll('}', '\\}'); }