Как группировать похожие задачи gulp, чтобы уменьшить повторение кода

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

Вот упрощенная версия структуры моего проекта:

У меня есть 2 папки сайта: один/ и два/

На каждом сайте есть две папки-ветви: a/ и b/.

Внутри каждой ветки есть три папки: inner/, outer/ и web/.

Моя задача — взять похожие файлы деталей из внутренней и внешней папок и объединить их в соответствующие веб-папки. Ниже приведен простой пример желаемого результата.

-- inner/
 |-- color1
 |-- color2
 |-- fruit1
 |-- fruit2
-- outer/
 |-- color1
 |-- color2
 |-- fruit1
 |-- fruit2
-- web/
 |-- colors.txt
 |-- fruits.txt

Я создал файл config.json для хранения конкретной конфигурации сайта. В настоящее время он используется только для настройки путей к сайтам. Вот config.json

{
  "sites": {
    "one": {
      "a": "/path/to/one/a/",
      "b": "/path/to/one/b/"
    },
    "two": {
      "a": "/path/to/two/a/",
      "b": "/path/to/two/b/"
    }
  }
}

И наконец, вот gulpfile.js

// Include local Gulp
var gulp = require("gulp");

// Get data from config.json
var sites = require("./config.json").sites;

// Include Gulp specific plugins
var gConcat = require("gulp-concat");
var gHeader = require("gulp-header");
var gUtil = require("gulp-util");
var gNotify = require("gulp-notify");

// Setup directories
var outer = "outer/";
var inner = "inner/";
var web = "web/";

// Misc
var alertMessage = "# GENERATED FILE - DO NOT MODIFY\n\n";

// 8 total tasks for concatenation

// Concatenate to colors.txt - 4 tasks
// Color task 1: [ Site => one ] [ Branch => a ]
gulp.task("one_a_color", function() {
    return gulp.src([sites.one.a + outer + "color?", sites.one.a + inner + "color?"])
        .pipe(gConcat("colors.txt"))
        .pipe(gHeader(alertMessage))
        .pipe(gulp.dest(sites.one.a + web))
        .pipe(gNotify());
});

// Color task 2: [ Site => one ] [ Branch => b ]
gulp.task("one_b_color", function() {
    return gulp.src([sites.one.b + outer + "color?", sites.one.b + inner + "color?"])
        .pipe(gConcat("colors.txt"))
        .pipe(gHeader(alertMessage))
        .pipe(gulp.dest(sites.one.b + web))
        .pipe(gNotify());
});

// Color task 3: [ Site => two ] [ Branch => a ]
gulp.task("two_a_color", function() {
    return gulp.src([sites.two.a + outer + "color?", sites.two.a + inner + "color?"])
        .pipe(gConcat("colors.txt"))
        .pipe(gHeader(alertMessage))
        .pipe(gulp.dest(sites.two.a + web))
        .pipe(gNotify());
});

// Color task 4: [ Site => two ] [ Branch => b ]
gulp.task("two_b_color", function() {
    return gulp.src([sites.two.b + outer + "color?", sites.two.b + inner + "color?"])
        .pipe(gConcat("colors.txt"))
        .pipe(gHeader(alertMessage))
        .pipe(gulp.dest(sites.two.b + web))
        .pipe(gNotify());
});

// Concatenate to fruits.txt - 4 tasks
// Fruit task 1: [ Site => one ] [ Branch => a ]
gulp.task("one_a_fruit", function() {
    return gulp.src([sites.one.a + outer + "fruit?", sites.one.a + inner + "fruit?"])
        .pipe(gConcat("fruits.txt"))
        .pipe(gHeader(alertMessage))
        .pipe(gulp.dest(sites.one.a + web))
        .pipe(gNotify());
});

// Fruit task 2: [ Site => one ] [ Branch => b ]
gulp.task("one_b_fruit", function() {
    return gulp.src([sites.one.b + outer + "fruit?", sites.one.b + inner + "fruit?"])
        .pipe(gConcat("fruits.txt"))
        .pipe(gHeader(alertMessage))
        .pipe(gulp.dest(sites.one.b + web))
        .pipe(gNotify());
});

