Войти с помощью github

Наверное все кто используют IDE, а не emacs сталкивались с проблемой, что IDE совсем не понимает BEM блоки. Это все связано с особенностями реализации наследования внутри bem-core - inherit.

При помощи JSDoc я всячески стараюсь объяснить IDE что же такое BEM-block. Но, увы 100% результат пока не достиг, имеются недостатки. Вот пример того как я описываю блоки:

/**
 * @module myBlock
 */
modules.define('myBlock', ['i-bem__dom'], function(provide, BEMDOM) {

/**
 * @augments BEM
 * @bem
 */
var myBlock = BEMDOM.decl(this.name, /** @lends myBlock.prototype */ {
    onSetMod: {
        js: {
            /**
             * @constructs
             */
            inited: function() { /* some code goes here */ }
        }
    }
}, /** @lends myBlock */ {
    live: function() {
        this.liveBindTo('some-elem', 'click', this.prototype._onSomeElemClick);
    }
});

provide(myBlock);

});

Подробнее:

  • имена классов и модулей не должны содержать тире, хоть BEM и предлагает нам называть блоки через тире, то JSDoc в именах классов тире не понимает, и напроч забивает на весь последующий код.
  • результат выполнения BEMDOM.decl надо класть в переменную, если сразу положить в provide работать не будет.

В целом данная схема оформления позволяет комфортно работать с прототипной частью блока, что касается live, там я пока не нашел решения.

Я не нашел никакого способа объяснить IDE, что эта часть наследуется от BEMDOM и BEM одновременно, но только от их статической части. Кажется к такому JSDoc не готов. В связи с чем у меня IDE не понимает что this.prototype, это прототип нашего же блока.

Сам я недавно стал заморачиваться с JSDoc и еще многого про него не знаю. Если у вас есть идеи как усовершенствовать описание блоков буду рад любой информации/критике

Есть набор тестов браузерных на основе другого инструмента, который работает с selenium wd также. Эти тесты открывают в сумме все страницы и воспроизводят многие состояния страницы.

Браузерные тесты и тесты на верстку с помощью gemini можно разделить условно на 2 повторяющиеся фазы: 1) Открытие страницы и вызов нужного состояния страницы. 2) Получить что-то со страницы / сделать скриншот.

С одной стороны тесты на верстку и функциональные тесты сами по себе. Но время прогона увеличивается в 2 раза, так как фактически 2 раза состояния воспроизводятся одни. Сначала в браузерных тестах, затем в тестах на верстку.

Если я верно понимаю, то мы можем в 1 инструменте стартануть selenium сессию и получить id сессии. А в другом инструменте (в данном слуаете gemini) послать команду на снятие скриншота. Но для этого нам нужно подсунуть id сессии (возможно еще что-то сделать). На более низком уровне это просто rest вызовы к selenium с указанием параметров и id сессии.

Gemini очень просто было бы внедрить в существующие тесты. Первый инструмент создает и останавливает сессии selenium и приводит страницу к нужному состоянию. А gemini в нужном месте выполняет свои запросы.

Насколько реально такое сделать или нет и почему? В чем сложность такого подхода. Было бы удобно скрестить функционал webdriver.io/nightwatchjs с gemini таким образом.

Как это можно сделать?

Привет.

Кто-нибудь использовал уже BackstopJS (ссылка: https://css-tricks.com/automating-css-regression-testing/). Меня интересует сравнение с Gemini. В чём они друг друга лучше?

Все привет! Недавно начал разбираться с BEM. Для нового проекта понадобилась адаптивная сетка, решил использовать bem-grid.

postcss([bemgrid({
    maxWidth: '1100px',
    gutter: '10px',
    flex: 'flex'
})]).process('a {color: red}').then(function(result) {
    console.log(result.css);
});

В консоль выводится код сетки, а вот сохранить данный код в файл никак не могу. Для сборки использую Gulp

Подскажите, есть где-то хорошее руководство по созданию технологий для enb?

Допустим у нас есть блок phones, в котором несколько item. Есть несколько элементов: title,description,image, button. Элемент button в свою очередь содержит блок button из bem-components.

В index.bemjson.js описываем следующим образом

...
{
            block: 'phones',
            phones: [
                {
                    title: 'Apple iPhone 4S 32Gb',
                    image: 'http://mdata.yandex.net/i?path=b1004232748_img_id8368283111385023010.jpg',
                    price: '259',
                    url: '/'
                },
                {
                    title: 'Samsung Galaxy Ace S5830',
                    image: 'http://mdata.yandex.net/i?path=b0206005907_img_id5777488190397681906.jpg',
                    price: '73',
                    url: '/'
                }
           ]
}
...

В phones.bemhtml следующим

...
block('phones')(
    content()(function() {
        return this.ctx.phones.map(function(item){
            return [
                {
                    elem: 'item',
                    content: [
                        {
                            elem: 'title',
                            content: item.title
                        },
                        {
                            elem: 'image',
                            url: item.image
                        },
                        {
                            elem: 'price',
                            content: item.price
                        },
                        {
                            elem: 'button',
                            content: 
                                 {
                                     block: 'button',
                                     url: item.url,
                                     text: 'Купить',
                                  }
                         }
                    ]
                }
            ];
        });
    })
);
...

Вопрос: Как сделать так, чтобы кнопка отображалась только тогда, когда это необходимо? То есть могут быть элементы item с кнопкой, а могут быть и без кнопки. Как сделать внутреннюю логику никак не могу понять. Вариант с добавлением модификатора, который делает display:none мне не нравится. Думаю лучше будет реализовать как-то иначе.

Привет всем! Начал посматривать в сторону redux. Сама идея довольна интересна, хотя окончательно в голове не уложилась. Хотелось бы узнать мнение сообщества о паттерне, укладывается ли он в методологию БЭМ.

Если я делаю исключительно верстку проектов используя project-stub, а дальше как обычно режу готовый html и натягиваю на движок, то можно ли считать сайт сделанным на полном стеке БЭМ?)))

