Webpacker: Enhancement: Wepb support

Created on 13 Feb 2019  Â·  14Comments  Â·  Source: rails/webpacker

I figure out a way with webpack and webpacker too webp support to transform any of the jpg & png into webp using this plugin - https://github.com/iampava/imagemin-webp-webpack-plugin together with a helper method for nice progressive enhancement for those browsers that support it.

If it thought to be something useful, I could integrate and issue a pull request?

All 14 comments

Good idea! Any progress?

I was waiting for someone on the project to say if it was something they'd like included.

Just out of curiosity, why do it in webpack, instead of CarrierWave for example? Or is it for converting existing assets on server startup?

For static images, I guess you could do it manually, but this simplifies it and nicely creates webp images of any png or jpgs you have that are not dynamically added (carrierwave or active storage would definitely be the way to go for dynamic images)

I ran into some trouble with this myself this weekend. Similar to this post https://github.com/iampava/imagemin-webp-webpack-plugin/issues/18 the manifest.json file gets built before the webp files are generated by the plugin.

After adding the library via yarn I made the following edits

// config/webpack/environment.js
environment.plugins.append('Imagemin', new ImageminWebpWebpackPlugin())

// config/webpacker.yml
static_assets_extensions:
  …
  - .webp

I can see that my jpgs are getting emitted as webp files but image_pack_tag cannot find them as they don't exist in the manifest.

Any insight that could point me in the right direction?

I gave up on using webpacker, instead, I use rails as pure API now, and let client-side handle webpack on its own.

I was able to get this working in my app with help from @rossta. This gist contains my current environment.js file.

I'm happy to answer any questions about it, however, automating this as a part of Webpacker is beyond my familiarity with it at the moment. The tl;dr is that the JS library that puts together the manifest, WebpackAssetsManifest has a hooks feature that you can tap into and add any images you expect to be generated by the plugin.

Hey @agrberg this is related kind of but you seem to know what is what regards webp so thought you may be able to help here.
I have been able to upload webp images directly using active_storage out of the box and they are definitely being served from Digital Ocean Spaces as webp however when rendering in the view they are frustratingly being converted to png's which is totally contrary to the whole reason for webp in first place. Have you had trouble rendering in view as webp?

I have not had that problem myself but I'm also not 100% I understand. I will attempt to repeat it so that I do.

You have file.webp and have uploaded it into some sort of storage bucket (similar to s3 I assume) on Digital Ocean _and_ can verify that when you request it, the url and file returned are #{some_http_path}/file.webp.

However, when the view renders you are getting back a png file in an img tag.'

Some of the above might not be accurate so let me know where. The first thing I'd check is <img src=#{is_this_webp_or_png} />. I'd also manually request the file via curl and inspect it to determine what it is. I can name anything file.webp, even a text file, but the contents are what matters.

So far the only idea I have is that uploading the file is converting it to png via an image manipulation tool like imagemagick or similar.

I think it shouldn't be the responsibility of Webpacker to transform images in webp by default.

You can configure it in your own projects as explained here or handle static webp images in the webpacker.yml config file.

What do you think ? Can we close this issue ?

I think we've identified a different issue that is also up for debate.

My original post supports saying Webpacker does not need to handle the webp generation but should Webpacker automagically detect files that were generated during compilation and add them to the manifest?

Clearly we can work around this using JavaScript in this gist but Webpacker doesn't require anything else to magically get existing static assets or compile CSS to an external file and add them to the manifest.

Hi @agrberg,

Your solution worked, I now have a /public/packs/manifest.json with both (jpg/png) images and their webp versions. However, I'm not sure if I understand the way you did to route browsers to the versions they accept.

Hence I tried some different ways, and ended up using a rack middleware that append .webp if Accept: header explicitely allows it. Here's how I've done that:

  1. I've changed a tiny bit your gist to _append_ instead of _replace_ the extension.

    // config/webpack/environment.js
    const { environment } = require('@rails/webpacker')
    const ImageminWebpWebpackPlugin = require('imagemin-webp-webpack-plugin')
    
    const IMAGE_REGEXP = /\.(?:jpe?g|png)$/
    
    environment.plugins.prepend('ImageminWebpWebpackPlugin', new ImageminWebpWebpackPlugin({
     detailedLogs: false, // true to show compression per image.
     overrideExtension: false // foo.png produces foo.png.webp instead of foo.webp
    }));
    
    const manifest = environment.plugins.get('Manifest')
    manifest.hooks.customize.tap('ImageminWebpWebpackPlugin', (entry, _original, manifest, _asset) => {
     const { key, value } = entry
    
     if (IMAGE_REGEXP.test(key)) {
       manifest.set(key + '.webp', value + '.webp')
     }
    });
    
    module.exports = environment
    
  2. I've yarn add imagemin-webp-webpack-plugin
  3. I've added webp to my static_assets_extensions list in config/webpacker.yml
  4. I've added a rack middleware in my config.ru:

    class Webp
     # :nodoc:
     def initialize(app)
       @app = app
     end
    
     # Redirect png and jpg images to webp if current browser has webp support.
     def call(env)
       # Since this middleware is based on `Accept:` header, we must have a
       # CloudFront cache that whitelists this header.
       accept_header = env.fetch("HTTP_ACCEPT", "")
       path_info = env.fetch("PATH_INFO", "")
    
       # No need to handle anything here.
       has_webp = accept_header.include?("image/webp")
       is_media = path_info.include?("/media/images/")
       return @app.call(env) unless has_webp && is_media
    
       # Only jpg and png are yet converted to webp.
       return @app.call(env) unless path_info.end_with?(".png", ".jpg", ".jpeg")
    
       env["REQUEST_PATH"] += ".webp"
       env["PATH_INFO"] += ".webp"
       env["REQUEST_URI"] += ".webp"
    
       @app.call(env)
     end
    end
    use Webp
    
  5. I've changed my CDN configuration to make it pass and cache Accept: headers.

Not sure about the part where I make a savage change to the REQUEST_PATH variable, but it works! I hope this may help some of the readers 🙂.

@guillaumebriday About including a support for webp (or soon avif) in webpacker, I'd be all for that, and IMHO it is a good idea to help users with that. Rails is meant to be easy to use thanks to its _convention over configuration_ philosophy. However the canonical way may be to support it would be to have a picture_tag rails helper, which could itself then set sources for various image formats, webp being for now the most interesting.

@BuonOmo I didn't do anything as advanced as you. My image_tags point to the .webp version so there's no need to mess around with the request.

I think static assets might be served w/o hitting Rails. Can you confirm Webp#call is getting called?

@agrberg I can, my website is already in pre-production using that method, tested with IE and chrome yet. And I can edit this message with an url to the production working website when it's deployed (ETA 2020-12-20).

My image_tags point to the .webp version so there's no need to mess around with the request.

Yeah, much simpler! However this would not cover cases where one has an image embedded in css (a background-image property for instance), or if one needs to handle deprecated browsers...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ankitrg picture ankitrg  Â·  3Comments

iChip picture iChip  Â·  3Comments

amandapouget picture amandapouget  Â·  3Comments

itay-grudev picture itay-grudev  Â·  3Comments

christianrojas picture christianrojas  Â·  3Comments