sn62

Finished
1function wrap(text, tag) {
2 return `<${tag}>${text}</${tag}>`;
3}
1/**
2 * Refactored Markdown Parser
3 *
4 * This refactored version improves readability and maintainability while preserving
5 * identical functionality to the original implementation.
6 *
7 * Key improvements:
8 * 1. Better separation of concerns - each function has a single responsibility
9 * 2. Clearer state management - using a class to encapsulate parsing state
10 * 3. More readable control flow - eliminated complex null-checking chains
11 * 4. Consistent naming conventions
12 * 5. Better organization of helper functions
13 */
414
5function isTag(text, tag) {
6 return text.startsWith(`<${tag}>`);
7}
15class MarkdownParser {
16 constructor() {
17 this.inList = false;
18 }
819
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}
20 /**
21 * Wraps text with HTML tags
22 * @param {string} text - Text to wrap
23 * @param {string} tag - HTML tag to use
24 * @returns {string} Wrapped HTML
25 */
26 wrap(text, tag) {
27 return `<${tag}>${text}</${tag}>`;
28 }
1429
15function parse__(markdown) {
16 return parser(markdown, '__', 'strong');
17}
30 /**
31 * Parses bold text (__) to <strong> tags
32 * @param {string} markdown - Markdown text
33 * @returns {string} HTML with <strong> tags
34 */
35 parseBold(markdown) {
36 const pattern = /__(.+)__/;
37 return markdown.replace(pattern, '<strong>$1</strong>');
38 }
1839
19function parse_(markdown) {
20 return parser(markdown, '_', 'em');
21}
40 /**
41 * Parses italic text (_) to <em> tags
42 * @param {string} markdown - Markdown text
43 * @returns {string} HTML with <em> tags
44 */
45 parseItalic(markdown) {
46 const pattern = /_(.+)_/;
47 return markdown.replace(pattern, '<em>$1</em>');
48 }
2249
23function parseText(markdown, list) {
24 const parsedText = parse_(parse__(markdown));
25 if (list) {
26 return parsedText;
27 } else {
28 return wrap(parsedText, 'p');
50 /**
51 * Parses inline formatting (bold and italic)
52 * @param {string} markdown - Markdown text
53 * @returns {string} HTML with inline formatting
54 */
55 parseInlineFormatting(markdown) {
56 return this.parseItalic(this.parseBold(markdown));
2957 }
30}
3158
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;
59 /**
60 * Parses header markdown (# Header) to HTML headers (h1-h6)
61 * @param {string} markdown - Markdown line
62 * @returns {string|null} HTML header or null if not a header
63 */
64 parseHeader(markdown) {
65 let count = 0;
66 for (let i = 0; i < markdown.length; i++) {
67 if (markdown[i] === '#') {
68 count += 1;
69 } else {
70 break;
71 }
3972 }
73
74 // Not a header or invalid header level
75 if (count === 0 || count > 6) {
76 return null;
77 }
78
79 const headerTag = `h${count}`;
80 // Note: Headers do not apply inline formatting in the original implementation
81 const headerContent = markdown.substring(count + 1);
82 const headerHtml = this.wrap(headerContent, headerTag);
83
84 // Close list if we're currently in one
85 if (this.inList) {
86 this.inList = false;
87 return `</ul>${headerHtml}`;
88 }
89
90 return headerHtml;
4091 }
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}
5292
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];
93 /**
94 * Parses list items (* item) to HTML list items
95 * @param {string} markdown - Markdown line
96 * @returns {string|null} HTML list item or null if not a list item
97 */
98 parseListItem(markdown) {
99 if (!markdown.startsWith('*')) {
100 return null;
101 }
102
103 const itemContent = markdown.substring(2);
104 const formattedContent = this.parseInlineFormatting(itemContent);
105 const listItemHtml = this.wrap(formattedContent, 'li');
106
107 // Open list if we're not already in one
108 if (!this.inList) {
109 this.inList = true;
110 return `<ul>${listItemHtml}`;
60111 }
112
113 return listItemHtml;
61114 }
62 return [null, list];
63}
64115
65function parseParagraph(markdown, list) {
66 if (!list) {
67 return [parseText(markdown, false), false];
68 } else {
69 return [`</ul>${parseText(markdown, false)}`, false];
116 /**
117 * Parses paragraph text to HTML paragraphs
118 * @param {string} markdown - Markdown line
119 * @returns {string} HTML paragraph
120 */
121 parseParagraph(markdown) {
122 const formattedText = this.parseInlineFormatting(markdown);
123
124 // Close list if we're currently in one
125 if (this.inList) {
126 this.inList = false;
127 return `</ul>${this.wrap(formattedText, 'p')}`;
128 }
129
130 return this.wrap(formattedText, 'p');
70131 }
71}
72132
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');
133 /**
134 * Parses a single line of markdown
135 * @param {string} line - Markdown line
136 * @returns {string} HTML output
137 */
138 parseLine(line) {
139 // Try to parse as header
140 const headerResult = this.parseHeader(line);
141 if (headerResult !== null) {
142 return headerResult;
143 }
144
145 // Try to parse as list item
146 const listItemResult = this.parseListItem(line);
147 if (listItemResult !== null) {
148 return listItemResult;
149 }
150
151 // Parse as paragraph (default)
152 return this.parseParagraph(line);
83153 }
84 return [result, inListAfter];
85154}
86155
87156/**
88 * @param {string} markdown
89 * @returns {string}
157 * Parses markdown text to HTML
158 * @param {string} markdown - Markdown text to parse
159 * @returns {string} HTML output
90160 */
91161export function parse(markdown) {
162 const parser = new MarkdownParser();
92163 const lines = markdown.split('\n');
93164 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;
165
166 for (const line of lines) {
167 result += parser.parseLine(line);
99168 }
100 if (list) {
101 return result + '</ul>';
102 } else {
103 return result;
169
170 // Close any open list at the end
171 if (parser.inList) {
172 result += '</ul>';
104173 }
174
175 return result;
105176}
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.