30 сент. 2014 г.

Run grunt task with custom (or dynamically generated) configuration

Today I faced a problem about how to run grunt task with some dynamically generated params.

Usually grunt tasks are configured in the following way:

grunt.initConfig({
     requirejs: {
            'optimize': {
                options: {
                    /* put your options here */
                }
            }
        }
})

But what if your config have dynamic nature?
for example in my case my config looks like:

grunt.initConfig({
     requirejs: {
            'optimize': {
                options: grunt.file.readJSON("/path/to/build.json")
            }
        }
})

the problem is that file "/path/to/build.json" only appears there after calling another grunt task,
lets call it grunt init,
but whole grunt configuration is loaded when you run any grunt task so I get into a vicious circle:

i cant run init task because build.json isn't yet in place,
i cant it put in place until I run grunt init.

solution is to use custom grunt tasks plus use grunt.config API:

var merge = require('merge');
....

grunt.registerTask('requirejs-custom-opts', 'Run requirejs task with custom options', function () {
    var dynamicConfig = {
        requirejs: {
            'optimize': {
                options: {
                       /* Your options go here */
                }
            }
        }
    }

    grunt.config.init(merge(true, grunt.config.get(), dynamicConfig));

    grunt.task.run(['requirejs']);
});

Note how grunt.config API was used - I merged whole grunt config with my own changes,
Please also note that in this case I can not use templates API because i need options object, not a string property.

now you can use your custom task by calling

grunt requirejs-custom-opts

At a first glance it looks like a hack. Probably it is.
But I was not able to found more correct way to do this.