uv

Finished
1212 return markdown.replace(pattern, replacement);
1313}
1414
15function parse__(markdown) {
15// Parse bold text (__text__ -> <strong>text</strong>)
16function parseBold(markdown) {
1617 return parser(markdown, '__', 'strong');
1718}
1819
19function parse_(markdown) {
20// Parse italic text (_text_ -> <em>text</em>)
21function parseItalic(markdown) {
2022 return parser(markdown, '_', 'em');
2123}
2224
25// Parse inline markdown elements (_ for italic, __ for bold) and wrap in paragraph tags if not in a list
2326function parseText(markdown, list) {
24 const parsedText = parse_(parse__(markdown));
27 const parsedText = parseItalic(parseBold(markdown));
2528 if (list) {
2629 return parsedText;
2730 } else {
2932 }
3033}
3134
35// Parse markdown headers (# to ######) and handle list state transitions
3236function parseHeader(markdown, list) {
33 let count = 0;
37 // Count consecutive # characters at the start
38 let headerLevel = 0;
3439 for (let i = 0; i < markdown.length; i++) {
3540 if (markdown[i] === '#') {
36 count += 1;
41 headerLevel += 1;
3742 } else {
3843 break;
3944 }
4045 }
41 if (count === 0 || count > 6) {
46
47 // Valid headers are # to ###### (1-6)
48 if (headerLevel === 0 || headerLevel > 6) {
4249 return [null, list];
4350 }
44 const headerTag = `h${count}`;
45 const headerHtml = wrap(markdown.substring(count + 1), headerTag);
51
52 const headerTag = `h${headerLevel}`;
53 const headerContent = markdown.substring(headerLevel + 1).trim();
54 const headerHtml = wrap(headerContent, headerTag);
55
56 // If we were in a list, close it before the header
4657 if (list) {
4758 return [`</ul>${headerHtml}`, false];
4859 } else {
5061 }
5162}
5263
64// Parse list items (* text) and handle list state transitions
5365function parseLineItem(markdown, list) {
5466 if (markdown.startsWith('*')) {
55 const innerHtml = wrap(parseText(markdown.substring(2), true), 'li');
67 const itemContent = markdown.substring(2); // Remove '* ' prefix
68 const innerHtml = wrap(parseText(itemContent, true), 'li');
69
70 // If we're already in a list, just add the item
71 // Otherwise, start a new list
5672 if (list) {
5773 return [innerHtml, true];
5874 } else {
6278 return [null, list];
6379}
6480
81// Parse regular paragraphs and handle list state transitions
6582function parseParagraph(markdown, list) {
6683 if (!list) {
84 // Not in a list, just parse as a paragraph
6785 return [parseText(markdown, false), false];
6886 } else {
87 // We were in a list, close it and start a paragraph
6988 return [`</ul>${parseText(markdown, false)}`, false];
7089 }
7190}
7291
92// Parse a single line of markdown by trying different parsers in order
93// Order: headers -> list items -> paragraphs
7394function parseLine(markdown, list) {
95 // Try parsing as header first
7496 let [result, inListAfter] = parseHeader(markdown, list);
97
98 // If not a header, try parsing as list item
7599 if (result === null) {
76100 [result, inListAfter] = parseLineItem(markdown, list);
77101 }
102
103 // If not a list item, parse as paragraph
78104 if (result === null) {
79105 [result, inListAfter] = parseParagraph(markdown, list);
80106 }
107
108 // This should never happen as one of the parsers should always match
81109 if (result === null) {
82 throw new Error('Remove this line and implement the function');
110 throw new Error('Unable to parse markdown line');
83111 }
112
84113 return [result, inListAfter];
85114}
86115
87116/**
88 * @param {string} markdown
89 * @returns {string}
117 * Parse markdown text and convert it to HTML
118 * @param {string} markdown - The markdown text to parse
119 * @returns {string} - The resulting HTML
90120 */
91121export function parse(markdown) {
92122 const lines = markdown.split('\n');
93 let result = '';
94 let list = false;
123 let htmlResult = '';
124 let inList = false; // Track if we're currently inside a list
125
126 // Process each line of markdown
95127 for (let i = 0; i < lines.length; i++) {
96 let [lineResult, newList] = parseLine(lines[i], list);
97 result += lineResult;
98 list = newList;
128 let [lineHtml, newListState] = parseLine(lines[i], inList);
129 htmlResult += lineHtml;
130 inList = newListState;
99131 }
100 if (list) {
101 return result + '</ul>';
132
133 // If we're still in a list at the end, close it
134 if (inList) {
135 return htmlResult + '</ul>';
102136 } else {
103 return result;
137 return htmlResult;
104138 }
105139}
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.