17
votes

I'm looking for a grunt plugin that will automatically change the references to static assets (js/css) inside an html file like this:

<link rel="stylesheet" type="text/css" href="style.css?v=12345678" />
<script src="script.js?v=12345678"></script>

I searched at the gruntjs.com/plugins -> "version", but it seems that all of them change the actual version of the files instead of references to them.

Am I missing it? Is there a plugin that can perform this task?

2

2 Answers

22
votes

For this I use grunt-filerev for the versionning and grunt-usemin for the automatic update of the references in source files.

These two modules works well together (usemin replacing references with a mapping provided by filerev)

Hope this helps

edit: a few code examples (only showing you what's interesting in your case):

I use usemin & filerev only when packaging my app (for deployment) :

In the head of my index.html, the following code tell usemin to take all the files between the build tag and agregate it into one named ie-shims.js

[...]
<!-- build:js /js/ie-shims.js -->
    <script src="/vendor/html5shiv/dist/html5shiv.js"></script>
    <script src="/vendor/respond/dest/respond.src.js"></script>
<!-- endbuild -->
[...]

Next, in my gruntfile.js, i have two node :

[...]
filerev: {
    options: {
        encoding: 'utf8',
        algorithm: 'md5',
        length: 8
    },
    source: {
        files: [{
            src: [
                'www/js/**/*.js',
                'www/assets/**/*.{jpg,jpeg,gif,png,ico}'
            ]
        }]
    }
},
useminPrepare: {
    html: 'src/index.html',
    options: {
        dest: 'www'
    }
},

// usemin has access to the revved files mapping through grunt.filerev.summary
usemin: {
    html: ['www/*.html'],
    css: ['www/css/**/*.css'],
    js: ['www/js/**/*.js'],
    options: {
        dirs: ['www'],
        assetsDirs: ['www'],
        patterns: {
            js: [
                [/["']([^:"']+\.(?:png|gif|jpe?g))["']/img, 'Image replacement in js files']
            ]
        }
    }
} [...]

Finally, I have a grunt task that put all that together :

grunt.registerTask('build', 'Build task, does everything', ['useminPrepare', 'filerev', 'usemin']);

Then, the generated HTML is like that :

[...]
<script src="/js/ie-shims.9f790592.js"></script>
[...]
2
votes

I found a neat post about keeping Grunt clean, that walks through an entire folder structure, Gruntfile.js config and task running, over at http://www.jayway.com/2014/01/20/clean-grunt/. Your comment on the earlier answer is about folder structure, so it should also help with that, since the structure there doesn't have index.html file in the root either.

  1. Prep your html file as per the docs of grunt-usemin (and/or the post I linked)
  2. You need to add grunt-contrib-copy, so you can copy index_src.html and rename it to index.html (used this for inspiration), and proceed with the 'usemin' task on that.
  3. Change references to your assets to relative paths (ex. ../js/controller.js)
  4. Then configure your Gruntfile.js like this:

    [...]
    useminPrepare: {
        html: 'templates/index.html',
        options: {
            dest: 'js'
        }
    },
    
    // Concat - best to implement it this way for useminPrepare to inject the config
    concat: {
        options: {
            separator: ';'
        },
        // dist configuration is provided by useminPrepare
        dist: {}
    },
    
    // Uglify - best to implement it this way for useminPrepare to inject the config
    uglify: {
        // dist configuration is provided by useminPrepare
        dist: {}
    },
    
    filerev: {
        options: {
            encoding: 'utf8',
            algorithm: 'md5',
            length: 20
        },
        source: {
            files: [{
                src: [
                    'js/**/*.js'
                ]
            }]
        }
    },
    
    copy: {
      rename: {
        files: [
          {
            cwd: 'templates',
            src: ['**/index_src.html'],
            dest: 'templates',
            rename: function(dest, src) {
              return dest + src.replace(/_src\.html$/i, '.html');
            }
          }
        ]
      }
    },
    
    // usemin has access to the revved files mapping through grunt.filerev.summary
    usemin: {
        html: ['templates/index.html'],
        options: {
            assetsDirs: ['js']
        }
    } [...]
    

    I'm not a 100% sure about the regex to rename the file, but make a backup of the folder and give it a try. Also, I'm answering as per the pastebin link you gave, which did not include any css files. If there are any, things get a bit complicated, so let me know.

  5. You could then use the grunt task Bixi suggested, but include your copy step (and concat & uglify)

    grunt.registerTask('build', 'Build task, does everything', [
    'useminPrepare',
    'concat',
    'uglify',
    'copy:rename',
    'filerev',
    'usemin'
    ]);