Skip to content

Commit

Permalink
parse tree
Browse files Browse the repository at this point in the history
  • Loading branch information
voischev committed Aug 21, 2015
1 parent 79ee017 commit ac089af
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 208 deletions.
278 changes: 73 additions & 205 deletions lib/parser.js
Original file line number Diff line number Diff line change
@@ -1,216 +1,84 @@
/*jshint -W082 */

import htmlparser from 'htmlparser2';
import bemNaming from 'bem-naming';
import vm from 'vm';

var parser = {};

class Convert {
parse(html) {

let naming = bemNaming(),
bufArray = [],
results = [];

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

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 (!last) {
results.push(comment);
return;
}

last.content || (last.content = []);
last.content.push(comment);
},
onopentag: function(tag, attrs) {
let buf = {},
classes = attrs.class && attrs.class.split(' '),
block;

if (classes && classes.length) {
let entities = classes.map(cls => {
let entity = naming.parse(cls);

if (!entity) {
return cls;
}

return entity;
}),
visitedBlocksOnCurrentNode = [];

function onEntity(entity) {
if (typeof entity === 'string') {
return buf.cls ? buf.cls.push(entity) : buf.cls = [entity];
}

let blockName = entity.block,
modFieldName = entity.elem ? 'elemMods' : 'mods';

if (naming.isBlock(entity)) {
visitedBlocksOnCurrentNode.push(blockName);

if (visitedBlocksOnCurrentNode.length === 1) {
block = entity;

for (let i in block) {
buf[i] = block[i];
}

return;
}
}

if (naming.isBlockMod(entity) && visitedBlocksOnCurrentNode.indexOf(blockName) < 0) {
onEntity({ block: blockName });
}

if (
entity.block === buf.block &&
entity.elem === buf.elem &&
entity.modName
) {
buf[modFieldName] || (buf[modFieldName] = {});
buf[modFieldName][entity.modName] = entity.modVal;
} else { // build mixes
if (entity.modName) {
let mixes = buf.mix,
currentMixingItem;

if (mixes) {
for (let i = 0; i < buf.mix.length; i++) {
if ((mixes[i].block === entity.block) && mixes[i].elem === entity.elem) {
currentMixingItem = mixes[i];
}
}
}

if (currentMixingItem) {
currentMixingItem[modFieldName] || (currentMixingItem[modFieldName] = {});
currentMixingItem[modFieldName][entity.modName] = entity.modVal;

return;
} else {
entity[modFieldName] = {};
entity[modFieldName][entity.modName] = entity.modVal;
delete entity.modName;
delete entity.modVal;
}
}

buf.mix = (buf.mix || []).concat(entity);
}
}

entities.forEach(onEntity);

if (!buf.block && buf.mix.length) {

let mainEntity = buf.mix.shift();
if (!buf.mix.length) delete buf.mix;

for (let i in mainEntity) {
buf[i] = mainEntity[i];
}
}
}

delete attrs.class;

let 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(prop => {
buf.mix.forEach((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;
}

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

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

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

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

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

return;
}

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

text = text.trim();

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

last.content || (last.content = []);
last.content.push(text);

function toTree (html) {

let bufArray = [],
results = [];

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

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 (!last) {
results.push(comment);
return;
}
});

parser.write(html);
parser.end();
last.content || (last.content = []);
last.content.push(comment);
},
onopentag: function(tag, attrs) {
let buf = {};

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

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

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

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

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

return;
}

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

text = text.trim();

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

last.content || (last.content = []);
last.content.push(text);
}
});

parser.write(html);
parser.end();

return results;
}

parser.convert = new Convert();
export default parser;
export default {
toTree: toTree
};


function isEmpty(obj) {
Expand Down
4 changes: 2 additions & 2 deletions lib/posthtml.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { convert } 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 convert.parse(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

0 comments on commit ac089af

Please sign in to comment.