-
Notifications
You must be signed in to change notification settings - Fork 320
Expand file tree
/
Copy pathmarkdown.ts
More file actions
84 lines (69 loc) · 3.04 KB
/
markdown.ts
File metadata and controls
84 lines (69 loc) · 3.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { readFile } from 'fs/promises';
import { join, normalize, resolve } from 'path';
import { generateDynamicMarkdown, hasDynamicMarkdownGenerator } from './markdown-generators';
const partialsCache = new Map<string, string>();
export async function processMarkdownWithPartials(content: string): Promise<string> {
const partialsDir = join(process.cwd(), 'src', 'partials');
const resolvedPartialsDir = resolve(partialsDir);
const partialRegex = /\{%\s*partial\s+file="([^"]+)"\s*\/%\}/g;
const matches = [...content.matchAll(partialRegex)];
if (matches.length === 0) return content;
let result = content;
for (const match of matches) {
const partialFile = match[1];
const fullMatch = match[0];
if (!partialsCache.has(partialFile)) {
try {
const normalizedPartialFile = normalize(partialFile).replace(
/^(\.\.(\/|\\|$))+/,
''
);
const partialPath = resolve(partialsDir, normalizedPartialFile);
if (
!partialPath.startsWith(resolvedPartialsDir + '/') &&
partialPath !== resolvedPartialsDir
) {
console.warn(`Partial path traversal attempt detected: ${partialFile}`);
partialsCache.set(partialFile, '');
continue;
}
const partialContent = await readFile(partialPath, 'utf-8');
partialsCache.set(partialFile, partialContent);
} catch (error) {
console.error('An error occured while loading partial', error);
console.warn(`Partial not found: ${partialFile}`);
partialsCache.set(partialFile, '');
}
}
const partialContent = partialsCache.get(partialFile) || '';
result = result.replaceAll(fullMatch, partialContent);
}
return result;
}
/**
* Gets markdown content for a route.
* - For dynamic routes with registered generators, generates markdown from data sources
* - For static routes, reads from +page.markdoc files
*
* Dynamic routes include:
* - Model reference pages: /docs/references/{version}/models/{model}
* - Service reference pages: /docs/references/{version}/{platform}/{service}
*/
export const getMarkdownContent = async (routeId: string | null) => {
if (!routeId) return null;
// Try dynamic markdown generators first (for API reference pages)
if (hasDynamicMarkdownGenerator(routeId)) {
return await generateDynamicMarkdown(routeId);
}
// Fall back to static markdoc files
try {
const routesRoot = join(process.cwd(), 'src', 'routes');
const cleaned = routeId.replace(/^[\\/]+/, '');
const normalized = normalize(cleaned).replace(/^(\.\.(\/|\\|$))+/, '');
const basePath = resolve(routesRoot, normalized, '+page.markdoc');
if (!basePath.startsWith(routesRoot)) return null;
return await readFile(basePath, 'utf-8');
} catch {
return null;
}
};