6

I have a project that uses GruntJS with grunt-contrib-sass, grunt-contrib-watch and grunt-newer.

My main app.scss file imports a few .scss files with the @import directive like

@import "common/vars.scss";

If I make changes in the main app.scss everything works. The problem is that when I make changes in a file that is being imported (like vars.scss), the app.scss doesn't get updated.

This is my watch task:

    watch: {
      css: {
        files: ['scss/**/*.scss'],
        tasks: ['newer:sass'],
        options: {
          spawn: false,
        }
      }
    }

And this is the sass task itself:

sass: {
      dist: {
        options: {
          style: 'compressed',
          noCache: true
        },
        /* looks for every .scss file in the scss folder (checks nested folders too), 
         * compile it and create another file wit the same name in the style folder */
        files: [{
          expand: true,
          cwd: 'scss',
          src: ['**/*.scss'],
          dest: 'style',
          rename: function (dest, src) {
            var folder    = src.substring(0, src.lastIndexOf('/'));
            var filename  = src.substring(src.lastIndexOf('/'), src.length);

            filename  = filename.substring(0, filename.lastIndexOf('.'));

            return dest + '/' + folder + filename + '.css';
          }
        }]
      }
    }

Any idea how to change the Gruntfile.js to cover this scenario too? :) Thanks.

isHristov
  • 1,546
  • 2
  • 16
  • 18
  • I'm not sure this will work, but I note that the 'files' member of watch and the files:src member of sass are both arrays. Maybe you could add a pattern to match for other files in there? – Owen C. Jones Jun 02 '14 at 14:27

2 Answers2

4

Note: This is a minimally modified copy of this comment in an issue on the grunt-newer GitHub page.


I came up with this simple delegation task that does the trick for tasks where you would like to react to changes other than specified in the src property:

grunt.initConfig( {
    delegate: {
        some_other_TASK: {
            src: [ 'some/src/path/**/*.ext' ],
            dest: 'some/dest/path/'
        }
    }
} );

grunt.registerTask( 'delegate', function() {
    grunt.task.run( this.args.join( ':' ) );
} );

A complete example for grunt-contrib-sass would look like the following:

module.exports = function( grunt ) {
    require( 'load-grunt-tasks' )( grunt );

    grunt.initConfig( {
        delegate: {
            sass: {
                src: [ 'scss/**/*.scss' ],
                dest: 'style'
            }
        },

        sass: {
          dist: {
            options: {
              style: 'compressed',
              noCache: true
            },
            files: [{
              expand: true,
              cwd: 'scss',
              src: ['*.scss'],
              dest: 'style',
              ext: '.css'
            }]
          }
        }
    } );

    grunt.registerTask( 'delegate', function() {
        grunt.task.run( this.args.join( ':' ) );
    } );
};

You just have to call newer:delegate:sass (via CLI, or some other task). grunt-newer checks the files given in the according delegate target (where I have the globbing pattern **/ included). If there are new files found (also .scss files in subfolders), the according sass task (with the given target) will be called.

I'm unsure, though, if making a separate grunt plugin would be of use here.


Edit: I recently released the above code (cleaned up) as separate npm package grunt-delegate.

tfrommen
  • 177
  • 3
  • 17
-2

I think your watch tasks variable is backwards, you can check by opening terminal, going to the directory you would normally run grunt watch but try running newer:sass if that doen't work, try sass:newer if the later works change that line in your watch settings.

Also, if you only have one sass task you can just change it to:

watch: {
      css: {
        files: ['scss/**/*.scss'],
        tasks: ['sass']
        options: {
          spawn: false
        }
      }
    }

Just as a side note, it seems that your sass has needlessly complicated rename function, if you're just trying to take scss/**/*.scss and turn them into style/**/*.css you should be able to use this (taken from the grunt-contrib-sass readme):

grunt.initConfig({
  sass: {
    dist: {
      files: [{
        expand: true,
        cwd: 'scss',
        src: ['**/*.scss'],
        dest: 'styles',
        ext: '.css'
      }]
    }
  }
});

if you have files that use multiple dots eg: file.src.scss add extDot: 'last' to the files array

Jason C
  • 50
  • 3
  • None of these work. If I run only "sass" and not "newer:sass" than all files are generated, but I explicitly want to use the newer plugin so it can generate only those files that needs to be generated. I'll probably force to generate the main .scss to .css file every time even when it is not needed. My files use multiple dots indeed and that's exactly the reason to use the rename function. I tried to remove it and added what you suggested (ext and extDot options) but it didn't help - file.src.scss just became file.css. – isHristov Jun 04 '14 at 05:22
  • does `grunt newer:sass:dist` work? (I've never used the newer plugin, but it might need you to specifically name the task) – Jason C Jun 05 '14 at 00:48
  • `newer:sass` would be correct. newer is just not meant to work like that. See the override option for newer. – Hafenkranich Apr 12 '16 at 16:36