popClingwrap
popClingwrap

Reputation: 4217

Gulp task sequencing

I have already asked a simplified version of this question but the answer then did not work on my larger problem so I am going to post the whole thing here and see if anything comes of it.

Project Structure

I have many projects to work with that all share a structure like this (more child dirs/sub levels than this but you get the idea) :

|-build
  |-sub_EN_01
    |-assets
      |-image.jpg
    |-file_EN.txt

  |-sub_EN_02
    |-assets
      |-image.jpg
    |-file_EN.txt

When a new project is started I want to copy the contents of build to a new dir and then run a task that will change all instances of EN to FR in both file/dir names and file contents.

Copy And Rename

The task I have written does this fine. A call like this gulp copy-dirs --match EN --replacement FR specifies which strings should replace what and is all good but leaves me the original EN files alongside the new FR files :

var gulp = require("gulp");
var foreach = require("gulp-foreach");
var args = require("yargs").argv;
var rename = require("gulp-rename");
var replace = require("gulp-replace-task");

gulp.task("copy-dirs", function(){
    return gulp.src("./build/*"+args.match+"*")//Original parent dirs to copy
        .pipe(foreach(function(pDirStream, pDirFile){//Loop parent dirs
            var newDirName = pDirFile.relative.replace(args.match, args.replacement);//Name of parent dir with replacement applied
            gulp.src("./build/"+pDirFile.relative+"/**/*")//Get contents of dir
                .pipe(foreach(function(childStream, childFile){//Loop child files
                    var newFileName = childFile.relative.replace(args.match, args.replacement);//Name of child with replacement applied
                    childStream.pipe(replace({usePrefix: false, patterns:[
                        {
                            match: args.match,
                            replacement: args.replacement
                        }
                    ]}))
                        .pipe(rename(newFileName));

                    return childStream;
                }))
                .pipe(gulp.dest("./build/"+newDirName));//Save it to new location

            return pDirStream;
        }));
});

Cleaning Up

So, I wrote a second small task to clean up which if I then call with gulp clean --match EN and it clears out the original EN files:

var del = require("del");

gulp.task("clean", function(){
   return del(["./build/*"+args.match+"*/**"]);
});

The Problem

Boom! Done. Except this only works if I run copy-dirs and then manually run clean. What I wanted to do was sequence them like this:

gulp.task("project-rename", ["clean"]);

gulp.task("clean", ["copy-dirs"], function(){
   return del(["./build/*"+args.match+"*/**"]);
});

..but when I try this and call gulp project-rename --match EN --replacement FR I get Error: ENOENT, lstat followed a file path to an asset and am left with a selection of half copied/deleted projects.

The Hack

I managed to fix things by using gulp-wait to pause for a second at the end of copy-dirs but this just feels really hacky and so I would like to ask:

Thank You

If you got to here then you are a better person than me. I know this is a beast of post but I have tried to explain it as best I can. I love what gulp offers but I just can't quite get my head around how it actually works in situations more complex than a single task. Any advice will be gratefully received.

Yours in frustrated desperation :)

Upvotes: 1

Views: 78

Answers (1)

Louis
Louis

Reputation: 151370

You are trying to do things that Gulp and its plugins should do for you.

I've based the following solution on your prose description of what you want to do rather than the code you've shown so it is possible that it behaves a bit differently. I've commented the code to explain what it does. Also, I've tested it with a file tree that replicates what you've shown in your question and found it to work. One thing I'd do differently but which you seem to want is that I would not want a gulpfile.js that takes its initial input and write its final output in the same directory. It seems dangerous to me.

var gulp = require("gulp");
var args = require("yargs").argv;
var rename = require("gulp-rename");
var replace = require("gulp-replace-task");
var del = require("del");
var gulpFilter = require('gulp-filter');

var match = args.match;
var replacement = args.replacement;
var match_re = RegExp(match, "g");

gulp.task("copy-dirs", function () {
    // Set a filter that we'll use later. We have `restore: true` so
    // that we can restore the stream to its original set of files
    // after we are done with the filter.
    var filter = gulpFilter("**/*.txt", {restore: true});

    // We match all descendants of any directory in ./build that has
    // the string specified by `match` in it.
    return gulp.src("./build/*" + match + "*/**")
        // In file paths every instance of the string contained by the
        // `match` option will be replaced with the string specified by
        // the `replacement` option.
        .pipe(rename(function (path) {
            path.dirname = path.dirname.replace(match_re, replacement);
            path.basename = path.basename.replace(match_re, replacement);

            // If you really want to change extensions, you'll also
            // have to process path.ext.
        }))
        // Filter it down to only the files for which text replacement
        // makes sense.
        .pipe(filter)
        .pipe(replace({usePrefix: false, patterns:[
            {
                match: match,
                replacement: replacement
            }
        ]}))
        // Go back to the whole set of files so that we can store them.
        .pipe(filter.restore)
        .pipe(gulp.dest("./build"));
});

gulp.task("clean", ["copy-dirs"], function(){
   return del(["./build/*"+match+"*/**"]);
});

gulp.task("project-rename", ["clean"]);

Upvotes: 2

Related Questions