Всем привет.

Я один из немногих, кажется, кто сейчас использует возможность посылать запросы из bemtree. Одна из причин, по которой я так делаю - полная независимость блоков, то есть блок сам знает, какие данные ему нужны. Однако асинхронный bemtree мне неудобен. Я какое-то время назад собирался написать об этом bem-team, с предложением создать синхронный аналог, но оказалось, что работа над таким аналогом уже идёт и, в принципе, на него уже можно переходить. Останавливает меня от перехода только всё то же желание хранить информацию о том, как получать данные для блока, в папке блока.

Эта была проблема. Теперь о том, каким я вижу решение. Мне кажется, можно хранить информацию о получении данных в файлах отдельной технологии, скажем, block.data.js. Тут всё просто и понятно. Сложность в том, что хотелось бы, чтобы при сборке к каждому такому блоку прибавлялась информация о содержимом файлов этой технологии тех блоков, от которых он зависит - для того, чтобы было легко сформировать один запрос за всеми данными, нужными для отрисовки блока.

Зачем я пишу здесь:

  1. Чтобы получить советы по реализации такой технологии. У меня есть опыт написания технологий для enb, но обычно они проще.
  2. Чтобы услышать мнения о подходе в целом. Есть ли какие-то проблемы, которые я не вижу?

Привет господа.

У меня концептуальный вопрос: почему сложные правила именования не упрощают используя встроенные примитивы HTML?

Вот мой вопрос целиком: http://ru.stackoverflow.com/questions/510782/%D0%9F%D0%BE%D1%87%D0%B5%D0%BC%D1%83-%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0-%D0%B8%D0%BC%D0%B5%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F-bem-%D0%BD%D0%B5-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B5%D1%82-%D1%82%D0%B5%D0%B3%D0%B8-%D0%B8-%D0%B0%D1%82%D1%80%D0%B8%D0%B1%D1%83%D1%82%D1%8B

пакет enb-bem-specs запускаю тесты:

build   05-Apr-2016 23:31:50    23:31:50.863 - [spec] [desktop.specs/actions-more/actions-more.html] 
build   05-Apr-2016 23:31:50    { [Error: Command failed: /bin/sh -c /usr/home/.../node_modules/enb-bem-specs/node_modules/mocha-phantomjs/bin/mocha-phantomjs --reporter spec file:////usr/home/.../desktop.specs/actions-more/actions-more.html
build   05-Apr-2016 23:31:50    ]
build   05-Apr-2016 23:31:50      killed: false,
build   05-Apr-2016 23:31:50      code: 2,
build   05-Apr-2016 23:31:50      signal: null,
build   05-Apr-2016 23:31:50      cmd: '/bin/sh -c /usr/home/..../node_modules/enb-bem-specs/node_modules/mocha-phantomjs/bin/mocha-phantomjs --reporter spec file:////usr/home/.../desktop.specs/actions-more/actions-more.html' }

Подскажите, куда копать?

Существует ли формализованный список по которому можно проверить качество вёрстки по БЭМ?

Я буду читать две лекции по БЭМ для junior-разработчиков внутри EPAM и давать домашнее задание. Потом нужно будет оценить качество его выполнения, выставить оценки. Эти оценки непосредственно будут влиять на их перспективы на работе. Слушателей будет около 100 человек, критерии должны быть простые и понятные.

Я пока вижу такой вариант: Задание:

  1. Сверстать (вручную, BEM CSS) каркас страницы по заданному дизайну — на выходе страничка
  2. Внести правки:
  3. переместить какой-то блока между холдерами
  4. отредактировать какой-то блок
  5. удалить какой-тоблок
  6. вставить сторонний блок

Критерий оценки:

  1. Ничего не должно развалиться
  2. Верстка должна соответсвовать неким критериям.

