bem / bh Goto Github PK
View Code? Open in Web Editor NEWBH template engine
Home Page: http://bem.github.io/bh/
License: MIT License
BH template engine
Home Page: http://bem.github.io/bh/
License: MIT License
Trying to use "BH" for building HTML examples in my library https://github.com/toivonen/bouwdoos/tree/v1
I get files like desktop.sets/page/page.examples/myexample.bh.js and inside there is
var BH = require("../../node_modules/bh/lib/bh.js");
As if it tries to look for "node_modules" folder into "desktop.sets" folder, not in a root.
However I run enb
not in the root but like this
varya-2:bouwdoos toivonen$ cd desktop.sets/
varya-2:desktop.sets toivonen$ ../node_modules/enb/bin/enb make
21:36:04.980 - build started
...
I did this to store .bem/enb-make.js
file into desktop.sets
folder because I might have more than one set and different configs for them.
Looks like BH + ENB works properly only if run from the root.
Should I rewrite my configs so that I will use only one? Or this can be fixed here?
=============================== Coverage summary ===============================
Statements : 89.41% ( 346/387 )
Branches : 80.84% ( 270/334 )
Functions : 85% ( 34/40 )
Lines : 89.53% ( 342/382 )
================================================================================
Should be 100%.
In some cases call of ctx.applyBase methon in block matching template could make misunderstanding
API:
ctx.tParams({ p1: 'v1', p2: 'v2' });
New bem-core library uses data-bem
attribute instead of onclick
.
https://github.com/bem/bem-core/blob/v1/common.blocks/i-bem/__dom/i-bem__dom.js#L54
Can you add support for the new attribute for your awesome library?
I see two possible solutions:
data-bem
or onclick
.jsAttrName
for technologies bh-server
and bh-client-module
.it('should not override user js', function() {
bh.match('button', function(ctx) {
ctx.js({ a: 2 });
});
bh.apply({ block: 'button', js: { x: 1 } })
.should.equal('<div class="button i-bem" onclick="return {"button":{"a":2,"x":1}}"></div>');
});
Тест в котором реализована асинхронная шаблонизация delfrrr@4e19ef8
После разговора с mdevils@ добавил applyBaseAsync и возможность изменять json дерево в асинхронных матчерах
Если в целом ок, то впиливаю в bh.js и делаю пул риквест
Сейчас пакет требует enb
, а еще подключает пакеты, которые используются только в коде enb-технологий.
Есть несколько случаев, когда такие зависимости будут лишними:
bh
, напрямую, без какого бы то ни было инструмента для сборки.bh
вместе с иными инструментами для сборки (bem-tools
, gulp
, grunt
и т.д.), написав для этих инструментов технологий/плагинов, в которых будет требоваться bh
-пакет.https://gist.github.com/xdghcnt/1253e5f518215577fe22
As you can see, I want my test-block contained two logical sections that don't appear in the output, and it works as I planned:
a1 a2 a3 b1 b2 b3
Then I try to append some more elements in next level of defenition:
https://gist.github.com/xdghcnt/013d17c183e60a897e64
And I get this:
b1 b2 b3 a2 a3 b1 b2 b3 b4 b5 b6
So I have to use workaround like this for now:
https://gist.github.com/xdghcnt/cb688b5d0751cc27eeae
(First I used ctx._somekey to pass value to next level and it was really ugly, but now it seems not bad and may be even better than construction that producing described bug or smth.)
Also, this example works ok:
https://gist.github.com/xdghcnt/4aeb2d2f4c3c7d63d5b9
I have an bemjson object, it contains an array which first element is false
or undefined
{
block: 'button',
content: [
false, // can olso be undefined
{ elem: 'inner' },
{ elem: 'inner' },
{ elem: 'inner' }
]
}
and matcher
bh.match('button__inner', function(ctx) {
if (ctx.isFirst()) {
ctx.mod('first', 'yes');
}
if (ctx.isLast()) {
ctx.mod('last', 'yes');
}
});
when I apply matcher to the bemjson getting
<div class="button">
<div class="button__inner"></div>
<div class="button__inner"></div>
<div class="button__inner button__inner_last_yes"></div>
</div>
instead of expected
<div class="button">
<div class="button__inner button__inner_first_yes"></div>
<div class="button__inner"></div>
<div class="button__inner button__inner_last_yes"></div>
</div>
module.exports = function (bh) {
bh.match("test-block", function (ctx) {
ctx.content([
{
elem: 'test-elem'
},
{
elem: 'test-elem',
elemMods: {
mod: 'val'
}
}
]);
});
bh.match("test-block__test-elem", function (ctx) {
ctx.content('test');
});
bh.match("test-block__test-elem_mod_val", function (ctx) {
ctx.content('test');
});
};
TypeError: Cannot read property 'mod' of undefined
Version is "3.1.2"
Ok in "2.2.0"
For example, there is no any information about methods
setOptions
enableInfiniteLoopDetection
bemjson
{ content: '<script>&</script>' }
expected
<div><script>&amp;</script></div>
actual
<div><script>&</script></div>
mix
mix classes and not tags
, attrs
and other mixes
and it's a consistency problem.
I want to use link
block as mixin and not to hack it every time, so I want to write this way, but it's not working:
{
block: 'media',
elem: 'link',
mix: [{ block: 'link', url: 'http://github.com/' }],
content: [
{
elem: 'icon',
url: 'duck.png'
},
{
elem: 'title',
content: 'comment'
}
]
}
So I have to write this ugly code:
{
block: 'link',
mix: [{ block: 'media', elem: 'link' }],
url: 'http://github.com/',
content: [
{
block: 'media', elem: 'icon',
url: 'duck.png'
},
{
block: 'media', elem: 'title',
content: 'comment'
},
]
}
Then, I want to use one block for analytics tracking, and it have onclick
attrs and goal option. I want to mix it and expect to see onclick
attr on parent block like this way:
i-track.bh.js:
bh.match('i-track', function (ctx, json) {
ctx.attr('onclick', 'track("' + json.goal + '")');
});
and use it like this:
{
block: 'link',
mix: [{ block: 'i-track', goal: 'clck_goal' }],
url: 'http://github.com/',
content: 'Tracking link'
}
But it's not working too.
I want to use custom font on my site. So I have one block i-face
with mods for each custom font, but in reality only one font is being used in project, so it's reasonable to create shortcut block textbook
, which have it's own mix with concrete textbook
font.
In other words, I don't want to write mix: [{ block: 'i-font', mods: { face: 'textbook' }}]
every time, because it's hard to remember and want to short it to this: mix: [{ block: 'textbook' }]
, where textbook block have such bh
file:
bh.match('textbook', function (ctx, json) {
ctx.mix([{ block: 'i-font', mods: { face: 'textbook' }}]);
});
And finally it's not working at all.
Если в метод apply передать undefined
, то BH и BEMHTML ведут себя по разному.
BEMHTML возвращает пустую строку, BH падает с ошибкой.
Замечено при переписывании модуля slider на клиенте - https://github.yandex-team.ru/lego/islands-components/blob/dev/common.blocks/slider/__ui/slider__ui.js#L324
It seems that project's package.json
has some useless decencies like vow
, vow-fs
and inherit
.
{ block: 'foo', mix: [{ elem: 'bar', elemMods: { baz: 'ololo' } }] }
rendered to:
"<div class="foo foo__bar"></div>"
Сейчас работает так:
ctx.mix([{
block: 'y-user',
userPic: userPic,
userLogin: userLogin
}]);
Если миксумая сущность одна, хочется передавать её объектом
ctx.mix({
block: 'y-user',
userPic: userPic,
userLogin: userLogin
});
То же самое в bemjson:
return {
block: 'y-user',
mix: {block: 'y-head', elem: 'user'}
}
it('should ignore empty array items', function() {
bh.match('button', function(ctx) {
ctx.isFirst() && ctx.mod('pos', 'first');
ctx.isLast() && ctx.mod('pos', 'last');
});
bh.apply([
false,
{ block: 'button' },
{ block: 'button' },
{ block: 'button' },
[]
]).should.equal(
'<div class="button button_pos_first"></div>' +
'<div class="button"></div>' +
'<div class="button button_pos_last"></div>'
);
});
Expected:
<div class="button button_pos_first"></div>
<div class="button"></div>
<div class="button button_pos_last"></div>
Actual:
<div class="button"></div>
<div class="button"></div>
<div class="button"></div>
This mix
https://github.com/toivonen/toivonen.github.com/blob/feature/18_bouwdoos/s/desktop.blocks/header/header.bh.js#L9 and all the others do not work in the template.
The same block in bemhtml is https://github.com/toivonen/toivonen.github.com/blob/feature/18_bouwdoos/s/desktop.blocks/header/header.bemhtml . Mix works with bemhtml version.
The expected result you can see here http://varya.me/ in the elements of the header
block.
bemjson:
{
block : 'link',
content : {
block : 'link-content',
tag : '',
content : 'Empty link'
}
}
output:
<a class="link ..." ... ><div class="link-content">Empty link</div></a>
BEMJSON
{
block: 'score',
content: [
{ elem: 'goals', content: 0 },
' : ',
{ elem: 'goals', content: 3 }
]
}
BH:
bh.match('score__goals', function(ctx) {
ctx.tag('span');
});
Result HTML
<div class="score">
<span class="score__goals"></span> : <span class="score__goals">3</span>
</div>
var test = {
block: 'input',
mods: {
type: 'textarea'
},
content: [
{ elem: 'control' }
]
};
var bhResult = bh.processBemJson(test);
console.log(test === bhResult); // => true
Сейчас есть метод ctx.attr, который позволяет работать только с одним атрибутом. Хочется иметь метод, который будет работать с объектами и позволить выставлять несколько атрибутов пачкой.
I got curious, how good BH performance versus some popular template engines. There is a template-benchmark in a wild, so I wrote test runner for BH (this is my first time with BH, so be gentle it's maybe not the fastest version of BEMJSON).
Also I found no way to disable html escaping in bh code, so numbers for escaped and unescaped versions are the same.
P.S. this benchmark is using obsolete versions of engines, so numbers may vary.
Rendering 100000 templates:
ECT
Escaped : 1399ms
Unescaped : 84ms
Total : 1483ms
Dust
Escaped : 1674ms
Unescaped : 278ms
Total : 1952ms
Hogan.js
Escaped : 1964ms
Unescaped : 534ms
Total : 2498ms
Gaikan
Escaped : 1691ms
Unescaped : 50ms
Total : 1741ms
Fest
Escaped : 1651ms
Unescaped : 169ms
Total : 1820ms
EJS without `with`
Escaped : 3335ms
Unescaped : 224ms
Total : 3559ms
doT
Escaped : 2230ms
Unescaped : 46ms
Total : 2276ms
Swig
Escaped : 3882ms
Unescaped : 310ms
Total : 4192ms
Underscore
Escaped : 2355ms
Unescaped : 1440ms
Total : 3795ms
EJS
Escaped : 5350ms
Unescaped : 1448ms
Total : 6798ms
Eco
Escaped : 4927ms
Unescaped : 577ms
Total : 5504ms
Handlebars.js
Escaped : 4653ms
Unescaped : 2049ms
Total : 6702ms
Jade without `with`
Escaped : 6867ms
Unescaped : 2617ms
Total : 9484ms
CoffeeKup
Escaped : 2794ms
Unescaped : 5159ms
Total : 7953ms
BH
Escaped : 6095ms
Unescaped : 5680ms // 5299ms - for manually disabled escaping
Total : 11775ms
Jade
Escaped : 11691ms
Unescaped : 8164ms
Total : 19855ms
/cc @denchistyakov
add to package.json
I have two blocks g-media and person. g-media has such bh.js:
module.exports = function (bh) {
bh.match('g-media', function (ctx) {
ctx.mix([{ block: 'i-clearfix' }], true);
});
bh.match('g-media__content-wrap', function (ctx) {
ctx.mix([{ block: 'i-clearfix' }], true);
});
};
This block applied bh correctly:
{
block: 'g-media',
content: [
{
elem: 'img-wrap',
content: 'img'
},
{
elem: 'content-wrap',
content: 'content'
}
]
}
But when I mix g-media
and g-media__content-wrap
nothing happens (I thought that person__content-wrap should have g-media__content-wrap and i-clearfix classes):
{
block: 'person',
mix: [{block: 'g-media'}],
content: [
{
elem: 'photo',
mix: [{block: 'g-media', elem: 'img-wrap'}],
content: 'img'
},
{
elem: 'content-wrap',
mix: [{block: 'g-media', elem: 'content-wrap'}],
content: 'content'
}
]
}
I have found very "strange" behavior of matching rules. Suppose I have such setup of bh
:
bh.match('block__elem', function () { console.log('block__elem'); });
bh.match('block__elem_emod', function () { console.log('block__elem_emod'); });
bh.match('block__elem_mod', function () { console.log('block__elem_mod'); });
bh.match('block_mod__elem', function () { console.log('block_mod__elem'); });
bh.match('block_emod__elem', function () { console.log('block_emod__elem'); });
bh.match('block_mod__elem_emod', function () { console.log('block_mod__elem_emod'); });
And applying them with this code:
console.log(bh.apply({block: 'block', elem: 'elem', mods: { mod: true, emod: true } }));
Which gives me this HTML:
<div class="block__elem block__elem_mod block__elem_emod"></div>
As you can see, HTML contains only three classes, but output of a program is:
block_mod__elem_emod
block_emod__elem
block_mod__elem
block__elem_mod
block__elem_emod
block__elem
Which indicates, that all six matchers has been applied. Is this expected behaviour?
Right now if i call ctx.mix({})
without block
, elem
or mods
key i'll see
<div class="block block">
Please, describe setOptions
in readme.
bh.match(
[ 'item__mark', 'item__text' ],
function(ctx) {
ctx.tag('span')
}
)
Result: AssertionError: expected NaN to equal 333
Expected: successfully passed.
it('should return tParam after applyBase #2', function() {
bh.match('select', function(ctx) {
ctx.tParam('foo', 222);
});
bh.match('select__control', function(ctx) {
(ctx.tParam('foo') + ctx.tParam('bar')).should.equal(333);
});
bh.match('select__control', function(ctx) {
ctx.tParam('bar', 111);
ctx.applyBase();
});
bh.apply({ block: 'select', content: { elem: 'control' } });
});
Shouldn't these be equivalent?
Bh = require('bh').BH,
lo = require('lodash');
var json1 = {block : 'button', elem : 'item'},
json2 = lo.cloneDeep(json1),
template1 = function (bh) {
bh.match('button__item', function(ctx, json) {
ctx.mix({mods: {'pos': 'last'}});
});
},
template2 = function (bh) {
bh.match('button__item', function(ctx, json) {
ctx.mod('pos', 'last');
});
},
bh1 = new Bh(),
bh2 = new Bh();
template1(bh1);
bh1.apply(json1);
<div class="button__item button_pos_last"></div>
template2(bh2);
bh2.apply(json2);
<div class="button__item button__item_pos_last"></div>
E.g. bemjson:
{
block: 'bla'
}
with template
module.exports = function(bh) {
bh.match('bla', function(ctx, json) {
ctx.mix({
mods: { m1: 'v1' }
});
});
}
results in <div class="bla bla bla_m1_v1">
(bla
is duplicated).
For example,
we have BEMJSON
{
block : 'bla',
attrs : { a : undefined },
content : 'bla'
}
and BH-template
bh.match('bla', function(ctx) {
ctx.attrs({
a : 0,
b : 'some'
});
});
and HTML
<div class="bla" a="0" b="some">bla</div>
So param from BEMJSON was ignored. But we expect that parameter will not be added
I need to get some BEMJSON from BH layer and then compile it to HTML with BEMHTML. But BH process this structure:
{
"block": "input",
"mods": {
"type": "textarea"
},
"content": [
{ "elem": "control" }
]
}
into this:
{
"block": "input",
"mods": {
"type": "textarea"
},
"content": [
{
"elem": "control",
"block": "input",
"blockMods": {
"type": "textarea"
}
}
],
"blockMods": {
"type": "textarea"
}
}
And the fragment
{
"elem": "control",
"block": "input",
"blockMods": {
"type": "textarea"
}
}
is parsed wrong with BEMHTML. If it was mods
instead of blockMods
, BEMHTML would work as I expected.
May be it is possible to add an option for this parameter? How could I solve the problem now?
Is english readme in roadmap? It would be nice to have one.
BEMJSON
{ block : 'link', mods : { pseudo : true } }
HTML
<div class="link link_pseudo"></div>
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.