weekends

Finished
1// Constants for better readability
2const MAX_HEADER_LEVEL = 6;
3const LIST_ITEM_MARKER = '*';
4const BOLD_DELIMITER = '__';
5const ITALIC_DELIMITER = '_';
6const HTML_TAGS = {
7 BOLD: 'strong',
8 ITALIC: 'em',
9 PARAGRAPH: 'p',
10 LIST: 'ul',
11 LIST_ITEM: 'li'
12};
13
14// Text formatting utilities
115function wrap(text, tag) {
216 return `<${tag}>${text}</${tag}>`;
317}
418
5function isTag(text, tag) {
6 return text.startsWith(`<${tag}>`);
7}
8
919function parser(markdown, delimiter, tag) {
1020 const pattern = new RegExp(`${delimiter}(.+)${delimiter}`);
1121 const replacement = `<${tag}>$1</${tag}>`;
1323}
1424
1525function parse__(markdown) {
16 return parser(markdown, '__', 'strong');
26 return parser(markdown, BOLD_DELIMITER, HTML_TAGS.BOLD);
1727}
1828
1929function parse_(markdown) {
20 return parser(markdown, '_', 'em');
30 return parser(markdown, ITALIC_DELIMITER, HTML_TAGS.ITALIC);
2131}
2232
33// Text parsing with emphasis and strong formatting
2334function parseText(markdown, list) {
2435 const parsedText = parse_(parse__(markdown));
2536 if (list) {
2637 return parsedText;
2738 } else {
28 return wrap(parsedText, 'p');
39 return wrap(parsedText, HTML_TAGS.PARAGRAPH);
2940 }
3041}
3142
43// Parse markdown headers (# Header, ## Header, etc.)
3244function parseHeader(markdown, list) {
3345 let count = 0;
3446 for (let i = 0; i < markdown.length; i++) {
3850 break;
3951 }
4052 }
41 if (count === 0 || count > 6) {
53 if (count === 0 || count > MAX_HEADER_LEVEL) {
4254 return [null, list];
4355 }
4456 const headerTag = `h${count}`;
4557 const headerHtml = wrap(markdown.substring(count + 1), headerTag);
4658 if (list) {
47 return [`</ul>${headerHtml}`, false];
59 return [`</${HTML_TAGS.LIST}>${headerHtml}`, false];
4860 } else {
4961 return [headerHtml, false];
5062 }
5163}
5264
65// Parse markdown list items (* item)
5366function parseLineItem(markdown, list) {
54 if (markdown.startsWith('*')) {
55 const innerHtml = wrap(parseText(markdown.substring(2), true), 'li');
67 if (markdown.startsWith(LIST_ITEM_MARKER)) {
68 const innerHtml = wrap(parseText(markdown.substring(2), true), HTML_TAGS.LIST_ITEM);
5669 if (list) {
5770 return [innerHtml, true];
5871 } else {
59 return [`<ul>${innerHtml}`, true];
72 return [`<${HTML_TAGS.LIST}>${innerHtml}`, true];
6073 }
6174 }
6275 return [null, list];
6376}
6477
78// Parse markdown paragraphs
6579function parseParagraph(markdown, list) {
6680 if (!list) {
6781 return [parseText(markdown, false), false];
6882 } else {
69 return [`</ul>${parseText(markdown, false)}`, false];
83 return [`</${HTML_TAGS.LIST}>${parseText(markdown, false)}`, false];
7084 }
7185}
7286
87// Main line parsing logic - tries different parsing strategies in order
7388function parseLine(markdown, list) {
89 // Try parsing as header first
7490 let [result, inListAfter] = parseHeader(markdown, list);
91
92 // If not a header, try parsing as list item
7593 if (result === null) {
7694 [result, inListAfter] = parseLineItem(markdown, list);
7795 }
96
97 // If not a list item, parse as paragraph
7898 if (result === null) {
7999 [result, inListAfter] = parseParagraph(markdown, list);
80100 }
101
102 // This should never happen with valid input
81103 if (result === null) {
82 throw new Error('Remove this line and implement the function');
104 throw new Error('Invalid markdown line');
83105 }
106
84107 return [result, inListAfter];
85108}
86109
87110/**
88 * @param {string} markdown
89 * @returns {string}
111 * Main entry point for parsing markdown to HTML
112 * @param {string} markdown - The markdown text to parse
113 * @returns {string} - The resulting HTML
90114 */
91115export function parse(markdown) {
92116 const lines = markdown.split('\n');
93117 let result = '';
94118 let list = false;
119
120 // Process each line of markdown
95121 for (let i = 0; i < lines.length; i++) {
96122 let [lineResult, newList] = parseLine(lines[i], list);
97123 result += lineResult;
98124 list = newList;
99125 }
126
127 // Close any open list tags
100128 if (list) {
101 return result + '</ul>';
129 return result + `</${HTML_TAGS.LIST}>`;
102130 } else {
103131 return result;
104132 }
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.