great

Finished
1function wrap(text, tag) {
1// MARKDOWN PARSER - REFACTORED VERSION
2//
3// Refactored to improve readability and maintainability while preserving exact functionality.
4// Key improvements:
5// 1. More descriptive function names (parse__ -> parseBoldText, parse_ -> parseItalicText, etc.)
6// 2. Clearer variable names (list -> isInList, count -> hashCount)
7// 3. Better separation of concerns with dedicated functions for each parsing task
8// 4. Comprehensive comments explaining the purpose and logic of each function
9// 5. Improved control flow with clearer priority order for parsing
10// 6. Enhanced documentation with JSDoc comments
11
12// Utility function to wrap text in HTML tags
13function wrapTextInTag(text, tag) {
214 return `<${tag}>${text}</${tag}>`;
315}
416
5function isTag(text, tag) {
17// Check if text starts with a specific HTML tag
18function startsWithTag(text, tag) {
619 return text.startsWith(`<${tag}>`);
720}
821
9function parser(markdown, delimiter, tag) {
22// Generic parser for emphasis patterns (bold, italic)
23function parseEmphasis(markdown, delimiter, htmlTag) {
1024 const pattern = new RegExp(`${delimiter}(.+)${delimiter}`);
11 const replacement = `<${tag}>$1</${tag}>`;
25 const replacement = `<${htmlTag}>$1</${htmlTag}>`;
1226 return markdown.replace(pattern, replacement);
1327}
1428
15function parse__(markdown) {
16 return parser(markdown, '__', 'strong');
29// Parse bold text using __ delimiter
30function parseBoldText(markdown) {
31 return parseEmphasis(markdown, '__', 'strong');
1732}
1833
19function parse_(markdown) {
20 return parser(markdown, '_', 'em');
34// Parse italic text using _ delimiter
35function parseItalicText(markdown) {
36 return parseEmphasis(markdown, '_', 'em');
2137}
2238
23function parseText(markdown, list) {
24 const parsedText = parse_(parse__(markdown));
25 if (list) {
26 return parsedText;
27 } else {
28 return wrap(parsedText, 'p');
29 }
39// Parse inline formatting (bold and italic) within text
40function parseInlineFormatting(markdown) {
41 return parseItalicText(parseBoldText(markdown));
42}
43
44// Process text content with inline formatting
45function processTextContent(markdown, isInsideList) {
46 const formattedText = parseInlineFormatting(markdown);
47 return isInsideList ? formattedText : wrapTextInTag(formattedText, 'p');
3048}
3149
32function parseHeader(markdown, list) {
33 let count = 0;
50// Parse markdown headers (#, ##, ###, etc.)
51function parseHeader(markdown, isInList) {
52 // Count consecutive # characters at the beginning
53 let hashCount = 0;
3454 for (let i = 0; i < markdown.length; i++) {
3555 if (markdown[i] === '#') {
36 count += 1;
56 hashCount += 1;
3757 } else {
3858 break;
3959 }
4060 }
41 if (count === 0 || count > 6) {
42 return [null, list];
61
62 // Valid headers have 1-6 # characters
63 if (hashCount === 0 || hashCount > 6) {
64 return [null, isInList];
4365 }
44 const headerTag = `h${count}`;
45 const headerHtml = wrap(markdown.substring(count + 1), headerTag);
46 if (list) {
66
67 // Create appropriate header tag (h1, h2, etc.)
68 const headerTag = `h${hashCount}`;
69 const headerContent = markdown.substring(hashCount + 1);
70 const headerHtml = wrapTextInTag(headerContent, headerTag);
71
72 // If we were in a list, close it before the header
73 if (isInList) {
4774 return [`</ul>${headerHtml}`, false];
4875 } else {
4976 return [headerHtml, false];
5077 }
5178}
5279
53function parseLineItem(markdown, list) {
80// Parse list items starting with *
81function parseListItem(markdown, isInList) {
5482 if (markdown.startsWith('*')) {
55 const innerHtml = wrap(parseText(markdown.substring(2), true), 'li');
56 if (list) {
57 return [innerHtml, true];
83 const listItemContent = markdown.substring(2); // Skip '* ' prefix
84 const formattedContent = processTextContent(listItemContent, true);
85 const listItemHtml = wrapTextInTag(formattedContent, 'li');
86
87 // If we weren't in a list, start a new one
88 if (isInList) {
89 return [listItemHtml, true];
5890 } else {
59 return [`<ul>${innerHtml}`, true];
91 return [`<ul>${listItemHtml}`, true];
6092 }
6193 }
62 return [null, list];
94 return [null, isInList];
6395}
6496
65function parseParagraph(markdown, list) {
66 if (!list) {
67 return [parseText(markdown, false), false];
97// Parse regular paragraph text
98function parseParagraph(markdown, isInList) {
99 const paragraphHtml = processTextContent(markdown, false);
100
101 // If we were in a list, close it before the paragraph
102 if (isInList) {
103 return [`</ul>${paragraphHtml}`, false];
68104 } else {
69 return [`</ul>${parseText(markdown, false)}`, false];
105 return [paragraphHtml, false];
70106 }
71107}
72108
73function parseLine(markdown, list) {
74 let [result, inListAfter] = parseHeader(markdown, list);
109// Parse a single line of markdown, determining its type and converting to HTML
110// Returns [htmlResult, isInListAfterParsing]
111function parseLine(markdown, isInList) {
112 // Try parsing as header first (highest priority)
113 let [result, newListState] = parseHeader(markdown, isInList);
114
115 // If not a header, try parsing as list item
75116 if (result === null) {
76 [result, inListAfter] = parseLineItem(markdown, list);
117 [result, newListState] = parseListItem(markdown, isInList);
77118 }
119
120 // If not a list item, parse as paragraph (fallback)
78121 if (result === null) {
79 [result, inListAfter] = parseParagraph(markdown, list);
122 [result, newListState] = parseParagraph(markdown, isInList);
80123 }
124
125 // This should never happen as paragraph parsing always succeeds
81126 if (result === null) {
82 throw new Error('Remove this line and implement the function');
127 throw new Error('Unable to parse markdown line');
83128 }
84 return [result, inListAfter];
129
130 return [result, newListState];
85131}
86132
87133/**
88 * @param {string} markdown
89 * @returns {string}
134 * Parse markdown text and convert it to HTML
135 * @param {string} markdown - The markdown text to parse
136 * @returns {string} - The resulting HTML
90137 */
91138export function parse(markdown) {
92139 const lines = markdown.split('\n');
93 let result = '';
94 let list = false;
140 let htmlResult = '';
141 let isInList = false;
142
143 // Process each line of markdown
95144 for (let i = 0; i < lines.length; i++) {
96 let [lineResult, newList] = parseLine(lines[i], list);
97 result += lineResult;
98 list = newList;
145 const [lineHtml, newListState] = parseLine(lines[i], isInList);
146 htmlResult += lineHtml;
147 isInList = newListState;
99148 }
100 if (list) {
101 return result + '</ul>';
149
150 // Close any open list at the end
151 if (isInList) {
152 return htmlResult + '</ul>';
102153 } else {
103 return result;
154 return htmlResult;
104155 }
105156}
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.