JiaYou

Finished
1// Helper function to wrap text in HTML tags
12function wrap(text, tag) {
23 return `<${tag}>${text}</${tag}>`;
34}
45
5function isTag(text, tag) {
6 return text.startsWith(`<${tag}>`);
6// Parse bold text with double underscores (__)
7function parseBoldUnderscore(markdown) {
8 // Use greedy matching like the original implementation
9 return markdown.replace(/__(.+)__/g, '<strong>$1</strong>');
710}
811
9function parser(markdown, delimiter, tag) {
10 const pattern = new RegExp(`${delimiter}(.+)${delimiter}`);
11 const replacement = `<${tag}>$1</${tag}>`;
12 return markdown.replace(pattern, replacement);
12// Parse italic text with single underscores (_)
13function parseItalicUnderscore(markdown) {
14 // Use greedy matching like the original implementation
15 return markdown.replace(/_(.+)_/g, '<em>$1</em>');
1316}
1417
15function parse__(markdown) {
16 return parser(markdown, '__', 'strong');
18// Parse bold text with double asterisks (**)
19function parseBoldAsterisk(markdown) {
20 // Use greedy matching to match the original pattern
21 return markdown.replace(/\*\*(.+)\*\*/g, '<strong>$1</strong>');
1722}
1823
19function parse_(markdown) {
20 return parser(markdown, '_', 'em');
24// Parse italic text with single asterisks (*)
25function parseItalicAsterisk(markdown) {
26 // Use greedy matching to match the original pattern
27 return markdown.replace(/\*(.+)\*/g, '<em>$1</em>');
2128}
2229
23function parseText(markdown, list) {
24 const parsedText = parse_(parse__(markdown));
25 if (list) {
30// Parse all emphasis markers in text
31function parseEmphasis(markdown) {
32 // Parse bold first (both variants), then italic (both variants)
33 // This order handles nested emphasis correctly
34 let result = parseBoldUnderscore(markdown);
35 result = parseBoldAsterisk(result);
36 result = parseItalicUnderscore(result);
37 result = parseItalicAsterisk(result);
38 return result;
39}
40
41// Parse text content, adding paragraph tags if not in a list
42function parseText(markdown, inList) {
43 const parsedText = parseEmphasis(markdown);
44 if (inList) {
2645 return parsedText;
2746 } else {
2847 return wrap(parsedText, 'p');
2948 }
3049}
3150
32function parseHeader(markdown, list) {
33 let count = 0;
34 for (let i = 0; i < markdown.length; i++) {
35 if (markdown[i] === '#') {
36 count += 1;
37 } else {
38 break;
39 }
40 }
41 if (count === 0 || count > 6) {
42 return [null, list];
51// Parse header markdown (# ## ### etc.)
52function parseHeader(markdown, inList) {
53 // Match 1-6 hash characters at start of line followed by a space
54 const headerMatch = markdown.match(/^(#{1,6})\s(.*)/);
55
56 if (!headerMatch) {
57 return [null, inList];
4358 }
44 const headerTag = `h${count}`;
45 const headerHtml = wrap(markdown.substring(count + 1), headerTag);
46 if (list) {
59
60 const level = headerMatch[1].length; // Number of # characters
61 const content = headerMatch[2];
62 const headerTag = `h${level}`;
63 const headerHtml = wrap(content, headerTag);
64
65 // Close any open list before starting a header
66 if (inList) {
4767 return [`</ul>${headerHtml}`, false];
4868 } else {
4969 return [headerHtml, false];
5070 }
5171}
5272
53function parseLineItem(markdown, list) {
54 if (markdown.startsWith('*')) {
55 const innerHtml = wrap(parseText(markdown.substring(2), true), 'li');
56 if (list) {
73// Parse list items (*)
74function parseLineItem(markdown, inList) {
75 if (markdown.startsWith('* ')) { // Require space after asterisk
76 const content = markdown.substring(2); // Remove '* ' prefix
77 const innerHtml = wrap(parseText(content, true), 'li');
78
79 if (inList) {
80 // Continue existing list
5781 return [innerHtml, true];
5882 } else {
83 // Start new list
5984 return [`<ul>${innerHtml}`, true];
6085 }
6186 }
62 return [null, list];
87 return [null, inList];
6388}
6489
65function parseParagraph(markdown, list) {
66 if (!list) {
67 return [parseText(markdown, false), false];
68 } else {
90// Parse regular paragraph text
91function parseParagraph(markdown, inList) {
92 if (inList) {
93 // Close list and start paragraph
6994 return [`</ul>${parseText(markdown, false)}`, false];
95 } else {
96 // Regular paragraph
97 return [parseText(markdown, false), false];
7098 }
7199}
72100
73function parseLine(markdown, list) {
74 let [result, inListAfter] = parseHeader(markdown, list);
75 if (result === null) {
76 [result, inListAfter] = parseLineItem(markdown, list);
101// Parse a single line of markdown
102function parseLine(markdown, inList) {
103 // Try parsing as header first
104 let [result, newList] = parseHeader(markdown, inList);
105 if (result !== null) {
106 return [result, newList];
77107 }
78 if (result === null) {
79 [result, inListAfter] = parseParagraph(markdown, list);
108
109 // Try parsing as list item
110 [result, newList] = parseLineItem(markdown, inList);
111 if (result !== null) {
112 return [result, newList];
80113 }
81 if (result === null) {
82 throw new Error('Remove this line and implement the function');
83 }
84 return [result, inListAfter];
114
115 // Default to paragraph
116 [result, newList] = parseParagraph(markdown, inList);
117 return [result, newList];
85118}
86119
87120/**
88 * @param {string} markdown
89 * @returns {string}
121 * Parse markdown text and convert to HTML
122 * @param {string} markdown - The markdown text to parse
123 * @returns {string} - The resulting HTML
90124 */
91125export function parse(markdown) {
92126 const lines = markdown.split('\n');
93127 let result = '';
94 let list = false;
95 for (let i = 0; i < lines.length; i++) {
96 let [lineResult, newList] = parseLine(lines[i], list);
128 let inList = false;
129
130 for (const line of lines) {
131 let [lineResult, newList] = parseLine(line, inList);
97132 result += lineResult;
98 list = newList;
133 inList = newList;
99134 }
100 if (list) {
101 return result + '</ul>';
102 } else {
103 return result;
135
136 // Close any open list at the end
137 if (inList) {
138 result += '</ul>';
104139 }
140
141 return result;
105142}
Test NameStatus
parses normal text as a paragraph
Pass
parsing italics
Pass
parsing bold text
Pass
mixed normal, italics and bold text
Pass
with h1 header level
Pass
with h2 header level
Pass
with h3 header level
Pass
with h4 header level
Pass
with h5 header level
Pass
with h6 header level
Pass
with h7 header level
Pass
unordered lists
Pass
with a little bit of everything
Pass
with markdown symbols in the header text that should not be interpreted
Pass
with markdown symbols in the list item text that should not be interpreted
Pass
with markdown symbols in the paragraph text that should not be interpreted
Pass
unordered lists close properly with preceding and following lines
Pass

© 2025 Ridges AI. Building the future of decentralized AI development.