// Fruit task 3: [ Site => two ] [ Branch => a ]
gulp.task("two_a_fruit", function() {
    return gulp.src([sites.two.a + outer + "fruit?", sites.two.a + inner + "fruit?"])
        .pipe(gConcat("fruits.txt"))
        .pipe(gHeader(alertMessage))
        .pipe(gulp.dest(sites.two.a + web))
        .pipe(gNotify());
});

// Fruit task 4: [ Site => two ] [ Branch => b ]
gulp.task("two_b_fruit", function() {
    return gulp.src([sites.two.b + outer + "fruit?", sites.two.b + inner + "fruit?"])
        .pipe(gConcat("fruits.txt"))
        .pipe(gHeader(alertMessage))
        .pipe(gulp.dest(sites.two.b + web))
        .pipe(gNotify());
});

// Watch for all events in specified {directories}/{files}, then trigger appropriate task
// 8 total watch jobs
gulp.task("watch", function () {
    // Color related watch jobs - Total 4
    // Color watch 1: [ Site => one ] [ Branch => a ]
    gulp.watch([sites.one.a + outer + "**/color?", sites.one.a + inner + "**/color?"], function(event) {
        gUtil.log(event.path.split("/").pop(), "=>", event.type);
        gulp.start("one_a_color");
    });

    // Color watch 2: [ Site => one ] [ Branch => b ]
    gulp.watch([sites.one.b + outer + "**/color?", sites.one.b + inner + "**/color?"], function(event) {
        gUtil.log(event.path.split("/").pop(), "=>", event.type);
        gulp.start("one_b_color");
    });

    // Color watch 3: [ Site => two ] [ Branch => a ]
    gulp.watch([sites.two.a + outer + "**/color?", sites.two.a + inner + "**/color?"], function(event) {
        gUtil.log(event.path.split("/").pop(), "=>", event.type);
        gulp.start("two_a_color");
    });

    // Color watch 4: [ Site => two ] [ Branch => b ]
    gulp.watch([sites.one.b + outer + "**/color?", sites.one.b + inner + "**/color?"], function(event) {
        gUtil.log(event.path.split("/").pop(), "=>", event.type);
        gulp.start("two_b_color");
    });

    // Fruit related watch jobs - Total 4
    // Fruit watch 1: [ Site => one ] [ Branch => a ]
    gulp.watch([sites.one.a + outer + "**/fruit?", sites.one.a + inner + "**/fruit?"], function(event) {
        gUtil.log(event.path.split("/").pop(), "=>", event.type);
        gulp.start("one_a_fruit");
    });

    // Fruit watch 2: [ Site => one ] [ Branch => b ]
    gulp.watch([sites.one.b + outer + "**/fruit?", sites.one.b + inner + "**/fruit?"], function(event) {
        gUtil.log(event.path.split("/").pop(), "=>", event.type);
        gulp.start("one_b_fruit");
    });

    // Fruit watch 3: [ Site => two ] [ Branch => a ]
    gulp.watch([sites.two.a + outer + "**/fruit?", sites.two.a + inner + "**/fruit?"], function(event) {
        gUtil.log(event.path.split("/").pop(), "=>", event.type);
        gulp.start("two_a_fruit");
    });

    // Fruit watch 4: [ Site => two ] [ Branch => b ]
    gulp.watch([sites.one.b + outer + "**/fruit?", sites.one.b + inner + "**/fruit?"], function(event) {
        gUtil.log(event.path.split("/").pop(), "=>", event.type);
        gulp.start("two_b_fruit");
    });
});

// Run all tasks
gulp.task("background",
    [
        "one_a_color", "one_b_color", "two_a_color", "two_b_color",
        "one_a_fruit", "one_b_fruit", "two_a_fruit", "two_b_fruit",
        "watch"
    ]
);

