Skip to content

Instantly share code, notes, and snippets.

@0gust1
Last active April 25, 2016 12:50
Show Gist options
  • Save 0gust1/7543330 to your computer and use it in GitHub Desktop.
Save 0gust1/7543330 to your computer and use it in GitHub Desktop.
Inventory of solutions to modularize grunt configuration for large project & (legacy) codebases

Grunt config modularization

Inventory of solutions to modularize grunt configuration for large project & (legacy) codebases

Existing solutions

Grunt-modules

https://github.com/oztu/grunt-modules

Grunt Modules allows you to split up your grunt file into modular pieces. It's useful for managing very large codebases (if you have to have one).

In Grunt you define pieces of your modules within each multitask. Grunt Modules inverts the configuration so that you can have your module define it's tasks instead, allowing you to put entire cross-sections of your configuration in their own files.

Grunt-horde

https://github.com/codeactual/grunt-horde

Packageable grunt configuration modules

  • Separate files define values for initConfig, loadNpmTask, etc.
  • Store modules in regular directories or leverage NPM, ex. npm install git://.
  • Compose configuration from multiple modules w/ recursive merging, cascading, etc.

Grunt-loader

https://github.com/endel/grunt-loader

A modular approach for grunt configuration. Configure each of your plugins using YAML files, and don't ever touch your Gruntfile.js.

load-grunt-config

https://github.com/firstandthird/load-grunt-config

Split the gruntfile into tasks.

  • Each task has its own config file. Example: jshint.js, mocha.js, etc.
  • Auto load all grunt plugins. Uses load-grunt-tasks.
  • Auto expose package.json (<%= package.name %>).
  • Support for YAML files.
  • Support for coffeescript files.
  • Support for returning a function.
  • Easily register task aliases with aliases.(js|yaml|coffee).

Example of file layout :

├── Gruntfile.js
├── grunt-tasks
│   ├── options <= config directory
│   │   ├── imagemin.js <= config file for the imagemin task
│   │   ├── notify.js 
│   │   └── watch.js 
│   ├── set_config.js <= custom task
│   └── set_global.js <= another custom task

Bibliography :

Don't forget that several gruntfiles can live in various levels of your file structure...

Clever tricks (and an entry point to discover Grunt's API) : https://oncletom.io/2013/dynamic-grunt-targets-using-templates/

from @oncletom : https://twitter.com/oncletom/status/402737171045838848 : " faire un module npm par task, et tu peux utiliser grunt.util._.merge + initConfig "

@0gust1
Copy link
Author

0gust1 commented Nov 26, 2013

/*global module:false*/

module.exports = function(grunt) {

  var env = process.env.NODE_ENV || 'dev';
  var _ = require('lodash');

  /*** External config paths ***/
  /* Typical project layout :

  ├── Gruntfile.js 
  ├── package.json
  ├── grunt-config
  │   ├── homepage
  │   │   └── config.js
  │   ├── module1
  │   │   └── config.js
  │   ├── module2
  │   │   ├── config.js
  │   │   └── tasks.js
  │   ├── default_config.js
  │   ├── default_tasks.js
  │   └── template_module_grunt.txt
  ├── upload
  │   ├── mdp
  │   ├── multimedia-storage
  │   ├── mv-commit.sh
  │   ├── query
  │   ├── temp
  │   └── tmp
  └── www
      ├── crossdomain.xml
      ├── css
      ├── cssV3
      ├── favicon.ico
      ├── flash
      ├── fonts
  */

  //we have 1 base config, and possibly many module-specific config
  var configLocations = ['./grunt-config/default_config.js', './grunt-config/**/config.js'];

  //we have 1 base tasks definition, and possibly many module-specific config
  var tasksLocations = ['./grunt-config/default_tasks.js', './grunt-config/**/tasks.js'];

  /***************** External configuration management ***********************************/

  var configFiles = grunt.file.expand({
    filter: "isFile"
  }, configLocations );

  grunt.log.writeln('Gathering external configuration files'.underline.green);
  grunt.log.writeln("configFiles : " + grunt.log.wordlist(configFiles, {
    separator: ', ',
    color: 'cyan'
  }));

  var configArray = configFiles.map(function(file) {
    grunt.log.writeln("*** importing " + file);
    return require(file)(grunt, env);
  });

  var config = {};

  configArray.forEach(function(element) {
    config = _.merge(config, element);
  });

  grunt.initConfig(config);

  /***************** Task loading & registering *******************************************/
  // We load grunt tasks listed in package.json file
  require('load-grunt-tasks')(grunt);

  /****** External tasks registering ****************/
  grunt.log.writeln('Gathering external task files'.underline.green);

  var taskFiles = grunt.file.expand({
    filter: "isFile"
  }, tasksLocations);

  grunt.log.writeln("task files : " + grunt.log.wordlist(taskFiles, {
    separator: ', ',
    color: 'cyan'
  }));

  taskFiles.forEach(function(path) {
    grunt.log.writeln("*** loading :" + path);
    grunt.task.loadTasks(path);
  });

  grunt.registerTask('default', ['attention:gruntfile', 'jshint:gruntfile', 'logConfig']);

  grunt.registerTask('checkGruntFile', 'Tâche par défaut - aide et vérification du gruntfile', function() {
    grunt.log.subhead('* Tâche par défaut - aide et vérification du gruntfile *');
    grunt.log.writeln('Exécutez "grunt -h" pour avoir plus d\'informations sur les tâches disponibles');
    grunt.log.writeln('...');
    grunt.log.subhead('Vérification du gruntfile...');
    grunt.task.run(['jshint:gruntfile']);
  });


  grunt.registerTask('logConfig', 'Ecrire la configuration générée', function() {
    //grunt.task.run(['attention:gruntfile']);
    grunt.log.subhead('* Configuration générée : *');
    grunt.log.writeln(JSON.stringify(config, undefined, 2));
  });

};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment