sassgruntjsgrunt-contrib-watchgrunt-contrib-sass

How do I get Grunt to recompile dependent SASS files?


I'm trying to work my way through the updates to Visual Studio 2015 including using Grunt and so on.

I can get Grunt to recompile .scss files when they change, but I have a problem. I use SASS for theming, and a lot of my CSS is in a central _main.scss. What I want is when I edit that file, it should recompile all the theme-*.scss files that include _main.scss.

Is there any way to tell watch or stuff like that to recompile things when dependencies change? Even if I have to specify the dependencies manually?


Solution

  • I don't know if there's a way to follow dependencies from one file to another, but you can watch for changes in .scss files and then run a sass task to update your theme files.

    So you'll have your sass task like this:

        sass : {
            build: {
                files : {
                    'path/to/compiled-foo.css': 'path/to/theme-foo.scss',
                    'path/to/compiled-bar.css': 'path/to/theme-bar.scss',
                    // Or more generally
                    'path/to': 'path/to/theme-*.scss',
                }
            }
        },
    

    And then your watch task like this:

        watch : {
            themes: {
                files : [ 'path/to/_main.scss' ],
                tasks : [ 'sass' ],
                options : {
                    // You may change these to better suit your needs
                    spawn : false,
                    interrupt: true,
                    livereload: true
                },
            },
        },
    

    The downside of this is that all your themes will compile each time you change your _main.scss. If you have different files to watch for different themes then you can have more tasks inside watch (instead of themes you can make theme_foo and theme_bar call different tasks (e.g.: sass:theme_foo or sass:theme_bar) and then recompile just that theme.

    You can also run grunt watch on a specific task: grunt watch theme_foo, which won't update theme_bar but just theme_foo.


    Edit: You can modularize your _main.scss so it becomes _foo.scss, _bar.scss and _common.scss, and then change _common.scss when it's a change that affects all themes, and _foo.scss when it just affects theme_foo. This way you can monitor _foo.scss and update only theme_foo when it changes; or update all your themes just when _common.scss changes.


    Edit 2 (based on comments):
    Lets say we have two themes, blue and red. We'll have two sass tasks (one for each theme):

        sass : {
            red: {
                files : {
                    'path/to/compiled-red.css': 'path/to/theme-red.scss',
                }
            },
            blue: {
                files : {
                    'path/to/compiled-blue.css': 'path/to/theme-blue.scss',
                }
            },
        },
    

    Right now, if you run grunt sass it will update both themes. But if you run grunt sass red it will update just the red theme.

    To make your watch update just the required theme, you'll have two tasks:

        watch : {
            red: {
                files : [ 'path/to/theme-red.scss' ],
                tasks : [ 'sass:red' ],
                options : { /* ... */ },
            },
    
            blue: {
                files : [ 'path/to/theme-blue.scss' ],
                tasks : [ 'sass:blue' ],
                options : { /* ... */ },
            },
        },
    

    Notice red calls sass:red (the task for that theme and only that theme). Same happens with blue calling sass:blue.

    To make it update every theme when _main.scss changes, you add one more task inside watch:

        watch : {
            red: {
                files : [ 'path/to/theme-red.scss' ],
                tasks : [ 'sass:red' ],
                options : { /* ... */ },
            },
    
            blue: {
                files : [ 'path/to/theme-blue.scss' ],
                tasks : [ 'sass:blue' ],
                options : { /* ... */ },
            },
    
            all: {
                files : [ 'path/to/_main.scss' ],
                tasks : [ 'sass' ],
                options : { /* ... */ },
            },
        },
    

    Now all is watching your _main.scss, and when it changes every task in sass will be run (i.e. sass:red and sass:blue).