html-to-vdom
Advanced tools
Comparing version 0.6.0 to 0.7.0
var decode = require('ent').decode; | ||
var convertTagAttributes = require('./convert-tag-attributes'); | ||
var prefixLength = ('data-').length; | ||
var attributesToDecode = { | ||
'placeholder': true, | ||
'title': true, | ||
'alt': true | ||
}; | ||
var attributesToRename = { | ||
'class': 'className', | ||
'for': 'htmlFor', | ||
'tabindex': 'tabIndex' | ||
}; | ||
// Transform a data attribute name to a valid dataset key name | ||
// (See https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.dataset) | ||
var dataAttributeToDatasetKey = function dataAttributeToDatasetKey (name) { | ||
var unprefixed = name.slice(prefixLength); | ||
return unprefixed.replace(/\-([a-z])/g, function (m, charAfterHyphen) { | ||
return charAfterHyphen.toUpperCase(); | ||
}); | ||
}; | ||
var getDataset = function getDataset (tag) { | ||
var attributes = tag.attribs; | ||
if (typeof attributes === 'undefined' || | ||
attributes === null || | ||
Object.keys(attributes).length === 0) { | ||
return {}; | ||
} | ||
var dataset = {}; | ||
Object.keys(attributes).forEach(function (name) { | ||
var value = attributes[name]; | ||
if (!(/^data-/).test(name)) { | ||
return; | ||
} | ||
dataset[dataAttributeToDatasetKey(name)] = value; | ||
}); | ||
return dataset; | ||
}; | ||
var parseStyles = function(input) { | ||
var attributes = input.split(';'); | ||
var styles = attributes.reduce(function(object, attribute){ | ||
var entry = attribute.split(/:(.+)/); | ||
if (entry[0] && entry[1]) { | ||
object[entry[0].trim()] = entry[1].trim(); | ||
} | ||
return object; | ||
},{}); | ||
return styles; | ||
}; | ||
module.exports = function createConverter (VNode, VText) { | ||
@@ -71,27 +17,5 @@ var converter = { | ||
convertTag: function (tag, getVNodeKey) { | ||
var dataset = getDataset(tag); | ||
var attributes = convertTagAttributes(tag); | ||
var key; | ||
var attributes = { | ||
dataset: dataset | ||
}; | ||
Object.keys(tag.attribs).forEach(function (name) { | ||
var value = tag.attribs[name]; | ||
if (attributesToRename[name]) { | ||
attributes[attributesToRename[name]] = value; | ||
return; | ||
} | ||
if (attributesToDecode[name]) { | ||
attributes[name] = decode(value); | ||
return; | ||
} | ||
if (name === 'style') { | ||
attributes[name] = parseStyles(value); | ||
return; | ||
} | ||
attributes[name] = value; | ||
}); | ||
if (getVNodeKey) { | ||
@@ -98,0 +22,0 @@ key = getVNodeKey(attributes); |
var htmlparser = require('htmlparser2'); | ||
var parseHTML = function parseHTML (html) { | ||
var handler = new htmlparser.DomHandler(function (error, dom) { | ||
if (error) { | ||
throw new Error(error); | ||
} | ||
var handler = new htmlparser.DomHandler(); | ||
var parser = new htmlparser.Parser(handler, { | ||
lowerCaseAttributeNames: false | ||
}); | ||
var parser = new htmlparser.Parser(handler); | ||
parser.parseComplete(html); | ||
@@ -15,2 +13,2 @@ return handler.dom; | ||
module.exports = parseHTML; | ||
module.exports = parseHTML; |
{ | ||
"name": "html-to-vdom", | ||
"version": "0.6.0", | ||
"version": "0.7.0", | ||
"description": "Converts html into a vtree", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "mocha test" | ||
"test": "mocha test", | ||
"test-coverage": "NODE_ENV=test ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && rm -rf ./coverage" | ||
}, | ||
@@ -26,5 +27,7 @@ "repository": { | ||
"chai-as-promised": "^4.1.1", | ||
"coveralls": "^2.11.2", | ||
"istanbul": "^0.3.17", | ||
"mocha": "^1.21.4", | ||
"virtual-dom": "0.0.23" | ||
"virtual-dom": "2.0.1" | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
html-to-vdom [![Build Status](https://travis-ci.org/TimBeyer/html-to-vdom.svg?branch=master)](https://travis-ci.org/TimBeyer/html-to-vdom) | ||
html-to-vdom [![Build Status](https://travis-ci.org/TimBeyer/html-to-vdom.svg?branch=master)](https://travis-ci.org/TimBeyer/html-to-vdom) [![Coverage Status](https://coveralls.io/repos/TimBeyer/html-to-vdom/badge.svg?branch=master&service=github)](https://coveralls.io/github/TimBeyer/html-to-vdom?branch=master) | ||
============ | ||
@@ -13,2 +13,4 @@ | ||
As of v0.7.0, HTML attribute parsing has been improved by using [React's list of attributes and properties](https://github.com/facebook/react/blob/c265504fe2fdeadf0e5358879a3c141628b37a23/src/renderers/dom/shared/HTMLDOMPropertyConfig.js) to decide what to set on the VNode. This means that you should experience better compatibility and full support for HTML5. Custom attributes are also no longer lower cased automatically. Inline SVG is not yet supported, but will be worked on for the next version. | ||
As of v0.6.0, converting sibling nodes without an enclosing parent tag returns an array of type `VNode` instead of throwing an error | ||
@@ -73,2 +75,3 @@ | ||
Thanks to: | ||
* [@nkzawa](https://github.com/nkzawa) for making me aware of attribute lowercasing problems and submitting a PR to fix it | ||
* [@mattferrin](https://github.com/mattferrin) for noticing that promises could be removed from the API and contributing a PR to do so | ||
@@ -79,2 +82,2 @@ * [@tiagorg](https://github.com/tiagorg) for contributing a PR for style attribute parsing | ||
* [@bregenspan](https://github.com/bregenspan) for making the dataset conversion standards-compliant | ||
* [@jesseditson](https://github.com/jesseditson) for adding `<script>` and `<style>` tag support | ||
* [@jesseditson](https://github.com/jesseditson) for adding `<script>` and `<style>` tag support and contributing to achieve better attribute/property handling |
var VNode = require('virtual-dom/vnode/vnode'); | ||
var VText = require('virtual-dom/vnode/vtext'); | ||
var convertHTML = require('../../index')({ | ||
VNode: VNode, | ||
VText: VText | ||
}); | ||
var htmlToVDOM = require('../../index'); | ||
describe('htmlparser-to-vdom', function () { | ||
describe('when converting a single text node', function () { | ||
it('parses the text node correctly', function () { | ||
var html = 'test'; | ||
var converted = convertHTML(html); | ||
converted.text.should.eql('test'); | ||
describe('library initialization', function () { | ||
describe('when incorrect options are passed', function () { | ||
it('throws when there are no options', function () { | ||
should.throw(htmlToVDOM); | ||
}); | ||
}); | ||
describe('when converting multiple sibling nodes without a wrapper', function () { | ||
it('returns an array of vnodes', function () { | ||
var html = '<div id="foo"></div><div id="bar"></div>'; | ||
var converted = convertHTML(html); | ||
converted.should.be.an('array'); | ||
converted[0].properties.id.should.equal('foo'); | ||
converted[1].properties.id.should.equal('foo'); | ||
}); | ||
}); | ||
describe('when converting a tag', function () { | ||
it('parses a plain div correctly', function () { | ||
var html = '<div></div>'; | ||
var converted = convertHTML(html); | ||
converted.tagName.should.equal('div'); | ||
}); | ||
it('parses a div with an ID correctly', function () { | ||
var html = '<div id="test"></div>'; | ||
var converted = convertHTML(html); | ||
converted.properties.id.should.equal('test'); | ||
}); | ||
it('parses a div with classes correctly', function () { | ||
var html = '<div class="foo bar"></div>'; | ||
var converted = convertHTML(html); | ||
converted.properties.className.should.equal('foo bar'); | ||
}); | ||
it('parses an input with tabIndex correctly', function () { | ||
var html = '<input tabIndex="1"></input>'; | ||
var converted = convertHTML(html); | ||
converted.properties.tabIndex.should.equal('1'); | ||
}); | ||
it('parses a div with 1 style correctly', function () { | ||
var html = '<div style="top: -7px;"></div>'; | ||
var styles = { | ||
top: '-7px' | ||
}; | ||
var converted = convertHTML(html); | ||
converted.properties.style.should.deep.equal(styles); | ||
}); | ||
it('parses a div with 1 style without trailing semicolon correctly', function () { | ||
var html = '<div style="top: -7px"></div>'; | ||
var styles = { | ||
top: '-7px' | ||
}; | ||
var converted = convertHTML(html); | ||
converted.properties.style.should.deep.equal(styles); | ||
}); | ||
it('parses a div with styles correctly', function () { | ||
var html = '<div style="top: -7px; left: -6px; background: rgb(0,0,132);"></div>'; | ||
var styles = { | ||
top: '-7px', | ||
left: '-6px', | ||
background: 'rgb(0,0,132)' | ||
}; | ||
var converted = convertHTML(html); | ||
converted.properties.style.should.deep.equal(styles); | ||
}); | ||
it('parses a div with styles without trailing semicolon correctly', function () { | ||
var html = '<div style="top: -7px; left: -6px; background: rgb(0,0,132)"></div>'; | ||
var styles = { | ||
top: '-7px', | ||
left: '-6px', | ||
background: 'rgb(0,0,132)' | ||
}; | ||
var converted = convertHTML(html); | ||
converted.properties.style.should.deep.equal(styles); | ||
}); | ||
it('parses a div with styles correctly when spaces are missing', function () { | ||
var html = '<div style="top:-7px;left:-6px;background:rgb(0,0,132);"></div>'; | ||
var styles = { | ||
top: '-7px', | ||
left: '-6px', | ||
background: 'rgb(0,0,132)' | ||
}; | ||
var converted = convertHTML(html); | ||
converted.properties.style.should.deep.equal(styles); | ||
}); | ||
it('parses a div with styles correctly when spaces are abundant', function () { | ||
var html = '<div style=" top: -7px ; left : -6px ; background : rgb( 0 , 0 , 132 ) ; "></div>'; | ||
var styles = { | ||
top: '-7px', | ||
left: '-6px', | ||
background: 'rgb( 0 , 0 , 132 )' | ||
}; | ||
var converted = convertHTML(html); | ||
converted.properties.style.should.deep.equal(styles); | ||
}); | ||
}); | ||
describe('when converting a tag with data attributes', function () { | ||
it('converts a single data attribute correctly', function () { | ||
var html = '<div data-test="foobar"></div>'; | ||
var converted = convertHTML(html); | ||
should.exist(converted.properties.dataset.test); | ||
converted.properties.dataset.test.should.eql('foobar'); | ||
should.exist(converted.properties['data-test']); | ||
converted.properties['data-test'].should.eql('foobar'); | ||
}); | ||
it('converts a single hyphenated data attribute correctly', function () { | ||
var html = '<div data-test-data="foobar"></div>'; | ||
var converted = convertHTML(html); | ||
should.exist(converted.properties.dataset.testData); | ||
converted.properties.dataset.testData.should.eql('foobar'); | ||
should.exist(converted.properties['data-test-data']); | ||
converted.properties['data-test-data'].should.eql('foobar'); | ||
}); | ||
it('converts multiple data attributes correctly', function () { | ||
var html = '<div data-test="foobar" data-foobar="test"></div>'; | ||
var converted = convertHTML(html); | ||
should.exist(converted.properties.dataset.test); | ||
converted.properties.dataset.test.should.eql('foobar'); | ||
should.exist(converted.properties['data-test']); | ||
converted.properties['data-test'].should.eql('foobar'); | ||
should.exist(converted.properties.dataset.foobar); | ||
converted.properties.dataset.foobar.should.eql('test'); | ||
should.exist(converted.properties['data-foobar']); | ||
converted.properties['data-foobar'].should.eql('test'); | ||
}); | ||
}); | ||
describe('when converting a tag containing text', function () { | ||
it('converts to a tag with a child VText node correctly', function () { | ||
var html = '<div>Test</div>'; | ||
var converted = convertHTML(html); | ||
should.exist(converted.children); | ||
converted.children.length.should.eql(1); | ||
converted.children[0].text.should.eql('Test'); | ||
}); | ||
}); | ||
describe('when converting a tag containing a child tag', function () { | ||
it('converts to a tag with a child node correctly', function () { | ||
var html = '<div class="parent"><span class="child"></span></div>'; | ||
var converted = convertHTML(html); | ||
converted.tagName.should.eql('div'); | ||
converted.properties.className.should.eql('parent'); | ||
should.exist(converted.children); | ||
converted.children.length.should.eql(1); | ||
converted.children[0].tagName.should.eql('span'); | ||
converted.children[0].properties.className.should.eql('child'); | ||
}); | ||
}); | ||
describe('when converting a tag containing a child tag with text', function () { | ||
it('converts to a tag with a child node correctly', function () { | ||
var html = '<div class="parent"><span class="child">Test</span></div>'; | ||
var converted = convertHTML(html); | ||
converted.tagName.should.eql('div'); | ||
converted.properties.className.should.eql('parent'); | ||
should.exist(converted.children); | ||
converted.children.length.should.eql(1); | ||
converted.children[0].tagName.should.eql('span'); | ||
converted.children[0].properties.className.should.eql('child'); | ||
converted.children[0].children[0].text.should.eql('Test'); | ||
}); | ||
}); | ||
describe('when specifying a custom method for key', function () { | ||
it('sets key when specified via mapTagToKey', function () { | ||
var keyedConvertHTML = require('../../index')({ | ||
VNode: VNode, | ||
VText: VText | ||
it('throws when only VNode is supplied', function () { | ||
should.throw(function () { | ||
htmlToVDOM({ | ||
VNode: VNode | ||
}); | ||
}); | ||
var html = '<div id="key1">Test</div>'; | ||
var converted = keyedConvertHTML({ | ||
getVNodeKey: function (attribs) { | ||
return attribs.id; | ||
}}, html); | ||
should.exist(converted.key); | ||
converted.key.should.eql('key1'); | ||
}); | ||
it('allows binding value of getVNodeKey in convertHTML', function(){ | ||
var keyedConvertHTML = require('../../index')({ | ||
VNode: VNode, | ||
VText: VText | ||
it('throws when only VText is supplied', function () { | ||
should.throw(function () { | ||
htmlToVDOM({ | ||
VText: VText | ||
}); | ||
}); | ||
keyedConvertHTML = keyedConvertHTML.bind(null, { | ||
getVNodeKey: function (attribs) { | ||
return attribs.id; | ||
} | ||
}); | ||
var html = '<div id="key1">Test</div>'; | ||
var converted = keyedConvertHTML(html); | ||
should.exist(converted.key); | ||
converted.key.should.eql('key1'); | ||
}); | ||
}); | ||
describe('when converting a label containing the `for` attribute', function () { | ||
it('sets the htmlFor attribute correspondingly', function () { | ||
var html = '<label for="foobar"></label>'; | ||
var converted = convertHTML(html); | ||
should.exist(converted.properties.htmlFor); | ||
converted.properties.htmlFor.should.eql('foobar'); | ||
describe('when VNode and VText are supplied', function () { | ||
it('returns the conversion function', function () { | ||
htmlToVDOM({ | ||
VText: VText, | ||
VNode: VNode | ||
}).should.be.a('function'); | ||
}); | ||
}); | ||
describe('when converting a label not containing the `for` attribute', function () { | ||
it('does not set the htmlFor attribute correspondingly', function () { | ||
var html = '<label></label>'; | ||
var converted = convertHTML(html); | ||
should.not.exist(converted.properties.htmlFor); | ||
}); | ||
}); | ||
describe('when converting HTML containing html entities', function () { | ||
it('converts them back to characters', function () { | ||
var html = '<span><a href="foobar.com">test</a></span>'; | ||
var converted = convertHTML(html); | ||
converted.tagName.should.eql('span'); | ||
converted.children.length.should.eql(1); | ||
converted.children[0].text.should.eql('<a href="foobar.com">test</a>'); | ||
}); | ||
}); | ||
describe('when converting HTML containing html entities in placeholder, alt or title', function () { | ||
it('converts them to characters', function () { | ||
var html = '<input type="text" placeholder=""test"" alt=""test"" title=""test"">'; | ||
var converted = convertHTML(html); | ||
converted.tagName.should.eql('input'); | ||
converted.properties.placeholder.should.eql('"test"'); | ||
converted.properties.alt.should.eql('"test"'); | ||
converted.properties.title.should.eql('"test"'); | ||
}); | ||
}); | ||
describe('when converting HTML containing a script tag', function () { | ||
it('converts to a virtualdom node', function () { | ||
var html = '<div><script src="foo.js">alert("bar!");</script></div>'; | ||
var converted = convertHTML(html); | ||
var script = converted.children[0]; | ||
should.exist(script); | ||
script.tagName.should.eql('script'); | ||
script.children.length.should.eql(1); | ||
script.children[0].text.should.eql('alert("bar!");'); | ||
}); | ||
}); | ||
describe('when converting HTML containing a style tag', function () { | ||
it('converts to a virtualdom node', function () { | ||
var html = '<div><style>h1 {color:red;} p {color:blue;} </style></div>'; | ||
var converted = convertHTML(html); | ||
var script = converted.children[0]; | ||
should.exist(script); | ||
script.tagName.should.eql('style'); | ||
script.children.length.should.eql(1); | ||
script.children[0].text.should.eql('h1 {color:red;} p {color:blue;} '); | ||
}); | ||
}); | ||
describe('when converting HTML containing CDATA', function () { | ||
it('returns an empty string instead (cdata is unsupported)', function () { | ||
var html = '<![CDATA[ Within this Character Data block I can\ | ||
use double dashes as much as I want (along with <, &, \', and ")\ | ||
*and* %MyParamEntity; will be expanded to the text\ | ||
"Has been expanded" ... however, I can\'t use\ | ||
the CEND sequence (if I need to use it I must escape one of the\ | ||
brackets or the greater-than sign).\ | ||
]]>'; | ||
var converted = convertHTML(html); | ||
converted.text.should.eql(''); | ||
}); | ||
}); | ||
describe('when converting HTML containing a directive', function () { | ||
it('returns an empty string instead (directives are unsupported)', function () { | ||
var html = '<!DOCTYPE html>'; | ||
var converted = convertHTML(html); | ||
converted.text.should.eql(''); | ||
}); | ||
}); | ||
describe('when converting HTML containing a comment', function () { | ||
it('returns an empty string instead (comments are unsupported)', function () { | ||
var html = '<div><!-- some comment --></div>'; | ||
var converted = convertHTML(html); | ||
var comment = converted.children[0]; | ||
comment.text.should.eql(''); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
38248
21
733
81
6
1