My solution is a custom solution, specific to my app, but I hope it can be used for yours too! It's pretty simple.
Assuming you have a styles
folder containing several files and directories (less), then my solution was to define a white list of folders for which I would not compile all less files, but only those that have been changed.
Grunt-less config:
https://gist.github.com/Vadorequest/bd46bb4d6c326e837710
Grunt-watch config:
https://gist.github.com/Vadorequest/b48bcfda2d0205ba3f95
Folders architecture:

The "easiest" way to deal with this so far is to override the grunt.watch
event and so, to check whether or not the changed file may impact other files. There are several ways depending on your own architecture. Mine is simple enough because files that impact all others are either at the root of the styles
folder or inside sub folders. So I "just" had to check if the filePath
of the file belongs to the white list or not. If so, then I update the grunt config on the fly, changing the property src
to match only the changed file name.
grunt.event.on('watch', function(action, filePath, watchedTargetName) {
switch(watchedTargetName){
/*
Compile only what is needed.
Based on a white list of sub folders within the "styles" directory.
White listed folders will not require to compile all LESS file, but only the changed ones.
Others will require to compile everything.
*/
case 'styles':
// Root path from where the files are located.
var rootPath = 'assets/linker/styles/';
// Path of the file
var filePathRelativeToRootPath = path.relative(rootPath, filePath);
// Grunt task name (see less.js)
var gruntTask = 'less';
// Sub task to use.
var subTaskName = 'dev';
// List of folders that don't need to recompile everything.
var whiteListFolders = [
'common',
'devices',
'layous',
'themes',
'views',
];
if(action === 'changed'){
var isDir = path.dirname(filePath) !== '';
var dirName = filePathRelativeToRootPath.split(path.sep)[0];
// If the file is a directory and is belongs to the white list then we will override the grunt config on the fly to compile only that file.
if(isDir && _.contains(whiteListFolders, dirName)){
// We load the less config located at tasks/config/less.js
var config = grunt.config(gruntTask);
// Checks for any misconfiguration.
if(!config){
log.error('There is no config for the grunt task named ' + gruntTask);
}
if(!config[subTaskName]){
log.error('There is no sub task named ' + subTaskName + " for the the grunt task named " + gruntTask);
}
// Update the files.src to be the path to the modified file (relative to srcDir).
// Instead of updating all files, it will only update the one that has been changed.
config[subTaskName].files[0].src = filePathRelativeToRootPath;
grunt.config("less", config);
console.info('watcher LESS - The file ' + filePath + ' is in the white list and will be updated alone.');
}else{
console.info('watcher LESS - The file ' + filePath + ' is not is the white list, all LESS files will be updated.');
}
}
break;
}
} );
Basically, if I change a file named ayolan.less
within the themes
directory then here is what will be set in the memory when I'll update it. (See https://gist.github.com/Vadorequest/b48bcfda2d0205ba3f95#file-watch-js-L80-L81)
{
dev: {
files: [{
expand: true,
cwd: 'assets/linker/styles/',
src: 'themes/ayolan.less',// This has been changed in memory, for this specific watch event.
dest: '.tmp/public/linker/styles/',
ext: '.css'
}]
}
}
This allows me now to use http://www.browsersync.io/ that will update the browser about 1 second after I make a simple change in a LESS file, that was about 5 seconds before, because it had to compile all LESS files and copy them. (I made other performance changes as well, but it definitely helped to reach that goal!)