33

I want to accomplish something simple using gulp. I want to write a generic method to move files to an output directory from a particular source directory.

pretend we have something like so

var args = require('yargs');
function transform-move-jsx-Development() 
{
  gulp.src(config.sourceJSX)
  .pipe(react, browserify, etc....)
  .pipe(gulp.dest(config.output_development));
};

function transform-move-jsx-Production() 
{
  gulp.src(config.sourceJSX)
  .pipe(react, browserify, etc....)
  .pipe(gulp.dest(config.output_production));
};

gulp.task('transform-move-jsx-Development', transform-move-jsx-Development);
gulp.task('transform-move-jsx-Production', transform-move-jsx-Production);

gulp.task('prod', [transform-move-jsx-Production]);
gulp.task('dev', ['transform-move-jsx-Development']);

The two tasks: transform-move-jsx-Production and transform-move-jsx-Development are identical except for the output directory. I want to make it more DRY (Don't Repeat Yourself). I should be able to make a single method that can use a yarg parameter or something right? In this next example I pretend I can pass the path as an arg

So I try something like this using yargs

var args = require('yargs');

function transform-move-jsx() 
{ 
    return gulp.src(config.sourceJSX)
    .pipe(gulp.dest(args.outputDirectory));
};

gulp.task('dev', ['transform-move-jsx']);

However this now requires me to add arguments to the gulp call at the command line

gulp dev --"path to output, etc."

That is obviously less maintainable as we call more and more gulp tasks from inside of the dev gulp task. And would be messy anyways as we shouldn't need to know an implementation detail like what the output directory structure is when we run gulp dev

I could instead do something like this:

function transform-move-jsx(destination) 
{ 
    return gulp.src(config.sourceJSX)
    .pipe(gulp.dest(destination));
};

function transform-move-jsx-Development() 
{
    transform-move-jsx("./output/development/");
};

function transform-move-jsx-Production() 
{
    transform-move-jsx("./output/production/");
};

gulp.task('transform-move-jsx-Development',transform-move-jsx-Development);
gulp.task('transform-move-jsx-Production', transform-move-jsx-Production);

gulp.task('prod',  transform-move-jsx-Production);
gulp.task('dev',  transform-move-jsx-Development);

This seems better in that it is more flexible, however now my gulpfile is littered with several unnecessary functions.

Is there a better way ?

jth41
  • 3,808
  • 9
  • 59
  • 109
  • not familiar with gulp much but did you try calling `moveViews()` and `moveJs()` from `move()` itself ? – joyBlanks Sep 01 '15 at 01:48
  • I don't understand why you would do this through gulp instead of using `mv` or `cp`??? – Evan Davis Sep 01 '15 at 14:54
  • 5
    This deserves more attention, what a great question - have you found a workable solution for this since then? I'm just running into this. – Dan Kanze Apr 29 '16 at 13:35
  • @DanKanze I would be interested in your opinion too for my fresh answer below. – Wtower Jan 03 '17 at 22:17
  • Do you always know the paths for `dev` and `prod`? If so just create a variable `destination` at the topmost scope of your `gulpfile`, and set it from within the `task`. – Mudlabs Nov 05 '20 at 08:44

7 Answers7

29

You were on the right track with your second try, just needed to utilize a bit of DRY and closures

function createTransformTaskClosure (destination) {
    return function () {
        return gulp.src(config.sourceJSX)
                   .pipe(gulp.dest(destination));
    };
}

gulp.task('dev', createTransformTaskClosure(config.output_development));
gulp.task('prod', createTransformTaskClosure(config.output_production));
Dennis Baizulin
  • 1,410
  • 8
  • 11
  • 5
    Using **gulp 4**, I had to use this task signature `gulp.task('dev', () => { return createTransformTaskClosure(config.output_development) }`); – SliverNinja - MSFT Dec 10 '17 at 23:16
1

I did something similar using a single function:

function getDest(folder){
    var folder = folder || '';
    var newDest;
    if (args.build){
        newDest = config.build.home + folder;
    }
    if (args.temp){
        newDest = config.temp.home + folder;
    }
    return newDest;
}

Then, when I'd call the gulp.dest():

.pipe(gulp.dest(getDest())) // defaults to ./ directory

Or if I wanted a subfolder:

.pipe(gulp.dest(getDest(config.css)))

My config file simply had paths, i.e.:

config = {
   css: '/css/',
   home: './',
   js: '/js/'
}

Hope that helps.

MaxRocket
  • 920
  • 1
  • 12
  • 26
0

To begin with, for the record, the relevant github issue gulp#1225.

Gulp cannot handle parameters in tasks. In order to handle dev/production build, we can use yargs and gulp-if:

var gulp = require('gulp');
var argv = require('yargs').argv;
var gulpif = require('gulp-if');
var production = argv.production;

Then, for example, in a CSS task:

