Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

html-parse-stringify

Package Overview
Dependencies
Maintainers
2
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

html-parse-stringify - npm Package Compare versions

Comparing version 1.0.1 to 1.0.2

.travis.yml

31

lib/parse-tag.js
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 {

31

lib/parse.js

@@ -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();
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc