tschaub / grunt-newer

Configure Grunt tasks to run with newer files only.
https://npmjs.org/package/grunt-newer
MIT License
945 stars 61 forks source link

Renaming a file causes newer to update all files next time around #68

Closed veganben closed 9 years ago

veganben commented 9 years ago

I think this is easier to explain with some code

My file system:

 |– Gruntfile.js
 |_ devfiles
 |    |_ components
 |         |_ component1
 |         |     |_ style.css
 |         |     |_ index.html
 |         |_ component2   
 |               |_ style.css
 |               |_ index.html 
 |_buildfiles

and the relevant bit of my Gruntfile

    copy: {
        componentDev: {
            files: [
                {
                    cwd: "devfiles/components/",
                    expand: true,
                    src: ['*/**/*.css'],
                    filter: 'isFile',
                    rename: function(a,b){var q = b.split("/"); return "buildfiles/" + q[q.length-1]}
                }
           ]
       }
   },
    watch: {
        componentDev: {
            files: 'devfiles/components/**',
            tasks: ['newer:copy:componentDev'],
        }
   }

grunt.registerTask('devtest', ['watch:componentDev']);

So, the idea is to watch the dev files directory, pull out whichever "style.css" changed, and copy it to the buildfiles directory. (the watch "files" is a bit vague because there is other stuff going on as well) First time that it "watches", all the styles.css files get copies, which is fine, they just overwrite each other and the one from the alphabetically-last component wins. This is ok.

The developer then starts with their work, and next time they change a style.css in any component, this one should overwrite the styles.css in buildfiles. (as it is the only one with a modification date larger than the last run date) This is what I understand by "Run Grunt tasks with only those source files modified since the last successful run."

The modification time should be compared to the last run time. However, renaming the watched file when copying it (or the deleting of old copied and watched files) screws everything up and on the next run, all the matching files are copied again, and I once again end up with the last-alpha-component.

This must mean that there is some comparison between the modification date of the source file and the creation date of the copied file. This has to be a bug. Or the description of grunt-newer is wrong.

veganben commented 9 years ago

Or, third option, I am doing something wrong :)

tschaub commented 9 years ago

The problem is that Grunt resolves your src:dest mapping as a many:1 mapping (as it should). It sees that there are many src files (your devfiles/**/style.css) that you are using to generate a single dest file (buildfiles/style.css). When the newer task sees this many:1 mapping, it uses the following logic: if any source file is newer than the destination file, run the task with all source files. The reason for this logic is because typically when people are doing a many:1 mapping, they need all input files - a concatenation type task for example.

It used to be that this plugin provided two tasks: newer and any-newer. The newer task only included source files that were newer than the destination file. The any-newer task provided all source files if any one was newer than the destination file. The any-newer task is now gone (as it was a source of endless confusion), but you may want to try installing the 0.5.4 release and see if the newer task does what you want.

I'm not sure I fully understand why you are copying many source files to one destination file (without necessarily knowing which wins), but I think you could use flatten instead of your rename function.

module.exports = function(grunt) {
  grunt.initConfig({
    copy: {
      componentDev: {
        files: [{
          expand: true,
          cwd: 'devfiles/components',
          src: '**/*.css',
          dest: 'buildfiles',
          flatten: true
        }]
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.registerTask('default', ['copy']);
};

Sorry I don't have a better solution.

veganben commented 9 years ago

Thank you for your reply. I appreciate it, and I certainly appreciate the work of open-source contributors like yourself. I understand the mapping question, and I understand why this is the prevalent use-case, and I understand that my needs are probably a little esoteric. I don't want to pollute this discussion with my particular use-case, so I won't go into it here (though I would be happy to discuss it with anyone who is interested in another forum). I think there isn't a "better" solution other than for me to lower my "magic" threshold. I am trying to force grunt to do things that actually break all the sensible things that it is trying to do.

Or, write my own grunt plugin. Which I don't much fancy, but, needs must :)