Search code examples
gulpgulp-concat

Gulp concatenation with custom iteration ordering


I am attempting to concat a number of js files within a nested directory structure to a single file in a different location. They have to be concatenated in a specific order and I cannot find a way of changing the default order in which gulp's glob search retrieves nested files. I have tried various glob patterns to no avail.

My directory structure is as follows:

components

 - componentA
  - controllers
   - controllerA1.js
   - controllerA2.js
  - services
   - serviceA1.js
  - configA.js
  - moduleA.js

  - componentB
   - controllers
    - controllerB1.js
    - controllerB2.js
   - services
    - serviceB1.js
   - configB.js
   - moduleB.js

I want the files to concatenate to a single file in the following order:

configA.js
moduleA.js
controllerA1.js
controllerA2.js
serviceA1.js

configB.js 
moduleB.js
controllerB1.js
controllerB2.js
serviceB.js

So that gulp iterates into each component and iterates down through as far as it can go before moving onto the next component and doing the same.

Instead it concatenates in the following order:

configA.js
moduleA.js
configB.js
moduleB.js
controllerA1.js
controllerA2.js
serviceA1.js
controllerB1.js
controllerB2.js
serviceB1.js

In other words it goes into a top level directory, iterates through each of the top level files in that directory and then jumps to the next top level directory and does the same, before returning to the first top level directory and iterating through the next level down etc etc.

I've tried a couple of different methods which have each presented problems.

I have tried using the gulp-recursive-folder plugin to customise the iteration order as follows:

gulp.task('generateTree', recursivefolder({
        base: './components',
        exclude: [    // exclude the debug modules from thus build 
            //'debug-modules'
        ] 
    }, function(folderFound){
    //This will loop over all folders inside pathToFolder main and recursively on the children folders, secondary 
    //With folderFound.name gets the folderName 
    //With folderFound.path gets all folder path found 
    //With folderFound.pathTarget gets the relative path beginning from options.pathFolder 
    return gulp.src(folderFound.path + "/**/*.js")
            .pipe($.concat("app.js"))
            .pipe(gulp.dest('./build/assets/js/'));
}));

This iterates in the order I want but I believe it is writing the first top level dir as one stream and then writing the second dir as another stream so that the second stream overwrites the first. So I am left with only the following files being concatenated:

configB.js
moduleB.js
controllerB1.js
controllerB2.js
serviceB.js

So I've also tried using the add-stream plugin to recursively add to the same stream before writing to file. I won't bore anyone with the details but basically I can't get this to work as desired either. Can anyone recommend a post/tutorial/plugin? Thanks.


Solution

  • gulp.src() respects the ordering of globs that are passed to it and emits files in the same order. That means if you explicitly pass a glob for each component to gulp.src() it will first emit the files for the first component, then for the second component and so on:

    gulp.task('default', function() {
      return gulp.src([
          'components/componentA/**/*.js',
          'components/componentB/**/*.js'
        ])
        .pipe($.concat('app.js'))
        .pipe(gulp.dest('./build/assets/js/'));
    });
    

    Obviously you don't want to maintain that array manually. What you want to do is generate the array based on the components that are available in your project. You can use the glob module for that:

    var glob = require('glob');
    
    gulp.task('default', function() {
      return gulp.src(glob.sync('components/*').map(c => c + '/**/*.js'))
        .pipe($.concat('app.js'))
        .pipe(gulp.dest('./build/assets/js/'));
    });