Skip to content
This repository has been archived by the owner on Apr 20, 2018. It is now read-only.

blockReplacements run before filerev linking #444

Open
monitorjbl opened this issue Oct 1, 2014 · 10 comments
Open

blockReplacements run before filerev linking #444

monitorjbl opened this issue Oct 1, 2014 · 10 comments

Comments

@monitorjbl
Copy link

When using filerev before usemin, the output given to a blockReplacements function is the original filename instead of the filerev-adjusted file name (suffixed with a hash). To properly use this option, the blockReplacements functions should be called after the filrev-linking has been done and the hashed filenames are available.

@leocwolter
Copy link

In other words: we want to manipulate the final imported assets uris, and not the ones generated before filerev. This way we can support our application at other contexts than root, for example.

@monitorjbl
Copy link
Author

For anyone else that is having this issue, here's a workaround. Specify your blockReplacement however you like (in this case, we had to prepend a JSP context root onto the outputted path):

grunt.initConfig({
    config: {
        root: 'src/main/webapp/'
    },

    usemin: {
        html: ['<%= config.root %>/WEB-INF/{jsp,tags}/**/*.{jsp,jspf,tag}'],
        options: {
            blockReplacements: {
                css: function (block) {
                    return '<link rel="stylesheet" href="${contextPath}' + block.dest + '"/>';
                },
                js: function (block) {
                    return '<script src="${contextPath}' + block.dest + '"></script>';
                }
            }
        }
    }    
});

Then, create a custom task that also replaces the filerev map with the prefixed data. Note that you need to stick the prefix after the root path on the filesystem. In our case, it was src/main/webapp/ and it was configured in the config.root property in grunt.initConfig. Your case may be different, but you can use the same string replacement method that I'm using here:

grunt.registerTask('remapFilerev', function(){
    var root = grunt.config().config.root;
    var summary = grunt.filerev.summary;
    var fixed = {};

    for(key in summary){
        if(summary.hasOwnProperty(key)){
            var orig = key.replace(root, root+'${contextPath}/');
            var revved = summary[key].replace(root, root+'${contextPath}/');
            fixed[orig] = revved;
        }
    }

    grunt.filerev.summary = fixed;
});

Finally, insert this task between filerev and usemin in your build task:

grunt.registerTask('build', ['default', 'useminPrepare', 'filerev', 'remapFilerev', 'usemin']);

Really hokey, and it took me a long time to figure out. Hopefully this issue gets fixed so you can just use the blockReplacement method. On the plus side, I did get to learn about the excellent Node debugger.

@stephanebachelier
Copy link
Collaborator

@monitorjbl @leocwolter regarding the remapFilerev implementation, are you trying to add a contextPath to each revved assets ?

If this is the case, I think you should rewrite all you revved assets after the usemin process is complete using any grunt plugin like grunt-cdnify.

Waiting for your answer.

@monitorjbl
Copy link
Author

The goal was to append the ${contextPath} prefix to all referenced assets. In the application, this would be a JSP variable filled in at runtime with the absolute root path of the server.

Does your suggestion still apply? I'm not that familiar with other Grunt plugins.

@stephanebachelier
Copy link
Collaborator

@monitorjbl there are plugins like grunt-cdn or grunt-cdnify.
The idea is to run the task after usemin task has completed.

@stephanebachelier
Copy link
Collaborator

@monitorjbl I do not want to say that's a better to use another plugin. Your solution to create your own grunt task is good too.

@stephanebachelier
Copy link
Collaborator

CC @sindresorhus what do you think about it ?
CDN or rewriting assets with a base path/url, might not be usemin priority, but filerev give us a hasmap with all revved assets. Using another grunt plugin should be the way to go IMO, but these plugins seems to have some issues with parsing.

@mixxorz
Copy link

mixxorz commented Mar 8, 2015

I encountered this issue as well while trying to use usemin with filerev in a Django project. I could use something like grunt-cdn but I'd rather not. Django already handles linking to its static assets via the configured settings. Whether the static assets would eventually end up on S3, or on the server's file system should be handled by Django.

Basically, I want my grunt workflow to be agnostic about where the files will eventually end up being hosted. Though, my use case might just be too specific. That is, trying to use usemin for Django templates.

@tconroy
Copy link

tconroy commented Jul 31, 2015

Ahh I just found this after posting a similar issue. I've attempted to use grunt-cdn but it's not working out for me. Did anyone else come up with a reliable solution? I could definitely see this being a useful feature to incorporate some sort of build prefix.

@fodma1
Copy link

fodma1 commented Mar 8, 2016

After I spent 1.5 days fixing this problem, I thought I share my solution for the simplest usecase: I wanted to prepend the revved url paths with the cdn base url/the app's base url (for the local devenv).

module.exports = function( grunt ) {

  var options = {
    routes: {
      'static': 'myapp/static',
      'dist': 'myapp/dist',
      'tmp': '.tmp'
    },
    paths: {
      baseTemplateSource: 'myapp/templates/layout/base_src.html',
      baseTemplateTarget: 'myapp/templates/layout/base.html',
      baseTemplateStatic: 'myapp/static/base.html'
    }
  };

  grunt.initConfig({
    options: options,

    usemin: {
      html: ['<%= options.paths.baseTemplateStatic %>'],
      css: ['<%= options.routes.dist %>/css/*.css'],
      options: {
        assetsDirs: ['<%= options.routes.dist %>', '<%= options.routes.dist %>/css'],
        blockReplacements: {
          css: function (block) {
            var originalRevvedDest = grunt.filerev.summary[options.routes.dist + block.dest];
            var customDest = originalRevvedDest.replace(options.routes.dist, '{{ STATIC_BASE_URL }}');
            return '<link rel="stylesheet" href="' + customDest + '">'
          },
          js: function (block) {
            var originalRevvedDest = grunt.filerev.summary[options.routes.dist + block.dest];
            var customDest = originalRevvedDest.replace(options.routes.dist, '{{ STATIC_BASE_URL }}');
            return '<script src="' + customDest + '"></script>';
          }
        }
      }
    }
  });
};

(Note I'm serving my templates with Flask, so {{ STATIC_BASE_URL }} is for jinja2, but can be replaced with url prefixes)

Luckily there's a PR waiting for reviewers: #613

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants