Resolving LiveReload Conflicts in Grunt

Grunt is an excellent automation tool and part of the Yeoman toolset. It has this ridiculously cool feature that allows you to watch files for changes and then tell every browser looking at your webpages to refresh — mobile, tablet, desktop, whatever — and it does this without browser extensions. It’s pretty speccy.

There’s a problem though: if your HTML, CSS and JS is all generated from HAML, SASS and Coffeescript like me, it doesn’t work like you’d expect it to:


# Examples in Coffeescript, that's how I roll.

  grunt.initConfig {
    watch:
      livereload:
        files: [
          'src/example/**/*.{haml|coffee|sass}'
          'www/example/**/*.{html|js|css}'
        ]
        tasks: ['coffee', 'compass', 'hamlpy', 'livereload']
  }

Rather than compiling the Coffeescript, SASS and HAML, followed by a LiveReload, the watch event doesn’t pick up the changes to the HTML, CSS and Javascript so that reload never happens. Makes sense really, it’s not watching during the task run so it misses it.

You can’t run two watch tasks concurrently though, unless you open another terminal session. If you want to keep it all in one process, you can do this:


mm = require 'minimatch'

  grunt.initConfig {
    watch:
      livereload:
        files: [
          'src/example/**/*.{haml|coffee|sass}'
          'www/example/**/*.{html|js|css}'
        ]
        tasks: ['reloadDispatcher:example']
    reloadDispatcher:
      example:
        "**/*.haml" : ['hamlpy']
        "**/*.sass" : ['compass']
        "**/*.coffee" : ['coffee']
        "**/*.{html,css,js}" : ['livereload']
  }

  grunt.registerMultiTask 'reloadDispatcher', 'Run tasks based on extensions.', () ->
    for pattern, tasks of this.data
      if grunt.regarde.changed.some mm.filter pattern
        grunt.task.run tasks

With this new task, what it’s doing is checking the list of files changed that the watcher (grunt.regarde) detected against your patterns in the config, and then for any matches it runs the appropriate tasks. Dunno how it will behave in bigger projects but that’s enough experimentation for now.

Autoformatting Indented SASS

Personally I like to use SASS in it’s indented syntax. It’s easy to read, and because it’s sensitive to white-space you can change the structure very quickly using simple commands in Vim.

After watching a demonstration about Tabular I added a handy little function that automatically formats your properties as you write them, so they are lined up and easy to read like this:

span.day
  display:        block
  font-size:      ms(2)
  text-align:     right
  border-bottom:  1px solid $bg_color
  line-height:    1.25em
  margin-bottom:  0.25em

Here’s how you do it. Start off by installing the Tabular plugin with your script manager of choice, I use Vundle. Once you’ve done that, pop the following into .vim/ftplugin/sass.vim :

inoremap <silent>  :   :<Esc>:call <SID>align()<CR>a
function! s:align()
  let reg = '^\s*[a-z\-]*:\s*'
  if getline('.') =~# reg && (getline(line('.')-1) =~# reg || getline(line('.')+1) =~# reg)
    Tabularize /:\zs
    if getline(line('.')-1) =~# reg
      let endpos = strlen(matchstr(getline(line('.')-1), reg))
    else
      let endpos = strlen(matchstr(getline(line('.')+1), reg))
    endif
    normal! 0

    let currentlinelen = strlen(getline('.')) 
    if currentlinelen < endpos
      exe "normal A" . repeat(' ', endpos - currentlinelen) . "\"
    else
      call cursor(line('.'), endpos)
    endif
  endif
endfunction

All done, from now on when you’re in a SASS file Vim will automatically line up your properties when you type the colon, so the entire set stays neatly formatted as you go.