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

Всем привет!

Пишу проект с использованием bem. На бэкенде koa сервер, который в том числе и осуществляет сборку фронетенда. Для удобста разработки в dev окружении при рендере страницы происходит пересборка проекта с помощью enb/lib/api/make.js. Столкнулся с проблемой, что при изменении блоков и перезагрузки страницы пересборка запускается (и выполняется успешно), но отдается старая версия. Пробывал прибивать кэши BEMHTML и BEMTREE файлов:

delete require.cache[require.resolve(bemtreePath)];
delete require.cache[require.resolve(bemhtmlPath)];

, но это не помогло.

Пробывал прибивать кэш модуля рендера:

delete require.cache[__filename];

тоже ничего. Подскажите как это правильно сделать.

UPD: решил проблему! Проблема была в том, что я подгружал BEMHTML и BEMTREE файлы в САМОМ начале модуля и дроапал кэши в КОНЦЕ модуля, а в самом модуле была регистрация middleware. Когда перенес загрузку BEMHTML и BEMTREE в начало middleware и очистку кэша в конец middleware все заработаело

app.use(function *(next) {
      const BEMTREE = require(bemtreePath).BEMTREE;
      const BEMHTML = require(bemhtmlPath).BEMHTML;
     .....
       // remove require cache
      delete require.cache[require.resolve(bemtreePath)];
      delete require.cache[require.resolve(bemhtmlPath)];
})

Накидал docker контейнер для запуска project-stub - https://github.com/nejtr0n/bem Возможно кому то пригодится. Запускается просто:

git clone https://github.com/nejtr0n/bem.git && cd bem
docker-compose up

При первом запуске project-stub соберётся в папку bem.

Привет! скиньте пожалуйста пример подключения JQuery плагина FancyBox и инициализации его на странице, буду признателен если опишите процесс по пунктам что, куда, зачем и как.

Здравствуйте. Как я понял одна из фишек БЭМ - это декларативность написания кода, а также повторное использование блоков. В react и angular также есть директивы и компоненты, которые помогают многие вещи делать декларативно и переиспользовать. Обладает ли БЭМ - платформа какими - то уникальными преимуществами? Есть ли смысл использовать БЭМ в небольших проектах?

Всем доброго дня.

Пытаюсь подписаться на делегированное БЭМ-событие с пересечением границы блока.

Есть структура:

страница
  панель
    ссылки

Если я в js: inited панели подписываюсь

NotesLink.on(this.domElem, "click", this._onLinkClicked, this);

то события приходят, а если - в js: inited страницы, то - нет.

Пытался в качестве первого параметра подавать domElem панели - события всё равно не приходят.

Что наводит на мысль, что БЭМ-события не пересекают границ блока, содержащего блоки, генерирующие события. Это так?

Есть код в рамках модуля (не знаю, как обратиться в рамках БЭМ к хрефу, поэтому по старинке):

var
    afishaLoc = '#afisha',
    loc = window.location.hash;

if(loc == afishaLoc) {
    $('a[href="' + afishaLoc + '"]').parent('li').click();
}

