Merry

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 * Parses Markdown syntax and converts it to HTML.
3 *
4 * Refactored for better readability and maintainability while preserving exact functionality.
5 *
6 * Features supported:
7 * - Headers (# Header 1, ## Header 2, etc.)
8 * - Paragraphs (normal text)
9 * - Unordered lists (* item)
10 * - Bold text (__bold__ or **bold**)
11 * - Italic text (_italic_ or *italic*)
12 *
13 * @param {string} markdown - The markdown text to parse
14 * @returns {string} - The resulting HTML
15 */
16export function parse(markdown) {
17 const lines = markdown.split('\n');
18 let html = '';
19 let inList = false;
20
21 for (const line of lines) {
22 let lineHtml = '';
23
24 // Handle headers (# Header 1, ## Header 2, etc.)
25 // Match exactly 1-6 # characters at the start of the line, followed by space or end of line
26 const headerMatch = line.match(/^(#{1,6})(?:\s(.*))?$/);
27 if (headerMatch) {
28 const level = headerMatch[1].length;
29 // Content is either the captured group or empty string
30 const content = headerMatch[2] || '';
31 lineHtml = `<h${level}>${parseInlineFormatting(content)}</h${level}>`;
32
33 // Close list if we were in one
34 if (inList) {
35 lineHtml = '</ul>' + lineHtml;
36 inList = false;
37 }
3938 }
40 }
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];
50 }
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];
39 // Handle list items (* item)
40 // Match lines starting with * followed by any character (including space or empty)
41 else if (line.match(/^\*(.*)$/)) {
42 // Remove leading spaces and get content
43 const contentMatch = line.match(/^\*\s*(.*)$/);
44 const content = contentMatch ? contentMatch[1] : '';
45 const listItemHtml = `<li>${parseInlineFormatting(content)}</li>`;
46
47 if (!inList) {
48 lineHtml = '<ul>' + listItemHtml;
49 inList = true;
50 } else {
51 lineHtml = listItemHtml;
52 }
6053 }
54 // Handle paragraphs (normal text)
55 else {
56 // Close list if we were in one
57 if (inList) {
58 html += '</ul>';
59 inList = false;
60 }
61
62 // Empty lines become empty paragraphs
63 if (line === '') {
64 lineHtml = '<p></p>';
65 } else {
66 lineHtml = `<p>${parseInlineFormatting(line)}</p>`;
67 }
68 }
69
70 html += lineHtml;
6171 }
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];
70 }
71}
72
73function parseLine(markdown, list) {
74 let [result, inListAfter] = parseHeader(markdown, list);
75 if (result === null) {
76 [result, inListAfter] = parseLineItem(markdown, list);
77 }
78 if (result === null) {
79 [result, inListAfter] = parseParagraph(markdown, list);
80 }
81 if (result === null) {
82 throw new Error('Remove this line and implement the function');
72
73 // Close any open list at the end
74 if (inList) {
75 html += '</ul>';
8376 }
84 return [result, inListAfter];
77
78 return html;
8579}
8680
8781/**
88 * @param {string} markdown
89 * @returns {string}
82 * Parses inline formatting elements like bold and italic text.
83 *
84 * @param {string} text - The text to parse for inline formatting
85 * @returns {string} - The text with HTML tags for formatting
9086 */
91export 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 }
87function parseInlineFormatting(text) {
88 // Parse bold text (__text__ or **text**)
89 text = text.replace(/(__|\*\*)(.*?)\1/g, '<strong>$2</strong>');
90
91 // Parse italic text (_text_ or *text*)
92 text = text.replace(/([_*])(.*?)\1/g, '<em>$2</em>');
93
94 return text;
10595}
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.