docs: fix resolve llms.txt missing wrapped component examples (#4384)

This commit is contained in:
KinfeMichael Tariku
2025-09-03 18:19:44 +03:00
committed by GitHub
parent 9fb9bd92f8
commit 7433ca5052

View File

@@ -8,6 +8,250 @@ import { remarkAutoTypeTable } from "fumadocs-typescript";
import { remarkInclude } from "fumadocs-mdx/config";
import { readFile } from "fs/promises";
function extractAPIMethods(rawContent: string): string {
const apiMethodRegex = /<APIMethod\s+([^>]+)>([\s\S]*?)<\/APIMethod>/g;
return rawContent.replace(apiMethodRegex, (match, attributes, content) => {
// Parse attributes by matching
const pathMatch = attributes.match(/path="([^"]+)"/);
const methodMatch = attributes.match(/method="([^"]+)"/);
const requireSessionMatch = attributes.match(/requireSession/);
const isServerOnlyMatch = attributes.match(/isServerOnly/);
const isClientOnlyMatch = attributes.match(/isClientOnly/);
const noResultMatch = attributes.match(/noResult/);
const resultVariableMatch = attributes.match(/resultVariable="([^"]+)"/);
const forceAsBodyMatch = attributes.match(/forceAsBody/);
const forceAsQueryMatch = attributes.match(/forceAsQuery/);
const path = pathMatch ? pathMatch[1] : "";
const method = methodMatch ? methodMatch[1] : "GET";
const requireSession = !!requireSessionMatch;
const isServerOnly = !!isServerOnlyMatch;
const isClientOnly = !!isClientOnlyMatch;
const noResult = !!noResultMatch;
const resultVariable = resultVariableMatch
? resultVariableMatch[1]
: "data";
const forceAsBody = !!forceAsBodyMatch;
const forceAsQuery = !!forceAsQueryMatch;
const typeMatch = content.match(/type\s+(\w+)\s*=\s*\{([\s\S]*?)\}/);
if (!typeMatch) {
return match; // Return original if no type found
}
const functionName = typeMatch[1];
const typeBody = typeMatch[2];
const properties = parseTypeBody(typeBody);
const clientCode = generateClientCode(functionName, properties, path);
const serverCode = generateServerCode(
functionName,
properties,
method,
requireSession,
forceAsBody,
forceAsQuery,
noResult,
resultVariable,
);
return `
### Client Side
\`\`\`ts
${clientCode}
\`\`\`
### Server Side
\`\`\`ts
${serverCode}
\`\`\`
### Type Definition
\`\`\`ts
type ${functionName} = {${typeBody}
}
\`\`\`
`;
});
}
function parseTypeBody(typeBody: string) {
const properties: Array<{
name: string;
type: string;
required: boolean;
description: string;
exampleValue: string;
isServerOnly: boolean;
isClientOnly: boolean;
}> = [];
const lines = typeBody.split("\n");
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("/*"))
continue;
const propMatch = trimmed.match(
/^(\w+)(\?)?:\s*(.+?)(\s*=\s*["']([^"']+)["'])?(\s*\/\/\s*(.+))?$/,
);
if (propMatch) {
const [, name, optional, type, , exampleValue, , description] = propMatch;
let cleanType = type.trim();
let cleanExampleValue = exampleValue || "";
cleanType = cleanType.replace(/,$/, "");
properties.push({
name,
type: cleanType,
required: !optional,
description: description || "",
exampleValue: cleanExampleValue,
isServerOnly: false,
isClientOnly: false,
});
}
}
return properties;
}
// Generate client code example
function generateClientCode(
functionName: string,
properties: any[],
path: string,
) {
if (!functionName || !path) {
return "// Unable to generate client code - missing function name or path";
}
const clientMethodPath = pathToDotNotation(path);
const body = createClientBody(properties);
return `const { data, error } = await authClient.${clientMethodPath}(${body});`;
}
// Generate server code example
function generateServerCode(
functionName: string,
properties: any[],
method: string,
requireSession: boolean,
forceAsBody: boolean,
forceAsQuery: boolean,
noResult: boolean,
resultVariable: string,
) {
if (!functionName) {
return "// Unable to generate server code - missing function name";
}
const body = createServerBody(
properties,
method,
requireSession,
forceAsBody,
forceAsQuery,
);
return `${noResult ? "" : `const ${resultVariable} = `}await auth.api.${functionName}(${body});`;
}
function pathToDotNotation(input: string): string {
return input
.split("/")
.filter(Boolean)
.map((segment) =>
segment
.split("-")
.map((word, i) =>
i === 0
? word.toLowerCase()
: word.charAt(0).toUpperCase() + word.slice(1),
)
.join(""),
)
.join(".");
}
// Helper function to create client body (simplified version)
function createClientBody(props: any[]) {
if (props.length === 0) return "{}";
let body = "{\n";
for (const prop of props) {
if (prop.isServerOnly) continue;
let comment = "";
if (!prop.required || prop.description) {
const comments = [];
if (!prop.required) comments.push("required");
if (prop.description) comments.push(prop.description);
comment = ` // ${comments.join(", ")}`;
}
body += ` ${prop.name}${prop.exampleValue ? `: ${prop.exampleValue}` : ""}${prop.type === "Object" ? ": {}" : ""},${comment}\n`;
}
body += "}";
return body;
}
function createServerBody(
props: any[],
method: string,
requireSession: boolean,
forceAsBody: boolean,
forceAsQuery: boolean,
) {
const relevantProps = props.filter((x) => !x.isClientOnly);
if (relevantProps.length === 0 && !requireSession) {
return "{}";
}
let serverBody = "{\n";
if (relevantProps.length > 0) {
const bodyKey =
(method === "POST" || forceAsBody) && !forceAsQuery ? "body" : "query";
serverBody += ` ${bodyKey}: {\n`;
for (const prop of relevantProps) {
let comment = "";
if (!prop.required || prop.description) {
const comments = [];
if (!prop.required) comments.push("required");
if (prop.description) comments.push(prop.description);
comment = ` // ${comments.join(", ")}`;
}
serverBody += ` ${prop.name}${prop.exampleValue ? `: ${prop.exampleValue}` : ""}${prop.type === "Object" ? ": {}" : ""},${comment}\n`;
}
serverBody += " }";
}
if (requireSession) {
if (relevantProps.length > 0) serverBody += ",";
serverBody +=
"\n // This endpoint requires session cookies.\n headers: await headers()";
}
serverBody += "\n}";
return serverBody;
}
const processor = remark()
.use(remarkMdx)
.use(remarkInclude)
@@ -23,9 +267,12 @@ export async function getLLMText(docPage: any) {
// Read the raw file content
const rawContent = await readFile(docPage.data._file.absolutePath, "utf-8");
// Extract APIMethod components & other nested wrapper before processing
const processedContent = extractAPIMethods(rawContent);
const processed = await processor.process({
path: docPage.data._file.absolutePath,
value: rawContent,
value: processedContent,
});
return `# ${category}: ${docPage.data.title}