Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assets (Sprockets 4.1.1 and Sprockets-rails 3.4.2) not work correctly with images within engine with third party libraries #747

Closed
Sega100500 opened this issue Jul 1, 2022 · 6 comments

Comments

@Sega100500
Copy link

Sega100500 commented Jul 1, 2022

System configuration

  • Sprockets version is 4.1.1
  • Sprockets-rails is 3.4.2
  • Ruby version 3.1.2
  • Ruby on Rails 7.0.3
  • jQuery-UI 1.13.1 (third-party js-library stored local)
  • gem ckeditor 5.1.1 (with third-party js-library ckeditor stored local)

Problem

I use my own engine "Admin", in which I connect the jQuery-UI library.
With sprockets 3.7.2 and sprockets-rails 3.2.2 all work fine! ... but later versions not working correctly
(config.assets.compile = true)

Directory of engine is 'admin'.
Config manifest file for admin: admin/app/assets/config/admin.js:

//= link_tree ../images/admin
//= link_directory ../javascripts/admin .js
//= link_directory ../stylesheets/admin .css

//= link_tree ../javascripts/admin/lib .js

//= link_tree ../stylesheets/admin/lib .css
//= link_tree ../stylesheets/admin/lib .png
//= link_tree ../stylesheets/admin/lib .jpg
//= link_tree ../stylesheets/admin/lib .jpeg
//= link_tree ../stylesheets/admin/lib .gif

(replace with require_tree also not work)

connect library jQuery-UI with stylesheet_link_tag and javascript_include_tag

jQuery-UI files placed in:
admin/app/assets/javascripts/admin/lib/jquery-ui
admin/app/assets/stylesheets/admin/lib/jquery-ui

in initializer I also add:

Rails.application.config.assets.precompile += %w( admin/*.js admin/*.css admin/**/*.js admin/**/*.css )
Rails.application.config.assets.precompile += %w( admin/*.png admin/**/*.png admin/*.gif admin/**/*.gif )
Rails.application.config.assets.precompile += %w( admin/*.jpg admin/**/*.jpg admin/*.jpeg admin/**/*.jpeg )

Rails.application.config.assets.precompile += Ckeditor.assets
Rails.application.config.assets.precompile += %w( ckeditor/*.js ckeditor/**/*.js ckeditor/*.css ckeditor/**/*.css ckeditor/*.md ckeditor/*.txt ckeditor/*.html )
Rails.application.config.assets.precompile += %w( ckeditor/*.png ckeditor/**/*.png ckeditor/*.gif ckeditor/**/*.gif )

Rails.application.config.assets.paths << Admin::Engine.root.join('app','assets','images')
Rails.application.config.assets.paths << Admin::Engine.root.join('app','assets','images','admin')

Rails.application.config.assets.paths << Admin::Engine.root.join('app','assets','javascripts','admin')
Rails.application.config.assets.paths << Admin::Engine.root.join('app','assets','stylesheets','admin')

jQuery-UI library is loaded, but background images of elements are not displayed

Inspection of example jQuery-UI element show this style:

.ui-widget-header {
  border: 1px solid #4297d7;
  background: #5c9ccc url(/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png) 50% 50% repeat-x;
  color: #fff;
  font-weight: bold;
}

Also I use gem ckeditor, in this case not showing icons images and images placed in page.
Images loaded in ckeditor with mini_magic and ActiveStorage and link to image is like this:

'/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--a6378272645dcac2d56fb1a971b3e8fc3a682b86/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJYW5CbkJqb0dSVlE2QzNKbGMybDZaVWtpQ1Rnd01ENEdPd1pVIiwiZXhwIjpudWxsLCJwdXIiOiJ2YXJpYXRpb24ifX0=--2ccbd7d12a252e03eb26bb2401505e4efaeff669/7d85fe9c160bb2f3b559a45303cc8ce4.jpg'

With sprockets 3.7.2 and sprockets-rails 3.2.2 all work fine! ... but later versions not working correctly

What could be wrong? I tried many options for specifying paths, direct specifying files, but it does not work as it should.
Please, help!...

gems for non-digest assets are not helped to me:
non-digest-assets gem.
sprockets-redirect gem.
smart_assets gem.

Has no one come across this yet? On the web, I saw a lot of suggestions just to make a downgrade... but I think it's wrong. All libraries should work properly. And the application should always use the latest development versions of all libraries of Ruby on Rails.

@Sega100500 Sega100500 changed the title Assets not work correctly with images in engine with third party libraries Assets (Sprockets 4.1.1 and Sprockets-rails 3.4.2) not work correctly with images in engine with third party libraries Jul 1, 2022
@Sega100500 Sega100500 changed the title Assets (Sprockets 4.1.1 and Sprockets-rails 3.4.2) not work correctly with images in engine with third party libraries Assets (Sprockets 4.1.1 and Sprockets-rails 3.4.2) not work correctly with images within engine with third party libraries Jul 1, 2022
@rafaelfranca
Copy link
Member

Can you provide an example application that reproduce the issue?

@Sega100500
Copy link
Author

Sega100500 commented Jul 2, 2022

@rafaelfranca
I think I have described the situation in sufficient detail...

Default application configuration (Ruby on Rails 7.0.3, previously tried in versions 5.2.x with same effect), with minimal changes.

Inside my engine admin, I connect a third-party jQuery-UI library. JS-scripts and css-styles are connected without problems via javascript_include_tag and stylesheet_link_tag , but background images (declared in styles sheets of library) of ui-elements are not displayed. There is also a similar situation in the gem ckeditor - the JS third-party library is used, but the icons in the editor are not displayed. The problem is that the path to the images is not recognized correctly within css-styles of third-party libraries on the page - most likely converted by preprocessor.

