How to run two grunt watch tasks simultaneously

GruntjsGrunt Contrib-Watch

Gruntjs Problem Overview


Is it possible to run two watch tasks simultaneously?

I understand that I can have any number of tasks I want inside watch settings and just launch grunt watch and it will watch all of them, like this

...
watch: {
    A: {
        files: "js/dev/**/*.coffee",
        tasks: ["coffee", "requirejs"]
    },
    B: {
        files: "js/dev/**/*.coffee",
        tasks: ["coffee"]
    },
    C: {
        files: "js/dev/**/*.html",
        tasks: ["copy"]
    }
}
...

...but I don't need this. I just want to have different set of tasks for development and production. As you can guess, the only difference between A (production) and B (development) is minification and concatenation. I don't need to launch A and B tasks at the same time.

First I came with this idea

grunt.registerTask("prod", ["watch:A", "watch:C"]);
grunt.registerTask("dev", ["watch:B", "watch:C"]);

But this didn't work. Just first watch tasks is working (C never works). Is that possible to do what I want?

Gruntjs Solutions


Solution 1 - Gruntjs

I've found using grunt-concurrent works:

concurrent: {
  options: {
    logConcurrentOutput: true
  },
  prod: {
    tasks: ["watch:A", "watch:C"]
  },
  dev: {
    tasks: ["watch:B", "watch:C"]
  }
}

Then:

grunt.registerTask("prod", ["concurrent:prod"]);
grunt.registerTask("dev", ["concurrent:dev"]);

Solution 2 - Gruntjs

EDIT: concurrent now has a logConcurrentOutput option! More info here: https://github.com/sindresorhus/grunt-concurrent#logconcurrentoutput.

Watch is a weirdly concurrent but blocking task, so you have to be creative to get multitask-like functionality working.

Concurrent loses all output from the watch tasks, which isn't ideal.

Try dynamically writing the config object in a custom task:

grunt.registerTask('watch:test', function() {
  // Configuration for watch:test tasks.
  var config = {
    options: {
      interrupt: true
    },
    unit: {
      files: [
        'test/unit/**/*.spec.coffee'
      ],
      tasks: ['karma:unit']
    },
    integration: {
      files: [
        'test/integration/**/*.rb',
        '.tmp/scripts/**/*.js'
      ],
      tasks: ['exec:rspec']
    }
  };

  grunt.config('watch', config);
  grunt.task.run('watch');
});

Solution 3 - Gruntjs

The best and only working solution is there : https://npmjs.org/package/grunt-focus Add this plugin and then :

focus: {
            sources: {
                include: ['js', 'html', 'css', 'grunt']
            },
            testu: {
                include: ['js', 'html', 'css', 'testu', 'grunt']
            },
            testi: {
                include: ['js', 'html', 'css', 'testu', 'testi', 'grunt']
            }
        },
        watch: {
            js: {
                files: paths.js,
                tasks: ['jshint'],
                options: {
                    livereload: true
                }
            },
            html: {
                files: paths.html,
                options: {
                    livereload: true
                }
            },
            css: {
                files: paths.css,
                tasks: ['csslint'],
                options: {
                    livereload: true
                }
            },
            testu: {
                files: ['test/**/*.js', 'test/**/*.css'],
                tasks: ['mochaTest'],
                options: {}
            },
            testi: {
                files: ['test/**/*.js', 'test/**/*.css'],
                tasks: ['exec:cleanTestDB', 'protractor_webdriver', 'protractor'],
                options: {}
            },
            grunt: {
                files: ['Gruntfile.js', 'server/config/env/*.js'],
                options: {
                    reload: true
                }
            }
        }

Then you use focus:sources or focus:testu as your convenience.

JM.

Solution 4 - Gruntjs

grunt-concurrent or grunt-focus are both good solutions, but both of them break livereload functionality.

My solution to this is to compose the watch configuration dynamically, with the assumption that you won't be running both configuration at the same time.

