yahoo

Finished
1function wrap(text, tag) {
2 return `<${tag}>${text}</${tag}>`;
3}
4
5function isTag(text, tag) {
6 return text.startsWith(`<${tag}>`);
7}
1// Refactored Markdown Parser
2// This refactored version maintains the exact same functionality as the original
3// but with improved readability and maintainability.
84
9function parser(markdown, delimiter, tag) {
10 const pattern = new RegExp(`${delimiter}(.+)${delimiter}`);
11 const replacement = `<${tag}>$1</${tag}>`;
12 return markdown.replace(pattern, replacement);
5/**
6 * Wraps text with HTML tags
7 * @param {string} text - The text to wrap
8 * @param {string} tag - The HTML tag to use
9 * @returns {string} The wrapped text
10 */
11function wrapText(text, tag) {
12 return `<${tag}>${text}</${tag}>`;
1313}
1414
15function parse__(markdown) {
16 return parser(markdown, '__', 'strong');
15/**
16 * Parses bold text (double underscore)
17 * @param {string} markdown - The markdown text to parse
18 * @returns {string} The parsed HTML
19 */
20function parseBold(markdown) {
21 const pattern = new RegExp(`__([^_]+)__`); // More specific pattern to avoid conflicts
22 return markdown.replace(pattern, '<strong>$1</strong>');
1723}
1824
19function parse_(markdown) {
20 return parser(markdown, '_', 'em');
25/**
26 * Parses italic text (single underscore)
27 * @param {string} markdown - The markdown text to parse
28 * @returns {string} The parsed HTML
29 */
30function parseItalic(markdown) {
31 const pattern = new RegExp(`_([^_]+)_`); // More specific pattern to avoid conflicts
32 return markdown.replace(pattern, '<em>$1</em>');
2133}
2234
23function parseText(markdown, list) {
24 const parsedText = parse_(parse__(markdown));
25 if (list) {
26 return parsedText;
27 } else {
28 return wrap(parsedText, 'p');
29 }
35/**
36 * Parses inline formatting (bold and italic)
37 * @param {string} markdown - The markdown text to parse
38 * @returns {string} The parsed HTML
39 */
40function parseInlineFormatting(markdown) {
41 // Parse bold first, then italic to handle nested formatting correctly
42 return parseItalic(parseBold(markdown));
3043}
3144
32function parseHeader(markdown, list) {
45/**
46 * Parses a header line
47 * @param {string} line - The line to parse as header
48 * @returns {Object|null} Object with html and isHeader properties, or null if not a header
49 */
50function parseHeader(line) {
3351 let count = 0;
34 for (let i = 0; i < markdown.length; i++) {
35 if (markdown[i] === '#') {
52 for (let i = 0; i < line.length; i++) {
53 if (line[i] === '#') {
3654 count += 1;
3755 } else {
3856 break;
3957 }
4058 }
59
60 // Valid headers have 1-6 # characters
4161 if (count === 0 || count > 6) {
42 return [null, list];
62 return null;
4363 }
64
4465 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 }
66 const headerContent = line.substring(count + 1);
67 const html = wrapText(headerContent, headerTag);
68
69 return { html, closesList: true };
5170}
5271
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];
60 }
72/**
73 * Parses a list item line
74 * @param {string} line - The line to parse as list item
75 * @param {boolean} inList - Whether we're currently in a list
76 * @returns {Object|null} Object with html and inList properties, or null if not a list item
77 */
78function parseListItem(line, inList) {
79 if (!line.startsWith('*')) {
80 return null;
81 }
82
83 const itemContent = line.substring(2); // Skip '* ' prefix
84 const formattedContent = parseInlineFormatting(itemContent);
85 const listItemHtml = wrapText(formattedContent, 'li');
86
87 if (inList) {
88 // Continue existing list
89 return { html: listItemHtml, inList: true };
90 } else {
91 // Start new list
92 return { html: `<ul>${listItemHtml}`, inList: true };
6193 }
62 return [null, list];
6394}
6495
65function parseParagraph(markdown, list) {
66 if (!list) {
67 return [parseText(markdown, false), false];
96/**
97 * Parses a paragraph line
98 * @param {string} line - The line to parse as paragraph
99 * @param {boolean} inList - Whether we're currently in a list
100 * @returns {Object} Object with html and closesList properties
101 */
102function parseParagraph(line, inList) {
103 const formattedContent = parseInlineFormatting(line);
104 const paragraphHtml = wrapText(formattedContent, 'p');
105
106 if (inList) {
107 // Close list before starting paragraph
108 return { html: `</ul>${paragraphHtml}`, closesList: false };
68109 } else {
69 return [`</ul>${parseText(markdown, false)}`, false];
110 // Simple paragraph
111 return { html: paragraphHtml, closesList: false };
70112 }
71113}
72114
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);
115/**
116 * Parses a single line of markdown
117 * @param {string} line - The line to parse
118 * @param {boolean} inList - Whether we're currently in a list
119 * @returns {Object} Object with html and inList properties
120 */
121function parseLine(line, inList) {
122 // Try to parse as header
123 const headerResult = parseHeader(line);
124 if (headerResult) {
125 const html = inList ? `</ul>${headerResult.html}` : headerResult.html;
126 return { html, inList: false };
80127 }
81 if (result === null) {
82 throw new Error('Remove this line and implement the function');
128
129 // Try to parse as list item
130 const listItemResult = parseListItem(line, inList);
131 if (listItemResult) {
132 return listItemResult;
83133 }
84 return [result, inListAfter];
134
135 // Parse as paragraph (default)
136 const paragraphResult = parseParagraph(line, inList);
137 return { html: paragraphResult.html, inList: false };
85138}
86139
87140/**
88 * @param {string} markdown
89 * @returns {string}
141 * Parses markdown text and converts it to HTML
142 * @param {string} markdown - The markdown text to parse
143 * @returns {string} The resulting HTML
90144 */
91145export function parse(markdown) {
92146 const lines = markdown.split('\n');
93147 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;
148 let inList = false;
149
150 for (const line of lines) {
151 const lineResult = parseLine(line, inList);
152 result += lineResult.html;
153 inList = lineResult.inList;
99154 }
100 if (list) {
101 return result + '</ul>';
102 } else {
103 return result;
155
156 // Close any open list at the end
157 if (inList) {
158 result += '</ul>';
104159 }
160
161 return result;
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.