diff --git a/.gitignore b/.gitignore index 123ae94..69c56eb 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ build/Release # Dependency directory # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules + +# Package +package-lock.json diff --git a/README.md b/README.md index 674ef70..b6d05a8 100644 --- a/README.md +++ b/README.md @@ -7,5 +7,8 @@ example usage: var slackify = require('slackify-html'); var text = slackify('this link is important'); -// text variable contains 'this is *important*' +// text variable contains 'this is *important*' ``` + +### How to setup repo and test locally +https://app.getguru.com/card/TqGjya8c/slackifyhtml-basic-setup diff --git a/package.json b/package.json index 7625649..4f13ef1 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,6 @@ "htmlparser": "^1.7.7" }, "devDependencies": { - "tap": "^1.4.0" + "tap": "^14.10.7" } } diff --git a/slackify-html.js b/slackify-html.js index f317a13..93b8152 100644 --- a/slackify-html.js +++ b/slackify-html.js @@ -14,27 +14,268 @@ module.exports = function slackify(html) { return entities.decode(walk(dom)); else return ''; +}; + +function walkList(dom, ordered, nesting, start) { + var out = ''; + if (dom) { + var listItemIndex = start ? start : 1; + dom.forEach(function (el) { + if ('text' === el.type && el.data.trim() !== '') { + out += el.data; + } + else if ('tag' === el.type) { + switch (el.name) { + case 'li': + for (i=0; i < nesting * 2; i++) { + out += ' '; + } + out += (ordered ? listItemIndex++ + '. ' : "• ") + walk(el.children, nesting + 1) + '\n'; + break; + default: + out += walk(el.children, nesting + 1); + } + } + }); + } + return out; } -function walk(dom) { +function walkPre(dom) { var out = ''; - if (dom) + if (dom) { dom.forEach(function (el) { if ('text' === el.type) { out += el.data; } + else if ('tag' === el.type) { + out += walkPre(el.children) + '\n'; + } + }); + } + return out; +} + +function walkTable(dom) { + var out = ''; + if (dom) { + dom.forEach(function (el) { if ('tag' === el.type) { + if ('thead' === el.name) { + out += walkTableHead(el.children); + } + else if ('tbody' === el.name) { + out += walkTableBody(el.children); + } + } + }); + } + + return out; +} + +function walkTableHead(dom) { + var out = ''; + if (dom) { + var headers = []; + dom.forEach(function (el) { + if ('text' === el.type && el.data.trim() !== '') { + out += el.data; + } + else if ('tr' === el.name) { + out += walkTableHead(el.children); + } + else if ('th' === el.name) { + var header = walkTableHead(el.children); + headers.push(header); + out += '| ' + header + ' '; + } + }); + if (headers.length > 0) { + out += ' |\n'; + headers.forEach(function (item) { + out += '| '; + for (i = 0; i < item.length; i++) { + out += '-'; + } + out += ' '; + }); + out += ' |\n'; + } + } + + return out; +} + +function walkTableBody(dom) { + var out = ''; + if (dom) { + dom.forEach(function (el) { + if ('text' === el.type && el.data.trim() !== '') { + out += el.data; + } + else if ('td' === el.name) { + out += '| ' + walk(el.children) + ' '; + } + else if ('tr' === el.name) { + out += walkTableBody(el.children) + '|\n'; + } + }); + } + return out; +} + +function walk(dom, nesting) { + if (!nesting) { + nesting = 0; + } + var out = ''; + if (dom) + dom.forEach(function (el) { + if ('text' === el.type) { + out += el.data; + } + else if ('tag' === el.type) { switch (el.name) { case 'a': - out += '<' + el.attribs.href + '|' + walk(el.children) + '>'; + if (el.attribs && el.attribs.href) { + out += '<' + el.attribs.href + '|' + walk(el.children) + '>'; + } + else { + out += walk(el.children); + } break; + case 'h1': + case 'h2': + case 'h3': + case 'h4': case 'strong': case 'b': - out += '*' + walk(el.children) + '*'; + content = walk(el.children); + var contentArr = content.split('\n'); + var innerOutput = ''; + for (var i=0; i'; + break; + case 'blockquote': + content = walk(el.children); + var innerOutput = ''; + var contentArr = content.split('\n'); + contentArr.forEach((item) => { + if (el.name === 'br' || el.name === 'p') { + innerOutput += '>' + item; + } else { + innerOutput += '>' + item + '\n'; + } + }); + if (innerOutput.endsWith('\n>\n')) { + innerOutput = innerOutput.substr(0, innerOutput.length - 2); + } + out += innerOutput + '\n'; break; default: out += walk(el.children); diff --git a/tester.js b/tester.js new file mode 100644 index 0000000..cc56a0f --- /dev/null +++ b/tester.js @@ -0,0 +1,8 @@ +var slackify = require('./slackify-html'); +var fs = require('fs'); +fs.readFile('/Users/petermichel/Desktop/delme.html', 'utf8', function (err,data) { + if (err) { + return console.log(err); + } + console.log(slackify(data)); +}); diff --git a/tests.js b/tests.js index 708da2b..504fc38 100644 --- a/tests.js +++ b/tests.js @@ -30,3 +30,125 @@ tap.test('vcheck example', function vcheck(t) { '*2.4-SNAPSHOT* • revision • build 2015-09-07 14:06 • wbl 1.3.33 • '); t.end(); }); + +tap.test('test bold text', function boldtext(t) { + t.equals(slackify('totally bold'), '*totally bold*'); + t.equals(slackify('