Приведенный выше файл gulp работает и выполняет свою работу. Однако, как видите, большинство кодов повторяются, меняется только часть — gulp.src и gulp.dest, а также имена задач.

Мой вопрос. Можно ли упростить этот gulp-файл, чтобы вместо повторения кода для каждой задачи можно было объединить похожие задачи вместе.


person Faiyaz Haider    schedule 30.03.2015    source источник


Ответы (1)


Не такая уж простая задача, но давайте посмотрим, сможем ли мы ее оптимизировать. Gulp и Globs очень хорошо работают с массивами, поэтому мы должны сначала преобразовать ваши пути в массив:

var gulp = require('gulp');
var concat = require('gulp-concat');
var es = require('event-stream');

var sites = require('./config.json').sites;

var toArray = function(conf) {
    var arr = [];
    for(var key in conf) {
        if(typeof conf[key] === 'object') {
            arr = arr.concat(toArray(conf[key]));
        } else {
            arr.push(conf[key]);
        }
    }
    return arr;
};

var sites = toArray(sites);

Теперь, когда у нас есть пути, мы создаем шарики для фруктов и цветов.

var globs = [];
sites.forEach(function(data) {
    globs.push(data + '**/color*');
    globs.push(data + '**/fruit*');
});

С вашей текущей конфигурацией вы получаете массив из 8 записей. Далее давайте определим concat-задачу. Вот что вы имеете в виду под «пакетным» вместе, нам нужен так называемый массив потоков (я писал об этом здесь). Это простое отображение существующего массива на множество gulp-потоков, которые в конце объединяются с помощью модуля event-stream. Что касается цветов/фруктов, нам нужно проявить немного творчества с именами concat и именами адресатов. Обратите внимание, что я использую плагин changed для предотвращения бесполезных сборок.

gulp.task('concat', function() {
    var tasks = globs.map(function(glob) {
        var file = glob.indexOf('color') >= 0 ? 'col' : 'fru';
        var dest = glob.replace('**/color*','').replace('**/fruit*','') + 'web';
        return gulp.src(glob)
            .pipe(concat(file + '.txt'))
            .pipe(gulp.dest(dest))
    });

    return es.merge.apply(null, tasks);
});

Теперь эта задача делает все, что нам нужно, и постепенно. Таким образом, наш процесс просмотра довольно прост.

gulp.task('watch', ['concat'], function() {
    gulp.watch(globs, ['concat']);
});

Надеюсь это поможет!

Обновить

Хорошо, я сделал некоторые адаптации, которые должны предотвратить пересборку всего вашего проекта.

Сначала я извлек concatStream в функцию. На самом деле это единственное, что вы уже сделали со своим собственным образцом:

var concatStream = function(glob) {
    var file = glob.indexOf('color') >= 0 ? 'farbe' : 'frucht';
    var dest = glob.replace('**/color*','').replace('**/fruit*','') + 'web';
    return gulp.src(glob)
        .pipe(concat(file + '.txt'))
        .pipe(header(alertMessage))
        .pipe(notify())
        .pipe(gulp.dest(dest))
};

В зависимости от Glob (шаблон файла, в котором мы выбираем цвета или фрукты из наших каталогов), мы определяем новый вывод (файл, это «col», когда «color» находится в нашей строке поиска, «fru» в противном случае) и новый пункт назначения. (это просто старая папка без шаблона поиска цветов/фруктов). gulp.task('concat') теперь делает следующее:

gulp.task('concat', function() {
    var tasks = globs.map(concatStream);
    return es.merge.apply(null, tasks);
});

Каждый из наших глобусов (console.log их, если вы хотите знать, что там) сопоставляется с concatStream, затем новый массив потоков объединяется и выполняется.

Задача наблюдения теперь новая... мы делаем то же самое, что и с нашей задачей 'concat':

gulp.task('watch', ['concat'], function() {
    globs.map(function(glob) {
        gulp.watch(glob, function() {
           return concatStream(glob);
        })
    })
});

