GruntJS - wrong image paths after grunt build
Asked Answered
C

3

5

Grunt is messing my minified CSS and I cannot figure out why and how to avoid it.

To be brief, before the minification I have some background images like this:

.head-image {
  height: 380px;
  background: url("../images/head1_bg.png") repeat-x;
  -webkit-background-size: cover;
  background-size: cover;
}

Then, after the whole process, I have a minified CSS that goes like this (snippet):

[...]background:url(/home/domo/projects/lp/.tmp/images/head1_bg.png) repeat-x;background-size:cover}[...]

The aforementioned path not only is an absolute path, but it's even wrong, since the images can be found in dist/images, not in .tmp/images (which doesn't even exists...).

A very similar issue is mentioned here: yeoman#824
I tried 2 solutions from that thread (#17759188 and #29137057) but to no avail.

I even tried using the supposedly patched version of yeoman, as stated in another Stack Overflow question (Yeoman CSS image paths) post, still with no success...

Maybe someone has already walked this path and found a solution that I haven't found on the internet.

Edit: I can add one more detail

image path inside HTML partials are not affected, like this:
ng-src="./images/box_{{$index+1}}.png"

Gruntfile.js

// Generated on 2014-07-14 using generator-angular 0.8.0
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: {
      // configurable paths
      app: require('./bower.json').appPath || 'app',
      dist: 'dist'
    },

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['bowerInstall']
      },
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: true
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      compass: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        tasks: ['compass:server', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/}/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: '0.0.0.0',
        livereload: 35729
      },
      livereload: {
        options: {
          open: true,
          base: [
            '.tmp',
            '<%= yeoman.app %>'
          ]
        }
      },
      test: {
        options: {
          port: 9001,
          base: [
            '.tmp',
            'test',
            '<%= yeoman.app %>'
          ]
        }
      },
      dist: {
        options: {
          base: '<%= yeoman.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: [
        'Gruntfile.js',
        '<%= yeoman.app %>/scripts/{,*/}*.js'
      ],
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= yeoman.dist %>/*',
            '!<%= yeoman.dist %>/.git*'
          ]
        }]
      },
      server: '.tmp'
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    bowerInstall: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        ignorePath: '<%= yeoman.app %>/'
      },
      sass: {
        src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        ignorePath: '<%= yeoman.app %>/bower_components/'
      }
    },

    // Compiles Sass to CSS and generates necessary files if requested
    compass: {
      options: {
        sassDir: '<%= yeoman.app %>/styles',
        cssDir: '.tmp/styles',
        generatedImagesDir: '.tmp/images/generated',
        imagesDir: '<%= yeoman.app %>/images',
        javascriptsDir: '<%= yeoman.app %>/scripts',
        fontsDir: '<%= yeoman.app %>/styles/fonts',
        importPath: '<%= yeoman.app %>/bower_components',
        httpImagesPath: '/images',
        httpGeneratedImagesPath: '/images/generated',
        httpFontsPath: '/styles/fonts',
        relativeAssets: false,
        assetCacheBuster: false,
        raw: 'Sass::Script::Number.precision = 10\n'
      },
      dist: {
        options: {
          generatedImagesDir: '<%= yeoman.dist %>/images/generated'
        }
      },
      server: {
        options: {
          debugInfo: true
        }
      }
    },

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

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '<%= yeoman.app %>/index.html',
      options: {
        dest: '<%= yeoman.dist %>',
        flow: {
          html: {
            steps: {
              js: ['concat', 'uglifyjs'],
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on rev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= yeoman.dist %>']
      }
    },

    // The following *-min tasks produce minified files in the dist folder
    cssmin: {
      options: {
        root: '<%= yeoman.app %>'
      }
    },

    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.{png,jpg,jpeg,gif}',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['*.html', 'views/{,*/}*.html'],
          dest: '<%= yeoman.dist %>'
        }]
      }
    },

    // ngmin tries to make the code safe for minification automatically by
    // using the Angular long form for dependency injection. It doesn't work on
    // things like resolve or inject so those have to be done manually.
    ngmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: '*.js',
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '.htaccess',
            '*.html',
            'views/{,*/}*.html',
            'images/{,*/}*.{webp}',
            'fonts/*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'compass:server'
      ],
      test: [
        'compass'
      ],
      dist: [
        'compass:dist',
        'imagemin',
        'svgmin'
      ]
    },

    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css',
    //         '<%= yeoman.app %>/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/scripts/scripts.js': [
    //         '<%= yeoman.dist %>/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    // Test settings
    karma: {
      unit: {
        configFile: 'karma.conf.js',
        singleRun: true
      }
    }
  });


  grunt.registerTask('serve', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'bowerInstall',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve:' + target]);
  });

  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'bowerInstall',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngmin',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    'rev',
    'usemin',
    'htmlmin'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};