Критерии

  1. Можно четко выделить блок, элемент и модификатор в именах (https://twitter.com/harisov/status/716160112486977540)
  2. … Ваши предложения, мнение?

Делаю вроде простую вещь, но "что-то идет не так".

Хочу, чтобы при нажатии на кнопку "Добавить ещё один адрес" через bemhtml вставлялся такой же блок .address-wrap. Он вставляется, но не понимаю, как сделать так, чтобы на кнопке вновь созданного блока висело событие на создание очередного.

Ниже примеры кода для bemjson, js, deps

часть bemjson

                                      {
                                            block: 'address-wrap',
                                            js: true,
                                            content: [
                                                {
                                                    block: 'content',
                                                    elem: 'title',
                                                    elemMods: { size: 'xs', countable: true },
                                                    count: '3.',
                                                    content: 'Укажите адрес вашего офиса'
                                                },
                                                {
                                                    block: 'form-item-v2',
                                                    mix: { block: 'row' },
                                                    content: [
                                                        {
                                                            block: 'col-md-4',
                                                            content: {
                                                                block: 'form',
                                                                elem: 'label',
                                                                content: 'Город'
                                                            }
                                                        },
                                                        {
                                                            block: 'col-md-4',
                                                            content: {
                                                                block: 'form-input',
                                                            }
                                                        }
                                                    ]
                                                },
                                                {
                                                    block: 'form-item-v2',
                                                    mods: { unerrored: true },
                                                    mix: { block: 'row' },
                                                    content: [
                                                        {
                                                            block: 'col-md-4',
                                                            content: {
                                                                block: 'form',
                                                                elem: 'label',
                                                                content: 'Улица'
                                                            }
                                                        },
                                                        {
                                                            block: 'col-md-4',
                                                            content: {
                                                                block: 'form-input',
                                                            }
                                                        }
                                                    ]
                                                },
                                                {
                                                    block: 'form-item-v2',
                                                    mods: { unerrored: true },
                                                    mix: { block: 'row' },
                                                    content: [
                                                        {
                                                            block: 'col-md-4',
                                                            content: {
                                                                block: 'form',
                                                                elem: 'label',
                                                                content: 'Дом/Корпус'
                                                            }
                                                        },
                                                        {
                                                            block: 'col-md-4',
                                                            content: {
                                                                block: 'form-input',
                                                            }
                                                        }
                                                    ]
                                                },
                                                {
                                                    block: 'form-item-v2',
                                                    mods: { unerrored: true },
                                                    mix: { block: 'row' },
                                                    content: [
                                                        {
                                                            block: 'col-md-4',
                                                            content: {
                                                                block: 'form',
                                                                elem: 'label',
                                                                content: 'Офис'
                                                            }
                                                        },
                                                        {
                                                            block: 'col-md-4',
                                                            content: {
                                                                block: 'form-input',
                                                            }
                                                        }
                                                    ]
                                                },
                                                {
                                                    block: 'form-item-v2',
                                                    mods: { unerrored: true },
                                                    mix: { block: 'row' },
                                                    content: [
                                                        {
                                                            block: 'col-md-4',
                                                            mix: { block: 'col-md-offset-4' },
                                                            content: {
                                                                block: 'btn',
                                                                mods: { size: 'm', 'full-width': true, color: 'green-transparent' },
                                                                mix: { block: 'address-wrap', elem: 'btnAdd' },
                                                                content: 'Добавить ещё один адрес'
                                                            }
                                                        }
                                                    ]
                                                }
                                            ]
                                        },

address-wrap.js

modules.define('address-wrap', ['i-bem__dom', 'BEMHTML', 'jquery'], function(provide, BEMDOM, BEMHTML, $){
    provide(BEMDOM.decl(this.name,
        {
            /* Instance methods */
            onSetMod: {
                'js': {
                    'inited': function() {
                        var that = this;
                        this.counter = 0;

                        this.bindTo('btnAdd', 'click', function (e) {

                            var bemJSON = {
                                block: 'address-wrap',
                                js: true,
                                content: [
                                    {
                                        block: 'content',
                                        elem: 'title',
                                        elemMods: { size: 'xxs'},
                                        mix: { block: 'offset', mods: { 'margin-top': 'reset' } },
                                        content: 'Укажите дополнительный адрес вашего офиса'
                                    },
                                    {
                                        block: 'form-item-v2',
                                        mix: { block: 'row' },
                                        content: [
                                            {
                                                block: 'col-md-4',
                                                content: {
                                                    block: 'form',
                                                    elem: 'label',
                                                    content: 'Город'
                                                }
                                            },
                                            {
                                                block: 'col-md-4',
                                                content: {
                                                    block: 'form-input'
                                                }
                                            }
                                        ]
                                    },
                                    {
                                        block: 'form-item-v2',
                                        mods: { unerrored: true },
                                        mix: { block: 'row' },
                                        content: [
                                            {
                                                block: 'col-md-4',
                                                content: {
                                                    block: 'form',
                                                    elem: 'label',
                                                    content: 'Улица'
                                                }
                                            },
                                            {
                                                block: 'col-md-4',
                                                content: {
                                                    block: 'form-input'
                                                }
                                            }
                                        ]
                                    },
                                    {
                                        block: 'form-item-v2',
                                        mods: { unerrored: true },
                                        mix: { block: 'row' },
                                        content: [
                                            {
                                                block: 'col-md-4',
                                                content: {
                                                    block: 'form',
                                                    elem: 'label',
                                                    content: 'Дом/Корпус'
                                                }
                                            },
                                            {
                                                block: 'col-md-4',
                                                content: {
                                                    block: 'form-input'
                                                }
                                            }
                                        ]
                                    },
                                    {
                                        block: 'form-item-v2',
                                        mods: { unerrored: true },
                                        mix: { block: 'row' },
                                        content: [
                                            {
                                                block: 'col-md-4',
                                                content: {
                                                    block: 'form',
                                                    elem: 'label',
                                                    content: 'Офис'
                                                }
                                            },
                                            {
                                                block: 'col-md-4',
                                                content: {
                                                    block: 'form-input'
                                                }
                                            }
                                        ]
                                    },
                                    {
                                        block: 'form-item-v2',
                                        mods: { unerrored: true },
                                        mix: { block: 'row' },
                                        content: [
                                            {
                                                block: 'col-md-4',
                                                mix: { block: 'col-md-offset-4' },
                                                content: {
                                                    block: 'btn',
                                                    mods: { size: 'm', 'full-width': true, color: 'green-transparent' },
                                                    content: 'Добавить ещё один адрес'
                                                }
                                            }
                                        ]
                                    }
                                ]
                            };

                            that.createAndBind(bemJSON);

                        });
                    }
                }
            },

            addAdress: function(){
                var that = this;
                console.log('add address');
                that.counter++;
            },

            delAddress: function(){
                var that = this;
                console.log('del address');
                that.counter--;
            },

            createAndBind: function(bemJSON){
                var that = this;
                BEMDOM.after(that.domElem[0], BEMHTML.apply(bemJSON));
            }
        },
        {
            /* Statics methods */
        }));
});

address-wrap.deps.js

([
    {
        mustDeps: 'i-bem',
        shouldDeps: [
            { elem: 'jquery' }
        ]
    },
    {
        tech: 'js',
        mustDeps   : [
            {
                block: 'address-wrap',
                tech: 'bemhtml'
            }
        ]
    }
])

Задача научиться рендерить Virtual DOM из текущих шаблонов на bemhtml

Состав команды: @nik-kor @amel-true @khvostov @voischev @awinogradov. Все супер котаны!)

