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

3

29

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:

  • Have I misconfigured Grunt or done something wrong?

  • Or is the workflow I am describing simply not the right one and should I use another one instead.

Thanks!

Alimentary answered 15/8, 2013 at 13:46 Comment(1)
Try this one... https://mcmap.net/q/502736/-gruntjs-wrong-image-paths-after-grunt-build It helped me!Fabricant
A
5

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!

Alimentary answered 11/9, 2013 at 9:34 Comment(2)
Is it possible to share your configuration file? I am almost in same situation but having trouble to configure usemin with cssmin.Coneflower
Please provide a Grunt config showing exactly what you did, we can't figure it out by looking at the link you provided.Saccharide
A
6

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;
    }
    },
}
Ahern answered 10/9, 2013 at 8:1 Comment(3)
Thanks for your comment. However: my problem is solved in a more elegant way by now. I should have updated my question: sorry!Alimentary
I updated my answer. I hope that you agree that this approach is more clean. Thanks again!Alimentary
Thanks for your answer! Helped me to figure out a solution on my own. However, your answer doesnt check for multiple url() definitions in one line, like Bootstrap 3 does with their font definition: src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');Rubbing
A
5

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!

Alimentary answered 11/9, 2013 at 9:34 Comment(2)
Is it possible to share your configuration file? I am almost in same situation but having trouble to configure usemin with cssmin.Coneflower
Please provide a Grunt config showing exactly what you did, we can't figure it out by looking at the link you provided.Saccharide
M
-2

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.

Maddie answered 24/8, 2013 at 16:52 Comment(5)
For the stylesheets we build ourselves this can indeed be a solution. However, for vendor libraries (like Bootstrap and jQuery UI mentioned in the example) this cannot be done without altering the contents of these files.Alimentary
I don't see anything wrong with modifying a copy of Bootstrap (either dynamically as a copy in your dist folder, or just by going ahead and doing a search and replace in the original file), after all it's just a tool to help you build a site. The alternative might just be to copy over the original directory structure to keep the images but leave the uncompressed version, or to symlink it should you upload the components directory on your production site.Maddie
I believe that updating vendor libraries by hand is not a good practise. Among other things, this makes it harder to update. Offcourse If the operation is done automatically in a systematic way it is fine. One option besides the relative is to automatically rewrite all image paths to an absolute path. Maybe this is accomplishable via Grunt?Alimentary
Yes, use one of the search & replace plugins: npmjs.org/package/grunt-text-replace, npmjs.org/package/grunt-replacer or npmjs.org/package/grunt-combineMaddie
But then you can ONLY test your app when it's in production mode, right? There is no developer mode that you can test it inPyralid

© 2022 - 2024 — McMap. All rights reserved.