webpack-rails icon indicating copy to clipboard operation
webpack-rails copied to clipboard

Loading images with rails helpers

Open Matt-Butler opened this issue 9 years ago • 8 comments

What is the recommended way of referencing images with webpack-rails?

Is it possible to use something like this?

= image_tag *webpack_asset_paths('my-image', extension: 'png')

Matt-Butler avatar Sep 28 '16 19:09 Matt-Butler

I don't know, to be honest. I haven't played with images in webpack. Give it a go and see!

mipearson avatar Sep 29 '16 02:09 mipearson

I think I understand the issue now. My images are not part of the assetsByChunkName attribute in the manifest.json file. This helper will not find my image assets: *webpack_asset_paths('file', extension: 'png')

manifest.json

  "assetsByChunkName":{
      "file":[
         "file-554ffb8dcc2a3d701ac2.js",
         "file.css"
      ]
   },

There are two potential options:

  1. Figure out how to add images to the assetsByChunkName attribute in manifest.json
  2. Find out how to push my assets onto the path variable https://github.com/mipearson/webpack-rails/blob/5575507c1043de984b43f984eb59d5ad980f1b87/lib/webpack/rails/manifest.rb#L31

Matt-Butler avatar Sep 29 '16 16:09 Matt-Butler

I found another work around by just making a css class for every image. I am also using the webpack url/file loader.

<div class="graphic" />
.graphic {
  background-image: url('./file.png');
  background-repeat: no-repeat;
  background-size: contain;
  width: 6rem;
  height: 6rem;
}

Matt-Butler avatar Sep 29 '16 17:09 Matt-Butler

Did anyone find a way to use image tags yet? I'm not sure using classes is really a viable solution.

jeffutter avatar Oct 22 '16 14:10 jeffutter

I developed a fairly nice solution to this using Rails 4.1. (Should work on other Rails versions but the patches may vary.)

  1. Install the asset-map-webpack-plugin and add it to your webpack config.

  2. Somewhere in your JS, require all of the image assets that you want to reference in your Rails app with image_tags. I used an image manifest JS file that I required in from the entry file. (I don't love this step but I haven't thought of a better way to do it yet.)

  3. Create an initializer (something like config/initializers/image_assets.rb) and patch ActionView to reference the file generated by asset-map-webpack-plugin when resolving image paths. Here's what I wrote (may vary based on where you're putting your built assets):

module ActionView
  module Helpers
    module AssetUrlHelper
      def compute_asset_path(source, options = {})
        if options[:type] == :image
          asset_map[source]
        else
          dir = ASSET_PUBLIC_DIRECTORIES[options[:type]] || ""
          File.join(dir, source)
        end
      end

      private

      def asset_map
        return @asset_map if @asset_map

        translated = {}
        asset_manifest['assets'].each do |asset, mapped|
          # Only include image assets in the lookup hash.
          next unless asset.include?('app/assets/images')

          # Trim to ensure path matching is correct --
          # if image is at `app/assets/images/path/to/image.png`, 
          # we want to use `image_tag('path/to/image.png') as expected.
          asset_tail = asset.split('app/assets/images/').last
          translated[asset_tail] = mapped
        end
        if Rails.env.development?
          translated
        else
          @asset_map = translated
        end
      end

      def asset_manifest
        return @asset_manifest if @asset_manifest

        if Rails.env.development?
          host = ::Rails.configuration.webpack.dev_server.manifest_host
          port = ::Rails.configuration.webpack.dev_server.manifest_port
          http = Net::HTTP.new(host, port)
          http.use_ssl = ::Rails.configuration.webpack.dev_server.https
          http.verify_mode = OpenSSL::SSL::VERIFY_NONE
          JSON.parse(http.get('/assets/asset_map.json').body)
        else
          @asset_manifest = JSON.parse(File.read(Rails.root.join(Rails.configuration.webpack.output_dir, 'asset_map.json')))
        end
      end
    end
  end
end

I also had to patch asset-map-webpack-plugin to get it to work in development. The original version uses fs.writeFileSync to naively write out the asset json file, rather than adding it to the webpack output assets (and so in development it still tries to write out to the filesystem rather than storing/serving in memory from the dev server). I may send a PR to the original repo with my changes, or just publish my own package with more targeted configuration for this use case.

@mipearson, let me know if you think this approach is worth standardizing and including in webpack-rails itself. I can clean up my code and send a PR.

schneidmaster avatar Jan 15 '17 06:01 schneidmaster

I have accomplished the same thing with already existing stats plugin here is my fork https://github.com/pavels/webpack-rails it needs more refinement, but works ok

pavels avatar Jan 17 '17 10:01 pavels

Any progress on this? We are running into the same issue.

jeffutter avatar Jan 30 '17 18:01 jeffutter

Try using the url-loader helper and using JS import/requires:

https://gist.github.com/alexkrolick/cb120d2e0f8450f1cd49d175f7a66dfe#file-webpack-config-js-L54-L57

alexkrolick avatar Feb 08 '17 19:02 alexkrolick