bem-site / bem-forum-content-ru

Content BEM forum for Russian speak users
MIT License
56 stars 6 forks source link

Одна платформа → несколько бандлов в одном уровне #1438

Closed Realetive closed 6 years ago

Realetive commented 6 years ago

Бандлы для сборки имеют одинаковое содержание независимо от платформы:

project/
├── …
├── bundles/
│   ├── desktop.bundles/
│   │   ├── about/
│   │   │   └── about.bemdecl.js
│   │   └── …
│   └── touch.bundles/
│       ├── about/
│       │   └── about.bemdecl.js
│       └── …
└── …

Т. е. /bundles/desktop.bundles/about/about.bemdecl.js === /bundles/touch.bundles/about/about.bemdecl.js. Каждый раз при добавлении нового бандла его приходится дублировать во все уровни сборки, что кажется избыточным. Хотелось бы сократить структуру до:

project/
├── …
├── bundles/
│   ├── about/
│   │   └── about.bemdecl.js
│   ├── contacts/
│   │   └── contacts.bemdecl.js
│   └── …
└── …

И в процессе сборки уже получать:

project/
├── …
├── bundles/
│   ├── about
│   │   ├── about.bemdecl.js
│   │   ├── about.desktop.bemhtml.js
│   │   ├── about.phone.bemhtml.js
│   │   ├── about.touch.bemhtml.js
│   │   ├── about.desktop.bemtree.js
│   │   ├── about.phone.bemtree.js
│   │   └── about.touch.bemtree.js
│   └── …
└── …

Думал в сторону enb/techs/symlink, но он тоже возвращает Error: Concurrent techs for target: about.desktop.bemdecl.js, techs: "symlink" vs "symlink"; А нужно, видимо, что-то похожее на механизм {lang}, только {platform}.

ilyar commented 6 years ago

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

tadatuta commented 6 years ago

