Gulp-файл для frontend проекта

Решение не претендует на идеальность. Но вполне подойдет для разработки небольшого frontend проекта, написанного с использованием coffeescript и less. Важное замечание: использование бекенда не предусмотрено. Nodejs использован только для билда ассетов (такое решение удобно, к примеру, если вы планируете создать гибридное оффлайн приложение для смартфона).

Ключевые особенности нашего проекта, и, как следствие, gulp-файла:

1)  Поддерживает разные среды (prod, dev, etc)

2) Компиляция и минификация less, CoffeScript файлов, объединение нескольких в один (конкатенация)

3) Поддержка тестов (jasmine)

4) В dev-среде работает watch (динамически перебилдивает ассеты при изменении исходников)

У проекта следующая структура (лишнее не отображаю): 

.
├── css
├── js
├── node_modules
├── public
│   ├── bower_components
│   ├── css
│   ├── img
│   └── js

└── spec

Исходники располагаются в css, js  директориях (те, которые в корне). В public лежит все, что уже готово к отображению: index.html файл, скомпиленные ассеты. Директория spec - для jasmine тестов.

Более подробно о построении gulpfile:

Список используемых модулей:

var gulp = require('gulp'),
    less = require('gulp-less'),
    coffee = require('gulp-coffee'),
    concat = require('gulp-concat'),
    watch = require('gulp-watch'),
    batch = require('gulp-batch'),
    fs = require('fs'),
    argv = require('yargs').argv
;

Для начала, определим среду:

// Environment detecting
var env = argv.env || 'prod',
    envFile = '.env'
;

if (!argv.env && fs.existsSync(envFile)) {
    env = fs.readFileSync(envFile, 'utf8');
}

Как видим, среда определяется либо аргументом команды gulp, либо содержимым envFile (у меня envFile  - файл .env в корне проекта).

Добавим сборку .less файлов:

gulp.task('less', function () {
    gulp.src('./css/**/*.less')
        .pipe(less())
        .pipe(concat('style.css'))
        .pipe(gulp.dest('./public/css/'))
    ;
});

Обратите внимание на маску: таким образом, будут собираться рекурсивно все файлы в директории ./css

Добавим сборку .coffee файлов. Здесь немного сложнее. Дело в том, что удобно разделить исходники (назовем их "библиотеки") и скрипт запуска приложения. Это нужно, к примеру, для запуска тестов.

gulp.task('coffee-lib', function () {
    gulp.src(['./js/**/*.coffee', '!./js/app.coffee'])
        .pipe(coffee())
        .pipe(concat('lib.js'))
        .pipe(gulp.dest('./public/js/'))
    ;
});

gulp.task('coffee-app', function () {
    gulp.src('./js/app.coffee')
        .pipe(coffee())
        .pipe(concat('app.js'))
        .pipe(gulp.dest('./public/js/'))
    ;
});

Как видите, в таске coffee-lib исключен файл ./js/app.coffee.

Добавим таск для тестов:

gulp.task('coffee-spec-common', function () {
    gulp.src('./spec/common.coffee')
        .pipe(coffee())
        .pipe(concat('spec-common.js'))
        .pipe(gulp.dest('./public/js/'))
    ;
});

gulp.task('coffee-spec', function () {
    gulp.src(['./spec/**/*.coffee', '!./spec/common.coffee'])
        .pipe(coffee())
        .pipe(concat('spec.js'))
        .pipe(gulp.dest('./public/js/'))
    ;
});

coffee-spec-common таск компилит общие утилиты для тестов (у меня, к примеру, там находятся методы, проверяющие равность двухмерных массивов и другие) вынесен отдельно для того, чтобы гарантировать подключение перед тестами.

Добавим watch и batch для удобства разработки:

gulp.task('watch', function() {
    watch(['./js/**/*.coffee'], batch(function(events, cb) {
        gulp.start('default', cb);
    }));

    watch(['./spec/**/*.coffee'], batch(function(events, cb) {
        gulp.start('default', cb);
    }));

    watch(['./css/**/*.less'], batch(function(events, cb) {
        gulp.start('default', cb);
    }));
});

(Batch добавляет задержку перед перекомпиляцией. Он устраняет проблему с производительностью при переключении веток в git).

Добавим watch в дев режиме и главный таск:

var tasks = ['coffee-lib', 'coffee-app', 'less'];

if (env === 'dev') {
    tasks.push('watch');
    tasks.push('coffee-spec-common');
    tasks.push('coffee-spec');
}

gulp.task('default', tasks);

Полный пример можно посмотреть здесь