totally bold in paragraph

'), '*totally bold in paragraph*\n'); + t.equals(slackify('*already slackified bold*'), '*already slackified bold*'); + t.equals(slackify('*bold inside asterisks*'), '*bold *inside* asterisks*'); + t.equals(slackify('*asterisks *inside* asterisks*'), '*asterisks *inside* asterisks*'); + t.equals(slackify('

A sentence with bold text in between.

'), 'A sentence with *bold text* in between.\n'); + t.end(); +}); + +tap.test('test bold text with headers', function boldheaders(t) { + t.equals(slackify('

a completely bold title

'), '*a completely bold title*\n'); + t.equals(slackify('

a completely bold title

'), '*a completely bold title*\n'); + t.equals(slackify('

*asterisk title*

'), '*asterisk title*\n'); + t.equals(slackify('

*asterisk title with *bold**

'), '*asterisk title with bold*\n'); + t.equals(slackify('

alternating bold header content

'), '*alternating bold header content* \n'); + t.equals(slackify('

too many *asterisks* bold text

'), '*too many asterisks bold text*\n'); + t.equals(slackify('

header3 bold tag continues

outside'), '*header3 bold tag continues* \n outside'); + t.equals(slackify('

h1 with bold text

h2 with bold text

h3 with bold text

h4 with bold text

'), '*h1 with bold text*\n*h2 with bold text*\n*h3 with bold text*\n*h4 with bold text*\n'); + t.end(); +}); + +tap.test('test code block', function codeblock(t) { + var input = '
{  "name": "slackify-html",  "version": "1.0.0",  "description": "convert simple html to slack markdown",  "main": "slackify-html.js",  "scripts": {    "test": "tap tests.js"  }}
'; + var expected = '```\n{\n "name": "slackify-html",\n "version": "1.0.0",\n "description": "convert simple html to slack markdown",\n "main": "slackify-html.js",\n "scripts": {\n "test": "tap tests.js"\n }\n}\n```\n'; + var output = slackify(input); + t.equals(output,expected); + t.end(); +}); + +tap.test('test code block text only', function codeblocktextonly(t) { + var input = '
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
'; + var expected = '```\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n```\n'; + var output = slackify(input); + t.equals(output,expected); + t.end(); +}); + +tap.test('test blockquote with line breaks/new lines', function blockquotenewlines(t) { + t.equals(slackify('
block quote with
line
breaks
'), '>block quote with \n>line\n>breaks\n\n'); + t.equals(slackify('
block quote with embedded\n\n newlines
'), '>block quote with embedded\n>\n> newlines\n\n'); + t.equals(slackify('
block quote with trailing newlines\n\n
'), '>block quote with trailing newlines\n>\n\n'); + t.end(); +}); + +tap.test('test blockquote with paragraphs and line breaks/new lines', function blockquoteparagraphs(t) { + t.equals(slackify('

