Здравствуйте. Я хотел бы рассказать как с помощью Brunch можно собрать MariontteJS+ES6 приложение.



Сегодня уже 2016-й год и способов собирать приложения очень много. Ниже я предлагаю рассмотреть Brunch

Ставим brunch глобально и генерируем пустой скелет
npm install -g brunch brunch new

Если не хотите его ставить глобально тогда просто
git clone https://github.com/brunch/dead-simple
Все равно будем запускать brunch с помощью npm.

Теперь нужно сконфигурировать package.json и bower.json.

package.json
{
  "name": "marionette-es6-brunch",
  "description": "Marionette brunch es6 simple app",
  "author": "denar90",
  "version": "0.0.1",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/denar90/marionette-es6-brunch"
  },
  "scripts": {
    "postinstall": "./node_modules/.bin/bower install",
    "start": "./node_modules/.bin/brunch w --server"
  },
  "dependencies": {
    "javascript-brunch": "~1.8.0",
    "css-brunch": "~1.7.0",
    "stylus-brunch": "~1.8.1",
    "handlebars-brunch": "~1.9.0",
    "uglify-js-brunch": "~1.7.8",
    "clean-css-brunch": "~1.8.0",
    "jshint-brunch": "~1.8.0",
    "babel-brunch": "~6.0.0",
    "babel-preset-es2015": "~6.3.13",
    "babel-plugin-transform-decorators-legacy": "~1.3.4",
    "bower": "~1.7.2",
    "brunch": "~2.1.0"
  }
}


bower.json
{
  "name": "marionette-es6-brunch",
  "version": "0.0.1",
  "dependencies": {
    "marionette": "~2.4.4",
    "bootstrap": "~3.3.2",
    "core.js": "~2.0.2"
  }
}


Нам особенно интересны эти библиотеки:
  • babel-brunch
  • babel-preset-es2015
  • babel-plugin-transform-decorators-legacy
  • core-js

Именно они делают всю магию с es6.

Перейдем к конфигу самого brunch.

brunch-config.js
exports.config = {
  paths: {
    watched: ['app']
  },
  files: {
    javascripts: {
      defaultExtension: "js",
      joinTo: {
        "javascripts/app.js": /^app/,
        "javascripts/vendor.js": /^bower_components/
      },
      order: {
        before: [
          'bower_components/jquery/dist/jquery.js',
          'bower_components/underscore/underscore.js',
          'bower_components/backbone/backbone.js',
          'bower_components/marionette/lib/backbone.marionette.js',
          'bower_components/bootstrap/dist/js/bootstrap.js'
        ]
      }
    },
    stylesheets: {
      defaultExtension: "styl",
      joinTo: "stylesheets/app.css"
    },
    templates: {
      defaultExtension: "hbs",
      joinTo: "javascripts/app.js"
    }
  },
  plugins: {
    babel: {
      presets: ['es2015'],
      ignore: [
        /^(bower_components|vendor|node_modules)/
      ],
      pattern: /\.(es6|jsx)$/,
      plugins: ['babel-plugin-transform-decorators-legacy']
    }
  },
  modules: {
      autoRequire: {
        'javascripts/app.js': ['initialize']
    }
  },
  server: {
    port: 8080,
    run: true
  }
};


Вот строчки которые помогают билдить es6

...
plugins: {
    babel: {
      presets: ['es2015'],
      ignore: [
        /^(bower_components|vendor|node_modules)/
      ],
      pattern: /\.(es6|jsx)$/,
      plugins: ['babel-plugin-transform-decorators-legacy']
    }
  }
...

Теперь можно спокойно перейти к коду. Здесь все стандартно: Model, View, и т.д.
Единственный момент на который хотелось бы обратить внимание — это установка атрибутов. Есть несколько способов:

export default class ItemView extends Marionette.ItemView {
    tagName() { return "li"; }

    constructor(options) {
        super(options);
    }
}

export default Marionette.ItemView.extend({
    tagName: 'li',

    initialize(options) {
    }
});

import { props } from 'decorators';

@props({
    tagName: 'li'
})
export default class ItemView extends Marionette.ItemView {
    initialize(options) {
    }
});

Мне нравится последний способ. Я нашел его здесь

Выполняем
npm start

и наслаждаемся своим творением открыв в браузере localhost:8080/.

Единственное что нам еще нужно — это тесты.
Наши тесты будут лежать в папке specs.

