gulp-bem / gulp-bem-bundle-builder

DEPRECATED repository, moved to https://github.com/bem/gulp-bem/tree/master/packages/gulp-bem-bundle-builder
Other
3 stars 1 forks source link

Стандартные конфиги для технологий #3

Open belozer opened 8 years ago

belozer commented 8 years ago

Круто было бы так описывать сборку

gulp.task('common', function() {
    return gulp.src('bundles/common/common.bemdecl.js')
        .pipe(builder({'js': true, 'css': true, 'bemhtml.js': true, 'html': true}))
        .pipe(gulp.dest('./bundles/common'))
});

Если технология имеет значение true, тогда использовать шаблонную функцию для сборки.

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

qfox commented 8 years ago

Идея ясна. Может быть так:

const transforms = require('gulp-bem-transforms-classic');
const builder = Builder();
  // ...
  .pipe(builder({js: transforms.js, css: tranforms.css})) // так?

Потому что, строго говоря, js, css, html это просто текстовые метки.

belozer commented 8 years ago

@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}))
belozer commented 8 years ago
.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) {...} 
}))
belozer commented 8 years ago

Когда мы объявляем const builder = Builder([techs, contibTech]);

Происходит маппинг технологий по своим (определённым в технологии) ключам. Если в builder({}) ключ технологии переназначается функцией, то происходит переназначения обработчика на пользовательский.

Если по ключу передаётся объект - то это входящие параметры для технологии, которую мы замапили.

belozer commented 8 years ago

Или маппинг можно проводить непосредственно перед самой сборкой

.pipe(builder([baseTechs, contibTech], { 
  'js': true, 
  'css': { autoprefixer: true }, 
  'super-tech': true, 
  'my-tech': function (bundle) {...} 
}))

upd. данный подход позволяет для разных бандлов собирать одни и теже технологии, но с разным подходом (используя разные библиотеки)

belozer commented 8 years ago

Сами пакеты шаблонов могу включать в себя как 1 шаблон, так и целую библиотеку шаблонов (baseTechs).

belozer commented 8 years ago
const contibTech = require('gulp-bem-super-contrib-tech')({'contrib-js': 'js'})

переопределение пользовательских ключей для технологий

belozer commented 8 years ago

В конечном итоге 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'))
});

Крутой же? ) Лаконичный и понятный ;)

qfox commented 8 years ago

🙀

belozer commented 8 years ago

@zxqfox я не пойму настроение смайла ) Это плохо, хорошо или невозможно?

qfox commented 8 years ago

@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 сам по себе может это содержать, но разве не проще это делать в отдельных пакетах?

qfox commented 8 years ago

Кстати, я скорее это вижу примерно таким 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 смахивает)

Yeti-or commented 8 years ago

Проблема predefined-base-tech в том что они потребуют постоянных расширений опциями тому включи автопрефиксер тому postCSS итп и вот ты уже пишешь не понятные флоу сборки в виде пайпов а конфигурируешь опциями эти технологии не верю что будет {js: true}, который всех устроит

belozer commented 8 years ago

@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 сделано плагинами и воркфлоу понятен.

belozer commented 8 years ago

Меня не волнует какие манипуляции происходят в require('postcss-media-minmax')(). Я знаю что он даёт на выходе и всё, остальное мне не нужно знать.

upd ну или require('postcss-css-variables')() или require('postcss-nested') и т.д.

belozer commented 8 years ago

По поводу опций - можно наблюдать какие из них сообщество чаще всего использует и выпускать новую версию конфига с дефолтными (часто используемыми) параметрами.

belozer commented 8 years ago

Да и данный gulpfile врятли будет использоваться самостоятельно. Скорее всего будет внедрятся в уже существующий проект / воркфлоу и чем меньше манипуляций нужно проводить с ним, тем больше скажут "спасибо" люди, которые будут подключать gulp-bem в свой проект.

Я бандлы сейчас собираю так:

var gulp = require('gulp'),
    enb = require('enb');

gulp.task('makeBundles', function make() {
  return enb.make([], { dir: './bem' });
});
belozer commented 8 years ago

@zxqfox может лучше вместо predefines использовать techs или plugins?

qfox commented 8 years ago

@belozyorcev Да, что угодно. Вопросы наименования ;-) В программировании две проблемы: сесть and сделать.

belozer commented 8 years ago

А можно библиотеку стандартных бэм технологий назвать xxx ;) Всё по-взрослому

const builder = Builder({
    plugins: require('gulp-bem-xxx'),
    use: { 'js': true, 'bemhtml': true }
});
hvab commented 8 years ago

