Skip to content

Commit

Permalink
feat(parser): Own parser html ti tree
Browse files Browse the repository at this point in the history
  • Loading branch information
voischev committed Aug 21, 2015
1 parent f5c1d15 commit 5ef4f59
Show file tree
Hide file tree
Showing 13 changed files with 253 additions and 81 deletions.
3 changes: 2 additions & 1 deletion .jscsrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"excludeFiles": [
".git/**",
"node_modules/**",
"coverage/**"
"coverage/**",
"build/**"
],
"fileExtensions": [".js"],
"esnext": true,
Expand Down
1 change: 1 addition & 0 deletions .jshintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
coverage
build
130 changes: 53 additions & 77 deletions lib/parser.js
Original file line number Diff line number Diff line change
@@ -1,107 +1,71 @@
var htmlparser = require('htmlparser2'),
bemNaming = require('bem-naming'),
vm = require('vm');
/*jshint -W082 */
import htmlparser from 'htmlparser2';

function isEmpty(obj) {
for(var key in obj) {
if(Object.prototype.hasOwnProperty.call(obj, key)) {
return false;
}
}

return true;
}
function toTree (html) {

var convert = function(html) {
var bufArray = [],
results = {};
let bufArray = [],
results = [];

bufArray.last = function() {
return this[this.length - 1];
};

var parser = new htmlparser.Parser({
onopentag: function(tag, attrs) {
var buf = {},
classes = attrs.class && attrs.class.split(' '),
block = classes && bemNaming.parse(classes.shift()),
i;

for (i in block) {
buf[i] = block[i];
}
let parser = new htmlparser.Parser({
onprocessinginstruction: function(name, data) {
name === '!doctype' && results.push(`<${data}>`);
},
oncomment: function(data) {
let comment = `<!-- ${ data.trim() } -->`,
last = bufArray.last();

if (classes && classes.length) {
classes.map(bemNaming.parse).forEach(function(entity) {
if (entity.block === buf.block && entity.modName) {
buf.mods || (buf.mods = {});
buf.mods[entity.modName] = entity.modVal;
} else {
buf.mix = (buf.mix || []).concat(entity);
}
});
if (!last) {
results.push(comment);
return;
}

delete attrs.class;

var js = attrs['data-bem'] || attrs.onclick;

if (js) {
js = js.replace(/&quot;/g, '\'');
js = js.replace(/^return/, '');
js = vm.runInNewContext('(' + js + ')');

if (js[block.block]) {
if (isEmpty(js[block.block])) {
buf.js = true;
} else {
buf.js = js[block.block];
}
}
delete js[block.block];

Object.keys(js).forEach(function(prop) {
buf.mix.forEach(function(entity, idx) {
if (entity.block && entity.block == prop) {
if (isEmpty(js[prop])) {
buf.mix[idx].js = true;
} else {
buf.mix[idx].js = js[prop];
}
}
});
});

delete attrs['data-bem'];
delete attrs.onclick;
}
last.content || (last.content = []);
last.content.push(comment);
},
onopentag: function(tag, attrs) {
let buf = {};

if (tag != 'div') {
if (tag !== 'div') {
buf.tag = tag;
}

if (!isEmpty(attrs)) buf.attrs = attrs;

if (isEmpty(buf)) buf = { content: '' };

bufArray.push(buf);
},
onclosetag: function(tag) {
var buf = bufArray.pop();
onclosetag: function() {
let buf = bufArray.pop();

if (bufArray.length === 0) {
return results = buf;
results.push(buf);

return;
}
var last = bufArray.last();

let last = bufArray.last();
if (!(last.content instanceof Array)) {
last.content = [];
}
last.content.push(buf);
},
ontext: function(text) {
if (text.match(/(^\n|^\n\s+$)/g)) return;
if (text.match(/(^[\s\n]+$)/g)) return;

text = text.trim();

var last = bufArray.last();
if (!last) return;
let last = bufArray.last();
if (!last) {
results.push(text);
return;
}

last.content = last.content || [];
last.content || (last.content = []);
last.content.push(text);
}
});
Expand All @@ -110,6 +74,18 @@ var convert = function(html) {
parser.end();

return results;
}

export default {
toTree: toTree
};

exports.convert = convert;

function isEmpty(obj) {
for(let key in obj) {
if(Object.prototype.hasOwnProperty.call(obj, key)) {
return false;
}
}
return true;
}
4 changes: 2 additions & 2 deletions lib/posthtml.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import parser from './parser.js';
import { toTree } from './parser.js';
import { BH } from 'bh';

var tmpl = new BH();
Expand All @@ -15,7 +15,7 @@ class Posthtml {
* @return {String} json jsontree
*/
parse(html) {
return [parser.convert(html)];
return toTree(html);
}

/**
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
],
"dependencies": {
"babel": "^5.6.14",
"bem-naming": "^0.5.1",
"bh": "^4.1.0",
"htmlparser2": "^3.8.3"
},
Expand Down
97 changes: 97 additions & 0 deletions test/classes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/* jshint mocha: true, maxlen: false */
import posthtml from '../index.js';
import { expect } from 'chai';

function test(html, reference, done) {
posthtml().process(html).then(result => {
expect(reference).to.eql(result.html);
done();
}).catch(error => done(error));
}

describe('Parse classes', () => {
it('block', done => {
let html = '<div class="block1">text</div>';
test(html, html, done);
});

it('block_modname', done => {
let html = '<div class="block1 block1_modname">text</div>';
test(html, html, done);
});

it('block_modname_modval', done => {
let html = '<div class="block1 block1_modname_modval">text</div>';
test(html, html, done);
});

it('mix blocks', done => {
let html = '<div class="block1 block2">text</div>';
test(html, html, done);
});

it('mix block1 block2_modname', done => {
let html = '<div class="block1 block2 block2_modname">text</div>';
test(html, html, done);
});

it('mix block1_modname block2_modname', done => {
let html = '<div class="block1 block1_modname block2 block2_modname">text</div>';
test(html, html, done);
});

it('mix block1 block2_modname_modval', done => {
let html = '<div class="block1 block2 block2_modname_modval">text</div>';
test(html, html, done);
});

it('mix block1_modval_modname block2_modname_modval', done => {
let html = '<div class="block1 block1_modname_modval block2 block2_modname_modval">text</div>';
test(html, html, done);
});

it('block__elem', done => {
let html = '<div class="block1"><div class="block1__elem">text</div></div>';
test(html, html, done);
});

it('block__elem_modname', done => {
let html = '<div class="block1"><div class="block1__elem block1__elem_modname">text</div></div>';
test(html, html, done);
});

it('block__elem_modname_modval', done => {
let html = '<div class="block1"><div class="block1__elem block1__elem_modname_modval">text</div></div>';
test(html, html, done);
});

it('mix elems', done => {
let html = '<div class="block1 block2"><div class="block1__elem block2__elem">text</div></div>';
test(html, html, done);
});

it('mix block1__elem block2__elem_modname', done => {
let html = '<div class="block1 block2"><div class="block1__elem block2__elem block2__elem_modname">text</div></div>';
test(html, html, done);
});

it('mix block1__elem_modname block2__elem_modname', done => {
let html = '<div class="block1 block2"><div class="block1__elem block1__elem_modval block2__elem block2__elem_modname">text</div></div>';
test(html, html, done);
});

it('mix block1__elem block2__elem_modname_modval', done => {
let html = '<div class="block1 block2"><div class="block1__elem block2__elem block2__elem_modname_modval">text</div></div>';
test(html, html, done);
});

it('mix block1__elem_modname_modval block2__elem_modname_modval', done => {
let html = '<div class="block1 block2"><div class="block1__elem block1__elem_modname_modval block2__elem block2__elem_modname_modval">text</div></div>';
test(html, html, done);
});

it('block1__elem__elem', done => {
let html = '<div class="block1__elem__elem">text</div>';
test(html, html, done);
});
});
19 changes: 19 additions & 0 deletions test/comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* jshint mocha: true, maxlen: false */
import posthtml from '../index.js';
import { expect } from 'chai';
import { minifer, file } from '../util/test.js';

const comments = minifer(file('templates/comments.html'));

function test(html, reference, done) {
posthtml().process(html).then(result => {
expect(reference).to.eql(result.html);
done();
}).catch(error => done(error));
}

describe('Parse comments', () => {
it('comments eqval', done => {
test(comments, comments, done);
});
});
26 changes: 26 additions & 0 deletions test/doctype.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* jshint mocha: true, maxlen: false */
import posthtml from '../index.js';
import { expect } from 'chai';
import { minifer, file } from '../util/test.js';

const doctype = minifer(file('templates/doctype.html'));
const html = minifer(file('templates/html.html'));

function test(html, reference, done) {
posthtml().process(html).then(result => {
expect(reference).to.eql(result.html);
done();
}).catch(error => done(error));
}

describe('Parse Doctype', () => {

it('doctype eqval', done => {
test(doctype, doctype, done);
});

it('empty doctype', done => {
test(html, html, done);
});

});
11 changes: 11 additions & 0 deletions test/templates/comments.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<html>
<head>
<title>Comments</title>
<!-- Comments 1 -->
</head>
<body>
<!-- Comments 2 -->
<p>Lorem ipsum dolor sit amet...</p>
</body>
</html>
<!-- Comments last -->
9 changes: 9 additions & 0 deletions test/templates/doctype.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<head>
<title>Doctype</title>
</head>
<body>
<p>Lorem ipsum dolor sit amet...</p>
</body>
</html>
8 changes: 8 additions & 0 deletions test/templates/html.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<html>
<head>
<title>Html</title>
</head>
<body>
<p>Lorem ipsum dolor sit amet...</p>
</body>
</html>
18 changes: 18 additions & 0 deletions test/text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* jshint mocha: true, maxlen: false */
import posthtml from '../index.js';
import { expect } from 'chai';

const text = 'text';

function test(html, reference, done) {
posthtml().process(html).then(result => {
expect(reference).to.eql(result.html);
done();
}).catch(error => done(error));
}

describe('Parse text', () => {
it('Text eqval', done => {
test(text, text, done);
});
});
Loading

0 comments on commit 5ef4f59

Please sign in to comment.