Результат

  1. Написали движок для рендера bemhtml шаблонов и положили аккуратно в PR https://github.com/bem/bem-xjst/pull/235. Экспортит атрибуты в виде массива вида ['div', {...attrs}, content, ...anotherContent].
  2. Сделали адаптер для React https://github.com/bem-contrib/react-xjst, который использует бандл из шаблонов в bemhtml и инстанс React для создания Virtual DOM , с помощью библиотеки react-dom.
  3. Начали доставлять bem-components в React, используя bemhtml шаблоны и стили. Собирается с помощью Webpack и специального лоадера для него https://github.com/alfa-bank-dev/bem-loader, который резолвит все нужное по депсам через bem-deps. Можно дописывать bemhtml шаблоны на уровне проекта как и раньше;)
  4. Запилили песочницу для bem-xjst с переключателем движков: BEMHTML, BEMTREE, VIDOM – https://github.com/bem/bem-xjst/pull/238. Одни шаблоны на все времена, тестить, сравнивать, все, что душе угодно можно делать.
  5. Так же планировали поработать над интеграцией с Redux. Но так сложилось, что задача решилась автоматически. На уровне React ничего не изменилось, поэтому данные как и раньше доезжают до компонентов, где формируется bemjson для функции рендера.

Превью готовых компонентов http://awinogradov.github.io/react-xjst-example/ и песочницы http://vladkhvo.github.io/.

Далее

Зарелизить react-components – библиотеку, которая использует шаблоны и стили из bem-components, но логику React в БЭМ-терминах.

Ура?;) Теперь мы имеем одни шаблоны и стили для параллельных стеков. Поддерживаем в одном месте и в ус не дуем! Предположительно можно написать подобных адаптеров и для других технологий;)

Есть блок, и он содержит в себе какие-то данные. Нужно сформировать другую структуру данных чтобы передать её в дочерний блок. Для этого я написал функцию но не хочу держать её в файле bemtree Как мне её вынести отдельно. Так чтобы можно было подключить и использовать только для данного блока? Cборщик enb

Устанавливаем project-stub

  1. Устанавливаем project-stub
