Потоковая передача данных в стиле gulp для Grunt, или для другого таск-менеджера

gulp

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

На данный момент уже существует активное сообщество разработчиков, которое создаёт плагины под gulp. Однако, пока еще не все плагины портированы. Например, когда мне нужно провести тестирование, из-за отсутствия некоторых из необходимых в данном случае плагинов, мне приходится оставаться верным Grunt. Я мог бы, конечно, использовать gulp и Grunt вместе, но, честно говоря, мне бы не хотелось в работе над одним проектом использовать целых две системы сборки. Есть еще один момент, который мне не нравится в gulp — это модель параллельного выполнения задач. Все задачи в этом менеджере выполняются одновременно по умолчанию, и это довольно неудобно, когда вам нужно, чтобы перечень задач выполнялся по порядку, как это происходит в Grunt.

К счастью, один из авторов gulp переписал программный код ядра файловой системы и вынес его в отдельный модуль — vinyl-fs. Методы модуля .src() и .dest() - это, по сути, всё, что нам нужно для модификации возможностей существующих плагинов gulp. На момент написания статьи этот модуль пока не включён в пакетный менеджер Node, однако, его можно легко установить самостоятельно, воспользовавшись соответствующим сокращенным адресом с github:

$ npm install wearefractal/vinyl-fs

Модуля vinyl-fs делает возможным использование плагинов gulp в задачах Grunt:

var fs = require('vinyl-fs'),
    gzip = require('gulp-gzip')

grunt.registerTask('zip', function () {
  fs.src('./build/build.js')
    .pipe(gzip())
    .pipe(fs.dest('./build'))
    .on('end', this.async())
})

Вы, возможно, заметили, что плагины gulp выполняют только одно простое действие, например, переименование, добавление шапки, добавление подвала, и так далее. Я не против таких мелких дополнительных зависимостей, однако, меня раздражает тот факт, что почти у всех плагинов в зависимостях прописан модуль event-stream, у которого, по сути, используется только одна функция .map(), доступная также в другом отдельном модуле map-stream. А кроме того, что делать в ситуации, когда мне нужно что-то сделать с файлом, но под это действие еще не существует необходимого мне плагина?

На самом деле, довольно просто запустить вручную необходимые мне действия с файлами, используя только модуль map-stream:

var fs = require('vinyl-fs'),
    map = require('map-stream'),
    zlib = require('zlib')
grunt.registerTask('zip', function () {
  fs.src('./build/build.js')
    .pipe(map(gzip))
    .pipe(fs.dest('./build'))
    .on('end', this.async())
})
// Нужно всего лишь изменить содержимое файла, 
// а затем запустить соответствующую функцию обратного вызова.
// Просто держите в памяти, что всё сводится к 
// добавлению и удалению информации из буфера.
function gzip (file, cb) {
  zlib.gzip(file.contents, function (err, buffer) {
    file.contents = buffer
    file.path = file.path + '.gz'
    cb(err, file)
  })
}

Потоковая передача данных через map-stream позволяет вам делать с файлом всё что угодно: изменять его содержимое, название, и так далее. Самое приятное во всем этом то, что можно использовать прямые зависимости (например, uglify-js для задачи uglify) в пользовательском скрипте, запуская его при помощи Grunt, make, npm run или любого другого инструмента, которым вы привыкли пользоваться. Отличная альтернатива ожиданию, пока кто-то напишет необходимый вам плагин под gulp.