Наконец-то я нашел простой инструмент для сборки блоков, спасибо вам за него) Разобравшись, мне теперь тоже кажется, что Сергей усложняет. Стандартные конфиги для технологий, это как стандартные галп-плагины для технологий, состоящие из наборов других галп-плагинов для обработки стилей, скриптов и картинок. Ведь нет ничего проще, чем взять нужные галп-плагины и вставить в сборку. Пока пробовал 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());
});

и всё работает, и всё понятно. Каждый может настроить под себя.

belozer commented 8 years ago

В продолжение этой темы. Соглашусь :) Был не прав. Придумал другую штуку, которая, как мне кажется, решает все проблемы озвученные в данном 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

Таким образом каждый может выпускать свои пресеты в открытый доступ, а другие могут их с лёгкостью подключаться в своих проектах.

При этом если человек не хочет использовать пресеты - он может делать сборку без них

maksimenka commented 7 years ago

А данная конструкция будет собирать картинки из блоков и класть в соответствующую дирректорию ?

  techMap: {
    image: ['jpg', 'png']
  }
  image: bundle => bundle.src('image')
        .pipe(gulp.dest('./dist/images'))

У меня почему-то не работает...

re: Оказалось забыл назвать файл картинки так же как блок...

qfox commented 7 years ago

@Maksimenka тут есть нюанс — таким образом можно будет перекинуть 1 файл, а если нужно несколько, то увы. Но можно их сложить в папку, а папку уже переносить.

maksimenka commented 7 years ago

С техмэп так и не понял для чего он нужен.

А как пробросить сразу несколько форматов файлов в один 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 и вроде все верно собирается.

qfox commented 7 years ago

@Maksimenka techMap нужен для соответствия технологий из зависимостей с технологиями на диске. Например, у вас в зависимостях css, а на диске оно реализовано в css и styl (в разных блоках по-разному). Или в зависимостях у вас bemhtml, а на диске bh.js или bemhtml.js, а может и вовсе bh.php или еще какой-то свой шаблонизатор (что-то одно на выбор, такое есть в bem-components).

qfox commented 7 years ago

А как пробросить сразу несколько форматов файлов в один bundle.src(''), пока делаю так:

Как вариант. Но лучше, всё-же, в папку: например, block.assets или block.images, и там всякие ресурсы для блока хранить.

maksimenka commented 7 years ago

@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 или другим плагином фильтруешь по формату и приводишь все к единому формату, объединяешь и тд. Картинки допустим также фильтруя можно сжимать различными плагинами, для каждого формата свой.

qfox commented 7 years ago

Вообще, странно, что так не работает. Действительно должно привозить все файлы с указанным расширением.

maksimenka commented 7 years ago

@zxqfox

Как вариант. Но лучше, всё-же, в папку: например, block.assets или block.images, и там всякие ресурсы для блока хранить.

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

qfox commented 7 years ago

Для каждого блока свои картинки:

blocks/
  football-team-selector/
    football-team-selector.assets/
      real-madrid.svg
      spartak.svg
      dinamo-kyiv.svg
      flags-sprite.png
qfox commented 7 years ago

Вообще, странно, что так не работает. Действительно должно привозить все файлы с указанным расширением.

Завел задачу про тесты: https://github.com/gulp-bem/gulp-bem-bundle-builder/issues/16

maksimenka commented 7 years ago

@zxqfox Вытекающий вопрос из этого, и как их собрать если gulp-bem-bundle-builder дергает только файлы по имени блока? Дай хотя бы направление как это сделать?

accets : bundle => bundle.src('assets')
    .pipe(debug()),
// Error: EISDIR: illegal operation on a directory, read
maksimenka commented 7 years ago

@zxqfox ?

hvab commented 7 years ago

@Maksimenka файлы элементов и модификаторов нужно указывать в зависимостях. Сборщик изначально настроен собирать по минимуму. То есть, собирает по технологиям только с именами блоков. Если надо, чтобы сборщик подтянул, например, header__logo.svg , то надо в папке с блоком создать файл с зависимостями header.deps.js:

({
  shouldDeps: [
    { elems: 'logo' }
  ]
})
maksimenka commented 7 years ago

@palegrow Это ясно, но интересует реализация предложенная @zxqfox :

blocks/
  football-team-selector/
    football-team-selector.assets/
      real-madrid.svg
      spartak.svg
      dinamo-kyiv.svg
      flags-sprite.png
qfox commented 7 years ago

@Maksimenka https://github.com/gulp-bem/gulp-bem-src/issues/14 Не знаю точно как это сейчас работает, но по логике должна возвращаться директория в виде винилового файла, которую в последствии можно будет переложить как есть, например, в другую папку. Каким именно образом — вероятно, надо будет найти (или написать) галповый плагин, который из виниловой папки сделает несколько виниловых файлов, которые в ней лежат, или симлинк (кому-то может быть так удобнее) ;-)

maksimenka commented 7 years ago

@zxqfox Ок, буду пробовать что-нибудь соорудить.