git clone https://github.com/bem/project-stub.git --depth 1 --branch v1.5.0 my-bem-project
cd my-bem-project
npm install # Do not use root privilege to install npm and bower dependencies.
  1. Устанавливаем bem-grid
npm i --save-dev bem-grid
bower install --save bem-grid

!!! ВАЖНО установить двумя способами через npm и bower UPD: говорят, что работает даже с одним npm

  1. Заходим в папку проекта. Редактируем файл my-bem-project/.enb/make.js Добавляем строчку
{ path: 'libs/bem-grid/common.blocks', check: false },
// make.js
...
  enbBemTechs = require('enb-bem-techs'),
    levels = [
        { path: 'libs/bem-core/common.blocks', check: false },
        { path: 'libs/bem-core/desktop.blocks', check: false },
        { path: 'libs/bem-components/common.blocks', check: false },
        { path: 'libs/bem-components/desktop.blocks', check: false },
        { path: 'libs/bem-components/design/common.blocks', check: false },
        { path: 'libs/bem-components/design/desktop.blocks', check: false },
        { path: 'libs/bem-grid/common.blocks', check: false }, // Добавили сюда
        'common.blocks',
        'desktop.blocks'
    ];
...
  1. В этом же файле находим //css и заменяем на
// make.js
...
        //css
            [techs.stylus, {
                target: '?.stylus.css',
                sourcemap: false,
                autoprefixer: {
                    browsers: ['ie >= 10', 'last 2 versions', 'opera 12.1', '> 2%']
                }
            }],
            [require('bem-grid').enb, {
                source: '?.stylus.css',
                target: '?.css',
                config : {
                    maxWidth : '1100px',
                    gutter : '10px',
                    flex : 'flex'
                }
            }],
...
  1. Готово. Теперь можем писать так
// index.bemjson.js
...
{
    block : 'row',
    content : [
        {
            elem : 'col',
            elemMods : { mw : 6 },
            content : 'left column'
        },
        {
            elem : 'col',
            elemMods : { mw : 6 },
            content : 'right column'
        }
    ]
}
...

В итоге получим

...
<div class="row">
    <div class="row__col row__col_mw_6">left column</div>
    <div class="row__col row__col_mw_6">right column</div>
</div>
...

Старался писать как можно подробней. Источник: по ссылке

Какой из вариантов вернее?

Первый
{
         block: 'row', //bem-grid row
         mix: {block: 'someelse'}, // добавляю падинги 
         content: [
              {
                  elem: 'col',
                  elemMods: {mw: 12},
                  content:
                       {
                            block: 'someelse',
                            elem: 'heading', // элемент блока someelse
                            content: 'Какой-то заголовок секции'
                       }
             },
             {
                 elem: 'col', 
                 elemMods: {mw: 6}, 
                 content: 
                     {
                         block: 'someelse',
                         mix: {block: 'box'},
                         elem: 'item',
                         content: 'some content'
                     }
             }
         ]
}
Второй
{
    block: 'row', //bem-grid row
    content: {
         block: 'someelse',
         content: [
              {
                  block: 'row',
                  elem: 'col',
                  elemMods: {mw: 12},
                  content:
                       {
                            elem: 'heading', // элемент блока someelse
                            content: 'Какой-то заголовок секции'
                       }
             },
             {
                 block: 'row',
                 elem: 'col', 
                 elemMods: {mw: 6}, 
                 content: 
                     {
                         mix: {block: 'box'},
                         elem: 'item',
                         content: 'some content'
                     }
             }
         ]
     }
}

В первом мы примиксовываем стили, во втором вкладываем блок "someelse" в строку "row".


В первом случае приходится потом постоянно объявлять блок при создании элементов блока

...
block: 'someelse', 
elem: 'item'
...

во втором случае приходится объявлять

...
block: 'row', 
elem: 'col'
...

Как быть? Может быть скинете примеры реализации в уже рабочих проектах?


Вы меня конечно простите, если я задаю тупые вопросы. Просто я хочу докопаться до сути, чтобы потом двигать БЭМ в массы.

Добрый день. Подскажите, пожалуйста, есть ли статья по темам? Не хочу использовать "islands". Как создавать свои темы?

Может кому-нибудь и пригодится. За пару месяцев работы с БЭМом я подметил для себя 3 вида сборки бандлов:

  • когда изменяется непосредственно bemjson
  • когда изменяется bemhtml блока
  • когда изменяются скрипты и стили

Причем только первые два вида могут влиять на конечную структуру html и скрипты и стили. Третий вид изменяет только подключаемые скрипты и стили. Общее здесь - скрипты и стили. Они могут быть модифицированы в любом из вариантов. Из этого следует, что будет как-то нерационально генерить каждый раз html при изменении скриптов или стилей.

А ведь они, в принципе, правятся довольно частенько.

Следовательно нужна такая сборка, которая не делала бы лишних дейтсвий: например, разработчик изменил css, а enb взял бы и пересобрал только css. Еще лучше, если бы эта работа была сопряжена с галповским вотчем.