Для каждого глоба мы создаем новый наблюдатель, который просто снова вызывает concatStream.


Обновить

Небольшой изменение

Внутри glob изменение подстановочного знака (*) на необязательное совпадение одного символа (?) позволит нам использовать то же имя для выходного файла (например, цвет и фрукты).

var globs = [];
    sites.forEach(function(data) {
        globs.push(data + '**/color?');
        globs.push(data + '**/fruit?');
    });

И это тоже...

var concatStream = function(glob) {
    var file = glob.indexOf('color') >= 0 ? 'color' : 'fruit';
    var dest = glob.replace('**/color?','').replace('**/fruit?','') + 'web';
    return gulp.src(glob)
        .pipe(concat(file + '.txt'))
        .pipe(header(alertMessage))
        .pipe(notify())
        .pipe(gulp.dest(dest))
};

Теперь я могу сохранить имена color и fruit для моего выходного файла, не беспокоясь о том, что glob соответствует имени и добавляет его существующее содержимое обратно в файл.

person ddprrt    schedule 30.03.2015
comment
Здесь около 6:30 утра. Только что увидел твой ответ. Я попробую это, как только доберусь до своего офиса. Спасибо, что сломали ваш ответ. - person Faiyaz Haider; 30.03.2015
comment
Пожалуйста. Только что попробовал еще раз, вы можете поменять gulp-changed на gulp-cached - person ddprrt; 30.03.2015
comment
Во-первых, ваш рефакторинг моих 150 строк кода всего лишь до 50 строк очень впечатляет. Хорошо, теперь дело - я только что провел несколько тестов, используя как gulp-changed, так и gulp-cahced. Первоначально, когда запускаются часы gulp, все конкатенируется, как и должно быть. Проблема начинается, когда я начинаю вносить изменения. При использовании gulp-changed объединенный файл выгружает данные поверх старых существующих данных, поэтому все дублируется и начинает размножаться с каждым изменением. С gulp-cache изменения в любом файле будут перезаписывать объединенный файл только данными из измененного файла. - person Faiyaz Haider; 30.03.2015
comment
Если вы удалите оба, я думаю, это будет работать нормально, но обработайте все ваши потоки, так что это определенно повлияет на производительность... позвольте мне проверить, если я найду лучшее решение. - person ddprrt; 30.03.2015
comment
На самом деле, удаление обоих дает мне тот же результат, что и gulp-changed. Новые изменения набрасываются на старые данные, множа все. - person Faiyaz Haider; 30.03.2015
comment
Умножение результатов сопоставимых имен файлов... мы должны удалить веб-папку из глобуса. Я изменил пример на разные имена файлов, которые не совпадают - person ddprrt; 30.03.2015
comment
Итак, ваше последнее изменение сработало. Потрясающее устранение неполадок. Не могли бы вы подробнее рассказать о gulp.task(concat), я хочу понять, что там происходит. Кроме того, я забыл спросить еще об одном. Теперь все работает нормально. Однако при добавлении нового файла gulp watch не обнаруживает его. Только потом, когда я вношу изменения в старый файл, gulp watch выбирает новый файл. - person Faiyaz Haider; 30.03.2015
comment
И я бы проголосовал за ваш ответ, но моя учетная запись новая и без особой репутации мне не разрешено голосовать. Извиняюсь - person Faiyaz Haider; 30.03.2015
comment
Не беспокойтесь :-) Я обновил ответ, теперь с дополнительным объяснением и улучшенным наблюдателем. Что касается gulp-watch, который не срабатывает, это на самом деле общая проблема с Gulp, и ее следует решать с помощью Gulp4. На данный момент у меня нет решения. - person ddprrt; 30.03.2015
comment
Извините, есть способ. Установите более продвинутый плагин gulp-watch. Ведет себя как gulp.watch, но умеет находить новые и удаленные файлы. Устаревший тогда с Gulp4 - person ddprrt; 30.03.2015