Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use grunt-regarde with grunt-contrib-coffee to only compile changed .coffee files?

My project has over 300 CoffeeScript files, so it takes several seconds to recompile everything. I'd like to only recompile the changed CoffeeScript files.

Here's the closest I've come so far, but the "frontend-src/coffeescript" folder structure is being copied from the src directory to the dest directory.

coffee: {
  changed: {
    expand: true,
    cwd: './',
    src: ['<%= grunt.regarde.changed %>'],
    dest: 'public/js/',
    ext: '.js'
  }
},
regarde: {
  coffee: {
    files: 'frontend-src/coffeescript/**/*.coffee',
    tasks: ['coffee:changed', 'livereload']
  }
}

This is all with Grunt 0.4.0. Any help would be greatly appreciated!

like image 603
Eric the Red Avatar asked Feb 25 '13 21:02

Eric the Red


2 Answers

I had this issue myself and I was able to come up with a solution for it inspired by the comments on this issue: https://github.com/gruntjs/grunt-contrib-watch/issues/14

It is actually for the grunt-contrib-watch plugin, but it should also work for grunt-regarde, as it has similar events.

The idea is to bind a callback the watch event, in which you add a new task to the grunt configuration with the changed file's path, then run it.

From my Gruntfile.coffee:

coffee:
    app:
        expand: true
        cwd: 'app/'
        src: ['*.coffee',"**/*.coffee"]
        dest: './public/temp'
        ext: '.js'
watch: 
    coffee:
        files: ['app/**/*.coffee']
        tasks: ['livereload']
        options:
            nospawn: true

grunt.event.on 'watch', (action, filepath) ->       
    cwd = 'app/'
    filepath = filepath.replace(cwd,'')
    grunt.config.set('coffee',
        changed:
            expand: true
            cwd: cwd
            src: filepath
            dest: './public/temp'
            ext: '.js'
    )
    grunt.task.run('coffee:changed')

The nospawn is important for the watch task, so it runs the new task before the livereload task. I am pretty sure regarde does not spawn child processes by default.

like image 120
Cristian Necula Avatar answered Oct 24 '22 20:10

Cristian Necula


I've had the same issue. I solved it using the regarde:file event.

First I listen for changed files by using the regarde:file event. This will feed the configuration for two tasks: clean:coffee if files in the source location has been deleted and coffee:refresh if files have been changed/added.

Then the regarde task will trigger its tasks, which will launch refresh:coffee (not to be mistaken from coffee:refresh). This task will check if there is configuration added for clean:coffee and/or for coffee:refresh and run these tasks if needed (via function grunt.task.run). If will also reset the flag, which will cause the next received regarde:file event to cleanup the configuration again.

In depth explanation:

First of all, regarde config:

 // watch for changed coffeescript files
 coffee: {
    files: 'path/to/coffee/**/*.coffee',
    tasks: ['refresh:coffee', 'livereload']
 },

Then I listen for the regarde:file event, where I update the clean:coffee and coffee:refresh file lists in their config.

Feed the configuration based on the regarde:file event:

grunt.event.on('regarde:file', function (status, target, filepath) {
   if (resetFlag) {
      // clean file list from previous cycle, so clean clean:coffee and coffee:refresh
      // file lists
      ...

      resetFlag = false;
   } 
   if (status === 'deleted') {
        if (filepath) {
            // calculate filepath's destination and  
            // add it to clean:coffee filelist
        }
    } else {
        if (!grunt.file.isDir(filepath)) {
            // add filepath to coffee:refresh filelist
        }
    }
}

It is easy to update configuration via grunt.config() function. Below the code snippets to feed coffee:refresh and clean:coffee.

Adding config to coffee:refresh:

var config = grunt.config('coffee') || {};
var value = config.refresh || {};
value.files = value.files || [];
...
var cwd = path.dirname(filepath),
    src = path.basename(filepath),
    dest = cwd.replace('path/to/source', 'path/to/dest');
    value.files.push({
       expand:true,
       src:src,
       dest:dest,
       cwd:cwd,
       ext:'.js'
    });
grunt.config('coffee', config);

Adding config to clean:coffee:

    var cwd = path.dirname(filepath),
        src = path.basename(filepath),
        dest = cwd.replace('path/to/source', 'path/to/dest');
        value.src.push(path.join(dest, src.replace('coffee', 'js')));
    // clean only what has been removed
        config = grunt.config('clean') || {};

    config.coffee = value;

    grunt.config('clean', config);

Task refresh:coffee gets triggered:

    grunt.registerMultiTask('refresh', 'refreshing the changed file(s)', function () {
        this.requires('regarde');

        var tasks = [];
        var clean = grunt.config('clean');

        // check if there is clean:refresh config available
        if (clean && clean[this.target]) {
            tasks.push('clean:' + this.target);
        }
        var config = grunt.config(this.target);

        // check if there is coffee:refresh config available
        if (config && config.refresh) {
            tasks.push(this.target + ':refresh');
        }
        // run the tasks
        grunt.task.run(tasks);

        // set the resetFlag back to true
        resetFlag = true;
    });
like image 20
asgoth Avatar answered Oct 24 '22 22:10

asgoth