2

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.

Willy
  • 1,055
  • 8
  • 23

1 Answers1

0

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.

Community
  • 1
  • 1
srlm
  • 3,186
  • 2
  • 27
  • 40
  • From `grunt:watch`'s [documentation](https://github.com/gruntjs/grunt-contrib-watch): _The watch event is not intended for replacing the standard Grunt API for configuring and running tasks. If you're trying to run tasks from within the watch event you're more than likely doing it wrong._ – Willy Jun 25 '15 at 18:39