15

grunt-usemin helps me to transform

<link href="/dependencies/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="/dependencies/nanoscroller/bin/css/nanoscroller.css" rel="stylesheet" />
<link href="/dependencies/dropzone/downloads/css/dropzone.css" rel="stylesheet" />

to a perfectly combined and minified js:

<link href="scripts/8e1991c7.libraries.js" rel="stylesheet" />

After concat, cssmin and uglify I have a almost perfect folder structure except for images and their locations.

Here is my problem:

All these vendor's css files are including image locations. The bad thing is that all of them are sharing different kind of locations. Some of them are using images inside css folder whereas others are using inside img folder.

How can I configure grunt usemin to rewrite all images urls?

thomaux
  • 19,133
  • 10
  • 76
  • 103
Cemo
  • 5,370
  • 10
  • 50
  • 82

6 Answers6

15

1) Rev images. Add paths of images into rev task.

rev: {
    dist: {
        files: {
            src: [
                '<%= yeoman.dist %>/static/scripts/{,*/}*.js',
                '<%= yeoman.dist %>/static/styles/{,*/}*.css',
                '<%= yeoman.dist %>/static/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
            ]
        }
    }
}

2) Add paths of file which are including image locations into usemin task.

usemin: {
    html: ['<%= yeoman.dist %>/{,*/}*.html'],
    css: ['<%= yeoman.dist %>/static/styles/{,*/}*.css'],
    options: {
        assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist%>/static/images'],
    }
}

3) Run grunt.

Frank Fang
  • 2,102
  • 3
  • 24
  • 47
  • Hi there, can you explain how assetsDirs works? Tried looking through documentation / examples, and it's still not wholly clear to me. – carlinyuen Mar 19 '14 at 21:08
  • Sorry for late response but how this configuration help to minify bower dependencies? – Cemo May 23 '14 at 13:38
  • 1
    @Cemo This is not aimed to "minify bower dependencies", but to rewrite the pathes of reversioned images in html/css files. – Frank Fang Jul 14 '14 at 05:53
  • 1
    I reviewed your question, I realize my answer does not solve your problem, at all. I fix such problem by copying all the images into a folder and replacing all the references :( – Frank Fang Jul 14 '14 at 05:56
10

I've solved the problem using the following.

useminPrepare: {
    html: 'src/index.html',
    options: {
        dest: 'build',
        flow: {
            html: {
                steps: {
                    js: ['concat', 'uglifyjs'],
                    css: ['cssmin']
                },
                post: {}
            }
        }
    }
},
cssmin: {
    options: {
        root: 'src'
    }
}

First, we are overriding useminPrepare's flow, removing the concat task from the css flows. This is needed because concat will destroy relative path information. Since cssmin will itself concat multiple files together, the sepearte concat task is only harmful. (https://github.com/yeoman/grunt-usemin/issues/225)

Lastly, we are telling cssmin where the "root" of your project is from the Gruntfile. This helps cssmin rewrite the relative urls it finds relative to this "root" directory.

jridgewell
  • 1,018
  • 1
  • 8
  • 12
  • This looks like a clean solution for css and images supplied in your project, but I don't think it solves the problem of vendor supplied css referring to vendor supplied images. – JBCP Feb 04 '14 at 23:27
  • Say you have a vendor supplied css/images in `src/vendor/blah`, with `blah/style.css` referencing "image.png". As long as `vendor/blah/image.png` is copied to the build directory, it will work perfectly. That's because the compiled css will be rewritten to reference "vendor/blah/image.png", and not just "image.png" as @Cemo is facing. – jridgewell Feb 05 '14 at 17:16
  • I have app/bower_components/select2/{select2.css|select2.png}. select2.css gets concatted by cssmin into app/{rev}.vendor.css. Using your solution I end up with two problems: First, cssmin assumes the png is still in the same directory as the css file, it doesn't realize the png is still in bower_components/select2. Second, regardless of what I put as the root, I end up with the absolute path for my locale system directory in the css file. – JBCP Feb 06 '14 at 01:26
  • It sounds like you're still using the concat task before cssmin is run. If _only_ cssmin is run, my relative urls are being rewritten 'bower_components/select2/select2.png'. – jridgewell Feb 06 '14 at 16:34
  • I did remove concat, but I will look into this again. – JBCP Feb 06 '14 at 17:20
  • I had the flow set in usemin, rather than useminPrepare. Now I am getting the paths replaced, but it is replaced with my absolute filesystem path rather than a relative path from the root. I will keep experimenting, since this is my ideal solution. – JBCP Feb 07 '14 at 17:32
  • Using root: '<%= yeoman.app %>' solved my problem. This solution is definitely a bit tricky to setup if you don't know the inner workings of usemin and cssmin, but it is also definitely the cleanest and gives you the simplest build artifacts. Thanks! – JBCP Feb 07 '14 at 17:38
  • No doubt it takes a minute to fully setup. Please edit my answer with anything that you think would help get the point across. – jridgewell Feb 07 '14 at 17:47
  • I added a few lines of clarification. It might be a bit pedantic now, but it should be pretty clear now. Great fix BTW, I would upvote it multiple times if I could. – JBCP Feb 07 '14 at 18:05
6

What fixed the CSS background-image revving for me was to add a CSS pattern in options, which finds all asset references in the CSS and replaces them with the revved assets.

// Performs rewrites based on rev and the useminPrepare configuration
usemin: {
  ...
  ...
  options: {
    ...
    ...
    // This is so we update image references in our ng-templates
    patterns: {
      js: [
        [/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images']
      ],
      css: [
        [/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the CSS to reference our revved images']
      ]
    }
  }
},
Gur Dotan
  • 922
  • 8
  • 12
1

I did a little digging around and found a pair of tasks that look like they'll get the job done: https://github.com/yeoman/grunt-filerev & https://github.com/richardbolt/grunt-cssurlrev. The only problem with this is that you'll have to configure the paths inside your Gruntfile manually, like so:

grunt.initConfig({
    filerev: {
      images: {
        src: ['img1.png', 'img2.png'],
        dest: 'tmp'
      }
    },
    cssurlrev: {
      dist: {
        src: ['public/css/*.css']
      },
    }
});

To my knowledge there isn't a plugin that does this task automatically.

Ben
  • 10,106
  • 3
  • 40
  • 58
1

I had to implement a new task. This is my preliminary implementation.

grunt.registerMultiTask('rewriteCssUrl', 'rewrite url in css', function () {
  var options = this.options({
     assets: grunt.filerev ? grunt.filerev.summary : {},
     postFilter: function identity(input){ return input}
  });

  var self = this;
  var assets = options.assets;

  self.filesSrc.forEach(function (file) {
     var css = grunt.file.read(file);
     var original = css;

     css = css.replace(/(?:src=|url\(\s*)['"]?([^'"\)]+)['"]?\s*\)?/gm, function (match, src) {
        var key = path.join(path.dirname(file), src);
        var asset = assets[path.normalize(key)];
        var val =  options.postFilter(asset);
        return match.replace(src, val || match);
     });

     if(original !== css) {
        grunt.log.writeln('✔ '.green + file + (' was changed.').grey);
        grunt.file.write(file, css);
     }
  });
});
Cemo
  • 5,370
  • 10
  • 50
  • 82
0

My approach to this problem was to basically create a separate styles/select2/select2.css for each vendor style, and then all relevant images can be copied by Grunt into styles/select2 (without having to worry about relative paths or overwriting etc) as part of the script. That is:

app/index.html

<!-- build:css(.tmp) styles/select2/select2.css -->
<link rel="stylesheet" href="bower_components/select2/select2.css">
<!-- endbuild -->

Gruntfile.js

Add a new copy task that will copy over vendor styles into the .tmp directory before they are minimised with cssmin:

    copy: {
        // this copies bower_components/*.css into .tmp so they can be compiled
        styles: {
            expand: true,
            cwd: '<%= yeoman.app %>',
            dest: '.tmp/',
            src: [
                'styles/{,*/}*.css',
                'bower_components/**/*.css'
            ]
        },
        dist: ...

And then once they are minimised, copy over the relevant assets (which in this case I'm assuming is just PNG and GIF images):

        // and once we have compiled all of our stylesheets, we need to also copy over any necessary image files
        distAssets: {
            expand: true,
            cwd: '<%= yeoman.app %>/bower_components',
            dest: '<%= yeoman.dist %>/styles',
            src: [
                '**/*.png',
                '**/*.gif'
            ]
        }
    },

Finally, add the new tasks into the build task:

grunt.registerTask('build', [
    'clean:dist',
    'replace:dist',
    'copy:styles',  // -- added
    'useminPrepare',
    // ... etc ...
    'copy',
    'rev',
    'usemin',
    'copy:distAssets'  // -- added
]);
jevon
  • 3,197
  • 3
  • 32
  • 40