return gulp.src(paths.css)
  .pipe(gulpif(!production, sourcemaps.init()))
  .on('error', handleError('CSS'))
  .pipe(concat('style.min.css'))
  .pipe(gulpif(production, autoprefixer(config.autoprefixer_versions)))
  .pipe(gulpif(production, minifyCSS()))
  .pipe(sourcemaps.write())
  .pipe(gulp.dest(paths.build + 'css/'));

As you can see, the use of gulp-if handles the pipe properly.

But the above still needs to run gulp with gulp mytask --production, and is not suitable really if indeed the same task needs to run in one build multiple times. It is not suitable because it is not DRY at all.

For example, if you need to produce a number of CSS builds for different site parts, you would have to repeat the above task n times. But since gulp cannot handle parameters, we need to do something like:

var taskMethods = {
  css: function (paths) {
    return gulp.src(paths.css)
      .pipe(gulpif(!production, sourcemaps.init()))
      .on('error', handleError('CSS'))
      .pipe(concat('style.min.css'))
      .pipe(gulpif(production, autoprefixer(config.autoprefixer_versions)))
      .pipe(gulpif(production, minifyCSS()))
      .pipe(sourcemaps.write())
      .pipe(gulp.dest(paths.build + 'css/'));
  }
};

var pathsIndex = {
  css: [...],
  build: '...'
};

var tasksIndex = {
  css: function() { return taskMethods(pathsIndex); }
}

var pathsAdmin = {...};
var tasksAdmin = {...};

gulp.task('cssIndex', tasksIndex.css);
gulp.task('cssAdmin', tasksAdmin.css);

So essentially we separate the task method from the gulp task, with the cost of having to specify a 'wire-up' of the task method to a new function that uses a certain path config. But this has the benefit that whenever you need to make a change to a task method, it is in one place. Also you can easily see which part of site uses which methods.

This is also how I deal with this issue in my gulpfile project.

Wtower
  • 18,848
  • 11
  • 103
  • 80
0

How about something like the following:

var args = require('yargs');
function transform-move-jsx(destination) {
    return function(){
        return gulp.src(config.sourceJSX)
                   .pipe(react, browserify, etc....)
                   .pipe(gulp.dest(destination));
    }
};

gulp.task('transform-move-jsx-Development', transform-move-jsx(config.output_development));
gulp.task('transform-move-jsx-Production', transform-move-jsx(config.output_production));

gulp.task('prod', [transform-move-jsx-Production]);
gulp.task('dev', ['transform-move-jsx-Development']);  
ordepim
  • 806
  • 7
  • 9
0

A bit late to the party. I have a similar problem, couldn't find a simple solution, so I come up with this approach to use a combination of npm and gulp.

Say your gulpfile is:

var args = require('yargs');

function transform-move-jsx() 
{ 
    return gulp.src(config.sourceJSX)
    .pipe(gulp.dest(args.outputDirectory));
};

And your npm file:

{
  ...
  "scripts": {
    "dev" : "gulp transform-moove-jsx --./output/development/ && gulp one && gulp two",
    "prod" : "gulp transform-moove-jsx --./output/production/  && gulp one && gulp two",
  }
}

This approach requires you already using npm in your setting, is system dependent and runs less efficiently with possible side effects, because each npm task runs the gulp file multiple times. But I consider it cleaner, more logical and removes a bunch of limitations of gulp.

Xun Yang
  • 4,209
  • 8
  • 39
  • 68
0

Does this help?

const { src, dest, series } = require('gulp');

function moveJSX(dest = "./output/development/", label = "dev") {

  const source = config.sourceJSX;

  function task() { return src(source).pipe(dest(dest)); };

  task.displayName = `moveJSX:${label}`;
  
  return task;
}

// usage
series(moveJSX(), moveJSX("./output/production/", "prod"));

Feel free to remove the label thingy.

-1

Not sure if this is any use, but just my few pence on an idea:-

Have the following in a gulp file.

var args = require('yargs'),
    config = require('./config.js'),
    run = require('gulp-sequence');

gulp.task('transform-move-jsx-all', function() {
    return gulp.src(config.sourceJSX)
        .pipe(react, browserify, etc....)
        .pipe(gulp.dest(config.current.output));
};

gulp.task('prod', function(done) {
    config.current = config.prod;
    run( 'transform-move-jsx' /* and any additional sequential tasks here*/)(done);
});

gulp.task('dev', function(done) {
    config.current = config.dev;
    run( 'transform-move-jsx' /* and any additional sequential tasks here*/)(done);
});

This then allows your simple tasks in gulp such as gulp dev or gulp prod.

It expects a config.js file similar to below though:

var config = {
    current : {
        // Will set when running each task
    },
    /* Keep dev and prod adhering to the same structure/model so that they are interchangable */
    dev : {
        output : 'dev_output',
    },
    prod : {
        output : 'prod_output'
    },
    sourceJSX : [ 'file1.jsx' ],
    etc...
}

There are probably neater ways to do it with yargs, but if you just want a clean gulp + task then it's a direction I'd take.

Adam Martin
  • 61
  • 1
  • 5