Сейчас у нас на работе есть подобная сборка, но она не такая гибкая, как хотелось бы. К тому же начальник на днях надоумил меня написать свою. Изначально мне эта идея показалась бредом (зачем писать, когда уже есть и работает), но потом посидев вечер и ночь перед компьютером, у меня стало получаться что-то работоспособное. Потом я стал немного допиливать, допиливать и вот допилил, думаю я. Дим, если читаешь эти строки, знай - я тебе реально благодарен)) Честно!

Так вот, работает эта сборка в 4 режимах в связке с вотчем и устроена слегка костыльно. Сначала о режимах:

full - пересобирает абсолютно все nofull - пересобирает измененные бандлы и все скрипты и стили nofull_css - "по умолчанию" пересобирает только стили nofull_js - "по умолчанию" пересобирает только скрипты

Сама костыльность заключается вот в чем. Для того чтобы узнать изменился бандл или нет, в папке с ним создается файл формата .uptime_[0-9]+ в названии которого находится дата последней модификации bemjson файла. Далее при обходе бандла скрипт смотрит на время модификации bemjson и на этот чудо файл и путем просто сравнения решает, изменялся ли bemjson файл или нет. Ну и далее все ясно: генерить новый html или идти дальше. "По умолчанию" я выделил в кавычки вот почему, эти режимы могут пересобирать измененные бандлы, но генерить они будут помимо html, или css или js. Поэтому их лучше и должно использовать при изменении css или js. Пожалуй, на этом все.

PS. Самое главное) После этой небольшой проделанной работы, хотелось бы выразить огромную благодарность всем участникам форума, которые отвечали на мои бестолковые вопросы ( отдельная благодарность Владимиру Гриненко за его ангельское терпение и постоянный труд по развитию и донесению идеологии БЭМа массам)), а также моему начальнику Диме, который несмотря на свою загруженность, помогал и помогает мне в освоении БЭМа, (и не только БЭМа), и без которого вряд ли бы эта сборка была написана. Спасибо вам, ребята, огромное!

Сначала gulpfile, как этим всем пользоваться

var gulp = require('gulp');
var watch = require('gulp-watch');
var enb = require('enb');
gulp.task('watch', function (cb) {
    watch('desktop.bundles/**/*.bemjson.js', function () {
        enb.make( [], { mode: 'nofull' } );
    });
    watch( '*.blocks/**/*.js', function () {
        enb.make( [], { mode: 'nofull_js' } );
    });
    watch( '*.blocks/**/*.css', function () {
        enb.make( [], { mode: 'nofull_css' } );
    });
    watch( '*.blocks/**/*.bemhtml', function () {
        enb.make( [], { mode: 'full' } );
    });
});

Собственно make.js

var path = require('path');
var fs = require('fs');
var platforms = ['desktop'];
var techs = {
    // essential
    fileProvider: require('enb/techs/file-provider'),

    // optimization
    borschik: require('enb-borschik/techs/borschik'),

    // css
    sass: require('enb-sass/techs/css-sass'),

    // js
    browserJs: require('enb-js/techs/browser-js'),

    // bemtree
    // bemtree: require('enb-bemxjst/techs/bemtree'),

    // bemhtml
    bemhtml: require('enb-bemxjst/techs/bemhtml'),
    bemjsonToHtml: require('enb-bemxjst/techs/bemjson-to-html')
},
enbBemTechs = require('enb-bem-techs'),
levels = [
    'common.blocks',
    'jquery.blocks',
    'desktop.blocks'
];

function getUpdateTime( file ) {
    var mtime = 0;
    try {
        mtime = +new Date(fs.statSync( file ).mtime)/1000;
    } catch(e) {
        return 0;
    } finally {
        return mtime;
    }
}

function createUpdadeFile( path, time ) {
    fs.closeSync(fs.openSync( path+'.uptime_'+time, 'w'));
}

function isUpdatingFile( path, time ) {
    var files = fs.readdirSync( path );
    var up_files = [];
    for( var i = 0; i < files.length; i++ ) {
        var file = files[i];
        if( /^\.uptime_[0-9]+$/.test( file ) ) {
            if( file !== '.uptime_'+time ) {
                fs.unlinkSync( path+file );
            } else {
                up_files.push( file );
            }
        }
    }
    if( up_files.length === 0 ) {
        createUpdadeFile( path, time );
        return true;
    }
    return false;

}

