diff --git a/README.md b/README.md index 2143df3..eb5bfb1 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,6 @@ It's designed to be as minimal as possible, for constrained use-cases where a fu - **Tiny:** it's 1kb of gzipped ES3 - **Simple:** pass a Markdown string, get back an HTML string -> **Note:** Tables are not yet supported. If you love impossible to read regular expressions, submit a PR! -> > **Note on XSS:** Snarkdown [doesn't sanitize HTML](https://github.com/developit/snarkdown/issues/70), since its primary target usage doesn't require it. ## Demos & Examples diff --git a/src/index.js b/src/index.js index 3938c5c..f518aed 100644 --- a/src/index.js +++ b/src/index.js @@ -24,7 +24,7 @@ function encodeAttr(str) { /** Parse Markdown into an HTML String. */ export default function parse(md, prevLinks) { - let tokenizer = /((?:^|\n+)(?:\n---+|\* \*(?: \*)+)\n)|(?:^``` *(\w*)\n([\s\S]*?)\n```$)|((?:(?:^|\n+)(?:\t| {2,}).+)+\n*)|((?:(?:^|\n)([>*+-]|\d+\.)\s+.*)+)|(?:!\[([^\]]*?)\]\(([^)]+?)\))|(\[)|(\](?:\(([^)]+?)\))?)|(?:(?:^|\n+)([^\s].*)\n(-{3,}|={3,})(?:\n+|$))|(?:(?:^|\n+)(#{1,6})\s*(.+)(?:\n+|$))|(?:`([^`].*?)`)|( \n\n*|\n{2,}|__|\*\*|[_*]|~~)/gm, + let tokenizer = /((?:^|\n+)(?:\n---+|\* \*(?: \*)+)\n)|(?:^``` *(\w*)\n([\s\S]*?)\n```$)|((?:(?:^|\n+)(?:\t| {2,}).+)+\n*)|((?:(?:^|\n)([>*+-]|\d+\.)\s+.*)+)|(?:!\[([^\]]*?)\]\(([^)]+?)\))|(\[)|(\](?:\(([^)]+?)\))?)|(?:(?:^|\n+)([^\s].*)\n(-{3,}|={3,})(?:\n+|$))|(?:(?:^|\n+)(#{1,6})\s*(.+)(?:\n+|$))|(?:`([^`].*?)`)|( \n\n*|\n{2,}|__|\*\*|[_*]|~~)|((?:(?:^|\n)\|.+)+)/gm, context = [], out = '', links = prevLinks || {}, @@ -101,6 +101,15 @@ export default function parse(md, prevLinks) { else if (token[17] || token[1]) { chunk = tag(token[17] || '--'); } + // Tables: + else if (token[18]) { + t = token[18].trim().split('\n').map(row => row.replace(/^\s*\|\s*|\s*\|\s*$/g, '').split(/\s*\|\s*/)); + inner = t[1] && t[1].every(cell => /^:?-+:?$/.test(cell)) ? t.splice(1, 1) && 'th' : 'td'; + chunk = '' + t.map((row, i) => { + let cell = i ? 'td' : inner; + return '' + row.map(str => '<'+cell+'>' + parse(str, links) + '').join('') + ''; + }).join('') + '
'; + } out += prev; out += chunk; } diff --git a/test/index.js b/test/index.js index 5262b16..7931e74 100644 --- a/test/index.js +++ b/test/index.js @@ -151,6 +151,24 @@ describe('snarkdown()', () => { }); }); + describe('tables', () => { + it('parses a table with a header', () => { + expect(snarkdown('| a | b |\n| --- | --- |\n| 1 | 2 |')).to.equal('
ab
12
'); + }); + + it('parses a table without a header', () => { + expect(snarkdown('| 1 | 2 |\n| 3 | 4 |')).to.equal('
12
34
'); + }); + + it('parses inline formatting within cells', () => { + expect(snarkdown('| **a** | [b](#b) |\n| --- | --- |\n| `c` | d |')).to.equal('
ab
cd
'); + }); + + it('parses a table surrounded by text', () => { + expect(snarkdown('before\n\n| a |\n| --- |\n| 1 |\n\nafter')).to.equal('before
a
1

after'); + }); + }); + describe('edge cases', () => { it('should close unclosed tags', () => { expect(snarkdown('*foo')).to.equal('foo');