• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wazeerc / PrevueMD / 28166430389

25 Jun 2026 11:19AM UTC coverage: 84.909% (+3.8%) from 81.089%
28166430389

push

github

wazeerc
chore: install deps

157 of 208 branches covered (75.48%)

Branch coverage included in aggregate %.

310 of 342 relevant lines covered (90.64%)

7.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.51
/src/utils/markdown-parser.ts
1
async function createProcessor() {
2
  const [unified, remarkParse, remarkGfm, remarkRehype, rehypeStringify] =
3
    await Promise.all([
11✔
4
      import('unified').then(m => m.unified),
6✔
5
      import('remark-parse').then(m => m.default),
6✔
6
      import('remark-gfm').then(m => m.default),
6✔
7
      import('remark-rehype').then(m => m.default),
6✔
8
      import('rehype-stringify').then(m => m.default),
6✔
9
    ]);
10

11
  return unified()
6✔
12
    .use(remarkParse)
13
    .use(remarkGfm)
14
    .use(remarkRehype, { allowDangerousHtml: false })
15
    .use(rehypeStringify);
16
}
17

18
type MarkdownProcessor = Awaited<ReturnType<typeof createProcessor>>;
19

20
let markdownProcessorPromise: Promise<MarkdownProcessor> | null = null;
14✔
21
const MAX_CACHE_ENTRIES = 10;
14✔
22
const MAX_CACHEABLE_MARKDOWN_LENGTH = 250_000;
14✔
23
const MAX_TOTAL_CACHE_CHARS = 750_000;
14✔
24
const parsedMarkdownCache = new Map<string, string>();
14✔
25
let totalCacheChars = 0;
14✔
26

27
function getCacheEntrySize(markdownText: string, markupText: string): number {
28
  return markdownText.length + markupText.length;
17✔
29
}
30

31
function refreshCachedMarkdown(markdownText: string, markupText: string): void {
32
  parsedMarkdownCache.delete(markdownText);
17✔
33
  parsedMarkdownCache.set(markdownText, markupText);
17✔
34
}
35

36
function trimMarkdownCache(): void {
37
  while (parsedMarkdownCache.size > MAX_CACHE_ENTRIES || totalCacheChars > MAX_TOTAL_CACHE_CHARS) {
15✔
38
    const oldestEntry = parsedMarkdownCache.entries().next().value;
2✔
39
    if (!oldestEntry) return;
2!
40

41
    const [markdownText, markupText] = oldestEntry;
2✔
42
    parsedMarkdownCache.delete(markdownText);
2✔
43
    totalCacheChars -= getCacheEntrySize(markdownText, markupText);
2✔
44
  }
45
}
46

47
function cacheParsedMarkdown(markdownText: string, markupText: string): void {
48
  if (markdownText.length > MAX_CACHEABLE_MARKDOWN_LENGTH) return;
17✔
49

50
  const existingMarkup = parsedMarkdownCache.get(markdownText);
15✔
51
  if (parsedMarkdownCache.has(markdownText)) totalCacheChars -= getCacheEntrySize(markdownText, existingMarkup ?? '');
15!
52

53
  refreshCachedMarkdown(markdownText, markupText);
15✔
54
  totalCacheChars += getCacheEntrySize(markdownText, markupText);
15✔
55
  trimMarkdownCache();
15✔
56
}
57

58
/**
59
 * Returns cached markup for previously parsed markdown and refreshes cache recency.
60
 */
61
export function getCachedMarkdown(markdownText: string): string | null {
62
  if (!parsedMarkdownCache.has(markdownText)) return null;
24✔
63

64
  const cachedMarkup = parsedMarkdownCache.get(markdownText);
2✔
65
  if (cachedMarkup === undefined) return null;
2!
66

67
  refreshCachedMarkdown(markdownText, cachedMarkup);
2✔
68
  return cachedMarkup;
2✔
69
}
70

71
/**
72
 * Clears cached markdown parse results.
73
 */
74
export function clearMarkdownParseCache(): void {
75
  parsedMarkdownCache.clear();
×
76
  totalCacheChars = 0;
×
77
}
78

79
/**
80
 * Loads and configures the markdown processor before the first parse request.
81
 */
82
export function preloadMarkdownParser(): Promise<MarkdownProcessor> {
83
  if (!markdownProcessorPromise) {
24✔
84
    markdownProcessorPromise = createProcessor().catch((error) => {
11✔
85
      markdownProcessorPromise = null;
×
86
      throw error;
×
87
    });
88
  }
89
  return markdownProcessorPromise;
24✔
90
}
91

92
/**
93
 * Parses markdown text to HTML, using the in-memory cache or the shared parser.
94
 */
95
export async function parseMarkdown(markdownTextToParse: string): Promise<string> {
96
  try {
22✔
97
    const cachedMarkup = getCachedMarkdown(markdownTextToParse);
22✔
98
    if (cachedMarkup !== null) return cachedMarkup;
22✔
99

100
    const markdownProcessor = await preloadMarkdownParser();
20✔
101

102
    const parsedMarkdown = await markdownProcessor.process(markdownTextToParse);
18✔
103
    const markupText = String(parsedMarkdown.value);
17✔
104

105
    cacheParsedMarkdown(markdownTextToParse, markupText);
17✔
106
    return markupText;
17✔
107
  }
108
  catch (error) {
109
    throw new Error(`Failed to parse markdown: ${error instanceof Error ? error.message : String(error)}`);
1!
110
  }
111
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc