Skip to content

Instantly share code, notes, and snippets.

@BrandonClapp
Last active October 4, 2021 18:00
Show Gist options
  • Save BrandonClapp/3655520fa8ba2eaf64ebe26634d592d1 to your computer and use it in GitHub Desktop.
Save BrandonClapp/3655520fa8ba2eaf64ebe26634d592d1 to your computer and use it in GitHub Desktop.
// Simple Markdown Parser
// Write a function that accepts a string containing a simplified
// version of markdown-formatted text, and returns a string
// containing the corresponding HTML text. We only need to support
// headers, paragraphs, and unordered lists.
// For example, this markdown:
// # This is a title!
// ## This is a subheader
// This is a paragraph.
// It spans two lines.
// This is a separate paragraph. And next is a list:
// - One
// - Two
// - Three
// would yield this HTML:
// <html>
// <body>
// <h1>This is a title!</h1>
// <h2>This is a subheader</h2>
// <p>This is a paragraph. It spans two lines.</p>
// <p>This is a separate paragraph. And next is a list:</p>
// <ul>
// <li>One</li>
// <li>Two</li>
// <li>Three</li>
// </ul>
// </body>
// </html>
function parseHeaders(line: string) {
if (line.startsWith('######')) {
const element = line.replace('######', '');
const parts = `<h6>${element.trim()}</h6>`;
return parts;
}
if (line.startsWith('#####')) {
const element = line.replace('#####', '');
const parts = `<h5>${element.trim()}</h5>`;
return parts;
}
if (line.startsWith('####')) {
const element = line.replace('####', '');
const parts = `<h4>${element.trim()}</h4>`;
return parts;
}
if (line.startsWith('###')) {
const element = line.replace('###', '');
const parts = `<h3>${element.trim()}</h3>`;
return parts;
}
if (line.startsWith('##')) {
const element = line.replace('##', '');
const parts = `<h2>${element.trim()}</h2>`;
return parts;
}
if (line.startsWith('#')) {
const element = line.replace('#', '');
const parts = `<h1>${element.trim()}</h1>`;
return parts;
}
}
function parse(input: string) {
const elements: Array<string> = input.split('\n');
let stack = [];
const lines = [];
let inListMode = false;
let inParaMode = false;
// note, this only works for paragraphs
for (let element of elements) {
// header detection
const isHeader = element.startsWith('#');
if (isHeader) {
lines.push(parseHeaders(element));
}
// List Items
const isListItem = element.startsWith('-');
if (isListItem) {
if (inListMode) {
stack.push(element);
} else {
inListMode = true;
stack.push(element);
continue
}
} else {
inListMode = false;
}
// we're no longer in list mode, so let's compile it
// and then add the compiled result to the lines array.
if (!inListMode && !inParaMode && stack.length > 0) {
const listItemsHtml = stack.map(line => {
return `<li>${line.replace('- ', '')}</li>`
}).join(' ');
// compile the list
lines.push(`<ul>${listItemsHtml}</ul>`);
stack = []; // Done with this mode, clear the stack.
}
// Paragraphs
const isParagraph = (content: string) => {
return !content.startsWith('#') && !content.startsWith('-') && content !== '';
}
if (isParagraph(element)) {
// Let's add the element to the stack.
if (inParaMode) {
stack.push(element);
} else {
inParaMode = true;
stack.push(element);
continue;
}
} else {
inParaMode = false;
}
// Compile the paragraphs
if (!inListMode && !inParaMode && stack.length > 0) {
// Compile paragraphs
const parasHtml = ['<p>', ...stack, '</p>'].join(' ')
// compile the list
lines.push(parasHtml);
stack = []; // Done with this mode, clear the stack.
}
}
return `
<html>
<body>
${lines.join('\n')}
</body>
</html>
`;
}
const output = parse(`
###### This is an h6
# This is a title!
## This is a subheader
### This is h3
#### This is h4
This is a paragraph.
It spans two lines.
This is a separate paragraph. And next is a list:
- One
- Two
- Three
And another paragraph
`);
console.log('output', output);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment