fistinz

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');
1/**
2 * Refactored Markdown Parser
3 *
4 * This class encapsulates all the logic for parsing Markdown syntax into HTML.
5 * The refactoring improves code organization, readability, and maintainability
6 * while preserving the exact same functionality.
7 */
8class MarkdownParser {
9 constructor() {
10 // State tracking for list context
11 this.inListContext = false;
12 // HTML output accumulator
13 this.htmlOutput = '';
2914 }
30}
3115
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;
16 /**
17 * Main parsing method that converts Markdown to HTML
18 * @param {string} markdown - The markdown text to parse
19 * @returns {string} - The resulting HTML
20 */
21 parse(markdown) {
22 // Reset state for new parsing operation
23 this.inListContext = false;
24 this.htmlOutput = '';
25
26 // Process each line separately
27 const lines = markdown.split('\n');
28
29 for (const line of lines) {
30 this.processLine(line);
31 }
32
33 // Close any open list at the end
34 if (this.inListContext) {
35 this.htmlOutput += '</ul>';
36 this.inListContext = false;
37 }
38
39 return this.htmlOutput;
40 }
41
42 /**
43 * Process a single line of markdown
44 * @param {string} line - A single line of markdown text
45 */
46 processLine(line) {
47 // Try to parse as header first
48 if (this.parseHeader(line)) {
49 return;
50 }
51
52 // Try to parse as list item
53 if (this.parseListItem(line)) {
54 return;
3955 }
56
57 // Default to paragraph
58 this.parseParagraph(line);
4059 }
41 if (count === 0 || count > 6) {
42 return [null, list];
60
61 /**
62 * Parse header markdown (# ## ### etc.)
63 * @param {string} line - Line to parse as header
64 * @returns {boolean} - True if line was parsed as header, false otherwise
65 */
66 parseHeader(line) {
67 // Count leading # characters
68 let headerLevel = 0;
69 for (let i = 0; i < line.length; i++) {
70 if (line[i] === '#') {
71 headerLevel++;
72 } else {
73 break;
74 }
75 }
76
77 // Valid headers have 1-6 # characters
78 if (headerLevel === 0 || headerLevel > 6) {
79 return false;
80 }
81
82 // Close any open list before starting header
83 if (this.inListContext) {
84 this.htmlOutput += '</ul>';
85 this.inListContext = false;
86 }
87
88 // Extract header content (skip # characters and space)
89 const headerContent = line.substring(headerLevel + 1);
90 const headerTag = `h${headerLevel}`;
91
92 // Add parsed header to output
93 this.htmlOutput += this.wrapHtml(headerContent, headerTag);
94 return true;
4395 }
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];
96
97 /**
98 * Parse list item markdown (* item)
99 * @param {string} line - Line to parse as list item
100 * @returns {boolean} - True if line was parsed as list item, false otherwise
101 */
102 parseListItem(line) {
103 if (!line.startsWith('* ')) {
104 return false;
105 }
106
107 // Open list if not already in list context
108 if (!this.inListContext) {
109 this.htmlOutput += '<ul>';
110 this.inListContext = true;
111 }
112
113 // Extract list item content (skip '* ' prefix)
114 const itemContent = line.substring(2);
115
116 // Add parsed list item to output
117 this.htmlOutput += this.wrapHtml(this.parseInlineText(itemContent), 'li');
118 return true;
50119 }
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];
120
121 /**
122 * Parse paragraph markdown (default case)
123 * @param {string} line - Line to parse as paragraph
124 */
125 parseParagraph(line) {
126 // Close any open list before starting paragraph
127 if (this.inListContext) {
128 this.htmlOutput += '</ul>';
129 this.inListContext = false;
60130 }
131
132 // Add parsed paragraph to output
133 this.htmlOutput += this.wrapHtml(this.parseInlineText(line), 'p');
61134 }
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];
135
136 /**
137 * Parse inline text formatting (__ for strong, _ for emphasis)
138 * @param {string} text - Text to parse for inline formatting
139 * @returns {string} - Text with inline formatting converted to HTML
140 */
141 parseInlineText(text) {
142 // Parse strong (__text__) first, then emphasis (_text_)
143 return this.parseEmphasis(this.parseStrong(text));
70144 }
71}
72
73function parseLine(markdown, list) {
74 let [result, inListAfter] = parseHeader(markdown, list);
75 if (result === null) {
76 [result, inListAfter] = parseLineItem(markdown, list);
145
146 /**
147 * Parse strong formatting (__text__ -> <strong>text</strong>)
148 * @param {string} text - Text to parse for strong formatting
149 * @returns {string} - Text with strong formatting converted to HTML
150 */
151 parseStrong(text) {
152 const pattern = /__(.+)__/;
153 return text.replace(pattern, '<strong>$1</strong>');
77154 }
78 if (result === null) {
79 [result, inListAfter] = parseParagraph(markdown, list);
155
156 /**
157 * Parse emphasis formatting (_text_ -> <em>text</em>)
158 * @param {string} text - Text to parse for emphasis formatting
159 * @returns {string} - Text with emphasis formatting converted to HTML
160 */
161 parseEmphasis(text) {
162 const pattern = /_(.+)_/;
163 return text.replace(pattern, '<em>$1</em>');
80164 }
81 if (result === null) {
82 throw new Error('Remove this line and implement the function');
165
166 /**
167 * Wrap content in HTML tags
168 * @param {string} content - Content to wrap
169 * @param {string} tag - HTML tag to wrap with
170 * @returns {string} - Content wrapped in HTML tags
171 */
172 wrapHtml(content, tag) {
173 return `<${tag}>${content}</${tag}>`;
83174 }
84 return [result, inListAfter];
85175}
86176
87177/**
88 * @param {string} markdown
89 * @returns {string}
178 * Parse markdown text and return HTML
179 * @param {string} markdown - The markdown text to parse
180 * @returns {string} - The resulting HTML
90181 */
91182export 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 }
183 const parser = new MarkdownParser();
184 return parser.parse(markdown);
105185}
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.