Search code examples
javascriptruby-on-railsgulp

Gulp-rev-all is creating new files on each run


I've got a problem with gulp-rev-all. I have the following basic gulpfile:

var gulp = require('gulp'),
    RevAll = require('gulp-rev-all'),
    autoPrefixer = require('gulp-autoprefixer'),
    rename = require('gulp-rename'),
    sass = require('gulp-sass'),
    sourceMaps = require('gulp-sourcemaps');

gulp.task('sass', function () {
  var stream = gulp.src('./app/assets/stylesheets/application.scss')
    .pipe(sourceMaps.init())
    .pipe(sass({errLogToConsole: true}))
    .pipe(autoPrefixer())
    .pipe(rename("application.css"))
    .pipe(sourceMaps.write("."))
    .pipe(gulp.dest("./public/assets/stylesheets/"))
});

gulp.task('production', function() {
  gulp.src('./public/*assets/**')
    .pipe(RevAll.revision())
    .pipe(gulp.dest('./public'))
    .pipe(RevAll.versionFile())
    .pipe(gulp.dest('./app/assets'));
});

gulp.task('default', ['sass', 'production']);

Expectation vs. reality

When I run gulp, it bombs out with the following error:

Error: revision() must be called first!

But when I remove the production task and run the following then the sass task finishes as usual:

gulp.task('default', ['sass']);

After this task has created the files, changing the default task back to include the production task will successfully finish and create a fingerprinted CSS file.

When I run gulp again it does something strange:

  1. First gulp task (without proudction task) creates:

    application.css (this task is idempotent)

  2. Then I run the gulp task with production for the first time. The folder now looks like:

    application.css

    application.4434fcd5.css

  3. Then I run it again and now the folder looks like:

    application.css

    application.4434fcd5.css

    application.4434fcd5.4434fcd5.css

The problem

I think both problems stem from something in the production task creating some sort of look up issue. What is it?


Solution

  • (1) Your first problem is this line:

    gulp.task('default', ['sass', 'production']);
    

    This doesn't do what you think it does. It doesn't run the sass task and then the production task. It runs both at the same time.

    That means that your production task will try to revision files in your ./public/ folder before your sass task has even created any files there. Since there are no files you end up with the revision() must be called first! error message.

    You need to tell gulp that your production task depends on your sass task:

    gulp.task('production', ['sass'], function() {
    

    And then you need to return the stream from your sass task so that gulp knows when your sass task has finished:

    gulp.task('sass', function () {
      var stream = gulp.src('./app/assets/stylesheets/application.scss')
      //...
      return stream;
    });
    

    (2) Your second problem is this line:

    gulp.src('./public/*assets/**')
    

    This doesn't just match application.css, but also application.4434fcd5.css. So your already-revisioned files are revisioned again.

    You can solve this in different ways, but the easiest is probably to just delete your entire ./public folder every time you run your sass task (using del).

    var del = require('del');
    
    gulp.task('clean', function() { 
      return del('./public');
    });
    
    gulp.task('sass', ['clean'], function() {
      //...
    });
    

    This has the added benefit that you don't end up with multiple revisioned files in your ./public/ folder when one of your files changes (which results in a different hash and therefore a different file name).