module.exports = function(config, sa) {

    // создаем папки merged в сборках бандлов
    platforms.forEach(function (platform) {
        var node = path.join(platform + '.bundles', 'merged');

        if (!fs.existsSync(node)) {
            fs.mkdirSync(node);
        }
    });

    var isFull = true;
    var noFull = 'all';


    config.mode( 'full', function() {
        isFull = true;
    });

    config.mode( 'nofull', function() {
        isFull = false;
    });
    config.mode( 'nofull_js', function() {
        isFull = false;
        noFull = 'js';
    });
    config.mode( 'nofull_css', function() {
        isFull = false;
        noFull = 'css';
    });



    config.nodes('*.bundles/*', function(nodeConfig) {
        var timeUpdateBem = getUpdateTime( path.dirname(nodeConfig.getPath())+'/'+path.basename(nodeConfig.getPath())+'/'+path.basename(nodeConfig.getPath()) + '.bemjson.js' );

        // если нет bemjson файла - выходим отсюда
        if( timeUpdateBem == 0 ) {
            if( path.basename(nodeConfig.getPath()) !== 'merged' ) {
                console.log( 'error: bundle ' + path.basename(nodeConfig.getPath()) + ' no bemjson-file' );
            }
            return;
        }
        if ( !isFull ) {
            isFull = isUpdatingFile( path.dirname(nodeConfig.getPath())+'/'+path.basename(nodeConfig.getPath())+'/', timeUpdateBem );
        }
        if( !isFull ) {
            nodeConfig.addTechs([
                [enbBemTechs.levels, { levels: levels }],
                [techs.fileProvider, { target: '?.bemjson.js' }],
                [enbBemTechs.bemjsonToBemdecl,  { target: '../merged/?.bemdecl.js' }],
                [enbBemTechs.deps, { bemdeclFile: '../merged/?.bemdecl.js', target: '?.deps.js' }],
            ]);
        } else {
            nodeConfig.addTechs([
                // essential
                [enbBemTechs.levels, { levels: levels }],
                [techs.fileProvider, { target: '?.bemjson.js' }],
                [enbBemTechs.bemjsonToBemdecl,  { target: '../merged/?.bemdecl.js' }],

                [enbBemTechs.deps, { bemdeclFile: '../merged/?.bemdecl.js', target: '?.deps.js' }],
                [enbBemTechs.files],

                // bemhtml
                [techs.bemhtml, { sourceSuffixes: ['bemhtml'], forceBaseTemplates: true, target: '../merged/?.bemhtml.js' }],

                // html
                [techs.bemjsonToHtml, { bemhtmlFile: '../merged/?.bemhtml.js', target: '../merged/?.html' } ],
            ]);
            nodeConfig.addTargets([ '../merged/?.html']);
        }
    });

    config.node('desktop.bundles/merged', function(nodeConfig) {
        var dir = path.dirname(nodeConfig.getPath());
        var bundles = fs.readdirSync(dir);
        var bemdeclFiles = [];
        var depsFiles = [];
        var targetsArr = [];
        var techsArr = [
            [techs.sass, {
                target: 'main.css',
                sourcemap: false,
                autoprefixer: {
                    browsers: ['ie >= 10', 'last 2 versions', 'opera 12.1', '> 2%']
                }
            }],
            [techs.borschik, { source: 'main.css', target: 'main.min.css', tech: 'cleancss', minify: true }],
            [techs.browserJs, { includeYM: false, target: 'main.js' }],
            [techs.borschik, { source: 'main.js', target: 'main.min.js', minify: true }],
        ]
        bundles.forEach(function (bundle) {
            if (bundle === 'merged' || bundle === '.bem') return;
            var node = path.join(dir, bundle);
            var target_bemdecl = bundle + '.bemdecl.js';
            var target_deps = bundle + '.deps.js';
            bemdeclFiles.push( target_bemdecl );
            depsFiles.push( target_deps );
            nodeConfig.addTechs([[enbBemTechs.provideDeps, { node: node, source: target_deps, target: target_deps }],]);
        });
        nodeConfig.addTechs([
            [enbBemTechs.mergeDeps,  { sources: depsFiles }],
            [enbBemTechs.files, { depsFile: '?.deps.js' } ],
            [enbBemTechs.levels, { levels: levels }],
        ]);
        if( noFull === 'css' ) {
            nodeConfig.addTechs([
                techsArr[0],
                techsArr[1],
            ]);
            targetsArr.push( 'main.min.css' );;
        } else if( noFull === 'js' ) {
            nodeConfig.addTechs([
                techsArr[2],
                techsArr[3],
            ]);
            targetsArr.push( 'main.min.js' );;
        } else {
            nodeConfig.addTechs([
                techsArr[0],
                techsArr[1],
                techsArr[2],
                techsArr[3],
            ]);
            targetsArr.push( 'main.min.css' );
            targetsArr.push( 'main.min.js' );
        }
        nodeConfig.addTargets(  targetsArr );
    });


};

А как сейчас с поддержкой модификаторов без значений в i-bem, bem-xjst?

Судя по этой странице: https://ru.bem.info/technology/i-bem/v2/i-bem-js-mods/#mods-api i-bem, например, уже поддерживает. Только в bem-core?

