23

I'm trying to dynamically create tasks (minify and concat) based on jsFiles object. The key will give the destination file name and the array contains the src files. When I run gulp I see all the tasks names being ran but it only writes the last key which is group2.js in this case. What's wrong here?

// imports here

var jsFiles = 
{
    group1:[file1.js,file2.js],
    group2:[file2.js,file3.js]
};

for (var key in jsFiles)
{
    gulp.task(key, function() {
        return gulp.src(jsFiles[key])
        .pipe(jshint())
        .pipe(uglify())
        .pipe(concat(key + '.js'))  // <- HERE
        .pipe(gulp.dest('public/js'));
    });
}

var defaultTasks = [];
for (var key in jsFiles)
{
    defaultTasks.push(key);
}
gulp.task('default', defaultTasks);
cmancre
  • 1,061
  • 3
  • 11
  • 23

5 Answers5

24

Another option is to use functional array looping functions combined with Object.keys, like so:

var defaultTasks = Object.keys(jsFiles);

defaultTasks.forEach(function(taskName) {
   gulp.task(taskName, function() {
       return gulp.src(jsFiles[taskName])
          .pipe(jshint())
          .pipe(uglify())
          .pipe(concat(key + '.js'))
          .pipe(gulp.dest('public/js'));
   });
});

I feel like this is a little cleaner, because you have the loop and the function in the same place, so it's easier to maintain.

OverZealous
  • 39,252
  • 15
  • 98
  • 100
  • Then with Gulp 4, you can call these tasks in parallel: gulp.parallel("scripts:app", "scripts:vendor") ! Simple, fantastic ^^ – Thomas Decaux Jan 23 '16 at 18:47
  • 1
    but how do you deal with say 50 separated task that all needs to work in the same way would you still use gulp.parallel ? – AKFourSeven Mar 28 '17 at 12:58
  • 1
    I'm a bit late to the party, but I would typically call those 50 tasks in parallel unless the tasks or your computer are unusually slow. I'm using it on roughly 200 simple tasks. – Travis Don Kindred Jul 26 '18 at 21:28
7

Capture the value of 'key' variable on each iteration using IIFE. In your example, at the moment of concat invocation loop will be finished already and variable key will have the last value.

for (var key in jsFiles)
{
    (function(key) {
        gulp.task(key, function() {
            return gulp.src(jsFiles[key])
                .pipe(jshint())
                .pipe(uglify())
                .pipe(concat(key + '.js'))  // <- HERE
                .pipe(gulp.dest('public/js'));
        });

    })(key);

}

For detailed explanation see this function closures - Avoiding the Reference Problem section

oaleynik
  • 655
  • 5
  • 19
  • 2
    This solutions works but lint keeps warning about creating functions inside loops. – cmancre Apr 09 '14 at 16:52
  • @cmancre than separated function can be declared and called in loop with the key param, which will be sent into function by ref. An answer for current question is in capturing of iteration variable. You didn't mention the requirement regarding successful linting in the question (: – oaleynik Apr 09 '14 at 17:01
6

Using Gulp#4.0, I like the use of gulp.parallel() such as :

var plugins = require('gulp-load-plugins');
var $ = plugins();

var jsFiles = {
   // Libraries required by Foundation
   "jquery" : [
      "bower_components/jquery/dist/jquery.js",
      "bower_components/motion-ui/dist/motion-ui.js",
      "bower_components/what-input/dist/what-input.js"
   ],
   "angular" : [
      "bower_components/angular/angular.min.js",
      "bower_components/angular-animate/angular-animate.min.js",
      "bower_components/angular-aria/angular-aria.min.js",
      "bower_components/angular-material/angular-material.min.js",
      "bower_components/angular-messages/angular-messages.min.js",
      "bower_components/angular-sanitize/angular-sanitize.min.js",
      "bower_components/angular-ui-i18n/angular-ui-i18n.min.js"
   ],
   // Core Foundation files
   "foundation-sites" : [
      "bower_components/foundation-sites/dist/js/foundation.js"
   ],
   // Dropzone Library
   "dropzone" : [
      "bower_components/dropzone/dist/dropzone.js",
      "bower_components/ng-dropzone/dist/ng-dropzone.min.js"
   ]
};

var defaultTasks = Object.keys(jsFiles);

defaultTasks.forEach(function (libName) {
   gulp.task( 'scripts:'+libName, function () {
      return gulp.src(jsFiles[libName])
         //.pipe($.jshint()) // if you want it
         //.pipe($.uglify()) // if you like it
         //.pipe($.concat(libName+'.js')) // .min.js if you Uglified it
         .pipe(gulp.dest('dist/lib/'+libName));
   });
});

gulp.task( 'bundle_javascript_dependencies',
   gulp.parallel(
      defaultTasks.map(function(name) { return 'scripts:'+name; })
   )
);

gulp.task('build',
   gulp.series(
      'clean',
      gulp.parallel( /* Any other task of function */ 'bundle_javascript_dependencies' )
   )
);

Working for me and I love it ! Thanks to OverZealous to have show me the way.

Frost Michael
  • 61
  • 1
  • 2
1

cmancre solution work fine... but here the modified and working one i'm currently using:

var jsFiles = 
{
    group1:[file1.js,file2.js],
    group2:[file2.js,file3.js]
};

var defaultTasks = [];

function createTask(key)
{
    gulp.task(key, function() {
        return gulp.src(jsFiles[key])
            .pipe(uglify())
            .pipe(concat(key + '.js'))
            .pipe(rename({suffix: ".min"})) //Will create group1.min.js
            .pipe(gulp.dest('./assets/js'));
    });
}

for (var key in jsFiles)
{
    createTask(key);
    defaultTasks.push(key);
}

gulp.task('default', defaultTasks, function(){
    for (var key in jsFiles)
    {
        //Will watch each js defined in group1 or group2
        gulp.watch(jsFiles[key], [key]) 
    }
});
Mikhael Aubut
  • 179
  • 1
  • 3
  • 12
0

Solution based on jslinterrors.com/dont-make-functions-within-a-loop:

var jsFiles = 
{
    group1:[file1.js,file2.js],
    group2:[file2.js,file3.js]
};

function createTask(key)
{
   return gulp.src(jsFiles[key])
        .pipe(jshint())
        .pipe(uglify())
        .pipe(concat(key + '.js'))
        .pipe(gulp.dest('public/js'));

}

for (var key in jsFiles)
{
    createTask(key);
}

var defaultTasks = [];
for (var key in jsFiles)
{
    defaultTasks.push(key);
}
gulp.task('default', defaultTasks);
cmancre
  • 1,061
  • 3
  • 11
  • 23
  • This solution didn't work for me. It appears createTask(key) isn't enough. If you don't explicitly call gulp.task(key) gulp won't be able to find the task when it does the default task. I ended up using oaleynik's answer. – Chris Aug 21 '14 at 21:24
  • how could I run all the task asynchronous? – Marten Zander Jan 19 '16 at 17:14