Open belozer opened 8 years ago
Идея ясна. Может быть так:
const transforms = require('gulp-bem-transforms-classic');
const builder = Builder();
// ...
.pipe(builder({js: transforms.js, css: tranforms.css})) // так?
Потому что, строго говоря, js, css, html это просто текстовые метки.
@zxqfox у меня возникает желание лезть в transforms.js
и теряется абстракция над сложностью процесса сборки.
Но мне в целом нравится реализация такого подхода как в enb
[techs.browserJs, {
includeYM: true,
sourceSuffixes: ['source.browser.js', 'vanilla.js', 'js', 'browser.js'],
iife : true
}],
но только не тогда, когда он превращается в длиннющий конвеер. Ты смотришь на него и офигиваешь от того куда и от куда всё берётся.
(https://github.com/bem/bem-components/blob/v3/.enb/make.js)
Я бы предложил так
const techs = require('gulp-bem-transforms-classic');
const contibTech = require('gulp-bem-super-contrib-tech');
const builder = Builder([techs, contibTech]);
// ...
.pipe(builder({js: true, css: true, 'super-tech': true}))
.pipe(builder({
'js': true,
'css': { autoprefixer: true },
'super-tech': true,
'my-tech': {
handler: function () {...}
}
}))
ну или как вначале предложил, только более раскрыто
.pipe(builder({
'js': true,
'css': { autoprefixer: true },
'super-tech': true,
'my-tech': function (bundle) {...}
}))
Когда мы объявляем const builder = Builder([techs, contibTech]);
Происходит маппинг технологий по своим (определённым в технологии) ключам. Если в builder({})
ключ технологии переназначается функцией, то происходит переназначения обработчика на пользовательский.
Если по ключу передаётся объект - то это входящие параметры для технологии, которую мы замапили.
Или маппинг можно проводить непосредственно перед самой сборкой
.pipe(builder([baseTechs, contibTech], {
'js': true,
'css': { autoprefixer: true },
'super-tech': true,
'my-tech': function (bundle) {...}
}))
upd. данный подход позволяет для разных бандлов собирать одни и теже технологии, но с разным подходом (используя разные библиотеки)
Сами пакеты шаблонов могу включать в себя как 1 шаблон, так и целую библиотеку шаблонов (baseTechs).
const contibTech = require('gulp-bem-super-contrib-tech')({'contrib-js': 'js'})
переопределение пользовательских ключей для технологий
В конечном итоге gulpfile из вебинара мог бы выглядеть так
const Builder = require('gulp-bem-bundle-builder');
const gulp = require('gulp');
const baseTechs = require('gulp-bem-basetechs')
const builder = Builder({
// default (common) levels
levels: ['l1'],
techMap: {
bemhtml: ['bemhtml.js']
}
});
gulp.task('bemdecl', function(){
return gulp.src('bundles/bemdecl/bemdecl.bemdecl.js')
.pipe(builder([baseTechs], { js: true }))
.pipe(gulp.dest('bundles/bemdecl'));
});
gulp.task('bemjson', function(){
return gulp.src('bundles/bemjson/bemjson.bemjson.js')
.pipe(builder([baseTechs], { js: true }))
.pipe(gulp.dest('bundles/bemjson'));
});
gulp.task('common', function() {
return gulp.src('bundles/common/common.bemdecl.js')
.pipe(builder([baseTechs], { 'js': true, 'css': true, 'bemhtml.js': true, 'html': true }))
.pipe(gulp.dest('./bundles/common'))
});
Крутой же? ) Лаконичный и понятный ;)
🙀
@zxqfox я не пойму настроение смайла ) Это плохо, хорошо или невозможно?
@belozyorcev Просто ты так сильно усложняешь ;-)
Ведь такое API можно сделать в отдельном модуле:
const myBuilder = (techsPackages, needed) =>
builder(Object.keys(needed).reduce((res, key) => {
res[key] = needed[key] === true ?
techsPackages.map(pkg => pkg[key])[0]
: needed[key];
return res;
}, {}));
// ...
.pipe(myBuilder([baseTechs], { 'js': true, 'css': true, 'bemhtml.js': true, 'html': true }))
С другой стороны, builder сам по себе может это содержать, но разве не проще это делать в отдельных пакетах?
Кстати, я скорее это вижу примерно таким API:
const builder = Builder({
predefines: { js: transformStream, css: ..., etc... },
predefines: require('gulp-bem-basetechs') // или так
predefines: Object.assign({},
require('gulp-bem-basetechs1'),
require('gulp-bem-basetechs2'),
require('gulp-bem-basetechs3')) // или так
});
builder({ js: true, css: true, ololo: true })
Но в таком случае не очень понятно почему не:
const js = require('gulp-bem-typical-js');
builder({ js });
И на left-pad смахивает)
Проблема predefined-base-tech в том
что они потребуют постоянных расширений опциями тому включи автопрефиксер тому postCSS итп
и вот ты уже пишешь не понятные флоу сборки в виде пайпов а конфигурируешь опциями эти технологии не верю что будет {js: true}
, который всех устроит
@Yeti-or часто приходится менять project-stub конфиг?
У меня нет. Но когда нужно, я копирую кусок кода и вставляю в свой конфиг при этом ищу куда бы его вставить правильно, чтобы всё заработало. Происходит дублирование и сложность переноса. А так можно было бы выносить конфиги в отдельные технологии.
И всё что нам нужно - подключить модуль / библиотеку и сказать "собери мне js и css". Нужного модуля нет? Не проблема, пишем свой обработчик. Обработчик нужен другим? Ок. Публикуем модуль в npmjs.com
plugins: [
require('postcss-nested'),
require('pobem'),
require('postcss-media-minmax')(),
require('postcss-custom-media'),
require('postcss-css-variables')(),
require('lost')(),
require('sharps').postcss({
columns: 12, // default
maxWidth: '1100px',
gutter: '10px',
flex: 'flex'
}),
require('postcss-svg')({
paths: ['bem/icons'],
defaults: "[fill]: #000000; [fill-opacity]: 1",
svgo: true,
debug: true
})
]
У postcss сделано плагинами и воркфлоу понятен.
Меня не волнует какие манипуляции происходят в require('postcss-media-minmax')()
.
Я знаю что он даёт на выходе и всё, остальное мне не нужно знать.
upd
ну или require('postcss-css-variables')()
или require('postcss-nested')
и т.д.
По поводу опций - можно наблюдать какие из них сообщество чаще всего использует и выпускать новую версию конфига с дефолтными (часто используемыми) параметрами.
Да и данный gulpfile врятли будет использоваться самостоятельно. Скорее всего будет внедрятся в уже существующий проект / воркфлоу и чем меньше манипуляций нужно проводить с ним, тем больше скажут "спасибо" люди, которые будут подключать gulp-bem в свой проект.
Я бандлы сейчас собираю так:
var gulp = require('gulp'),
enb = require('enb');
gulp.task('makeBundles', function make() {
return enb.make([], { dir: './bem' });
});
@zxqfox может лучше вместо predefines
использовать techs
или plugins
?
@belozyorcev Да, что угодно. Вопросы наименования ;-) В программировании две проблемы: сесть and сделать.
А можно библиотеку стандартных бэм технологий назвать xxx
;) Всё по-взрослому
const builder = Builder({
plugins: require('gulp-bem-xxx'),
use: { 'js': true, 'bemhtml': true }
});
Наконец-то я нашел простой инструмент для сборки блоков, спасибо вам за него) Разобравшись, мне теперь тоже кажется, что Сергей усложняет. Стандартные конфиги для технологий, это как стандартные галп-плагины для технологий, состоящие из наборов других галп-плагинов для обработки стилей, скриптов и картинок. Ведь нет ничего проще, чем взять нужные галп-плагины и вставить в сборку. Пока пробовал gulp-bem-bundle-builder
получил такое:
const builder = bundleBuilder({
levels: [
'blocks',
'design/blocks'
],
techMap: {
js: ['js'],
css: ['scss', 'css'],
image: ['jpg', 'png']
}
});
gulp.task('build', function() {
return bundlerFs('pages/*')
.pipe(builder({
css: bundle => bundle.src('css')
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(concat(bundle.name + '.css'))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('./dist/' + bundle.name)),
js: bundle => bundle.src('js')
.pipe(concat(bundle.name + '.js'))
.pipe(gulp.dest('./dist/' + bundle.name)),
image: bundle => bundle.src('image')
.pipe(flatten())
.pipe(gulp.dest('./dist/' + bundle.name + '/images'))
}))
.pipe(debug());
});
и всё работает, и всё понятно. Каждый может настроить под себя.
В продолжение этой темы. Соглашусь :) Был не прав. Придумал другую штуку, которая, как мне кажется, решает все проблемы озвученные в данном issue.
Делать наборы пресетов (просто чтобы не раздувать gulpfile и package.json).
вот пример (работающий на моём проекте) https://gist.github.com/belozer/7c2f3448f67caca79f76845a77d4e0e1
а вот это было до использования пресетов https://gist.github.com/belozer/06b1183bf2a68665375527452ebf5397
css: bundle =>
require('gulp-bem-preset-css')(bundle, {svgPaths: ['bem/icons']}),
если нам ничего с потоком делать не нужно можно просто сделать так
css: require('gulp-bem-preset-css'),
всё полотно плагинов для CSS обрабатывается по шаблону в модуле require('gulp-bem-preset-css')
.
на вход модуля у нас подаётся текущий bandle и насройки пресета
Вот пример как можно собирать клиенсткий JS (BEMTREE, BEMHTML, browser.js (+ browserify))
js: bundle => merge(
require('gulp-bem-preset-browserjs')(bundle, {includePaths: ['bem']}),
require('gulp-bem-preset-bemtree')(bundle, {ym: true}),
require('gulp-bem-preset-bemhtml')(bundle, {ym: true, engine: {elemJsInstances: true}})
)
.pipe(gulpif(env === 'production', babel({ presets: ['es2015'], compact: false })))
.pipe(gulpif(env === 'production', uglify()))
.pipe(concat(bundle.name + '.min.js')),
Вот примеры моих пресетов (ещё полностью не обкатаны, но использовать можно) https://github.com/belozer/gulp-bem-preset-css https://github.com/belozer/gulp-bem-preset-browserjs https://github.com/belozer/gulp-bem-preset-bemhtml https://github.com/belozer/gulp-bem-preset-bemtree
Таким образом каждый может выпускать свои пресеты в открытый доступ, а другие могут их с лёгкостью подключаться в своих проектах.
При этом если человек не хочет использовать пресеты - он может делать сборку без них
А данная конструкция будет собирать картинки из блоков и класть в соответствующую дирректорию ?
techMap: {
image: ['jpg', 'png']
}
image: bundle => bundle.src('image')
.pipe(gulp.dest('./dist/images'))
У меня почему-то не работает...
re: Оказалось забыл назвать файл картинки так же как блок...
@Maksimenka тут есть нюанс — таким образом можно будет перекинуть 1 файл, а если нужно несколько, то увы. Но можно их сложить в папку, а папку уже переносить.
С техмэп так и не понял для чего он нужен.
А как пробросить сразу несколько форматов файлов в один bundle.src(''), пока делаю так:
images: bundle => merge(
bundle.src('png'),
bundle.src('jpg'),
bundle.src('svg')
)
.pipe(flatten())
.pipe(gulp.dest("img"))
И предназначение данной конструкции тоже не ясно:
config: {
levels: {
'libs/super-library/blocks': {scheme: 'nested'},
'blocks': {scheme: 'nested'}
}
},
В данный момент у меня указаны только level и вроде все верно собирается.
@Maksimenka techMap нужен для соответствия технологий из зависимостей с технологиями на диске. Например, у вас в зависимостях css, а на диске оно реализовано в css и styl (в разных блоках по-разному). Или в зависимостях у вас bemhtml, а на диске bh.js или bemhtml.js, а может и вовсе bh.php или еще какой-то свой шаблонизатор (что-то одно на выбор, такое есть в bem-components).
А как пробросить сразу несколько форматов файлов в один bundle.src(''), пока делаю так:
Как вариант. Но лучше, всё-же, в папку: например, block.assets или block.images, и там всякие ресурсы для блока хранить.
@zxqfox Как ни крути, тяжело их текста воспринимается techMap.
Я сразу думал, что туда можно вкинуть форматы файлов которые надо собрать как приведено на примере выше:
const builder = bundleBuilder({
levels: [
'blocks',
'design/blocks'
],
techMap: {
image: ['jpg', 'png', 'svg']
}
});
gulp.task('build', function() {
return bundlerFs('pages/*')
.pipe(builder({
/*
По моей логике он должен был искать все форматы файлов указанные в
techMap: {
image: ['jpg', 'png', 'svg']
}
но в итоге поток пуст...
*/
image: bundle => bundle.src('image')
.pipe(gulp.dest('./dist/' + bundle.name + '/images'))
}))
});
Но оказалось не так, опять же если я не ошибаюсь. Но было бы удобно указывать в этом объекте технологии и фильтровать их по формату и производить дальнейшие манипуляции с ними.
К примеру собираешь файлы в разных технологиях css / sass / styl и с помощью guli-if или другим плагином фильтруешь по формату и приводишь все к единому формату, объединяешь и тд. Картинки допустим также фильтруя можно сжимать различными плагинами, для каждого формата свой.
Вообще, странно, что так не работает. Действительно должно привозить все файлы с указанным расширением.
@zxqfox
Как вариант. Но лучше, всё-же, в папку: например, block.assets или block.images, и там всякие ресурсы для блока хранить.
Если для блока то не понял, или ты имел ввиду просто папку для статики для различных блоков и просто брать из нее картинки?
Для каждого блока свои картинки:
blocks/
football-team-selector/
football-team-selector.assets/
real-madrid.svg
spartak.svg
dinamo-kyiv.svg
flags-sprite.png
Вообще, странно, что так не работает. Действительно должно привозить все файлы с указанным расширением.
Завел задачу про тесты: https://github.com/gulp-bem/gulp-bem-bundle-builder/issues/16
@zxqfox Вытекающий вопрос из этого, и как их собрать если gulp-bem-bundle-builder дергает только файлы по имени блока? Дай хотя бы направление как это сделать?
accets : bundle => bundle.src('assets')
.pipe(debug()),
// Error: EISDIR: illegal operation on a directory, read
@zxqfox ?
@Maksimenka файлы элементов и модификаторов нужно указывать в зависимостях. Сборщик изначально настроен собирать по минимуму. То есть, собирает по технологиям только с именами блоков. Если надо, чтобы сборщик подтянул, например, header__logo.svg
, то надо в папке с блоком создать файл с зависимостями header.deps.js
:
({
shouldDeps: [
{ elems: 'logo' }
]
})
@palegrow Это ясно, но интересует реализация предложенная @zxqfox :
blocks/
football-team-selector/
football-team-selector.assets/
real-madrid.svg
spartak.svg
dinamo-kyiv.svg
flags-sprite.png
@Maksimenka https://github.com/gulp-bem/gulp-bem-src/issues/14 Не знаю точно как это сейчас работает, но по логике должна возвращаться директория в виде винилового файла, которую в последствии можно будет переложить как есть, например, в другую папку. Каким именно образом — вероятно, надо будет найти (или написать) галповый плагин, который из виниловой папки сделает несколько виниловых файлов, которые в ней лежат, или симлинк (кому-то может быть так удобнее) ;-)
@zxqfox Ок, буду пробовать что-нибудь соорудить.
Круто было бы так описывать сборку
Если технология имеет значение
true
, тогда использовать шаблонную функцию для сборки.Если же это функция, тогда использовать написанный человеком алгоритм для решения нестандартной задачи, которую он хочет решить.