tschaub / grunt-newer

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

Option to handle plugins such as grunt-spritesmith? #22

Closed sebastienbarre closed 10 years ago

sebastienbarre commented 10 years ago

Great plugin. Seems to work out of the box with all the plugins that comply with the 'files', 'src' syntax, but I've issues with "grunt-spritesmith":

Running "newer:sprite:employees1x" (newer) task
[...]
Running "sprite:employees1x" (sprite) task
Verifying property sprite.employees1x exists in config...OK
Files: build/tmp/images/employees@1X/dan.lipsa.jpg -> [no dest]
Fatal error: grunt.sprite requires a src, destImg, and destCSS property

I was wondering if this has to do with how source and destination files are specified:

sprite: {
  employees1x: {
    src: 'build/tmp/images/employees@1X/*.jpg',
    destImg: 'build/images/employees/employees@1X.jpg',
    destCSS: 'build/css/employees@1X.css'
  }
}

Is grunt-newer expecting something like the below syntax? Not certain, just a hunch, I might not have identified the problem correctly.

sprite: {
  employees1x: {
    options: {
      destImg: 'build/images/employees/employees@1X.jpg',
      destCSS: 'build/css/employees@1X.css'
    },
    files: [{
      expand: true,
      cwd: 'build/tmp/images/employees@1X/',
      src: ['*.jpg']
    }]
  }
}

Would you have a recommendation on how to handle that case? Thank you. (I also submitted this issue in the grunt-spritesmith tracker, for suggestions)

sebastienbarre commented 10 years ago

Update: seems "grunt-spritely" supports just that syntax.

tschaub commented 10 years ago

@sebastienbarre - looks like you've come up with an alternative, but you may still find that things don't work as expected. If your target task doesn't specify any dest files (in the src/dest compact format or in one of the files object / array formats) then the task will be configured to run with only those src files that are newer than the last successful run. When you're building a sprite, I think you'd want to rebuild the sprite with all images any time any one of them was modified (as opposed to only building it out of the modified images). Meaning it doesn't seem to me like using the newer task is appropriate with sprite building. But I might be missing the point.

tschaub commented 10 years ago

To clarify, the newer task works well with two types of tasks:

1) Tasks that are configured with src files only (no dest files). In this case, the time of the previous successful run is used to determine which src files should be included (only those newer than the last run).

2) Tasks that are configured with src / dest pairs. In this case, there is either a 1:1 relationship (one src file produces one dest file, e.g. converting CoffeScript to JavaScript) or a n:1 relationship (many src files produce one dest file, e.g. using UglifyJS to concatentate & minify many source files into one file). In this case the modification time of the dest file is compared to modification time(s) of the src file(s).

The grunt-spritely task is different in that there is a n:2 relationship. Many images are used to produce two output files. Because destCSS and destImg are configuration options unique to grunt-spritely, the newer task doesn't know anything about them.

The grunt-newer plugin used to include a task called any-newer that seems like it would have worked for sprite generation. It would use all src files if any one of them was newer than the last successful run of the same task. This task was removed because it seemed to be a source of confusion.

I hope this clarifies things a bit (instead of creating more confusion).

sebastienbarre commented 10 years ago

@tschaub thanks. Yes, while this does make sense, it seems in practice that it still behaves correctly (I just tested), i.e. the whole spritesheet is recreated with all images, including the new one. Strange indeed. BTW, 'spritely' syntax *does8 use 'dest':

spritely: {
  icons: {
    options: {
      destCSS: 'build/css/icons_spritesheet.css',
      algorithm: 'binary-tree'
    },
    files: [{
      src: ['images/icons/*.png'],
      dest: 'build/images/icons/icons_spritesheet.png'
    }]
  }
}

You are right about the n:2, but for all intent and purposes, it's an n:1, because each time the PNG spritesheet is created, so should the CSS file. So really, it's fine if the comparison is done against 'dest' but not 'destCSS'.

BTW I am curious to know if 'newer' could support re-running a task when a file has been added but its timestamp is older than the task's destination. I know it might sound strange but I use Forklift2 as a file manager and when I copy a file from one directory to another, its creation time is kept from the original source file -- it is most often older than the task's destination, unfortunately. Meaning, my tasks are not re-run. I guess that would probably require more than just a timestamp, probably a hash of the list of alphabetically sorted source files that were last used to generate the destination. This could handle another useful case for a task like creating a spritesheet: re-running it when a file is deleted (as an option, of course, since this case makes poor sense for things like minifying, uglyfying, etc). Thanks

tschaub commented 10 years ago

Thanks for the update @sebastienbarre. It looks like grunt-spritely is better suited for use with newer.

Regarding the case where "older" source files are added or some source files are deleted, this could be handled by the different task (see #5). I've made some initial enhancements to support this but need to finish it off. The different task stores MD5 hashes for all source files used by a task. The same task should handle cases where there are source files that didn't exist previously or source files that no longer exist.

sebastienbarre commented 10 years ago

Great. Looking forward revisiting this in the future. Performing MD5 hashes of all source files would work, though as an optimization pass, I suspect the MD5 of the concatenation of all file names would be a quick way to trigger the task when files are added or removed, thus avoiding to MD5 the contents of all source files one by one to detect when files have changed (but I'm sure you had that in mind). Thanks again for the quick reply.