this.bindTo('item', 'click', function(e){
    console.log('Click!');
}

Условие выполняется, но клик почему-то не происходит. li имеет класс block-name__item

Всем привет,

Кратко об основных событиях в мире БЭМ за последние 4 месяца с предыдущего дайджеста.

Новости библиотек

  • Выпустили bem-core 4.0.0-rc.1. Подробнее о релизе можно прочитать на форуме bem.info. Основные изменения коснулись усовершенствования i-bem.js. Если вы готовы — этот релиз уже сейчас можно щупать, крутить и внедрять в свои проекты. Стабильная версия выйдет совсем скоро. Не забывайте сообщать нам обо всех найденных ошибках.
  • Выпустили мажорную версию bem-components 3.0.0 которая работает с bem-core 3.0.1. Несмотря на то, что релиз мажорный — переход на него должен быть практически безболезненным. Подробнее о релизе читайте здесь.
  • Выпустили bem-history 3.2.0. Основное изменение — поддержка bem-core 3.x в bower.json. И, конечно, несколько багфиксов в комплекте.
  • Переименовали организацию bem-incubator в bem-contrib. Это название гораздо лучше отражает предназначение организации. Если вы используете (или разрабатываете) какие-либо пакеты, следует обновить пути к репозиторию (какое-то время github поддерживает редиректы автоматически). Можно использовать инструкцию.

Новости технологий

  • Выпустили два мажорных, десятки минорных и патчевых релизов bem-xjst. Самая свежая весия — 6.5.1 Основные изменения:
    • BEMTREE
    • Реализовали поддержку ES6 arrow functions
    • Сделали опциональный эскейпинг контента
    • Поддержали JS-обвязку для элементов
    • Сделали режим для отрисовки HTML без /:
      вместо
    • Удалили часть устаревших методов и функций.

Подробнее обо всех изменениях bem-xjst читайте в release-notes, которые подробно пишутся к каждому релизу: https://github.com/bem/bem-xjst/releases.

Новости инструментов

Активно работали над ENB:

Работали над плагинами для ENB:

Новости сайта

  • Совсем недавно мы частично обновили дизайн сайта bem.info — он стал более современный, быстрый и красивый.
  • Выкатили украинскую версию https://uk.bem.info, впереди переводы и на другие языки.

Новости мероприятий

Интересные темы на форуме

Всем доброго времени суток. У кого есть готовый вариант модуля с YouTube iframe api https://developers.google.com/youtube/iframe_api_reference?hl=ru#Requirements ? Или может кто-то подскажет, как его разместить в рамках модуля? А то помещая код в модуль, он или не выполняется или выдаёт ошибку, хотя при инлайновой вставке на страницу, все нормально.

Очень интересно, но при работе с Notepad++ компилится в 2-3 раза быстрее, чем когда правлю bemjson вимом. У кого было такое? Чем это лечится? Заранее спасибо

Как-бы прописать блоку атрибут xlink:href: ? Обычный способ attrs: {xlink:href: '#name'}, даёт ошибку

Или

Возможно есть другой способ подключить Matherial Buttons - http://tympanus.net/codrops/2015/09/14/creating-material-design-ripple-effects-svg Как бы это сделали вы?

BEMHTML.apply({block: 'name', content: 'cont'}) ни чего не возвращает

2 года назад) некий @tadatuta на хакатоне написал тулзу для блогов из маркдауна и бэм-стека. Так вот после 2х лет забвения понадобилась мне тулза подобная чтобы на один манер собирать доки, блог, просто странички и тд. В общем случился форк, перерождение и отличный швейцарский нож для статики на bem-xjst и makrdown. https://github.com/awinogradov/bemark

Всем доброго времени суток. Очень понравилась работа вашего фреймворка i-bem.js. У нас на новом проекте пока-что используются наименования классов по БЭМ и сейчас налаживается работа фронтенда. В качестве работы с js хотелось бы выбрать ваш фреймворк, так как он наиболее полно дает возможности модульной разработки js кода, но пока-что никак не удается найти способ работать именно с ним. Во всех примерах, что мы нашли, включается в разработку автоматическая сборка HTML и вообще, разделение кода страниц на отдельные папки и файлы. Этот вариант нам не подходит, так как серверной частью у нас будет заниматься PHP фреймворк Symfony и собираться страницы будут шаблонизатором Twig. По-этому, прошу у вас помощи разобраться, как внедрить во фронт проекта ваш фреймворк для модульного js, при этом оставив файловую структуру и все остальное (кроме нейминга) своим.

P.S. на начальном этапе, весь css и js у нас будет собираться каждый в один файл грантом.

Всем привет. Данный пост адресован тем, кто хочет использовать БЭМ на сервере вплотную (а точнее NodeJS), но боится самой ноды. Я и сам сейчас занимаюсь выбором фреймворка для проекта. Изначально хотел использовать ExpressJS, но решил перейти на его "продолжение" - KoaJS (немного архитектура приятней).

Но вот наткнулся сейчас на ещё один фреймворк, после которого не смог не написать о нём сюда. AdonisJS.

Это некое воплощение Laravel (PHP) на NodeJS. Визуально всё выглядит очень легко и приятно и код даже чище, чем на PHP. Есть только один минус.