Credits answered 17/7, 2014 at 9:55 Comment(2)
5 views? how could this be even possible? :DCredits
Perhaps this can be of use? #14212653 Looks like they still haven't fixed it?Uncurl
L
5

Some time ago, I had a similar issue with cssmin...

My config was:

cssmin: {
   options: {
     root:  '<%= yeoman.app %>'
   }
},

then I have removed root property and it solved the problem.

UPD1: Also you need to add images to assetsDirs in configutarion for usemin:

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

UPD2: About images with ng-src="./images/box_{{$index+1}}.png", it is not possible to process them directly with usemin because there is no way to handle file revision in this expression...

But you can add separate map with image urls, like :

<div ng-init="boxes=[
    {src:'images/box1.png'},
    {src:'images/box2.png'}, 
    {src:'images/box3.png'}]>
    ....
    <img ng-src={{boxes[$index].src}}/>
    ....
</div>

And process those maps with additional pattern for usemin:

usemin: {
  html: ['<%= yeoman.dist %>/{,*/}*.html'],
  htmlExtra: ['<%= yeoman.dist %>/{,*/}*.html'],
  css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
  options: {
    assetsDirs: ['<%= yeoman.dist %>', '<%= yeoman.dist %>/images'],
    patterns:{
        htmlExtra:[
            [/['"]?src['"]?\s*:\s*['"]([^"']+)['"]/gm, 'Replacing urls in image maps']
        ]
    }
  }
},

Then after build you will get something like:

<div ng-init="boxes=[
        {src:'images/box1.xxxx.png'},
        {src:'images/box2.xxxx.png'}, 
        {src:'images/box3.xxxx.png'}]">
    ....
    <img ng-src={{boxes[$index].src}}/>
    ....
</div>
Lindsy answered 24/7, 2014 at 11:21 Comment(6)
Unfortunately this still don't resolve the issue because the CSS now points to non existent images (e.g. the old images), not to the revved ones. I'll add my Gruntfile.js thoughCredits
Updated an answer, added fix for usemin configuration. Also you are using an outdated version of yeoman/generator-angular, this issue was already fixed in latest versionLindsy
Almost there! Images in SCSS files are revved and their url updated. However images in the views are not, like this ng-src="./images/box{{$index+1}}.jpg" It still points to /box1.jpg instead of /somerandomhash.box1.jpg. usemin should take care of this too... shouldn't it?Credits
no, it can not replace dynamic urls...also, it will not even look for ng-src attribute at all (only src attributes would be processed)Lindsy
but probably you can add own patters to match this images github.com/yeoman/grunt-usemin#patternsLindsy
It will be harder than I initially thought, it seems, and I don't have a complete answer yet. Still, you helped me a lot with this and deserve your bounty. Thanks and congratulations :)Credits
A
0

Have you considered using Brunch? They run a different node plugin for CSS minification than Yeoman/Grunt does. I have no problem with maintaining my "../" relative paths after minifying with Brunch. Try their Angular skeleton, just drop your stylesheet in and run a build with

brunch build --production

to minify. See if you still have this compiling issue. Done in 5 mins.

Algor answered 24/7, 2014 at 11:10 Comment(1)
thanks for the suggestion, but I don't want to switch tool for the momentCredits
M
0

I've had the the same issue, eventually i was using the grunt.filerev.summary and the patterns js functions, which found the old img url and replaced it with the matching case in the grunt.filerev.summary json "map"

Metasomatism answered 6/12, 2015 at 10:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.