inline_svg icon indicating copy to clipboard operation
inline_svg copied to clipboard

Feature request: Add vite support

Open jamesst20 opened this issue 1 year ago • 4 comments

It would be nice to support Vite: https://github.com/ElMassimo/vite_ruby

jamesst20 avatar Jun 13 '24 14:06 jamesst20

module ViteInlineSvgFileLoader
  class << self
    def named(filename)
      vite = ViteRuby.instance
      vite_asset_path = vite.manifest.path_for(filename)

      if vite.dev_server_running?
        fetch_from_dev_server(vite_asset_path)
      else
        Rails.public_path.join(vite_asset_path.sub(%r{^/}, "")).read
      end
    end

    private

    def fetch_from_dev_server(path)
      config = ViteRuby.config
      dev_server_uri = URI("#{config.protocol}://#{config.host_with_port}#{path}")
      response = Net::HTTP.get_response(dev_server_uri)
      raise "Failed to load inline SVG from #{dev_server_uri}" unless response.is_a?(Net::HTTPSuccess)

      response.body
    end
  end
end

InlineSvg.configure do |config|
  config.asset_file = ViteInlineSvgFileLoader
end

Source: https://mattbrictson.com/blog/inline-svg-with-vite-rails

jamesst20 avatar Jun 14 '24 14:06 jamesst20

Thanks for the suggestion. Given the small amount of code required to build a Vite loader (as you demonstrated), and Vite's relatively small following, I'm not sure if it's worth building support directly into the gem.

Will leave this open so other Vite enthusiasts can upvote this idea.

jamesmartin avatar Jun 24 '24 06:06 jamesmartin

Thanks for sharing your code, @jamesst20! I was able to get it working in my app without much fuss.

The only hiccup I had was related to the filenames provided to inline_svg. Previously, code like <%= inline_svg("foo.svg") %> would know to look in app/assets/images/. These filenames stopped working after the switch to Vite Ruby, which makes sense–the images now live in app/frontend/images.

I had to update the filenames to match the asset manifest entries instead. So, given a manifest entry like:

// public/vite-dev/.vite/manifest-assets.json
{
  "images/foo.svg": {
    "file": "assets/foo-yC27NFda.svg",
    "src": "images/foo.svg",
    "integrity": "sha384-[sha]"
  },
  // ...
}

...I would now have to use the following value to reference the image:

- <%= inline_svg("foo.svg") %>
+ <%= inline_svg("images/foo.svg") %>

Not a huge change, but definitely a difference. I suspect that I could have dug a bit further and added some Vite configuration to avoid the change, but the impact was small enough for my current concerns that it was faster to just update the filenames.

raybrownco avatar Jun 25 '24 18:06 raybrownco

Thanks for sharing your code, @jamesst20! I was able to get it working in my app without much fuss.

The only hiccup I had was related to the filenames provided to inline_svg. Previously, code like <%= inline_svg("foo.svg") %> would know to look in app/assets/images/. These filenames stopped working after the switch to Vite Ruby, which makes sense–the images now live in app/frontend/images.

I had to update the filenames to match the asset manifest entries instead. So, given a manifest entry like:

// public/vite-dev/.vite/manifest-assets.json
{
  "images/foo.svg": {
    "file": "assets/foo-yC27NFda.svg",
    "src": "images/foo.svg",
    "integrity": "sha384-[sha]"
  },
  // ...
}

...I would now have to use the following value to reference the image:

- <%= inline_svg("foo.svg") %>
+ <%= inline_svg("images/foo.svg") %>

Not a huge change, but definitely a difference. I suspect that I could have dug a bit further and added some Vite configuration to avoid the change, but the impact was small enough for my current concerns that it was faster to just update the filenames.

My pleasure! The path change is indeed something that comes with Vite. All view helpers requires the folder name in the path. This is indeed a difference from shakapacker or sprockets.

This is also true for all these methods

image_tag -> vite_image_tag("images/...")
asset_path -> vite_asset_path("images/...")

jamesst20 avatar Jun 25 '24 18:06 jamesst20

Closing this now as there appears to be sufficient ability to workaround the lack of official support from the community.

jamesmartin avatar Sep 03 '24 02:09 jamesmartin

I've been using this approach happily for about half a year now, but I just noticed an issue with the ViteInlineSvgFileLoader custom file loader provided above: it doesn't work with inline_svg's fallback feature.

The fix is quite easy, however. Instead of raising a generic error in the fetch_from_dev_server method, raise a InlineSvg::AssetFile::FileNotFound.new instead:

  def fetch_from_dev_server(path)
    config = ViteRuby.config
    dev_server_uri = URI("#{config.protocol}://#{config.host_with_port}#{path}")
    response = Net::HTTP.get_response(dev_server_uri)
-   raise "Failed to load inline SVG from #{dev_server_uri}" unless response.is_a?(Net::HTTPSuccess)
+   raise InlineSvg::AssetFile::FileNotFound.new unless response.is_a?(Net::HTTPSuccess)
    response.body
  end

@jamesst20 you may want to update your code block above, in case future visitors want to make use of it.

raybrownco avatar Dec 13 '24 16:12 raybrownco