paragraph in blockquote

'), '>paragraph in blockquote\n\n'); + t.equals(slackify('

paragraph
with
line break blockquote

'), '>paragraph\n>with\n>line break blockquote\n\n'); + t.equals(slackify('

paragraph block quote with embedded\n\n newlines

'), '>paragraph block quote with embedded\n>\n> newlines\n\n'); + t.equals(slackify('

paragraph block quote with trailing newlines\n\n

'), '>paragraph block quote with trailing newlines\n>\n>\n\n'); + t.end(); +}) + +tap.test('test guru blockquote', function blockquote(t) { + t.equals(slackify('
block quote text
'), '>block quote text\n\n'); + t.equals(slackify('
block quote bold text
'), '>block quote *bold* text\n\n'); + t.equals(slackify('
block quote italic text
'), '>block quote _italic_ text\n\n'); + t.equals(slackify('
block quote underline text
'), '>block quote underline text\n\n'); + t.equals(slackify('
block quote strikethrough text
'), '>block quote strikethrough text\n\n'); + t.equals(slackify('
block quote highlight text
'), '>block quote highlight text\n\n'); + t.equals(slackify('
block quote color text
'), '>block quote color text\n\n'); + t.equals(slackify('
block quote link
'), '>block quote \n\n'); + t.equals(slackify('
block quote file
'), '>block quote file\n\n'); + t.equals(slackify('
block quote guru code snippet
'), '>`block quote guru code snippet`\n\n'); + t.end(); +}); + +tap.test('full example', function vcheck(t) { + var input = `

Security Overview Header

+

We take the security of your data very seriously!

+

In order to instill the necessary confidence, we wanted to provide full transparency on why, who, where, when and how we protect your data.

+

Given the sensitive nature of your content and need to maintain your privacy being a priority for us, we wanted to share the practices and policies we have put into place.

+

Privacy Policy

Here's a test blockquote with bolded information

Remember this list

+
    +
  1. foo
  2. +
  3. bar
  4. +
  5. buz
  6. +
+

and this list too...

+
    +
  • abc +
      +
    • sub 1
    • +
    • sub 2
    • +
    +
  • +
  • def
  • +
  • xyz
  • +
+

and this

+
blah
+
+

+ + + + + + + + + + + + + + + + + + + + +
Column 1Column 2Column 3
FooBarBaz
abcdefghi
+
`; +var expected = '*Security Overview Header*\n\n*We take the security of your data very seriously!*\n\nIn order to instill the necessary confidence, we wanted to provide full transparency on _why_, _who_, _where_, _when_ and _how_ we protect your data.\n\nGiven the sensitive nature of your content and need to maintain your privacy being a priority for us, we wanted to share the practices and policies we have put into place.\n\n\n>Here\'s a test blockquote *with bolded* information\n\nRemember this list\n\n1. foo\n2. bar\n3. buz\n\nand this list too...\n\n• _abc_\n • sub 1\n • sub 2\n\n\n• *def*\n• xyz\n\n\`and this\`\n\n\`\`\`\nblah\n\n\n\`\`\`\n\n\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Foo | Bar | Baz |\n| abc | def | ghi |\n\n'; +var output = slackify(input); + t.equals(output, + expected); + t.end(); +});