Replacing strings with values from an array with Gulp

602 Views Asked by At

I'm trying to instruct gulp-replace to replace all the instances of given strings flexes[i][0] in my files into one of two values flexes[i][1] or flexes[i][2].

My setup

const gulp = require('gulp'),
replace = require('gulp-replace'),

files = {
    input: 'input/*.html',
    output: 'output/'
},

flexes = [
    [
        "Old 1",
        "New 1A",
        "New 1B"
    ],[
        "Old 2",
        "New 2A",
        "New 2B"
    ]
]

My function

For all the input files, function should replace for all Old with:

  • New A if the passed mode argument is 1 or
  • New B if the passed mode argument is 2

and save to output folder.

function repla(mode) {
    return gulp
        .src(files.test_htm)
        .pipe(
            flexes.forEach(
                element => replace(
                    element[0], 
                    element[mode]
                )
            )
        )
        .pipe(gulp.dest(files.test))
}

exports.prev = repla(1)
exports.prod = repla(2)

exports.default = repla(1)  

Error

My solution produces an error

TypeError: Cannot read property 'on' of undefined
    at DestroyableTransform.Readable.pipe (/node_modules/readable-stream/lib/_stream_readable.js:564:8)
    at repla (/gulpfile.js:39:10)
    at default (/node_modules/undertaker/lib/set-task.js:13:15)
    at bound (domain.js:426:14)
    at runBound (domain.js:439:12)
    at asyncRunner (/node_modules/async-done/index.js:55:18)
    at processTicksAndRejections (internal/process/task_queues.js:79:11)

How should I fix it?

2

There are 2 best solutions below

7
user120242 On

.pipe expects a stream. .forEach returns undefined, which is being passed to pipe as a parameter, which is why you are getting that error.
The way pipe works, is that you pass streams to .pipe for it to chain input and output through. What you can do in this case is keep a reference to the gulp chain, and chain pipes on it with a loop:

function repla(mode) {
    var g = gulp
        .src(files.test_htm)

        flexes.forEach(
          element => g = g.pipe(replace(
            element[0], 
            element[mode]
          ))
        )

    return g.pipe(gulp.dest(files.test))
}

If you have a lot to replace and see performance problems, you can consider creating a concatenated regex using |. You probably won't need it.

0
Mark On

Here is an approach that doesn't require a for loop of any kind. It uses gulp-replace's ability to take a function:

  // this form of your replaces is much easier to work with
let flexes = {
  "Old 1": ["New 1A", "New 1B"],
  "Old 2": ["New 2A", "New 2B"]
};
 
function replaceStrings(mode) {

  return gulp.src(files.input)
    
    .pipe(replace(/Old 1|Old 2/g, function (match) {

      // console.log(flexes[match][mode]);    // e.g., "New 1A" 
      return flexes[match][mode];
    }))
    .pipe(gulp.dest('build/'));
};

exports.prev = gulp.series(() => replaceStrings(0));
exports.prod = gulp.series(() => replaceStrings(1));

This exports.prev = gulp.series(() => replaceStrings(0)); is an easy way to pass an argument to a task function.

Call: gulp prev or gulp prod

This approach does require that you be able to craft a regex that matches all your strings to be replaced, as in /Old 1|Old 2/g. That may not be possible for you, but it can simply be /string1|string2|string3/g or more complicated. Obviously if you have a lot of those a for..in loop might be necessary (but I wouldn't use a forEach loop - search SO for problems with using forEach and gulp together).

The modified flexes data structure, allows for a really simple replace function:

return flexes[match][mode];



I personally think a better way to do this is with args in your command line.

const yargs = require('yargs');
const argv = yargs.argv;

function replaceStrings() {

  return gulp.src(files.input)
    
    .pipe(replace(/Old 1|Old 2/g, function (match) {

     //  console.log(argv.mode);  // 0 or 1
      return flexes[match][argv.mode];
    }))
    .pipe(gulp.dest('build/'));
};

exports.replace = gulp.series(replaceStrings);

Call: gulp replace --mode=0 or gulp replace --mode=1