Skip to content

Commit dae32b7

Browse files
authored
All relative links working now. (#303)
* links and jpgs working Signed-off-by: Vedansh Saini <[email protected]> * gifs working Signed-off-by: Vedansh Saini <[email protected]> * all rel links working Signed-off-by: Vedansh Saini <[email protected]> * lint Signed-off-by: Vedansh Saini <[email protected]> --------- Signed-off-by: Vedansh Saini <[email protected]>
1 parent 244c8c4 commit dae32b7

File tree

1 file changed

+92
-4
lines changed

1 file changed

+92
-4
lines changed

src/app/docs/[...slug]/page.tsx

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,29 @@ type PageProps = Readonly<{
1818
searchParams: Promise<{ version?: string }>
1919
}>
2020

21+
function resolvePath(baseFile: string, relativePath: string) {
22+
if (relativePath.startsWith('/')) return relativePath.slice(1);
23+
const stack = baseFile.split('/');
24+
stack.pop(); // Remove current filename
25+
const parts = relativePath.split('/');
26+
for (const part of parts) {
27+
if (part === '.') continue;
28+
if (part === '..') {
29+
if (stack.length > 0) stack.pop();
30+
} else {
31+
stack.push(part);
32+
}
33+
}
34+
return stack.join('/');
35+
}
36+
2137
export default async function Page(props: PageProps) {
2238
const params = await props.params
2339
const searchParams = await props.searchParams
2440

25-
// Get version from URL or use default
2641
const version = (searchParams.version as VersionKey) || getDefaultVersion()
2742
const branch = getBranchForVersion(version)
2843

29-
// Build page map for this branch
3044
const { routeMap, filePaths } = await buildPageMapForBranch(branch)
3145

3246
const route = params.slug ? params.slug.join('/') : ''
@@ -42,10 +56,84 @@ export default async function Page(props: PageProps) {
4256
`https://raw.githubusercontent.com/${user}/${repo}/${branch}/${docsPath}${filePath}`,
4357
{ headers: makeGitHubHeaders(), cache: 'no-store' }
4458
)
59+
4560
if (!response.ok) notFound()
4661

47-
const data = await response.text()
48-
const processedData = convertHtmlScriptsToJsxComments(data)
62+
const rawText = await response.text()
63+
64+
let contentWithIncludes = rawText;
65+
const includeRegex = /{%\s*include\s+["']([^"']+)["']\s*%}/g;
66+
const includeMatches = Array.from(rawText.matchAll(includeRegex));
67+
68+
if (includeMatches.length > 0) {
69+
const uniqueIncludes = [...new Set(includeMatches.map(m => m[1]))];
70+
71+
const includeContents = await Promise.all(uniqueIncludes.map(async (relativePath) => {
72+
const resolvedPath = resolvePath(filePath, relativePath);
73+
const url = `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${docsPath}${resolvedPath}`;
74+
75+
try {
76+
const res = await fetch(url, { headers: makeGitHubHeaders(), cache: 'no-store' });
77+
if (res.ok) {
78+
return { path: relativePath, text: await res.text() };
79+
}
80+
81+
const rootUrl = `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${resolvedPath}`;
82+
const rootRes = await fetch(rootUrl, { headers: makeGitHubHeaders(), cache: 'no-store' });
83+
if (rootRes.ok) {
84+
return { path: relativePath, text: await rootRes.text() };
85+
}
86+
87+
return { path: relativePath, text: `> **Error**: Could not include \`${relativePath}\` (File not found)` };
88+
} catch {
89+
return { path: relativePath, text: `> **Error**: Failed to fetch \`${relativePath}\`` };
90+
}
91+
}));
92+
93+
includeContents.forEach(({ path, text }) => {
94+
const escapedPath = path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
95+
const pattern = new RegExp(`{%\\s*include\\s+["']${escapedPath}["']\\s*%}`, 'g');
96+
contentWithIncludes = contentWithIncludes.replace(pattern, () => text);
97+
});
98+
}
99+
100+
const filePathToRoute = new Map<string, string>();
101+
Object.entries(routeMap).forEach(([r, fp]) => filePathToRoute.set(fp, r));
102+
103+
let rewrittenText = contentWithIncludes.replace(/(!?\[.*?\])\((.*?)\)/g, (match, label, link) => {
104+
if (/^(http|https|mailto:|#)/.test(link)) return match;
105+
106+
const isImage = label.startsWith('!');
107+
const [linkUrl, linkHash] = link.split('#');
108+
109+
const resolvedPath = resolvePath(filePath, linkUrl);
110+
111+
if (isImage) {
112+
const rawUrl = `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${docsPath}${resolvedPath}`;
113+
return `${label}(${rawUrl})`;
114+
} else {
115+
let targetRoute = filePathToRoute.get(resolvedPath);
116+
if (!targetRoute) targetRoute = filePathToRoute.get(resolvedPath + '.md');
117+
if (!targetRoute) targetRoute = filePathToRoute.get(resolvedPath + '.mdx');
118+
119+
if (targetRoute) {
120+
return `${label}(/docs/${targetRoute}${linkHash ? '#' + linkHash : ''})`;
121+
}
122+
123+
return `${label}(https://raw.githubusercontent.com/${user}/${repo}/${branch}/${docsPath}${resolvedPath})`;
124+
}
125+
});
126+
127+
rewrittenText = rewrittenText.replace(/<img\s+([^>]*?)src=["']([^"']+)["']([^>]*?)>/gi, (match, pre, src, post) => {
128+
if (/^(http|https|mailto:|#|data:)/.test(src)) return match;
129+
130+
const resolvedPath = resolvePath(filePath, src);
131+
const rawUrl = `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${docsPath}${resolvedPath}`;
132+
133+
return `<img ${pre}src="${rawUrl}"${post}>`;
134+
});
135+
136+
const processedData = convertHtmlScriptsToJsxComments(rewrittenText)
49137
.replace(/<br\s*\/?>/gi, '<br />')
50138
.replace(/align=center/g, 'align="center"')
51139
.replace(/frameborder="0"/g, 'frameBorder="0"')

0 commit comments

Comments
 (0)