let it go

Finished
1// Helper function to wrap text in HTML tags
12function wrap(text, tag) {
23 return `<${tag}>${text}</${tag}>`;
34}
45
6// Helper function to check if text starts with a specific HTML tag
57function isTag(text, tag) {
68 return text.startsWith(`<${tag}>`);
79}
810
9function parser(markdown, delimiter, tag) {
11// Generic parser for delimiter-based markdown (bold, italic, etc.)
12function parseDelimitedText(markdown, delimiter, tag) {
1013 const pattern = new RegExp(`${delimiter}(.+)${delimiter}`);
1114 const replacement = `<${tag}>$1</${tag}>`;
1215 return markdown.replace(pattern, replacement);
1316}
1417
15function parse__(markdown) {
16 return parser(markdown, '__', 'strong');
18// Parse bold text using __ delimiter
19function parseBoldText(markdown) {
20 return parseDelimitedText(markdown, '__', 'strong');
1721}
1822
19function parse_(markdown) {
20 return parser(markdown, '_', 'em');
23// Parse italic text using _ delimiter
24function parseItalicText(markdown) {
25 return parseDelimitedText(markdown, '_', 'em');
2126}
2227
28// Parse text with inline formatting (bold, italic)
2329function parseText(markdown, list) {
24 const parsedText = parse_(parse__(markdown));
30 const parsedText = parseItalicText(parseBoldText(markdown));
2531 if (list) {
2632 return parsedText;
2733 } else {
2935 }
3036}
3137
38// Parse markdown header (#, ##, ###, etc.)
3239function parseHeader(markdown, list) {
40 // Count consecutive # characters at the beginning
3341 let count = 0;
3442 for (let i = 0; i < markdown.length; i++) {
3543 if (markdown[i] === '#') {
3846 break;
3947 }
4048 }
49
50 // Valid headers have 1-6 # characters
4151 if (count === 0 || count > 6) {
4252 return [null, list];
4353 }
54
55 // Create appropriate header tag (h1, h2, h3, etc.)
4456 const headerTag = `h${count}`;
45 const headerHtml = wrap(markdown.substring(count + 1), headerTag);
57 const headerContent = markdown.substring(count + 1); // Skip # and space
58 const headerHtml = wrap(headerContent, headerTag);
59
60 // Close any open list before starting header
4661 if (list) {
4762 return [`</ul>${headerHtml}`, false];
4863 } else {
5065 }
5166}
5267
68// Parse markdown list item (* item)
5369function parseLineItem(markdown, list) {
5470 if (markdown.startsWith('*')) {
55 const innerHtml = wrap(parseText(markdown.substring(2), true), 'li');
71 // Parse the content of the list item (skip '* ' prefix)
72 const listItemContent = markdown.substring(2);
73 const parsedContent = parseText(listItemContent, true); // true indicates we're in a list
74 const listItemHtml = wrap(parsedContent, 'li');
75
76 // Either continue existing list or start a new one
5677 if (list) {
57 return [innerHtml, true];
78 return [listItemHtml, true];
5879 } else {
59 return [`<ul>${innerHtml}`, true];
80 return [`<ul>${listItemHtml}`, true];
6081 }
6182 }
6283 return [null, list];
6384}
6485
86// Parse markdown paragraph (regular text)
6587function parseParagraph(markdown, list) {
6688 if (!list) {
89 // Regular paragraph - just wrap in p tags
6790 return [parseText(markdown, false), false];
6891 } else {
92 // Close any open list before starting paragraph
6993 return [`</ul>${parseText(markdown, false)}`, false];
7094 }
7195}
7296
97// Parse a single line of markdown by trying different parsers in order
7398function parseLine(markdown, list) {
99 // Try parsing as header first
74100 let [result, inListAfter] = parseHeader(markdown, list);
101
102 // If not a header, try parsing as list item
75103 if (result === null) {
76104 [result, inListAfter] = parseLineItem(markdown, list);
77105 }
106
107 // If not a list item, parse as paragraph
78108 if (result === null) {
79109 [result, inListAfter] = parseParagraph(markdown, list);
80110 }
111
112 // This should never happen with valid input
81113 if (result === null) {
82 throw new Error('Remove this line and implement the function');
114 throw new Error('Invalid markdown line: ' + markdown);
83115 }
116
84117 return [result, inListAfter];
85118}
86119
87120/**
88 * @param {string} markdown
89 * @returns {string}
121 * Parse markdown text and convert to HTML
122 * @param {string} markdown - The markdown text to parse
123 * @returns {string} The resulting HTML
90124 */
91125export function parse(markdown) {
92126 const lines = markdown.split('\n');
93127 let result = '';
94 let list = false;
128 let inList = false; // Track whether we're currently inside a list
129
130 // Process each line of markdown
95131 for (let i = 0; i < lines.length; i++) {
96 let [lineResult, newList] = parseLine(lines[i], list);
132 const currentLine = lines[i];
133 let [lineResult, newInList] = parseLine(currentLine, inList);
134
97135 result += lineResult;
98 list = newList;
136 inList = newInList;
99137 }
100 if (list) {
138
139 // Close any unclosed list at the end
140 if (inList) {
101141 return result + '</ul>';
102142 } else {
103143 return result;
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.