@Realetive простого способа, похоже, нет :( я бы предложил использовать bem-tools-create для создания бандлов сразу на всех уровнях — это самое быстрое и гибкое решение (если в какой-то момент состав деклов все-таки поменяется в зависимости от уровня).

ilyar commented 6 years ago

@Realetive вопрос интересен, но не понятна проблема, какую задачу надо решить? Что меняет раскладывание файлов с одинаковым содержанием рядом?

ilyar commented 6 years ago

Кажется понял, надо по одной декларации собирать цели (enb:target) для разных платформ в одном узле (enb:node), так? Есть ощущение, что это уже где-то обсуждалось...

ilyar commented 6 years ago

@tadatuta А если так https://github.com/ilyar/sandbox/blob/master/bem/build/src/project-build-by-platform/.bem/enb-make.js? Демо https://github.com/ilyar/sandbox/tree/master/bem/build#build-by-platform

Realetive commented 6 years ago

@ilyar Что-то похожее и пытался делать, но у меня ругалось на Concurrent techs for target. Возможно, что-то не так делал. Спасибо, сейчас попробую по аналогии…

ilyar commented 6 years ago

Concurrent techs for target

Возникает, если добавлять в ноду уже добавленную технологию такими же параметрами , поэтому provide ?.bemjson.js (https://github.com/ilyar/sandbox/blob/master/bem/build/src/project-build-by-platform/.bem/enb-make.js#L26-L28) добавлено раньше остальных, если пометить ее в цикл работать не будет.

ilyar commented 6 years ago

Вернее получится, что мы задекларируем одну и туже цель (enb:target) разными технологиями.

tadatuta commented 6 years ago

@ilyar годно!

Realetive commented 6 years ago

@ilyar, спасибо! Наконец-то вернулся к вопросу. Итоговый конфиг (для истории):

const fs        = require('fs'),
      path      = require('path'),
      mkdirp    = require('mkdirp'),
      build     = require('./config/build'),
      techs     = require('./config/techs'),
      getLevels = require('./config/levels').getLevels,
      SETS      = require('./config/levels').SETS,
      platforms = Object.keys( SETS ),
      langs     = JSON.parse( process.env.LANGS ),
      __DEV__   = process.env.NODE_ENV !== 'production';

module.exports = ( config ) => {
  config.setLanguages( langs );
  let levels;
  platforms.forEach( ( platform ) => {
    levels = getLevels( platform );
    !__DEV__ || levels.push( { path: path.join( 'components', 'development.blocks' ), check: true } );

    // Сборка общих CSS/JS-бандлов из всех блоков
    [ 'css', 'js' ].forEach( catalog => {
      pathToStatic = path.resolve( 'static', 'assets', catalog, platform );
      fs.existsSync( pathToStatic ) || mkdirp( pathToStatic );
    } );
    config.node( 'dist/' + platform, ( nodeConfig ) => {
      nodeConfig.addTechs([
        [techs.bem.levels, { levels: levels }],
        [techs.bem.levelsToBemdecl ]
      ]);

      build( nodeConfig, platform );

      nodeConfig.addTechs([
        [techs.fileCopy, { source: '?.{lang}.min.js', target: '../../static/assets/js/' + platform + '/?.{lang}.min.js' }],
        [techs.fileCopy, { source: '?.min.css', target: '../../static/assets/css/' + platform + '/?.min.css' }]
      ]);

      nodeConfig.addTargets([
        '../../static/assets/css/' + platform + '/?.min.css',
        '../../static/assets/js/' + platform + '/?.{lang}.min.js'
      ]);
    } );
  });

  // Сборка бандлов по декларациям
  config.nodes( 'bundles/*', ( nodeConfig ) => {
    nodeConfig.addTechs([
      [techs.bem.levels, { levels: levels }],
      [techs.fileProvider, { target: '?.bemdecl.js' }]
    ]);

    build( nodeConfig );

    nodeConfig.addTargets([
      '?.{lang}.bemtree.js',
      '?.bemhtml.js'
    ]);
  });
};
Realetive commented 6 years ago

Извините, прошлый пример не учитывал несколько платформ. Исправил:

const fs        = require('fs'),
      path      = require('path'),
      mkdirp    = require('mkdirp'),
      build     = require('./config/build'),
      techs     = require('./config/techs'),
      getLevels = require('./config/levels').getLevels,
      SETS      = require('./config/levels').SETS,
      langs     = JSON.parse( process.env.LANGS ),
      __DEV__   = process.env.NODE_ENV !== 'production';

module.exports = ( config ) => {
  config.setLanguages( langs );

  let levels;
  const platforms = Object.keys( SETS );
  platforms.forEach( function( platform ) {
    [ 'css', 'js' ].forEach( catalog => {
      pathToStatic = path.resolve( 'static', 'assets', catalog, platform );
      fs.existsSync( pathToStatic ) || mkdirp( pathToStatic );
    } );

    levels = getLevels( platform );
    !__DEV__ || levels.push( { path: path.join( 'components', 'development.blocks' ), check: true } );
  } );

  // Сборка бандлов по декларациям
  config.nodes( 'bundles/*', ( nodeConfig ) => {
    nodeConfig.addTechs([
      [techs.bem.levels, { levels: levels }],
      [techs.fileProvider, { target: '?.bemdecl.js' }],
      [techs.bem.deps, { target: '.?.deps.js' } ],
      [techs.bem.files, { depsFile: '.?.deps.js' } ],
      [techs.keysets, { lang: '{lang}' }],
      [techs.i18n, {
        exports: { ym: true, commonJS: true },
        lang: '{lang}'
      }],
    ]);
  });

  platforms.forEach( function( platform ) {
    config.nodes( 'bundles/*', ( nodeConfig ) => {
      nodeConfig.addTechs([
        // bemtree
        [techs.bemtreeI18N, {
          sourceSuffixes: ['bemtree', 'bemtree.js'],
          target: '?.' + platform + '.{lang}.bemtree.js',
          lang: '{lang}'
        }],

        // templates
        [techs.bemhtml, {
          sourceSuffixes: ['bemhtml', 'bemhtml.js'],
          target: '?.' + platform + '.{lang}.bemhtml.js',
          forceBaseTemplates: true,
          engineOptions: {
            elemJsInstances: true,
            runtimeLint: true
          },
          requires: {
            moment: {
              globals: 'moment',
              commonJS: 'moment'
            },
            moment_ru: {
              globals: 'moment/locale/ru',
              commonJS: 'moment/locale/ru'
            }
          }
        }]
      ]);

      nodeConfig.addTargets([
        '?.' + platform + '.{lang}.bemtree.js',
        '?.' + platform + '.{lang}.bemhtml.js'
      ]);
    } );

    config.node( 'dist/' + platform, function( nodeConfig ) {
      nodeConfig.addTechs([
        [techs.bem.levels, { levels: levels }],
        [techs.bem.levelsToBemdecl ]
      ]);

      build( nodeConfig, platform );

      nodeConfig.addTechs([
        [techs.fileCopy, { source: '?.{lang}.min.js', target: '../../static/assets/js/' + platform + '/?.{lang}.min.js' }],
        [techs.fileCopy, { source: '?.min.css', target: '../../static/assets/css/' + platform + '/?.min.css' }]
      ]);

      nodeConfig.addTargets([
        '../../static/assets/css/' + platform + '/?.min.css',
        '../../static/assets/js/' + platform + '/?.{lang}.min.js'
      ]);
    } );
  } );
};
ilyar commented 6 years ago

@Realetive Если не сложно, сделай ПР https://github.com/ilyar/sandbox/tree/master/bem/build будет полезно иметь щупательные примеры в одном месте.

Realetive commented 6 years ago

@ilyar делаю в https://github.com/Nevatrip/frontend. Там модифицированный bem-express, i18n, universal-router — кра-со-та! 😃

ilyar commented 6 years ago

@Realetive расскажи про красоту, если я правильно понял сайт на php, а фронтенд bem-express, расскажи подробнее тут https://github.com/bem-site/bem-forum-content-ru/issues/1427