Skip to content

Commit

Permalink
fix: slightly more complete markdown (still a subset of it)
Browse files Browse the repository at this point in the history
  • Loading branch information
learosema committed Sep 28, 2024
1 parent ecb8afb commit b999f26
Show file tree
Hide file tree
Showing 29 changed files with 162 additions and 133 deletions.
53 changes: 47 additions & 6 deletions src/transforms/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ const MAGIC = [
[/^### (.+)$/g, '<h3>$1</h3>'],
[/^## (.+)$/g, '<h2>$1</h2>'],
[/^# (.+)$/g, '<h1>$1</h1>'],
[/ $/g, '<br/>'],
[/_(.+)_/g, '<u>$1</u>'],
[/\*(.+)\*/g, '<em>$1</em>'],
[/ $/gm, '<br/>'],
[/__(.+)__/g, '<em>$1</em>'],
[/_(.+)_/g, '<em>$1</em>'],
[/\*\*(.+)\*\*/g, '<strong>$1</strong>'],
[/\*(.+)\*/g, '<strong>$1</strong>'],
[/`(.+)`/, '<code>$1</code>'],
[/<(https?:\/\/.+)>/g, '<a href="$1">$1</a>'],
[
/\!\[(.+)\]\((.+)\)/g,
Expand Down Expand Up @@ -83,16 +85,55 @@ function parseList(block) {
return renderList(list);
}

export function markdown(input) {
return input.replace(/\r\n/g,'\n').split('\n\n')
function codeblocks(str, snippetStore) {
let counter = 1;
return str.split(/\n```/g).map((part, idx) => {
if (idx % 2 === 0) {
return part;
}
const lf = part.indexOf('\n');
if (lf === -1) {
return part;
}
const lang = part.slice(0, lf);
const code = part.slice(lf + 1);
const key = 'MARKDOWNSNIPPET' + (counter++)
const l = lang ? ` class="language-${lang}"` : '';
snippetStore.set(key,
`<pre${l}><code${l}>${code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')}\n</code></pre>`
);

return `{{ ${key} }}`
}).join('\n').trim();
}

export function markdownEscape(str) {
return str.replace(/\\([a-z\/\\`\{\}])/, '$1').replace(/(.?)([<>&])(.?)/g,
(_, before, char, after) =>
before + (/[^\s]+/.test(before) || /[^\s]+/.test(after) ? char:`&${{'<':'lt','>':'gt','&':'amp'}[char]};`) + after
);
}

export function markdown(input, escape = true) {
const vars = new Map();
const esc = (str) => escape?markdownEscape(str):str;
return esc(codeblocks(input.replace(/\r\n/g,'\n'), vars).split('\n\n')
.map(block => inlines(block.trim()))
.map(block => {
if (UL_PATTERN.test(block)) {
return parseList(block);
}
if (block.startsWith('> ')) {
return `<blockquote>\n${block.replace(/^> /gm, '')}\n</blockquote>`
}
if (/^<\w+>/.test(block)) {
return block;
}
if (/\{\{ MARKDOWNSNIPPET\d+ \}\}/.test(block)) {
return block;
}
return `<p>${block}</p>`
}).join('\n').trim();
}).join('\n\n')).replace(/\{\{\s*(\w+)\s*\}\}/g,
(outer, expr) => (vars.get(expr)) ? vars.get(expr) : outer
).trim() + '\n';
}
7 changes: 0 additions & 7 deletions tests/fixtures/mardown/0006-inline-elements.html

This file was deleted.

9 changes: 0 additions & 9 deletions tests/fixtures/mardown/0010-blockquote.md

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 9 additions & 0 deletions tests/fixtures/markdown/0005-inline-elements.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<h1>Inline Elements</h1>

<p>These are some <em>inline elements</em>.</p>

<p>There are more <strong>inline elements</strong>.</p>

<p>Lorem <strong>inline</strong> dolor <em>sit</em> amet.</p>

<p>Inline <code>code</code> element.</p>
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Inline Elements

These are some _inline elements_.

There are more *inline elements*.
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
<h1>Table of contents</h1>

<ol><li>Accessibility Fundamentals<ol><li>Disability Etiquette</li></ol></li><li>Accessibility Myths debunked<ol><li>Accessibility is expensive</li><li>Accessibility is ugly</li></ol></li><li>Quiz</li></ol>
9 changes: 9 additions & 0 deletions tests/fixtures/markdown/0010-blockquote.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<h1>This is a blockquote test</h1>

<blockquote>
To be or not to be, that is<br/>
the question.
-- Shakespear
</blockquote>

<p>This is a normal paragraph.</p>
7 changes: 7 additions & 0 deletions tests/fixtures/markdown/0010-blockquote.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This is a blockquote test

> To be or not to be, that is
> the question.
> -- Shakespear
This is a normal paragraph.
19 changes: 19 additions & 0 deletions tests/fixtures/markdown/0011-code-block.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<h1>Hello</h1>

<p>This is a code block below:</p>

<pre class="language-cpp"><code class="language-cpp">#include &lt;iostream&gt;

using namespace std;

int main()
{
cout &lt;&lt; "Hello World" &lt;&lt; endl;
return 0;
}
</code></pre>

<p>That's another code block:</p>

<pre class="language-sh"><code class="language-sh">npm install everything
</code></pre>
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Hello

This is a code block below:

```cpp
Expand All @@ -11,3 +13,9 @@ int main()
return 0;
}
```

That's another code block:

```sh
npm install everything
```
169 changes: 59 additions & 110 deletions tests/transforms/markdown.test.js
Original file line number Diff line number Diff line change
@@ -1,133 +1,82 @@
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { markdown } from '../../src/transforms/markdown.js';

const MD1 = `Lorem Ipsum
Dolor Sit
amet!
`;

const HTML1 = `<p>Lorem Ipsum</p>
<p>Dolor Sit</p>
<p>amet!</p>`;

const MD2 = `# Placeholder Text
Lorem Ipsum Dolor Sit amet consetetur adipiscing elit
## Headline 2
### Headline 3
#### Headline 4
##### Headline 5
###### Headline 6
`;

const HTML2 = `<h1>Placeholder Text</h1>
<p>Lorem Ipsum Dolor Sit amet consetetur adipiscing elit</p>
<h2>Headline 2</h2>
<h3>Headline 3</h3>
<h4>Headline 4</h4>
<h5>Headline 5</h5>
<h6>Headline 6</h6>`

const MD_IMG = `![Lea](lea.jpg)`
const HTML_IMG = `<p><img src="lea.jpg" alt="Lea" /></p>`

const MD_LIST = `# Unordered List
- Eat
- Sleep
- Code
- Repeat
`

const HTML_LIST = `<h1>Unordered List</h1>
<ul><li>Eat</li><li>Sleep</li><li>Code</li><li>Repeat</li></ul>`
import { readFile } from 'node:fs/promises';
import path from 'node:path';

const MD_NESTED_LIST = `# Lea
- Pronouns
- she/her
- Likes
- Pasta
- Coding
- Accessibility
`

const HTML_NESTED_LIST = `<h1>Lea</h1>
<ul><li>Pronouns<ul><li>she/her</li></ul></li><li>Likes<ul><li>Pasta</li><li>Coding</li><li>Accessibility</li></ul></li></ul>`

const MD_ORDERED_LIST = `# Lea
1. Frontend Dev
2. Loves Accessibility
3. Hates Ordered Lists
`


const HTML_ORDERED_LIST = `<h1>Lea</h1>
<ol><li>Frontend Dev</li><li>Loves Accessibility</li><li>Hates Ordered Lists</li></ol>`

const MD_NESTED_OL = `# Table of contents
1. Accessibility Fundamentals
1.1. Disability Etiquette
2. Accessibility Myths debunked
2.1. Accessibility is expensive
2.2. Accessibility is ugly
3. Quiz
`

const HTML_NESTED_OL = `<h1>Table of contents</h1>
<ol><li>Accessibility Fundamentals<ol><li>Disability Etiquette</li></ol>` +
`</li><li>Accessibility Myths debunked<ol><li>Accessibility is expensive</li>` +
`<li>Accessibility is ugly</li></ol></li><li>Quiz</li></ol>`
import { markdown } from '../../src/transforms/markdown.js';

describe('markdown transform', () => {
describe('markdown', () => {

const l = async (file) => {
const filename = path.resolve('tests/fixtures/markdown',file);
const inputFile = filename + '.md';
const outputFile = filename + '.html';
const [input, output] = await Promise.all([
readFile(inputFile, 'utf8'),
readFile(outputFile, 'utf8')
]);
return [input, output]
}

it('transforms chunks of text into paragraphs', async () => {
const [input, output] = await l('0000-paragraphs');
assert.equal(markdown(input), output);
});

it('transforms chunks of text into paragraphs', () => {
assert.equal(markdown(MD1), HTML1);
it('transforms headline correctly', async () => {
const [input, output] = await l('0001-headlines');
assert.equal(markdown(input), output);
});

it('transforms images correctly', async () => {
const [input, output] = await l('0002-images');
assert.equal(markdown(input), output);
})

it('transforms headline correctly', () => {
assert.equal(markdown(MD2), HTML2);
it('transforms links correctly', async () => {
const [input, output] = await l('0003-links');
assert.equal(markdown(input), output);
});

it('transforms links correctly', () => {
const MD_LINK = `This is a [link](https://lea.codes/)`
const HTML_LINK = `<p>This is a <a href="https://lea.codes/">link</a></p>`
it('transforms espaces correctly', async () => {
const [input, output] = await l('0004-escaping');
assert.equal(markdown(input), output);
});

const MD_LINK2 = `This is another link: <https://test.de>.`
const HTML_LINK2 = `<p>This is another link: <a href="https://test.de">https://test.de</a>.</p>`
it('transforms inline-elements', async () => {
const [input, output] = await l('0005-inline-elements');
assert.equal(markdown(input), output);
});

assert.equal(markdown(MD_LINK), HTML_LINK);
assert.equal(markdown(MD_LINK2), HTML_LINK2);
it('transforms unordered lists correctly', async () => {
const [input, output] = await l('0006-unordered-list');
assert.equal(markdown(input), output);
});

it('transforms images correctly', () => {
assert.equal(markdown(MD_IMG), HTML_IMG);
})
it('transforms unordered nested lists correctly', async () => {
const [input, output] = await l('0007-nested-list');
assert.equal(markdown(input), output);
});

it('transforms unordered lists correctly', () => {
assert.equal(markdown(MD_LIST), HTML_LIST);
it('transforms ordered lists correctly', async () => {
const [input, output] = await l('0008-ordered-list');
assert.equal(markdown(input), output);
});

it('transforms unordered nested lists correctly', () => {
assert.equal(markdown(MD_NESTED_LIST), HTML_NESTED_LIST);
it('transforms ordered nested lists correctly', async () => {
const [input, output] = await l('0009-nested-ordered-list');
assert.equal(markdown(input), output);
});

it('transforms ordered lists correctly', () => {
assert.equal(markdown(MD_ORDERED_LIST), HTML_ORDERED_LIST);
it('transforms blockquotes correctly', async () => {
const [input, output] = await l('0010-blockquote');
assert.equal(markdown(input), output);
});

it('transforms ordered nested lists correctly', () => {
assert.equal(markdown(MD_NESTED_OL), HTML_NESTED_OL);
it('transforms code blocks correctly', async () => {
const [input, output] = await l('0011-code-block');
assert.equal(markdown(input), output);
});


});
2 changes: 1 addition & 1 deletion tests/transforms/template-data.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,6 @@ describe('handleTemplateFile function', () => {
const result = await handleTemplateFile(config, {title: 'Lea was here'}, 'index.md');

assert.equal(result.filename, 'public/index.html');
assert.equal(result.content, '<body><h1>Lea was here</h1>\n<p>An article by Lea Rosema</p></body>')
assert.equal(result.content, '<body><h1>Lea was here</h1>\n\n<p>An article by Lea Rosema</p>\n</body>')
});
});

0 comments on commit b999f26

Please sign in to comment.