Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating over directories with Gulp?

I'm new to gulp, but I'm wondering if its possible to iterate through directories in a gulp task.

Here's what I mean, I know a lot of the tutorials / demos show processing a bunch of JavaScript files using something like "**/*.js" and then they compile it into a single JavaScript file. But I want to iterate over a set of directories, and compile each directory into it's own JS file.

For instance, I have a file structure like:

/js/feature1/something.js
/js/feature1/else.js
/js/feature1/foo/bar.js
/js/feature1/foo/bar2.js
/js/feature2/another-thing.js
/js/feature2/yet-again.js

...And I want two files: /js/feature1/feature1.min.js and /js/feature2/feature2.min.js where the first contains the first 4 files and the second contains the last 2 files.

Is this possible, or am I going to have to manually add those directories to a manifest? It would be really nice to pragmatically iterate over all the directories within /js/.

Thanks for any help you can give me.

-Nate

Edit: It should be noted that I don't only have 2 directories, but I have many (maybe 10-20) so I don't really want to write a task for each directory. I want to handle each directory the same way: get all of the JS inside of it (and any sub-directories) and compile it down to a feature-based minified JS file.

like image 897
Nathan Rutman Avatar asked Mar 03 '14 14:03

Nathan Rutman


3 Answers

There's an official recipe for this: Generating a file per folder

var fs = require('fs');
var path = require('path');
var merge = require('merge-stream');
var gulp = require('gulp');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');

var scriptsPath = 'src/scripts';

function getFolders(dir) {
    return fs.readdirSync(dir)
      .filter(function(file) {
        return fs.statSync(path.join(dir, file)).isDirectory();
      });
}

gulp.task('scripts', function() {
   var folders = getFolders(scriptsPath);

   var tasks = folders.map(function(folder) {
      return gulp.src(path.join(scriptsPath, folder, '/**/*.js'))
        // concat into foldername.js
        .pipe(concat(folder + '.js'))
        // write to output
        .pipe(gulp.dest(scriptsPath)) 
        // minify
        .pipe(uglify())    
        // rename to folder.min.js
        .pipe(rename(folder + '.min.js')) 
        // write to output again
        .pipe(gulp.dest(scriptsPath));    
   });

   // process all remaining files in scriptsPath root into main.js and main.min.js files
   var root = gulp.src(path.join(scriptsPath, '/*.js'))
        .pipe(concat('main.js'))
        .pipe(gulp.dest(scriptsPath))
        .pipe(uglify())
        .pipe(rename('main.min.js'))
        .pipe(gulp.dest(scriptsPath));

   return merge(tasks, root);
});
like image 197
Ghidello Avatar answered Nov 13 '22 00:11

Ghidello


You could use glob to get a list of directories and iterate over them, using gulp.src to create a separate pipeline for each feature. You can then return a promise which is resolved when all of your streams have ended.

var fs = require('fs');
var Q = require('q');
var gulp = require('gulp');
var glob = require('glob');

gulp.task('minify-features', function() {
  var promises = [];

  glob.sync('/js/features/*').forEach(function(filePath) {
    if (fs.statSync(filePath).isDirectory()) {
      var defer = Q.defer();
      var pipeline = gulp.src(filePath + '/**/*.js')
        .pipe(uglify())
        .pipe(concat(path.basename(filePath) + '.min.js'))
        .pipe(gulp.dest(filePath));
      pipeline.on('end', function() {
        defer.resolve();
      });
      promises.push(defer.promise);
    }
  });

  return Q.all(promises);
});
like image 17
Ben Avatar answered Nov 12 '22 23:11

Ben


I am trying myself to get how streams work in node. I made a simple example for you, on how to make a stream to filter folders and start a new given stream for them.

'use strict';

var gulp             = require('gulp'),
    es               = require('event-stream'),
    log              = require('consologger');

// make a simple 'stream' that prints the path of whatever file it gets into
var printFileNames = function(){

    return es.map(function(data, cb){

        log.data(data.path);
        cb(null, data);
    });
};

// make a stream that identifies if the given 'file' is a directory, and if so
// it pipelines it with the stream given
var forEachFolder = function(stream){

    return es.map(function(data, cb){

        if(data.isDirectory()){

            var pathToPass = data.path+'/*.*';  // change it to *.js if you want only js files for example

            log.info('Piping files found in '+pathToPass);

            if(stream !== undefined){
                gulp.src([pathToPass])
                .pipe(stream());
            }
        }

        cb(null, data);
    });
};


// let's make a dummy task to test our streams
gulp.task('dummy', function(){
    // load some folder with some subfolders inside
    gulp.src('js/*')
    .pipe(forEachFolder(printFileNames));
    // we should see all the file paths printed in the terminal
});

So in your case, you can make a stream with whatever you want to make with the files in a folder ( like minify them and concatenate them ) and then pass an instance of this stream to the forEachFolder stream I made. Like I do with the printFileNames custom stream.

Give it a try and let me know if it works for you.

like image 4
AntouanK Avatar answered Nov 13 '22 00:11

AntouanK