gruntjsgrunt-contrib-watchgrunt-contrib-compassgrunt-concurrent

How to combine multiple watch tasks for different compass tasks in Grunt


We are having some problems finetuning our Grunt setup. Our current project setup is like this. We have a Themes folder, in that themes folder there are different themes that all hold their own SCSS files and other bits related to that theme.

Our grunt file is setup like this with around 15 themes (leaving out default Grunt setup and JSHint because in the end Grunt is working):

compass: {
    options: {
        ...
    }
    theme1: {
        src: ['App/Themes/theme1/scss/**/*.scss'],
        tasks: ['compass'],
        options: {
            sassDir: 'App/Themes/theme1/scss',
            cssDir: 'App/Themes/theme1'
        }
    },
    theme2: {
        src: ['App/Themes/theme2/scss/**/*.scss'],
        tasks: ['compass'],
        options: {
            sassDir: 'App/Themes/theme2/scss',
            cssDir: 'App/Themes/theme2'
        }
    },

    ...
}

concurrent: {
    watch: {
        tasks: ['compass:theme1', 'compass:theme2', ..., 'compass:themeXX'],
        options: {
            logConcurrentOutput: true,
            spawn: false
        }
    }
}

grunt.loadNpmTasks('grunt-concurrent');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-contrib-watch');

grunt.registerTask('default', ['concurrent']);

The actual issue then is that when we start the default task also x watch threads are started. Which have a lot of overhead for the small watch task they have to do.

The solution I'm looking for is a way to setup a single watch task that can trigger the specific theme compass compile. Is there a way to do that? Or is the current setup the only way to do it? So no other option than to have x watch tasks?

Thanks.


Solution

  • First, scaffold a watch task in your config object that watches files but doesn't execute any tasks. Using the glob pattern, tell the watcher to spy on all .scss files within the themes directory:

    grunt.initConfig({
      compress: {}, //your existing compress object goes here
      watch: {
        themes: {
          files: ['App/Themes/**/*.scss'],
          tasks: []
        },
      },
    });
    

    Next, you're going to add a grunt.event listener to your gruntfile. The listener event will expose the file changed (example: App/Themes/theme1/scss/foobar.scss). With that, you can now determine which compress target (theme1) to run:

    grunt.event.on('watch', function(action, filepath, target) {
      if (target === 'themes') {
        var theme = filepath.split("/");
        grunt.task.run('compress.' + theme[2]); //tells grunt to run "compress.theme1" based on this example
      }
    });