load-grunt-configs

Loads grunt task configurations from separate files

load-grunt-configs

Loads grunt task configurations from separate files.

Grunt files tend to grow fast due to big amount of tasks and their configuration objects. This module allows you to split your Grunt task configuration objects into separate files any way you choose. There are similar modules that allow you to the same, but with grunt-load-configs you can configure targets for a single task in multiple files.

This means you no longer need to group all task targets into a single file, but can split them up according to their task dependencies.

you use the watch task to recompile your .scss files, but also to lint your source .js whenever one has changed. Typically you'd add a single watch configuration object to configure this, but with load-grunt-configs you can split these into several files and group all task targets together whenever it makes sense:

 
//config/css.js 
 
module.exports.tasks={
    watch:{
        scss: {
            files: ['app/sass/*.{scss,sass}'],
            tasks: ['compass:source']
        }
    },
    compass:{
        source:{
            //configuration settings 
        }
    }
};
 
//config/lint.js 
 
module.exports.tasks = {
    watch : {
        lint: {
            files: ['app/{,*/}*.js']
            tasks: ['jshint:source']
        }
    },
    jshint: {
        source: {
            //configuration settings 
        }
    }
}

load-grunt-configs supports the loading of grunt config files in following formats:

  • json
  • js modules
  • coffee modules
  • cson
  • yaml

Though the provided examples are mainly for json files and js modules, the same applies to the other formats. You can find examples of all formats in the config directory of this project.

I wrote a small utility Grunt task which takes your full-blown Grunt configuration and automatically splits it into separate files: grunt-generate-configs (supports all formats load-grunt-configs does too)

You only need to do this once:

$ npm install grunt-generate-configs -g
# cd to your project directory containing the Gruntfile 
$ generate_configs

This will create a separate .json file for each task inside a config directory. (See grunt-generate-configs for all options: a different format, directory, etc.)

Next you need to delete the full configuration object in your Gruntfile.js.

Then ...

Install the load-grunt-configs module with: npm install load-grunt-configs --save-dev

// Gruntfile.js 
module.exports = function (grunt) {
    grunt.loadNpmTasks('grunt-contrib-jshint');
 
    //loads the various task configuration files 
    var configs = require('load-grunt-configs')(grunt);
    grunt.initConfig(configs);
 
    grunt.registerTask('default', ['jshint']);
}

load-grunt-configs supports the loading of config files as .js, .json, .yaml or .coffee. You can even mix and match if you want. Take a look at the config folder of this project to see examples for all formats.