In this particular case, within engine admin the stylesheets of jQuery-UI library is connected using:

stylesheet_link_tag("admin/lib/jquery-ui/1.13.1/redmond/jquery-ui.min")

Application with sprockets 4.1.1 and sprockets-rails 3.4.2

This is example one of css-style of ui-element on page (inspection in browser):

.ui-widget-header {
  border: 1px solid #4297d7;
  background: #5c9ccc url(/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png) 50% 50% repeat-x;
  color: #fff;
  font-weight: bold;
}

background image is /images/ui-bg_gloss-wave_55_5c9ccc_500x100.png are not found (obviously because it's an absolute path), but it exists in library, of course. Even if the jQuery-UI library was inside the public directory (in subdirectory) this path would not be found!

...if that matters too...
i use gem 'sassc-rails' and in production config.assets.css_compressor = :sass , config.assets.compile = true
...but it doesn't work in development mode so I didn't even try production mode

I never use rails assets:precompile

Important!

When I switches to use (downgrade) sprockets 3.7.2 and sprockets-rails 3.2.2 the same style takes the form:

.ui-widget-header {
  border: 1px solid #4297d7;
  background: #5c9ccc url("images/ui-bg_gloss-wave_55_5c9ccc_500x100.png") 50% 50% repeat-x;
  color: #fff;
  font-weight: bold;
}

background image path is "images/ui-bg_gloss-wave_55_5c9ccc_500x100.png"
and all works fine!

The only and major main difference is the / at beginning of the path to image!

(and the same situation in ckeditor - 'icons.png' converts to '/icons.png')

within css-style file ('redmond/jquery-ui.css') of the third-party jQuery-UI library

this code:

.ui-widget-header {
	border: 1px solid #4297d7;
	background: #5c9ccc url("images/ui-bg_gloss-wave_55_5c9ccc_500x100.png") 50% 50% repeat-x;
	color: #ffffff;
	font-weight: bold;
}

Conclusion

It turns out that when using gems sprockets 4.1.1 and sprockets-rails 3.4.2 (sprockets all later versions than 3.7.2 and sprockets-rails all later versions than 3.2.2), in the style sheet paths to images on page are converted from relative to absolute paths. Why is this done? Very strange and incomprehensible for what... Which preprocessor does this?

@Sega100500
Copy link
Author

Sega100500 commented Jul 2, 2022

@rafaelfranca

Point to possible location of problem was found in changes sprockets-rails since 3.3.0

I found in sprockets-rails 3.2.2 → 3.3.0 changes that make it. It is from version sprockets 3.3.0 that such incorrect processing is observed.

in file data/lib/sprockets/rails/asset_url_processor.rb ADDED

module Sprockets
  module Rails
    # Rewrites urls in CSS files with the digested paths
    class AssetUrlProcessor
      REGEX = /url\(\s*["']?(?!(?:\#|data|http))([^"'\s)]+)\s*["']?\)/
      
      def self.call(input)
        context = input[:environment].context_class.new(input)
        data    = input[:data].gsub(REGEX) { |_match| "url(#{context.asset_path($1)})" }

        { data: data }
      end
    end
  end
end

This section of code does the conversion:

from background: #5c9ccc url("images/ui-bg_gloss-wave_55_5c9ccc_500x100.png") 50% 50% repeat-x;
to: background: #5c9ccc url(/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png) 50% 50% repeat-x;

But why do this for stylesheets of third-party?...

Problem not in REGEX, but in substitution path in context.asset_path($1)

@Sega100500
Copy link
Author

Sega100500 commented Jul 2, 2022

In sprockets-rails 3.4.2, they probably noticed that there can be a relative path, but for some reason they decided that it must begin with ./:

REGEX = /url\(\s*["']?(?!(?:\#|data|http))(?<relativeToCurrentDir>\.\/)?(?<path>[^"'\s)]+)\s*["']?\)/

BUT! relative path may be without any leading slashes and dots, like this one background: #5c9ccc url("images/ui-bg_gloss-wave_55_5c9ccc_500x100.png") 50% 50% repeat-x; - this start with subdirectory name.

Even if we assume that a path without initial slashes is found, then it would be most correct to add ./ to the beginning of the path, but not /.

@Sega100500
Copy link
Author

Sega100500 commented Jul 2, 2022

@rafaelfranca

The patch that fixed the bug

For experiment I a little patch file lib/sprockets/rails/asset_url_processor.rb

module Sprockets
  module Rails
    # Resolve assets referenced in CSS `url()` calls and replace them with the digested paths
    class AssetUrlProcessor
      REGEX = /url\(\s*["']?(?!(?:\#|data|http))(?<relativeToCurrentDir>\.\/)?(?<path>[^"'\s)]+)\s*["']?\)/
      def self.call(input)
        context = input[:environment].context_class.new(input)
        data    = input[:data].gsub(REGEX) do |_match|
          path = Regexp.last_match[:path]
          # original: "url(#{context.asset_path(path)})"
         
          # my patch
          orig_path = path
          path = context.asset_path(path)
          path = (path == "/#{orig_path}") ? ".#{path}" : path
          "url(#{path})"

        end

        context.metadata.merge(data: data)
      end
    end
  end
end

... and all works correctly in development and production environments! This patch works correctly with relative paths in both assets and third-paty css-styles.

Of course, these are not full-fledged corrections and the method context.asset_path(path) must be corrected. But I clearly and specifically showed exactly what the problem is.

Please fix this bug in the library.

@rafaelfranca
Copy link
Member

Closing since this is an issue in sprocekts-rails

@rafaelfranca rafaelfranca closed this as not planned Won't fix, can't repro, duplicate, stale Jul 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants