Roel van Duijnhoven
Roel van Duijnhoven

Reputation: 850

How to rewrite urls of images in vendor CSS files using Grunt

I am trying to move frontend dependencies out of the version control system. A combination of Bower.io and Grunt should be able to do this.

A problem however occurs that I am yet unable to solve with bundling multiple vendor libraries. For example assume I have the following directory structure where the components directory is the directory that Bower.io saves the dependencies in:

├── assets
└── components
    ├── bootstrap
    │   ├── img
    │   │   └── glyhs.gif
    │   └── less
    │       └── bootstrap.css
    └── jquery-ui
        ├── css
        │   └── style.css
        └── images
            ├── next.gif
            └── prev.gif

Now assume I want to bundle both jQuery's style.css and Bootstrap' bootstrap.css. I will save this bundled file in assets/bundled.css.

However in this file the references to the original images (../images/next.gif and ../img/glyhs.gif) are incorrect. They will have to be rewritten in order to work (so ../images/next.gif => ../components/jquery-ui/images/next.gif). I believe(d) this rewriting of URLs is something Grunt should be able to do. But I can not seem to get this to work using the cssmin/less/copy tasks. For example the following Grunt setup (only moving 1 file) fails to work:

module.exports = function (grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        less: {
            options: {
                compile: false,
                relativeUrls: true
            },
            bootstrap: {
                src: 'components/bootstrap/less/bootstrap.less',
                dest: 'assets/bootstrap.css'
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.registerTask('dist-css', ['less']);
};

Either:

Thanks!

Upvotes: 29

Views: 15321

Answers (3)

Roel van Duijnhoven
Roel van Duijnhoven

Reputation: 850

Just for reference: there is now a solution readily available. I posted this same issue to the CleanCss grunt plugin, and they have accepted it and published this behaviour in their new 1.1 release.

You can find the issue on the GitHub tracker here: https://github.com/GoalSmashers/clean-css/issues/129

This library makes it possible to either use absolute rewriting (from a root directory) or alter relative image paths based on a new output directory. Look for the --root or --ouput directives.

Thanks for the tips and answers people!

Upvotes: 5

Ulan Murzatayev
Ulan Murzatayev

Reputation: 870

You probably wat to take a look at this grunt package https://github.com/Ideame/grunt-css-urls. This package seems to be intended to solve exactly your problem.

Edit: after looking at this plugin I didn't like the idea of rewriting my markup in order to make my build process smoother. So I ended up writing my own tiny function which does the rewrite for me.

I use grunt's concat plugin for bundling my css files. Good thing about this plugin is that it suports file processing function before concatenation. Now my gruntfile looks like this:

grunt.initConfig({
concat: {
    options: {
    separator: '\n',
    process: function (src, filepath) {
        var cssPatt = new RegExp('app(\/.*\/).*\.css$');

        //filter out everithing except css files
        var file = cssPatt.exec(filepath);

        if (file) {
            var urlPatt = /url\(\'(.*)\'\)/g;

        console.log('In file: ' + filepath);

        //replace every url(...) with its absolute path
        return src.replace(urlPatt, function (match, p1) {
            console.log(' * ' + match + ' -> ' + 'url(\'' + file[1] + p1 + '\')');
            return 'url(\'' + file[1] + p1 + '\')';
        });
        }

        return src;
    }
    },
}

Upvotes: 6

Ben
Ben

Reputation: 10146

You'll want to do some search/replace on your dist css file to generate the correct relative paths. There are a number of grunt plugins that can do this for you, personally I prefer grunt-replace. Set up your non compressed assets with variables and then produce a dist css with the URLs dynamically generated.. So:

body {
    background:url(@@IMG_PATH/background.jpg);
}

Becomes this in dist:

body {
    background:url(path/to/background.jpg);
}

Hope this helps.

Upvotes: -2

Related Questions