To configure the jshint task for example, add a file config/jshint.json (in case you didn't use the generator to automatically generate it):

{
    "gruntfile"       : {
        "src" : "Gruntfile.js"
    }
}

By default the basename (without the file extension) of the filename will be used to recognize which task is being configured. jshint in the above example.

Task configuration is also possible through node modules, either by exposing an object:

//config/jshint.js 
module.exports = {
     gruntfile       : {
         src : "Gruntfile.js"
     }
 }

Or by exposing a function which returns an object:

//config/jshint.js 
module.exports = function(gruntoptions){
     return {
         gruntfile       : {
             src : "Gruntfile.js"
         }
     }
 }

If the returned object contains a tasks key, its value will be assumed to be a name/configuration pair mapping:

//config/grunt.json 
{
    "tasks" : {
        "jshint" : {
            "gruntfile"       : {
                "src" : "Gruntfile.js"
            }
        },
        "watch" : {
            "gruntfile"       : {
                "src" : "Gruntfile.js",
                "tasks" : ['jshint:gruntfile']
            }
        }
    }
}

The above will configure both the jshint and watch tasks.

But wait, there is more! You can split multi-task configurations in multiple files as well. For instance if you have the above config/grunt.json file and you add the following file:

//config/test.json 
{
    "tasks": {
        "jshint" : {
            "test": {
                "src": ["test/**/*.js"]
            }
        }
        "watch" : {
            "test" : {
                "src" : ["test/**/*.js"],
                "tasks" : ['jshint:test']
            }
        }
    }
}

Now both watch and jshint tasks have two targets: gruntfile and test.

If you declare a function in your config file it receives two arguments: grunt and options, which allows you to use the grunt instance and pass values.

//Gruntfile.js 
var options = {
    paths: {
        jshintrc: '.jshintrc'
    }
};
 
var configs = require('load-grunt-configs')(grunt, options);
 
//config/jshint.js 
module.exports = function(gruntoptions){
     return {
         gruntfile       : {
             src : "Gruntfile.js"
         }
     };
}

As a convenience method you can prefix your task targets with the task name, separated by a ":" (colon). This allows you to do this:

//config/monitor.js 
module.exports.tasks = {
    "watch:test" : {
        src : ["test/**/*.js"],
        tasks : ['jshint:test']
    },
    "watch:gruntfile"       : {
        src : "Gruntfile.js",
        tasks : ['jshint:gruntfile']
    }
}
 

This makes it easier when in the future you'd like to move the watch:gruntfile task configuration to another file for instance.

The tradeoff of a neatly organized Grunt configuration is sometimes having trouble locating which file is declaring exactly what.

In that case, or if loading some file is causing an error and aborting your Grunt process, you can always run:

grunt --verbose

A nice log of processed files and loaded tasks/targets will help you locate the problems. Here's the output when running on this very repo:

Loading grunt configs via "load-grunt-configs" from 6 file(s).
Loading config/connect.js...is fn(), invoking...OK
+ connect: [options, docs]
 
Loading config/jshint.json...OK
+ jshint: [options, lib, build, test]
 
Loading config/watch.js...is fn(), invoking...OK
+ watch: [lib, build, test, docs, livereload]
 
Loading config/clean.coffee...is fn(), invoking...OK
+ clean: [config, tmp]
 
Loading config/nodeunit.yml...OK
+ nodeunit: [load_grunt_configs]
 
Loading config/markdown.cson...OK
+ markdown: [docs]
 

You can modify the directory in which the configuration files need to reside:

//Gruntfile.js 
var options = {
    config : {
        src: "options/*.js"
    }
};
 
configs = require('load-grunt-configs')(grunt, options);

Will search for the configuration files in an options directory.

You can take a look at the Gruntfile.js and the configuration files in the config directory of this project. Or browse through the 3 demos in this repository:

  1. Configuration per task. Each task configuration goes into its own file: watch.js, jshint.js, concurrent.js, ...
  2. A single configs file. The entirety of the grunt configuration is moved to a seperate file (grunt.js). This way the Gruntfile.js only contains task declarations.
  3. Configuration by task type. Task target configurationsare spread over multiple files and grouped wherever logically it makes sense. E.g. build.js, serve.js, test.js
  4. Various formats: See the config directory of this project.
  • json with a single task configuration: E.g. configures jshint task in config/jshint.json (task name extracted from file name)
{
    "gruntfile"       : {
        "src" : "Gruntfile.js"
    }
}
  • json with multiple task configurations: E.g. configures jshint and watch tasks in config/<whatever makes sense to you>.json. Note the top-most key "tasks" here, it alerts the module not to extract the task name from the file name.
{
    "tasks" : {
        "jshint" : {
            "gruntfile"       : {
                "src" : "Gruntfile.js"
            }
        },
        "watch" : {
            "gruntfile"       : {
                "src" : "Gruntfile.js",
                "tasks" : ['jshint:gruntfile']
            }
        }
    }
}
  • json with multiple task configurations using task prefixed targets: E.g. configures jshint and watch tasks in config/<whatever makes sense to you>.json
{
    "tasks" : {
        "jshint:gruntfile"       : {
            "src" : "Gruntfile.js"
        },
        "watch:gruntfile"       : {
            "src" : "Gruntfile.js",
            "tasks" : ['jshint:gruntfile']
        }
    }
}
  • js with single task configuration as an object: E.g. configures jshint task in config/jshint.js
module.exports = {
     gruntfile       : {
         src : "Gruntfile.js"
     }
};
  • js with single task configuration as a function: E.g. configures jshint task in config/jshint.js
module.exports = function(gruntoptions){
    return {
        gruntfile       : {
            src : "Gruntfile.js"
        }
    };
};
  • js with multiple task configurations as an object: E.g. configures jshint and watch task in config/<whatever makes sense to you>.js
module.exports.tasks = { //note the `tasks` export here [!] 
    jshint: {
        gruntfile       : {
            src : "Gruntfile.js"
        }
    },
    watch : {
        gruntfile       : {
            src : "Gruntfile.js",
            tasks : ['jshint:gruntfile']
        }
    }
 
};
  • js with multiple task configurations as a function: E.g. configures jshint and watch task in config/<whatever makes sense to you>.js
module.exports = function(gruntoptions) {
    return {
        tasks:{
            jshint: {
                gruntfile       : {
                    src : "Gruntfile.js"
                }
            },
            watch : {
                gruntfile       : {
                    src : "Gruntfile.js",
                    tasks : ['jshint:gruntfile']
                }
            }
        }
    };
};
  • js with multiple task configurations as an object using task prefixed targets: E.g. configures jshint and watch task in config/<whatever makes sense to you>.js
module.exports.tasks = { //note the `tasks` export here [!] 
    "jshint:gruntfile"       : {
        src : "Gruntfile.js"
    },
    "watch:gruntfile"       : {
            src : "Gruntfile.js",
            tasks : ['jshint:gruntfile']
    }
};
  • js with multiple task configurations as a function using task prefixed targets: E.g. configures jshint and watch task in config/<whatever makes sense to you>.js
module.exports = function(gruntoptions) {
    return {
        tasks:{
            "jshint:gruntfile"       : {
                src : "Gruntfile.js"
            },
            "watch:gruntfile"       : {
                src : "Gruntfile.js",
                tasks : ['jshint:gruntfile']
            }
        }
    };
};
  • v0.4.0: Switch to cson-safe for parsing cson
  • v0.3.3:
    • Add cson support
    • Loads yaml files with safeLoad
  • v0.3.2:
    • fixes incorrect grunt dep
    • adds coffee config file
    • adds yaml support
  • v0.3.1:
    • fixes incorrect overwrite of passedin options for this task
    • adds tests
    • Updates examples and adds all various configuration possibilities
  • v0.3.0:
    • disallows function declaration anywhere else than as direct export
    • extracts taskname regardless of file extension
  • v0.2.0:
    • adds coffeescript support
    • adds parsing of taskname prefixed task targets
    • fixes incorrect handling of functions at task config leaves
  • v0.1.0:
    • adds "by type' demo
    • adds "by taskname" demo
    • adds "single file" demo
    • Initial commit

Copyright (c) 2014 Camille Reynders Licensed under the MIT license.

With special thanks to @stefanpenner and @thomasboyt. This module is based on the ideas in Thomas' excellent tutorial: http://www.thomasboyt.com/2013/09/01/maintainable-grunt.html