html-parse-stringify
Advanced tools
Comparing version 1.0.1 to 1.0.2
var attrRE = /([\w-]+)|['"]{1}([^'"]*)['"]{1}/g; | ||
// create optimized lookup object for | ||
// void elements as listed here: | ||
// http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements | ||
var lookup = (Object.create) ? Object.create(null) : {}; | ||
lookup.area = true; | ||
lookup.base = true; | ||
lookup.br = true; | ||
lookup.col = true; | ||
lookup.embed = true; | ||
lookup.hr = true; | ||
lookup.img = true; | ||
lookup.input = true; | ||
lookup.keygen = true; | ||
lookup.link = true; | ||
lookup.menuitem = true; | ||
lookup.meta = true; | ||
lookup.param = true; | ||
lookup.source = true; | ||
lookup.track = true; | ||
lookup.wbr = true; | ||
@@ -8,7 +28,7 @@ module.exports = function (tag) { | ||
var res = { | ||
selfClosing: tag.slice(-2, -1) === '/', | ||
type: 'tag', | ||
name: '', | ||
voidElement: false, | ||
attrs: {}, | ||
type: 'tag', | ||
children: [], | ||
name: '' | ||
children: [] | ||
}; | ||
@@ -21,2 +41,5 @@ | ||
if (i === 0) { | ||
if (lookup[match] || tag.charAt(tag.length - 2) === '/') { | ||
res.voidElement = true; | ||
} | ||
res.name = match; | ||
@@ -23,0 +46,0 @@ } else { |
@@ -0,11 +1,12 @@ | ||
/*jshint -W030 */ | ||
var tagRE = /<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g; | ||
var parseTag = require('./parse-tag'); | ||
// re-used obj for quick lookups of components | ||
var empty = Object.create ? Object.create(null) : {}; | ||
module.exports = function parse(html, options) { | ||
options = options || {}; | ||
options.components = options.components || {}; | ||
var result; | ||
options || (options = {}); | ||
options.components || (options.components = empty); | ||
var result = []; | ||
var current; | ||
var previous; | ||
var level = -1; | ||
@@ -23,3 +24,3 @@ var arr = []; | ||
} | ||
} | ||
} | ||
var isOpen = tag.charAt(1) !== '/'; | ||
@@ -29,8 +30,6 @@ var start = index + tag.length; | ||
var parent; | ||
previous = current; | ||
if (isOpen) { | ||
level++; | ||
current = parseTag(tag); | ||
@@ -42,3 +41,3 @@ if (current.type === 'tag' && options.components[current.name]) { | ||
if (!inComponent && nextChar !== '<') { | ||
if (!current.voidElement && !inComponent && nextChar && nextChar !== '<') { | ||
current.children.push({ | ||
@@ -52,5 +51,5 @@ type: 'text', | ||
// this is our base if we don't already have one | ||
if (!previous) { | ||
result = current; | ||
// if we're at root, push new base node | ||
if (level === 0) { | ||
result.push(current); | ||
} | ||
@@ -67,3 +66,3 @@ | ||
if (!isOpen || current.selfClosing) { | ||
if (!isOpen || current.voidElement) { | ||
level--; | ||
@@ -78,5 +77,5 @@ if (!inComponent && nextChar !== '<' && nextChar) { | ||
} | ||
}); | ||
}); | ||
return result; | ||
}; |
@@ -17,4 +17,4 @@ function attrString(attrs) { | ||
case 'tag': | ||
buff += '<' + doc.name + (doc.attrs ? attrString(doc.attrs) : '') + (doc.selfClosing ? '/>' : '>'); | ||
if (doc.selfClosing) { | ||
buff += '<' + doc.name + (doc.attrs ? attrString(doc.attrs) : '') + (doc.voidElement ? '/>' : '>'); | ||
if (doc.voidElement) { | ||
return buff; | ||
@@ -27,3 +27,5 @@ } | ||
module.exports = function (doc) { | ||
return stringify('', doc); | ||
return doc.reduce(function (token, rootEl) { | ||
return token + stringify('', rootEl); | ||
}, ''); | ||
}; |
{ | ||
"name": "html-parse-stringify", | ||
"description": "Parses well-formed HTML (meaning all tags closed) into an AST and back. quickly.", | ||
"version": "1.0.1", | ||
"version": "1.0.2", | ||
"author": "Henrik Joreteg <henrik@andyet.net>", | ||
@@ -9,6 +9,13 @@ "bugs": { | ||
}, | ||
"dependencies": { | ||
"void-elements": "^1.0.0" | ||
}, | ||
"devDependencies": { | ||
"jshint": "^2.5.10", | ||
"phantomjs": "^1.9.13", | ||
"precommit-hook": "^1.0.7", | ||
"tape": "^3.0.3" | ||
"run-browser": "^2.0.1", | ||
"tap-spec": "^2.1.2", | ||
"tape": "^3.0.3", | ||
"zuul": "^1.16.5" | ||
}, | ||
@@ -29,4 +36,6 @@ "homepage": "https://github.com/henrikjoreteg/html-parse-stringify", | ||
"scripts": { | ||
"test": "node test/index.js | tap-spec" | ||
"start": "run-browser test/*", | ||
"test": "zuul --phantom -- test/index.js", | ||
"test-ci": "zuul -- test/index.js" | ||
} | ||
} |
@@ -76,3 +76,3 @@ # html-parse-stringify | ||
// such as <img/> | ||
selfClosing: false, | ||
voidElement: false, | ||
@@ -87,3 +87,3 @@ // an array of child nodes | ||
attrs: {}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [ | ||
@@ -115,3 +115,3 @@ // this is a text node | ||
- `attrs` - an object of key/value pairs. If an attribute has multiple space-separated items such as classes, they'll still be in a single string, for example: `class: "class1 class2"` | ||
- `selfClosing` - `true` or `false`. Whether this tag has a self-closing slash such as: `<img/>`, or `<input/>` | ||
- `voidElement` - `true` or `false`. Whether this tag is a known void element as defined by [spec](http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements). | ||
- `children` - array of child nodes. Note that any continuous string of text is a text node child, see below. | ||
@@ -137,3 +137,3 @@ | ||
- `attrs` - an object of key/value pairs. If an attribute has multiple space-separated items such as classes, they'll still be in a single string, for example: `class: "class1 class2"` | ||
- `selfClosing` - `true` or `false`. Whether this tag has a self-closing slash such as: `<img/>`, or `<input/>` | ||
- `voidElement` - `true` or `false`. Whether this tag is a known void element as defined by [spec](http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements). | ||
- `children` - it will still have a `children` array, but it will always be empty. | ||
@@ -140,0 +140,0 @@ |
@@ -17,3 +17,3 @@ var test = require('tape'); | ||
name: 'div', | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [] | ||
@@ -30,3 +30,3 @@ }); | ||
name: 'something-custom', | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [] | ||
@@ -41,3 +41,3 @@ }); | ||
name: 'p', | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [] | ||
@@ -55,3 +55,3 @@ }); | ||
name: 'img', | ||
selfClosing: true, | ||
voidElement: true, | ||
children: [] | ||
@@ -58,0 +58,0 @@ }); |
@@ -0,1 +1,2 @@ | ||
/* global console */ | ||
var test = require('tape'); | ||
@@ -7,4 +8,4 @@ var HTML = require('../index'); | ||
var html = '<div class="oh"><p></p></div>'; | ||
var parsed = HTML.parse(html); | ||
t.deepEqual(parsed, { | ||
var parsed = HTML.parse(html); | ||
t.deepEqual(parsed, [{ | ||
type: 'tag', | ||
@@ -15,3 +16,3 @@ name: 'div', | ||
}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [ | ||
@@ -23,6 +24,6 @@ { | ||
children: [], | ||
selfClosing: false | ||
voidElement: false | ||
} | ||
] | ||
}); | ||
}]); | ||
t.equal(html, HTML.stringify(parsed)); | ||
@@ -33,3 +34,3 @@ | ||
t.deepEqual(parsed, { | ||
t.deepEqual(parsed, [{ | ||
type: 'tag', | ||
@@ -40,3 +41,3 @@ name: 'div', | ||
}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [ | ||
@@ -47,3 +48,3 @@ { | ||
attrs: {}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [ | ||
@@ -57,3 +58,3 @@ { | ||
] | ||
}); | ||
}]); | ||
t.equal(html, HTML.stringify(parsed)); | ||
@@ -64,7 +65,7 @@ | ||
t.deepEqual(parsed, { | ||
t.deepEqual(parsed, [{ | ||
type: 'tag', | ||
name: 'div', | ||
attrs: {}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [ | ||
@@ -79,3 +80,3 @@ { | ||
attrs: {}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [ | ||
@@ -96,3 +97,3 @@ { | ||
attrs: {}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [ | ||
@@ -110,3 +111,3 @@ { | ||
] | ||
}); | ||
}]); | ||
t.equal(html, HTML.stringify(parsed)); | ||
@@ -117,3 +118,3 @@ | ||
t.deepEqual(parsed, { | ||
t.deepEqual(parsed, [{ | ||
type: 'tag', | ||
@@ -125,5 +126,5 @@ name: 'div', | ||
}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [] | ||
}); | ||
}]); | ||
t.equal(html, HTML.stringify(parsed)); | ||
@@ -134,3 +135,3 @@ | ||
t.deepEqual(parsed, { | ||
t.deepEqual(parsed, [{ | ||
type: 'tag', | ||
@@ -143,5 +144,5 @@ name: 'div', | ||
}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [] | ||
}); | ||
}]); | ||
t.equal(HTML.stringify(parsed), '<div class="handles" other="47" and="attributes"></div>'); | ||
@@ -156,3 +157,3 @@ | ||
t.deepEqual(parsed, { | ||
t.deepEqual(parsed, [{ | ||
type: 'tag', | ||
@@ -163,3 +164,3 @@ name: 'div-custom', | ||
}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [ | ||
@@ -172,7 +173,7 @@ { | ||
}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [] | ||
} | ||
] | ||
}, 'should not include children of registered components in AST'); | ||
}], 'should not include children of registered components in AST'); | ||
@@ -186,7 +187,7 @@ html = '<div><my-component thing="one">ok</my-component><my-component thing="two">ok</my-component></div>'; | ||
t.deepEqual(parsed, { | ||
t.deepEqual(parsed, [{ | ||
type: 'tag', | ||
name: 'div', | ||
attrs: {}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [ | ||
@@ -199,3 +200,3 @@ { | ||
}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [] | ||
@@ -209,9 +210,161 @@ }, | ||
}, | ||
selfClosing: false, | ||
voidElement: false, | ||
children: [] | ||
} | ||
] | ||
}) | ||
}]); | ||
html = '<div><img></div>'; | ||
parsed = HTML.parse(html); | ||
t.deepEqual(parsed, [{ | ||
type: 'tag', | ||
name: 'div', | ||
attrs: {}, | ||
voidElement: false, | ||
children: [ | ||
{ | ||
type: 'tag', | ||
name: 'img', | ||
attrs: {}, | ||
voidElement: true, | ||
children: [] | ||
} | ||
] | ||
}], 'should handle unclosed void elements'); | ||
t.equal(HTML.stringify(parsed), '<div><img/></div>'); | ||
html = '<div></div><img>'; | ||
parsed = HTML.parse(html); | ||
t.deepEqual(parsed, [ | ||
{ | ||
type: 'tag', | ||
name: 'div', | ||
attrs: {}, | ||
voidElement: false, | ||
children: [] | ||
}, | ||
{ | ||
type: 'tag', | ||
name: 'img', | ||
attrs: {}, | ||
voidElement: true, | ||
children: [] | ||
} | ||
], 'should handle multiple root nodes'); | ||
t.equal(HTML.stringify(parsed), '<div></div><img/>'); | ||
html = '<div><void-web-component/></div>'; | ||
parsed = HTML.parse(html); | ||
t.deepEqual(parsed, [{ | ||
type: 'tag', | ||
name: 'div', | ||
attrs: {}, | ||
voidElement: false, | ||
children: [ | ||
{ | ||
type: 'tag', | ||
name: 'void-web-component', | ||
attrs: {}, | ||
voidElement: true, | ||
children: [] | ||
} | ||
] | ||
}], 'should handle custom void tags if self-closing'); | ||
html = '<div><void-registered-component/></div>'; | ||
parsed = HTML.parse(html, {components: {'void-registered-component': true}}); | ||
t.deepEqual(parsed, [{ | ||
type: 'tag', | ||
name: 'div', | ||
attrs: {}, | ||
voidElement: false, | ||
children: [ | ||
{ | ||
type: 'component', | ||
name: 'void-registered-component', | ||
attrs: {}, | ||
voidElement: true, | ||
children: [] | ||
} | ||
] | ||
}], 'should handle registered void tags if self-closing'); | ||
html = '<div> 9 <input type="text"/> 10 </div>'; | ||
parsed = HTML.parse(html); | ||
t.deepEqual(parsed, [{ | ||
type: 'tag', | ||
name: 'div', | ||
attrs: {}, | ||
voidElement: false, | ||
children: [ | ||
{ type: 'text', content: ' 9 ' }, | ||
{ | ||
type: 'tag', | ||
name: 'input', | ||
attrs: { | ||
type: 'text' | ||
}, | ||
children: [], | ||
voidElement: true, | ||
}, | ||
{ type: 'text', content: ' 10 ' }, | ||
] | ||
}], 'should not give voidElements children'); | ||
t.end(); | ||
}); | ||
test('simple speed sanity check', function (t) { | ||
var i = 100000; | ||
var groupSize = 1000; | ||
var waitLoopSize = 10000000; | ||
var groups = i / groupSize; | ||
var html = '<html><head><title>Some page</title></head><body class="hey there"><img src="someURL"><h3>Hey, we need content</h3><br></body></html>'; | ||
var parse = HTML.parse; | ||
var times = []; | ||
var count; | ||
var waitCount; | ||
var total = 0; | ||
var start, stepAverage; | ||
console.log('running ' + i + ' iterations...'); | ||
while(i--) { | ||
count = groupSize; | ||
// grab groups | ||
if (i % count === 0) { | ||
start = Date.now(); | ||
while(count--) { | ||
parse(html); | ||
} | ||
var diff = Date.now() - start; | ||
stepAverage = diff/groupSize; | ||
console.log('group ' + (groups - (i / groupSize)) + ': ' + stepAverage); | ||
times.push(stepAverage); | ||
total += stepAverage; | ||
waitCount = waitLoopSize; | ||
// forcing a bit of a pause between tests | ||
while(waitCount--) {} | ||
} | ||
} | ||
// trim off first | ||
// it's always a slower outlier | ||
// with higher variability that | ||
// makes it harder to find differences | ||
times.shift(); | ||
var max = Math.max.apply(null, times); | ||
var min = Math.min.apply(null, times); | ||
var average = total / times.length; | ||
console.log('max', max); | ||
console.log('min', min); | ||
console.log('avg', average); | ||
t.end(); | ||
}); |
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
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
22214
14
509
1
7
+ Addedvoid-elements@^1.0.0
+ Addedvoid-elements@1.0.0(transitive)