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

Images in engines: Asset was not declared to be precompiled in production. #426

Closed
brendon opened this issue Nov 23, 2016 · 8 comments
Closed

Comments

@brendon
Copy link

brendon commented Nov 23, 2016

Expected behavior

Prior to upgrading to Sprockets 3 I had image assets within my engine's directory at locations like:

assets/images/folder1/folder2/image.png

These images would be automatically precompiled. Images were always automatically precompiled without having to specify them in the precompile list weren't they?

Actual behavior

Now I get the error message:

Asset was not declared to be precompiled in production.
Add Rails.application.config.assets.precompile += %w( folder1/folder2/image.png ) to config/initializers/assets.rb and restart your server

Images nested deep in folders in the main app's assets/images directory are precompiled fine without needing to be declared.

System configuration

  • Sprockets version: 3.7.0
  • Ruby version 2.1.9

Example App

I'm happy to make one once I've confirmed that my assumptions about images and precompilation are correct on Sprockets 3.

@richpeck
Copy link
Contributor

You need to use link_tree to include the assets.

I recently had the same problem with a gem, and found that by explicitly including the CSS file in config.assets.precompile, I could call link_tree in the CSS file itself:

https://github.com/richpeck/exception_handler/blob/master/lib/exception_handler/engine.rb#L19

#config/application.rb
config.assets.precompile << %w(x.css)

https://github.com/richpeck/exception_handler/blob/master/app/assets/stylesheets/styles/_base.css.erb#L8

#app/assets/stylesheets/x.css
/*
   *= link_tree ../../images
*/

If you give me a link to your repo, I will be able to give pointers if you need.

@brendon
Copy link
Author

brendon commented Nov 23, 2016

Thanks Richard, that's strange hey? Is there a reason why images in the main applications assets/images (and recursively) are automatically added to the precompile list but the same isn't the case for engines? Does sprockets add a link_tree behind the scenes for the main applications assets/images directory?

I often don't have an associated css file in an engine, and these images are referenced from a view:

<%= image_tag 'folder1/folder2/image.png' %>

Does that mean I'd need to add the link to the top of the view using the image?

Is there a way to replicate that happens in the main application where the entire images directory is precompiled? Ideally (since we have about 25 engines) I'd like to be able to just glob those out in one place perhaps in an initialiser.

Unfortunately my application isn't publicly accessible. Depending on what I hear back from you (if I can't figure it out after that), I'll make an example app and point you to that :D

Thanks again for your help Richard, it's really appreciated :)

@richpeck
Copy link
Contributor

I'm not sure why Engine assets are not added to the precompilation list.

I used to add all the images to config.assets.precompile but since Sprockets 3.7+ uses manifest.js, you're meant to let that call all the assets.

If you don't have any CSS file available, why not add the globbed filepath to your config.assets.precompile array?

# engine.rb
config.assets.precompile << %w( folder1/**/* )

Does that mean I'd need to add the link to the top of the view using the image?

If there is no other place to call the directives, you may have to although it will be extremely hacky.

I found our solution by observing what font-awesome did. They included their dependent assets in their css file, and it worked every time.

I am experimenting with adding a manifest.js to an engine but it doesn't look like it's supported yet.

@brendon
Copy link
Author

brendon commented Nov 23, 2016

Ok, I've figured it all out. Searching through the rails repo for link_tree brought me to this:

https://github.com/rails/rails/blob/92703a9ea5d8b96f30e0b706b801c9185ef14f0e/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt

Which tipped me off about the new config/manifest.js concept mentioned here:

http://eileencodes.com/posts/the-sprockets-4-manifest/

You can see the new file at railsdiff:

http://railsdiff.org/4.2.7.1/5.0.0.1

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

Though since sprockets 4 hasn't been released yet, that functionality doesn't appear to be active. @eileencodes mentions the branching in the code that decides what goes into config.assets.precompile:

https://github.com/rails/sprockets-rails/blob/v3.2.0/lib/sprockets/railtie.rb#L104

and I noticed the concept of LOOSE_APP_ASSETS that only seems to be doing the magic on the base applications assets directory.

My solution was to add this to initializers/assets.rb (all my engines are stored in /components):

LOOSE_COMPONENT_ASSETS = lambda do |logical_path, filename|
  Dir.glob(Rails.root.join('components/**/app/assets')).any? do |assets_directory|
    filename.start_with?(assets_directory) &&
    !['.js', '.css', ''].include?(File.extname(logical_path))
  end
end

Rails.application.config.assets.precompile += [LOOSE_COMPONENT_ASSETS]

It works great so far :)

I'm sure there's a way to access a list of engines currently being used in the application and glob their directories, but perhaps it'll be hard to differentiate between inline engines and gem engines, so this could blow up.

I'll leave this open unless you think this is the best solution?

@brendon
Copy link
Author

brendon commented Nov 23, 2016

Thanks again Richard, just saw your update. My method seems to be working well and operates the same way as the base application. I didn't realise one could pass a lambda to config.assets.precompile. I assume the application throws every file in the whole application at it to check if it should be a compiled asset?

@brendon
Copy link
Author

brendon commented Nov 23, 2016

Oh, and the solution is moot given it's all about to change when manifest files come into force. On on Rails 4.2 so I'll just be careful not to upgrade sprockets again before Rails 5.0 :)

@richpeck
Copy link
Contributor

No problem, glad you got it working.

manifest.js is up in the air IMO. Best thing is to get it working for yourself then fix as things become more standardized.

Don't forget to close the issue if you fixed it!

@brendon
Copy link
Author

brendon commented Nov 23, 2016

Thanks Richard, will do. Hope this helps others who encounter the same problem.

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

No branches or pull requests

2 participants