gruntjs/grunt

Allow for optional merging of Object type option props

Opened this issue · 6 comments

Augment this.options() method with one more argument to allow for properties of type Object to merge instead of overwrite.

case:

  multiTask: {
    options: {
      anObject: {
        a: 1
      }
    },
    target: {
      options: {
        anObject: {
          b: 2
        }
      }
    }
  }

// in the task
grunt.registerMultiTask('multiTask', function() {
  var options = this.options({}, true);

  // desired result
  // options.anObject === {a:1, b:2}

});

I think you mean:

options.anObject // {a:1, b:2}

right, corrected

+1 This would make my life easier. I have a node module that provides default grunt options for other projects, and it would be nicer if it was possible to merge instead of overwrite options.

The main issue I see with this is the end user may not know whether a task's options will be merged or overwritten. I think it will cause confusion when their options get merged with some tasks but not others.

IMO a better way would be to let the user handle this to keep it transparent. The user could easily make a helper module for this, if they wanted:

var mergeOpts = (function() {
  var allopts = Object.create(null);
  return function(ns, opts) {
    allopts[ns] = grunt.util._.merge({}, allopts[ns], opts);
    return allopts[ns];
  }
}());
grunt.initConfig({
  task: {
    options: mergeOpts('task', {
      anObject: { a: 1 }
    }),
    target: {
      options: mergeOpts('task', {
        anObject: { b: 2 }
      }),
    }
  }
});

IMO a better way would be to let the user handle this to keep it transparent.

Yes, that makes better sense.

An effort has to be made to better elaborate that what grunt.initConfig() essentially needs is just a plain Object literal. Although that sounds self-evident from this standpoint, it's not quite how a new-comer perceives it...

@shama @thanpolas I think a better way would be to merge the options inside your task. If you're a plugin author, then I you can provide an option to do this, or just do it based on your situation, and provide documentation on how it works and what's to be expected.

We do this for options that are arrays of strings:

module.exports = function(grunt) {

  var customTaskName = 'customTask';

  grunt.registerMultiTask(customTaskName, 'This is a custom task.', function() {

    var arrayName = 'customArrayName';

    var taskOpts = grunt.config([customTaskName, 'options', arrayName]) || [];
    var targetOpts = grunt.config([customTaskName, this.target, 'options', arrayName]) || [];

    var mergedOpts = grunt.util._.union(taskOpts, targetOpts);
  });
};

You should be able to do this with your entire options object too...

module.exports = function(grunt) {

  var customTaskName = 'customTask';

  grunt.registerMultiTask(customTaskName, 'This is a custom task.', function() {

    var taskOpts = grunt.config([customTaskName, 'options']) || {};
    var targetOpts = grunt.config([customTaskName, this.target, 'options']) || {};

    var mergedOpts = grunt.util._.merge({}, taskOpts, targetOpts);
  });
};

I hope this helps someone.