sn62

Finished
1function wrap(text, tag) {
2 return `<${tag}>${text}</${tag}>`;
3}
4
5function isTag(text, tag) {
6 return text.startsWith(`<${tag}>`);
7}
8
9function parser(markdown, delimiter, tag) {
10 const pattern = new RegExp(`${delimiter}(.+)${delimiter}`);
11 const replacement = `<${tag}>$1</${tag}>`;
12 return markdown.replace(pattern, replacement);
13}
14
15function parse__(markdown) {
16 return parser(markdown, '__', 'strong');
17}
18
19function parse_(markdown) {
20 return parser(markdown, '_', 'em');
21}
22
23function parseText(markdown, list) {
24 const parsedText = parse_(parse__(markdown));
25 if (list) {
26 return parsedText;
27 } else {
28 return wrap(parsedText, 'p');
29 }
30}
31
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;
1/**
2 * A Markdown parser that converts Markdown syntax to HTML
3 */
4class MarkdownParser {
5 /**
6 * Parse Markdown text and convert it to HTML
7 * @param {string} markdown - The Markdown text to parse
8 * @returns {string} - The resulting HTML
9 */
10 parse(markdown) {
11 const lines = markdown.split('\n');
12 let html = '';
13 let inList = false;
14
15 for (const line of lines) {
16 // Handle headers (#, ##, ###, etc.)
17 if (this.isHeader(line)) {
18 // Close any open list before starting a header
19 if (inList) {
20 html += '</ul>';
21 inList = false;
22 }
23 html += this.parseHeader(line);
24 }
25 // Handle list items (* item)
26 else if (this.isListItem(line)) {
27 // Open a new list if we're not already in one
28 if (!inList) {
29 html += '<ul>';
30 inList = true;
31 }
32 html += this.parseListItem(line);
33 }
34 // Handle regular paragraphs
35 else {
36 // Close any open list before starting a paragraph
37 if (inList) {
38 html += '</ul>';
39 inList = false;
40 }
41 html += this.parseParagraph(line);
42 }
3943 }
44
45 // Close any remaining open list
46 if (inList) {
47 html += '</ul>';
48 }
49
50 return html;
4051 }
41 if (count === 0 || count > 6) {
42 return [null, list];
43 }
44 const headerTag = `h${count}`;
45 const headerHtml = wrap(markdown.substring(count + 1), headerTag);
46 if (list) {
47 return [`</ul>${headerHtml}`, false];
48 } else {
49 return [headerHtml, false];
52
53 /**
54 * Check if a line is a header (starts with #)
55 * @param {string} line - The line to check
56 * @returns {boolean} - True if the line is a header
57 */
58 isHeader(line) {
59 if (!line.startsWith('#')) return false;
60
61 // Count the number of # characters
62 let count = 0;
63 for (let i = 0; i < line.length; i++) {
64 if (line[i] === '#') {
65 count++;
66 } else {
67 break;
68 }
69 }
70
71 // Valid headers have 1-6 # characters
72 return count > 0 && count <= 6;
5073 }
51}
52
53function parseLineItem(markdown, list) {
54 if (markdown.startsWith('*')) {
55 const innerHtml = wrap(parseText(markdown.substring(2), true), 'li');
56 if (list) {
57 return [innerHtml, true];
58 } else {
59 return [`<ul>${innerHtml}`, true];
74
75 /**
76 * Parse a header line into HTML
77 * @param {string} line - The header line to parse
78 * @returns {string} - The HTML for the header
79 */
80 parseHeader(line) {
81 // Count the number of # characters
82 let count = 0;
83 for (let i = 0; i < line.length; i++) {
84 if (line[i] === '#') {
85 count++;
86 } else {
87 break;
88 }
6089 }
90
91 // Extract the header text (skip the # characters and the following space)
92 const headerText = line.substring(count + 1);
93 const headerTag = `h${count}`;
94
95 return this.wrap(this.parseInlineFormatting(headerText), headerTag);
6196 }
62 return [null, list];
63}
64
65function parseParagraph(markdown, list) {
66 if (!list) {
67 return [parseText(markdown, false), false];
68 } else {
69 return [`</ul>${parseText(markdown, false)}`, false];
97
98 /**
99 * Check if a line is a list item (starts with * )
100 * @param {string} line - The line to check
101 * @returns {boolean} - True if the line is a list item
102 */
103 isListItem(line) {
104 return line.startsWith('* ');
70105 }
71}
72
73function parseLine(markdown, list) {
74 let [result, inListAfter] = parseHeader(markdown, list);
75 if (result === null) {
76 [result, inListAfter] = parseLineItem(markdown, list);
106
107 /**
108 * Parse a list item line into HTML
109 * @param {string} line - The list item line to parse
110 * @returns {string} - The HTML for the list item
111 */
112 parseListItem(line) {
113 // Extract the list item text (skip the '* ' prefix)
114 const itemText = line.substring(2);
115
116 return this.wrap(this.parseInlineFormatting(itemText), 'li');
77117 }
78 if (result === null) {
79 [result, inListAfter] = parseParagraph(markdown, list);
118
119 /**
120 * Parse a paragraph line into HTML
121 * @param {string} line - The paragraph line to parse
122 * @returns {string} - The HTML for the paragraph
123 */
124 parseParagraph(line) {
125 return this.wrap(this.parseInlineFormatting(line), 'p');
126 }
127
128 /**
129 * Parse inline formatting (__ for strong, _ for emphasis)
130 * @param {string} text - The text to parse
131 * @returns {string} - The text with inline formatting converted to HTML
132 */
133 parseInlineFormatting(text) {
134 // Parse strong text (__text__ -> <strong>text</strong>)
135 let result = text.replace(/__(.+)__/g, '<strong>$1</strong>');
136
137 // Parse emphasis text (_text_ -> <em>text</em>)
138 result = result.replace(/_(.+)_/g, '<em>$1</em>');
139
140 return result;
80141 }
81 if (result === null) {
82 throw new Error('Remove this line and implement the function');
142
143 /**
144 * Wrap text with HTML tags
145 * @param {string} text - The text to wrap
146 * @param {string} tag - The HTML tag to use
147 * @returns {string} - The wrapped text
148 */
149 wrap(text, tag) {
150 return `<${tag}>${text}</${tag}>`;
83151 }
84 return [result, inListAfter];
85152}
86153
87154/**
88 * @param {string} markdown
89 * @returns {string}
155 * Parse Markdown text and convert it to HTML
156 * @param {string} markdown - The Markdown text to parse
157 * @returns {string} - The resulting HTML
90158 */
91159export function parse(markdown) {
92 const lines = markdown.split('\n');
93 let result = '';
94 let list = false;
95 for (let i = 0; i < lines.length; i++) {
96 let [lineResult, newList] = parseLine(lines[i], list);
97 result += lineResult;
98 list = newList;
99 }
100 if (list) {
101 return result + '</ul>';
102 } else {
103 return result;
104 }
160 const parser = new MarkdownParser();
161 return parser.parse(markdown);
105162}
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.