Добавим devDependecies в нашем package.json файле
"devDependencies": {
    "phantomjs": "~1.9.18",
    "coveralls": "~2.11.6",
    "karma": "~0.13.19",
    "karma-es6-shim": "~0.2.3",
    "karma-mocha": "~0.2.1",
    "karma-chai-plugins": "~0.6.1",
    "karma-coverage": "~0.5.3",
    "karma-coveralls": "~1.1.2",
    "karma-phantomjs-launcher": "~0.2.3"
  }

Немножко изменим brunch-config.js

brunch-config.js
exports.config = {
  paths: {
    watched: ['app', 'specs']
  },
  files: {
    javascripts: {
      defaultExtension: "js",
      joinTo: {
        "javascripts/app.js": /^app/,
        "javascripts/specs.js": /^specs/,
        "javascripts/vendor.js": /^bower_components/
      },
      order: {
        before: [
          'bower_components/jquery/dist/jquery.js',
          'bower_components/underscore/underscore.js',
          'bower_components/backbone/backbone.js',
          'bower_components/marionette/lib/backbone.marionette.js',
          'bower_components/bootstrap/dist/js/bootstrap.js',
          'bower_components/es6-shim/es6-shim.js'
        ]
      }
    },
    stylesheets: {
      defaultExtension: "styl",
      joinTo: "stylesheets/app.css"
    },
    templates: {
      defaultExtension: "hbs",
      joinTo: "javascripts/app.js"
    }
  },
  plugins: {
    babel: {
      presets: ['es2015'],
      ignore: [
        /^(bower_components|vendor|node_modules)/
      ],
      pattern: /\.(es6|jsx)$/,
      plugins: ['babel-plugin-transform-decorators-legacy']
    }
  },
  modules: {
      autoRequire: {
        'javascripts/app.js': ['initialize']
    }
  },
  server: {
    port: 8080,
    run: true
  },
  overrides: {
    testing: {
      modules: {
        autoRequire: {
          'javascripts/specs.js': ['specs/initialize']
        }
      }
    }
  }
};



Создадим наш
karma.conf.js
module.exports = function(config) {
    config.set({<code>
        browsers: ['PhantomJS'],
        frameworks: ['mocha', 'chai', 'es6-shim'],
        files: [
            "public/javascripts/vendor.js",
            "public/javascripts/app.js",
            "public/javascripts/specs.js"
        ],
        reporters: ['coverage', 'coveralls'],
        preprocessors: {
            'public/javascripts/app.js': 'coverage'
        },
        coverageReporter: {
            type: 'lcov',
            dir: 'coverage/',
            subdir: '.'<a href="https://github.com/denar90/marionette-es6-brunch"></a>
        },
        singleRun: true
    });
};


И добавим еще один скрипт в package.json.

"test": "./node_modules/.bin/brunch b --env testing && ./node_modules/.bin/karma start karma.conf.js"


Для полноты картины сделаем интеграцию с Travis CI.
travis.yml
language: node_js
node_js:
  - '4.0'
after_script:
  - cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js


Мой пример можно посмотреть здесь.

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

Комментарии (5)


  1. affka
    08.01.2016 10:29
    +1

    А в чем преимущество с Brunch и с чем его сравнивать?


    1. denar90
      08.01.2016 10:44

      Его авторы указывают на преимущества перед Grunt и Gulp: простота и быстродействие.


      1. k12th
        08.01.2016 11:23
        +1

        Про Gulp рассказывали то же самое:) С простотой оказалось не все так просто, а скорость достигается за счет параллелизации — благодаря которой очень трудно понять, что же именно тормозит:) Но конфиг из примера выглядит достаточно приятно.


        1. Accetone
          08.01.2016 11:52
          +1

          Автор Brunch немного лукавит приводя на сайте типичные примеры конфигов grunt, gulp и brunch. Для конкурентов приведён полный боекомплект, а для своего продукта элементарнейшый случай. И за маркетингом сразу не понять, действительно ли конфиги brunch проще.


          1. VasilioRuzanni
            08.01.2016 15:03

            Ну они реально проще, ибо если, скажем, gulp — по сути, утилита для создания своего build-pipeline-а, то Brunch — это уже готовый пайплайн, то есть просто более высокий уровень. При работе с gulp в больших проектах кроме самих gulp-тасков обычно появляются такие высокоуровневые конфиги аля brunch.