You can do something like this

grunt.config.merge({
  watch: {
    options: {
      livereload: true
    },
    C: {
      files: "js/dev/**/*.html",
      tasks: ["copy"]
    }
  }
});

grunt.registerTask('watch-forProd', function () {
  grunt.config.merge({
    watch: {
      A: {
        files: "js/dev/**/*.coffee",
        tasks: ["coffee", "requirejs"]
      }
    }
  });

  grunt.task.run('watch');
});

grunt.registerTask('watch-forDev', function () {
  grunt.config.merge({
    watch: {
      B: {
        files: "js/dev/**/*.coffee",
        tasks: ["coffee"]
      }
    }
  });

  grunt.task.run('watch');
});

grunt.registerTask("prod", ["watch-forProd"]);
grunt.registerTask("dev", ["watch-forDev"]);

Solution 5 - Gruntjs

SEPTEMBER 2018

You don't need to use grunt-concurrent anymore grunt now has this built in, here is a sample from one of my current projects...

module.exports = function(grunt) {
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		sass: {
			theme: {
				files: {
					'../../web/sites/all/themes/ifafri/css/generated/theme_ifafri_master.css' : '../../web/sites/all/themes/ifafri/css/master.scss'
				}
			},
			bootstrap: {
				files: {
					'../../web/sites/all/themes/ifafri/css/generated/theme_ifafri_bootstrap.css' : '../../web/sites/all/themes/ifafri/css/bootstrap/master.scss' 
				}
			}
		},
		watch: {
			theme: {
				files: '../../web/sites/all/themes/ifafri/css/*.scss',
				tasks: ['sass:theme'],
				options: {
					spawn: false,
					livereload: true,
					nospawn: false
				}
			},
			bootstrap: {
				files: '../../web/sites/all/themes/ifafri/css/bootstrap/*.scss',
				tasks: ['sass:bootstrap'],
				options: {
					spawn: false,
					livereload: true,
					nospawn: false
				}
			}
	}
	});
	grunt.loadNpmTasks('grunt-contrib-sass');
	grunt.loadNpmTasks('grunt-contrib-watch');
	grunt.loadNpmTasks('grunt-livereload');
	grunt.registerTask('default',['watch']);
}

Solution 6 - Gruntjs

Without worrying grunt.registerTask() in Gruntfile.js, I sometimes run grunt as background processes by typing the following in the command line:

$ grunt watch:A &
$ grunt watch:C &

You can make the commands as a batch script for more convenience. Hopefully it helps.

Solution 7 - Gruntjs

I know this not answer directly to the question, but my solution is now to use Gulp instead of Grunt. With Gulp you code and not only configure. So you are more free to do what you want.

JM.

Solution 8 - Gruntjs

Just change the port address and the livereload port. For eg. if the port is 9000 change it to 8000 and live reload from 35729 to 36729

Solution 9 - Gruntjs

Concurrent works fine for me

concurrent: {
			options: {
				logConcurrentOutput: true
			},
			set1: ['watch:html', 'watch:styles'],
		},

grunt.registerTask('default', 'watch');
grunt.registerTask('htmlcss', ['concurrent:set1']);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVitalii KorsakovView Question on Stackoverflow
Solution 1 - GruntjsNicolas HeryView Answer on Stackoverflow
Solution 2 - GruntjsRobWView Answer on Stackoverflow
Solution 3 - Gruntjsjmcollin92View Answer on Stackoverflow
Solution 4 - GruntjsAlex ShnayderView Answer on Stackoverflow
Solution 5 - GruntjsSimonView Answer on Stackoverflow
Solution 6 - Gruntjsuser3487528View Answer on Stackoverflow
Solution 7 - Gruntjsjmcollin92View Answer on Stackoverflow
Solution 8 - GruntjsYashView Answer on Stackoverflow
Solution 9 - GruntjsJobayer ArmanView Answer on Stackoverflow