Просто интересно в чем может быть причина? В папке merged лежат скопированные бемдеклы бандлов (копируются при обходе бандлов проекта), при заходе на merged-бандл срабатывает эта функция

config.nodes('*.bundles/merged', function(nodeConfig) {
        var dir = path.dirname(nodeConfig.getPath()),
        bundles = fs.readdirSync(dir),
        bemdeclFiles = [];
        bundles.forEach(function (bundle) {
            if (bundle === 'merged' || bundle === '.bem') return;
            var node = path.join(dir, bundle),
            target = bundle + '.bemdecl.js';
            bemdeclFiles.push(target);
        });
        nodeConfig.addTechs([
            [enbBemTechs.mergeBemdecl,  { sources: bemdeclFiles }],
        ]);
        nodeConfig.addTarget( 'merged.bemdecl.js' );
    });

И выдает ошибку There is no tech for target desktop.bundles\merged\example.bemdecl.js Что не так? Заранее спасибо

Рассмотрим 3 случая merged-сборки проекта в режиме watch: 1 - изменение бандла (bemjson) 2 - изменение css, js шаблона 3 - изменение самого шаблона (bemhtml)

При изменении бандла все просто, запускается enb-make и далее все происходит автоматически. А вот как быть при 2 остальных случаях? Не хотелось бы пересобирать весь проект при изменении одного блока, который используется в 2 бандлах, к примеру, из 50.

Как вариант: можно проходиться по депсам бандлов, сравнивать с измененным и уже от этого исходить. Но может есть уже какое-то готовое решение? Заранее спасибо.

Хочу доопределить инициализацию блока dropdown.

Нужно что бы он инициализировался когда инициализируется блок на котором он примиксован. Как мы знаем у dropdown свитчер блок рисуется в зависимости от его модификатора. А ещё в моду switcher можно засунуть любой другой блок и он все равно на него замиксуется.

Как правильно решить задачу: Инициализируем dropdown когда блок в моде switcher заинициализировался (*_js_inited) ну или по ховеру на контролле (*_hovered).

хочется (но не можется) в live для dropdown написать что-то типа такого:

this.liveInitOnBlockEvent(
    { modName: 'js', modVal: 'inited' }, 
    this.getMod('switcher') // или любая другая функция которая вернет тот самый правильный блок
);

Если мы никак не можем получить список тех блоков с которыми можно работать, то подобное нужно будет писать для каждого модификатора и блока отдельно или даже в отдельных файлах. А что если нам нужно связка модификаторов?

В общем подскажите. Завязываться на dom событие в этом случае кажется нифига не круто.

Привет! Контекст вопроса:

  1. У меня есть блок .hide { display: none }.
  2. Я его миксую, например, когда необходимо, чтобы присутствующая на странице кнопка (.btn) не отображалась до появления какого-либо события. После чего через js просто указывается display: block 3.В стилях у .btn задан display: inline-block.
  3. В сборке стили блока .hide оказались выше стилей .btn и поэтому миксование данного блока (.hide) не сказывается на отображении кнопки.

Можно ли как-то задать, чтобы определенные блоки включались в сборку всегда последними? p.s. сейчас просто задаю через attrs: { style: 'display-none' }.

Добрый день. Прошу сделать урок по bem-xjst. Я вот честно не врубаюсь как пользоваться. Спасибо.

Есть два блока switcher и collection.

Switcher представляет из себя две кнопочки, точнее даже две радиокнопки. И просто по checked на одну из них отправляет событие с данными

this.emit('switch', {
    type : newType,
    id : id,
    grid : this.params.grid,
    list : this.params.list
});

Есть блок collection, блок который слушает это событие и менят по модификаторы в зависимости от данных переданных в событие.

Все работает, кроме одного кейса, при инициализации switcher вызывает событие, а судя по всему к этому моменту collection не проснулся и не ловит его. Как лучше поступить?

Сейчас switcher ничего не знает о collection, а collection просто слушает событие от блоков Switcher и отсеивает их по id, который у них общий.

Switcher.on(
    'switch',
    this._onSwitch,
    this
);

Добрый день, используем в проекте фреймворк bnsf автора @apsavin Вопрос скорее всего к автору: как создавать публичные параметры приложения. Раньше не работал с yml. Методом научного тыка создал файл index.parameters.dist.yml и из него получаю нужные переменные в роутинге. Публичные параметры, как я понял, складываются в data-parameters блоку page, но не могу понять как их создавать. Хотелось бы больше информации по этому поводу.

Здравствуйте! Изучаю бэм, возник вопрос - правильно ли делать так:

<div class="info-box">
    <div class="info-box__item info-box__item_time">Время</div>
    <div class="info-box__item info-box__item_money">Деньги</div>
    <div class="info-box__item info-box__item_number">Номер</div>
</div>

Смущает _time; _money; _number в названии классов. Так можно делать? Естественно при условии, что на _time; _money; _number навешиваются доп стили, на каждый элемент свой стиль.