Количество материалов в сети по данным фреймворкам расположено по иерархии их перечисления в посте, начинаю с большего.

ExpressJS - самый старый, из-за этого и бесконечное число материалов. KoaJS - немного материалов. AdonisJS - самое меньшее кол-во материалов.

Но если вы привыкли к Yii, Laravel, RubyOnRails - то для вас AdonisJS будет прекрасным источником вдохновения для перехода на NodeJS.

Доброго дня.

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

Есть панель со ссылками, которые являются блоками. По событию содержимое панели нужно обновить новыми ссылками. Код выглядит так:

var html = // ручной рендеринг
BEMDOM.update(this.domElem, html);

В документации (https://ru.bem.info/technology/i-bem/v2/i-bem-js-init/#init-ajax) написано, что BEMDOM.update выполняет динамическую инициализацию блоков, но для моих ссылок этого не происходит, и приходится писать вот такой код инициализации:

$(".notes-link", this.domElem).each(function(_, link) {
    $(link).bem("notes-link");
});

Я подозреваю, что блоки для автоматической инициализации нужно обложить какими-нибудь специальными атрибутами. Пробовал оформлять ссылки так: <a href="#" class="notes-link i-bem" data-bem="{&quot;notes-link&quot;: {}}">link text</a>

Но не помогло.

Может кто-нибудь подсказать, как правильно инициализировать новодобавленный контент?

Насколько я помню ymodules не умеет подключать модули из внешних файлов. А очень надо. Если модуля нет в рантайме, то подгружать его по url, как в requirejs. Возможно ли это? Если да, то в какую сторону смотреть?

Всем привет!

Сегодня мы зарелизили bem-core@v4.0.0-rc.1.

Это релиз-кандидат большого релиза bem-core@v4.0.0, который появится совсем скоро.

Мы сделали достаточно большое количество обратно несовместимых изменений, ради того, чтобы API стало легче понять, запомнить и удобнее использовать.

Уже доступен миграционный гайд и обновлённая документация на i-bem.js. Если вы не используете bem-components, то можно начинать изучать, крутить, вертеть и подключать к своим проектам новую версию bem-core.

Что нового и важного:

  • Значительно упрощено и унифицировано API работы со всеми видами событий:

    this._domEvents().on('click', this._onClick);
    this._events(this.findChildBlock('button')).on('click', this._onButtonClick);
    

    Кроме того, теперь все основные способы использования не требуют самостоятельного удаления подписок при уничтожении экземпляра — отписка происходит автоматически. Подробнее в миграционном гайде.

  • Экземпляры для элементов перенесены в блоки i-bem и i-bem-dom и стали частью ядра. Теперь с элементами работать так же удобно, как с блоками:
this.findChildElem('button').setMod('disabled');

Подробнее в миграционном гайде.

  • Функциональность элемента collection блока i-bem перестала быть опциональной. Все методы, возвращавшие несколько БЭМ-сущностей, теперь возвращают коллекции:

    this.findChildBlocks('control').setMod('disabled');
    

    Подробнее в миграционном гайде.

  • Ленивая инициализация (поле live) разделена на две отдельные части — поле lazyInit и метод onInit(). Более явные названия без смешивания в одном месте двух вещей. Подробнее в миграционном гайде.
  • Удалены и переименованы множество методов — API стало чище и понятнее.

Все подробности о миграции на новую версию читайте в миграционном гайде. А документация на i-bem.js уже содержит полностью обновлённую информацию.

Мы просим вас писать нам любой фидбек о найденных ошибках и проблемах на форум или сразу в тикеты, чтобы мы могли сделать обратно несовместимые исправления до полного релиза bem-core@v4.0.0.

P. S. Релиз-кандидат bem-components с bem-core@v4 внутри будет в ближайшее время. Также мы ещё допишем английскую версию документации, чейнджлог и миграционный гайд.

При вызове у button событие click() не происходит клик. Как вызвать через консоль?

Добре!

Есть пример как запустить тесты под BH engine

https://github.com/enb/enb-bem-specs/blob/master/examples/silly/.enb/make.js

Но как выяснилось это не работает.

Смотрите. Мне важно, чтобы блок был добавлен в html до исполнения JS.

зовут его b-legend

Для того, чтобы он попал в тесты используется специальный слой переопределения

app-specs.blocks где и лежит спец файл BH

app-specs.blocks/b-legend/b-legend.bh.js

Внутренность у него проста:

module.exports = function(bh) {
    bh.match('spec-content', function () {
        return {block: 'b-legend'};
    });
};

И да используется библиотека bem-pr где существует блок spec-content, переопределяя который по идее внутри должны получить блок b-legend.

Все бы хорошо, но не работает сборка bemdecl файлов в enb-bem-specs.

Добавил слои:

layers = [
   'app.blocks',
   'app-specs.blocks'
];

specs.configure({
    langs: false,
    destPath: 'specs',
    levels: layers,
    templateEngine: {
        templateTech: require('enb-bh/techs/bh-bundle'),
        templateOptions: {
            devMode: false
        },
        htmlTech: require('enb-bh/techs/bemjson-to-html'),
        htmlTechOptionNames: { bemjsonFile: 'bemjsonFile', templateFile: 'bhFile' }
    },
    sourceLevels: [
        { path: 'libs/bem-pr/spec.blocks', check: false }
    ].concat(layers),

    jsSuffixes: ['js'],
    specSuffixes: ['spec.js']
});

Запустил таск, появилась куча файлов в spec/b-legend:

И даже b-legend.base.bemdecl.js, содержит нужный блок. Но после всех mergeDeps, mergeDecl этот блок не попал в сборку BH. Но попал в сборку JS.

Оказывается вся загвоздка в том, что при сборке HTML, не учитывается базовая зависимость ?.base.bemdecl.js и следовательно ?.bemdecl.js

Что может спасти ситуацию?

Добавить эти зависимости для сборки html используя mergeDeps:

// Client BEMHTML
        nodeConfig.addTechs([
            [depsByTechToBemdecl, {
                target: '?.js.template.bemdecl.js',
                sourceTech: 'js',
                destTech: 'bemhtml'
            }],
            [depsByTechToBemdecl, {
                target: '?.spec-js.template.bemdecl.js',
                sourceTech: 'spec.js',
                destTech: 'bemhtml'
            }],
            [mergeBemdecl, {
                target: '?.template.bemdecl.js',
                sources: [
                    '?.js.template.bemdecl.js',
                    '?.spec-js.template.bemdecl.js',
                    '?.bemjson.bemdecl.js'
                ]
            }],
            [depsTech, {
                target: '?-pre.template.deps.js',
                bemdeclFile: '?.template.bemdecl.js'
            }],
            [mergeDeps, {
                target: '?.template.deps.js',
                sources: ['?.bemdecl.js', '?-pre.template.deps.js']
            }],
            [files, {
                depsFile: '?.template.deps.js',
                filesTarget: '?.template.files',
                dirsTarget: '?.template.dirs'
            }],
            [templateEngine.templateTech, templateEngineOpts]
        ]);

Предусмотреть конфигурацию i-bem в режиме отладки, в котором в момент подписки this.on в аттрибуты соответствующего html-элемента будет записываться название метода и его блока ИЛИ записывать в некое хранилище ссылку на метод-обработчик, с некоторым уникальным ключом, и ключ этот добавлять опять же к html-элементу (что-то типа: data-event-debug-id).

Благодаря этому станет легче дебажить, а можно ещё и плагин накрутить.

Или всё и так просто, а я не в курсе?

Проблема такая: есть кнопка, но пока в коде найдёшь для неё обработчик потратишь уйму времени.

Выкатили украинскую версию

https://uk.bem.info

Будут ещё переводы на другие языки. Если хотите помочь или заметили ошибку — пишите.

Привет!

Только что вышел релиз bem-history 3.2.0.

Основное изменение — поддержка bem-core 3.x в bower.json. И, конечно, несколько багфиксов в комплекте.

Предполагается «бесплатное» обновление.

На бэминаре я задавал вопрос, связанный с "картой" для bemtree, сейчас постараюсь его более конкретно расписать.

На файловой системе у нас блоки расположены на плоскости.

блок-1
блок-2
блок-3
...
блок-N

но при построении, плоская структура превращается в иерархическую.

BEMTREE | BEMJSON | HTML

Приходит новый разработчик... И хватается руками за голову. Ему необходимо перекопать проект (если он знаком с БЭМ), в поисках того, как всё устроено.

Но мы показываем ему картину мира: blocks И просмотр кода в браузере|bemtree становится понятным. При данном визуальном представлении можно абстрагироваться над всеми отвлекающими факторами (тегами, кавычками, скобками, bemjson) и видеть цельную картину из блоков.

CSS

Но вот наш новобранец решает что ему не нравится блок header, мысли у него локальные... Он и меняет стили блока header, думая про popup. Но мы говорим ему: "Стой друг! Мысли глобальней - возьми картину мира, картину мест использования данного блока (зависимостей независимых блоков)": blocks

Теперь разработчик оценивает масштабы и к изменением подходит более обдуманно.

JS

Ну и на последок он решился сделать свой блок и написать ему бизнес-логику для браузера. И мы ему опять таки говорим: "Дружище лови себе памятку по компонентам. Наследуйся от блоков, в них уже многое реализовано!" i-bem

Это всё абстракция и выдуманные примеры, но хочется сделать БЭМ ещё понятней для новых членов команды. Вот я и задавал вопрос, используются ли подобные подходы в Яндекс?

Всем привет.

Наткнулся на реализации Flux для BEM. Сразу всплыла мысль про events__channels. Какой профит от использования Flux, если мы можем через каналы кидать события + данные?

Возможно я не очень сильно понимаю идею хранилищ.

Error: Seems like you have no base templates from i-bem.bemhtml

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

Хочется избавиться от написания bemjson в js файлах блоков. Учитывая, что на клиенте уже есть BEMHTML, кажется вес js не должен сильно увеличиться.

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

Привет!

Мы под покровом ночи выпустили bem-components 3.0.0!

Несмотря на мажорную версию, обновление должно быть «бесплатным»: просто обновите версию в bower.json и пересоберите проект.

Тогда почему мажор? Потому что:

  • обновили зависимость от библиотеки bem-core до версии 3.0.1, где оторвали FastClick в пользу собственного решения для iOS
  • отказались от поддержки древних версий bem-xjst (вы ведь уже давно обновились?)
  • переименовали *.bemhtml в *.bemhtml.js (даешь подсветку синтаксиса!)
  • и еще по мелочи.

Теперь версия публикуется с предкомпилированным CSS, так что больше нет необходимости использовать Stylus для сборки (вреда однако ж тоже никакого ;)

Кроме того, версия теперь публикуется еще и в npm.

И как всегда, помимо основного варианта поставки в виде исходников, доступна не требующая сборки dist-поставка для локальной установки в проект и для прямого подключения с CDN.

Подробно со списком изменений можно ознакомиться здесь.

Приятного обновления!

PS: project-stub уже обновлен.

Имеется блок chart с элементом legend. как можно вызвать методы элемента в ymodule? Каждая диаграмма содержит элементы(заголовок, легенда,график и тд) Хочу создать блок для диаграмм у которого в роле элементов выступают как раз элементы(заголовок, легенда,график и тд) В элементе легенды хочу сделать статический метод что то вроде js render() а из блока chart вызывать его. может где то я не прав в методолгоии и нужно реализовывать легенду отдельным блоком?

chart.js

modules.define('chart', ['i-bem__dom','d3','BEMHTML'], function (provide, BEMDOM, D3, BEMHTML) {
    provide(BEMDOM.decl(this.name, {
        onSetMod: {
            js: {
                inited: function () {
                    this._graphic = this.findElem('graphic');           //Находим елемент графика
                    this._legend = this._elem('legend');                //Находим елемент легенды
                    this._cdata = this.params.data;                     //предварительная обработка входящих данных
                                        this._legend.someMethod() // Ругается что не существует метод
                }
            }
        }
    }));
});

legend/chartlegend.js

modules.define('chart', ['i-bem__dom','d3'], function(provide, BEMDOM, D3) {
provide(BEMDOM.decl(this.name, {
    onSetMod: {
        js: {
            inited: function(){
        this.__base.apply(this, arguments);     // тут не понятно немножко момент № 1
                console.log("chart_legend inited");
            }
        }
    },
    someMethod: function() {
             console.log("chart_legend generate");
                 return "911";
    },

}));
});

обязательно js-модуль дефайнить как блок? если так делать то модель элемента поавторитетней модуля блока, поэтому нужно this.__base.apply() вызывать?!

И немножко не по теме, почему то в таком контексте(chart.js) не срабатывает js BEMHTML.apply({block:'someblock',content:'someContent'}); - ничего не возвращает.

До Яндекса я 9 лет работал веб-разработчиком на C#. Мне всегда очень нравилась идея БЭМ и я очень огорчался, что не могу использовать БЭМ-инструменты в проектах на .NET.

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

Итак...

Задача

По сути, задача состоит из двух относительно независимых частей:

  • сделать сборку БЭМ-бандлов в Visual Studio (т.е. под Windows);
  • сделать серверную шаблонизацию во время работы приложения.

    Сборка

    Простой вариант: запускаем enb.

В мире .NET есть такой сборщик - MsBuild. Именно с помощью него собирается проект, когда вы нажимаете Ctrl + F5 в Visual Studio. По сути, файлы .sln и .csproj, с которыми работает Visual Studio - это конфиги MsBuild. Среди прочего, MsBuild может запускать консольные приложения. Таким образом, мы можем добавить руками в файл проекта вызов enb make и он будет запускаться при сборке. Достоинства: это просто сделать + сборка на enb хорошо работает. Недостатки: нет интеграции с UI => неудобно настраивать и использовать.

Продвинутый вариант: сборка на gulp.

На последнем хакатоне по БЭМ (2-3 апреля 2016) уже был более-менее рабочий пример сборки БЭМ проектов на gulp, а в Visual Studio как раз есть интеграция с gulp с помощью плагина Task Runner Explorer (в последних двух версиях VS этот плагин устанавливается по умолчанию). Достоинства: интеграция с UI + gulp настраивается гибче и удобнее, чем enb + идея с потоками значительно меньше взрывает мозг, чем идея декларативного описания сборки. Ну и gulp намного более распространен. Недостатки: плагины для БЭМ-сборки на gulp еще довольно сырые (об этом ниже).

Что получилось

Файловая структура проекта:

<Project>
├─ Bem
    ├─ desktop.blocks
        ├─ block1
        └─ block2
    └─ desktop.bundles
        ├─ bundle1
            └─ bundle.bemjson.js
        └─ bundle2
├─ Controllers
  ...
└─ gulpfile.js

Сборка шаблонов

var gulp = require('gulp'),
    ...
    bemhtml = require('bem-xjst').bemhtml,
    bem = require('@bem/gulp');

// Создаём хелпер для сборки проекта
var project = bem({
    bemconfig: { ... } // уровни переопределения
});    

// Создаём хелпер для сборки бандла
var bundle = project.bundle({
    path: 'Bem/desktop.bundles/index',
    decl: 'index.bemjson.js'
});

gulp.task('bemhtml', function() {
    return bundle.src({ tech: 'bemhtml', extensions: ['.bemhtml', '.bemhtml.js'] })
        .pipe(concat(bundle.name() + '.bemhtml.js'))
        .pipe(through2(function(file, encoding, callback) {
            var src = file.contents.toString(encoding),
                bundle = bemhtml.generate(src);
            file.contents = new Buffer(bundle, encoding);
            callback(null, file);
        }))
        .pipe(gulp.dest('Bem/desktop.bundles/index'));
});

Вся магия в bundle.src - это как gulp.src, только возвращает файлы из всех уровней переопределения в правильном порядке.

Обнаруженные особенности:

  • если нет файлов deps, то bundle.src падает c неинформативной ошибкой;
  • мне так и не удалось запустить nodejs с поддержкой ES6 из Visual Studio, поэтому не смог воспользоваться нормальным плагином для сборки bemhtml (и написал руками).

Спасибо @tadatuta и @zxqfox за помощь и ответы на глупые вопросы.

Сборка js и стилей - аналогично. Весь код можно посмотреть здесь.

Таким образом, получился проект ASP.NET MVC, с которым мы работаем через Visual Studio. В проекте есть папка с БЭМ блоками и при сборке проекта через VS вместе с компиляцией кода на C# происходит сборка БЭМ-бандлов: серверные шаблоны, клиентский js и стили.

Серверная шаблонизация

Чего хотелось иметь в проекте на .NET:

  • возможность переопределять любые куски шаблонов;
  • возможность использования одних и тех же шаблонов на клиенте и на сервере;
  • возможность реиспользовать инфраструктуру без танцев с бубном в каждом проекте.

Хочется иметь библиотеку, которую можно подключить к проекту из NuGet и чтобы внутри у нее был полноценный шаблонизатор BEMHTML.

Решено было попробовать запустить bemhtml внутри .NET приложения с помощью какой-нибудь .NET обертки над node. Выбрал EDGE.js. Он умеет запускать ноду внутри .NET процесса (на текущий момент, только на .NET 4.5 под Windows, но написано, что будет и поддержка .NET Core). Созданный экземпляр можно кэшировать, чтобы не создавать несколько раз контекст v8.

Сначала провел небольшой эксперимент - отрендерил простой шаблон в консольном приложении.

Output

Основа всего - вот такой класс:

public class Bemhtml
{
    private readonly Func<object, Task<object>> func;

    public Bemhtml(string template)
    {
        this.func = Edge.Func(template + "; return function (data, cb) { cb(null, exports.apply(data));}");
    }

    public async Task<string> Apply(object data) 
    {
        return await func(data) as string;
    }
}

Ему в конструктор нужно передать текст bemhtml-бандла, сгенерированного с помощью bemhtml.generate(...) (это ровно то, что мы делали во время сборки шаблонов). Содержимое бандла примерно такое:

// код шаблонизатора BEMHTML
...

// шаблоны, обернутые в вызов функции compile
var api = new BEMHTML({});
api.compile(function(...) {
    // тут шаблоны, например:
    block('my-block').content()('123');
});

// помещаем скомпилированные шаблоны в переменную exports
api.exportApply(exports);

Далее мы доклеиваем к этому коду возврат функции, которая будет выполняться при вызове шаблонизации из .NET. Она получает данные и callback, шаблонизирует данные и результат передает входным параметром в callback:

return function (data, callback) { callback(null, exports.apply(data));}

Сгенерированный код мы передаем в Edge.Func("сгенерированный код") и получаем экземпляр .NET функции Func<object, Task<object>>, которую можно использовать из программы на C#. Функция - асинхронная (она возвращает Task<object>) и мы вызываем ее через await, либо используем примерно таким образом:

var obj = func(data);     // вызываем функцию
obj.Wait();               // ждем окончания выполнения
Console.Write(obj.Result) // в obj.Result результат шаблонизации

После того, как в консольном приложении шаблонизация отработала успешно, я написал небольшую библиотеку для использования в веб-приложениях ASP.NET MVC. Там есть:

  • класс-обертка над js шаблоном (описанный чуть выше),
  • менеджер шаблонов, отвечающий за создание экземпляров шаблонов и управляющий их кэшированием
  • ActionResult с помощью которго можно передать данные из серверного контроллера в BEMHTML.

    Что получилось

Сейчас есть небольшое ASP.NET MVC приложение, которое состоит из одной странички, отрендеренной с помощью BEMHTML. В приложение подключены bem-core и bem-components. Страничка открывается и, кажется, всё работает.

Посмотреть код можно здесь, а потыкать мышкой (и посмотреть в инспекторе) можно здесь.

Дальнейшие планы

  • параметризация и вынос параметров в конфиг .NET проекта;
  • отладка шаблонов на сервере (вроде EDGE.js это позволяет; идеальный вариант - если в качестве отладчика получится подключить Visual Studio, в которой открыт код проекта);
  • выложить в NuGet библиотеку с классами для ASP.NET MVC;
  • написать более сложный пример (с более сложными серверными данными и шаблонами);
  • потестировать производительность.

Также в планах - адаптировать этот текст для людей, ничего не знающих про БЭМ и запостить на хабр.

Спасибо за внимание!