Targeting specific file sets in grunt:watch

74 views Asked by At

We're using grunt-contrib-watch and grunt-shell with a Python based SCSS-compilation library (a requirement of the project) to compile SASS. To further complicate matters, we need to compile three separate CSS files. Directory structure looks like this, more or less:

global.scss
modules.scss
pages.scss
lib/
  bourbon/
  neat/
  bitters
global/
  global-styles.scss
modules/
  module-i.scss
  module-ii.scss
  module-iii.scss
pages/
  page-i.scss
  page-ii.scss
  page-iii.scss

I need to compile three outputs, global.css, modules.css, and pages.css. The source files in global, modules, and pages all make reference to the mixin libraries in lib.

How do I go about setting up grunt:watch tasks so that changes to, say, a file in the modules directory will only cause compilation specific to modules files?

I set up watch tasks like these:

watch: {
  pyscssglobal: {
    files: ['<%= src.scss %>/global.scss', '<%= src.scss %>/global/**/*.scss'],
    tasks: ['shell:pyscssglobal']
  },

  pyscssmodules: {
    files: ['<%= src.scss %>/modules.scss', '<%= src.scss %>/modules/**/*.scss' ],
    tasks: ['shell:pyscssmodules']
  },

  pyscsspages: {
    files: ['<%= src.scss %>/pages.scss', '<%= src.scss %>/pages/**/*.scss' ],
    tasks: ['shell:pyscsspages']
  }
}

... and shell tasks like these (again, this is a requirement of the project):

shell: {
  pyscssglobal: {
    command: '(python -mscss < scss/global.scss) > css/global.css'
  },
  pyscssmodules: {
    command: '(python -mscss < scss/modules.scss) > css/modules.css'
  },
  pyscsspages: {
    command: '(python -mscss < scss/pages.scss) > css/pages.css'
  }
}

But this means I have to have three separate watch tasks running. So I changed the watch so it looks like this:

all: {
  tasks: ['shell:pyscssglobal', 'shell:pyscssmodules', 'shell:pyscsspages'],
    files: ['scss/{,**/}*']
  }
}

... but this performs all three tasks every time I make a change to one file (expected).

How do I target specific tasks to run based on what files have changed? I can't figure this out. Thanks in advance for any help or guidelines you can offer.

1

There are 1 answers

1
srlm On

When the watch event fires you can run a function to examine the changed files and then determine which tasks to run:

grunt.event.on('watch', function (action, filepath) {
    var tasksToRun = [];

    if (filepath === 'fileA.scss') {
        tasksToRun.push('scss:fileA');
    }

    grunt.config('watch.main.tasks', tasksToRun);

});

And your configuration would look like:

watch: {
    main: {
        files: ['abc', 'def'],
                tasks: [] //all the tasks are run dynamically during the watch event handler
    }
}

And, to avoid making a bunch of different Sass targets you can use arguments to your tasks to specify which files to compile.

Source: I originally saw the watch technique used in generator-cg-angular's generated Gruntfile.