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

Rails Engine not compiling assets unless explicitly declared #542

Closed
iamdriz opened this issue Jan 15, 2018 · 15 comments
Closed

Rails Engine not compiling assets unless explicitly declared #542

iamdriz opened this issue Jan 15, 2018 · 15 comments

Comments

@iamdriz
Copy link

iamdriz commented Jan 15, 2018

I'm raising this as an issue as it feels like it doesn't work as expected or as described in the documentation and looking around the internet it seems there a number of questions/issues raised around this that all have hacky solutions of which either don't work, are messy or introduce additional problems. It could be just a unknown... or perhaps missing feature of sprockets but it seems that it's certainly something that isn't easy to implement and hard to believe that Rails doesn't support it.

So I have a Rails Engine that contains assets like so:

/assets
  /images
    /engine_name
      /image.png
    /home
      image.png
  /image.png

And in both my Host application and Engine I want to be able to use these images like so:

<%= image_tag('engine_name/image.png') %>   
<%= image_tag('home/image.png') %>  
<%= image_tag('image.png') %>

Now if I explicitly set these files to be precompiled in the assets.rb or the engine.rb file then they work:

app.config.assets.precompile << "engine_name/image.png"
app.config.assets.precompile << "home/image.png"

And I can even override the images in the Host application if I have a similar named image (and parent folder) that matches the one in the Engine.

But I want to make it so that I don't have to call them by name and instead just load them all in at the Engine level so I don't have to declare anything by file name or in each Host application.

I have tried adding this to my engine.rb:

module EngineName
  class Engine < ::Rails::Engine

    initializer "EngineName.assets.precompile" do |app|
      app.config.assets.precompile << "#{Rails.root}/app/assets/**/*"
    end

  end
end

But it doesn't work... I still end up getting the error:

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

So I've tried some alternate ways in the engine.rb:

This also works (so I don't have to specify each folder):
app.config.assets.precompile << "**/image.png"
and so does this (so I don't have to specify the name or folder):
app.config.assets.precompile << "**/*.png"

But I don't want to have to specify the extension of the image (as I might have lots)

So I've tried:
app.config.assets.precompile << "**/*.*"
But that does not work. Seems you MUST specify an extension.

So I tried this:
app.config.assets.precompile << "**"
Which does work for the images, but then breaks all the SASS and raises Sass syntax errors about unknown variables and that! So this isn't a solution either.

Is there a proper way to be able to get an Engine's assets to precompile without having to resort to a manic regex to only gets certain files?

@iamdriz iamdriz changed the title Rails Engine not compiling assets Rails Engine not compiling assets unless explicitly declared Jan 15, 2018
@rafaelfranca
Copy link
Member

Please use StackOverflow for questions/help, where a wider community will be able to help you. We reserve the issues tracker for issues only.

@iamdriz
Copy link
Author

iamdriz commented Jan 15, 2018

@rafaelfranca I have (https://stackoverflow.com/questions/48266471/make-rails-engine-compile-all-assets) But as stated above, several Stack Overflow questions and related articles about this same issue have been raised by others, so it seems like something that isn't working as expected. Does Rails require each file to be listed or each extension to be listed in the Engine?

@rafaelfranca
Copy link
Member

It is indeed working as expected.

Rails doesn't require each file to be listed in the precompile list. But it requires each engine to register its assets. You can do it in the same way rails applications does. If you want to register all assets in your engine you need to specify that using a asset manifest and one of the link* directives https://github.com/rails/sprockets#link.

@iamdriz
Copy link
Author

iamdriz commented Jan 15, 2018

@rafaelfranca Thanks for the response. I did actually try that. So in the Engine in /app/assets/config/engine_name_manifest.js I added: link_tree '../images assuming that it would then make the Engine compile all images (including all subfolders of images) and then make them available. But I get the same error as reported earlier that Rails expected it to be added to the precompile array. Does this not work this way? Can you not specify them in the Engine manifest file? Thanks again!

I also tried:

//= link_tree ../images
//= link ../images/image.png
//= link ../images/engine_name/image.png
//= link ../images/home/image.png

But still can't find the images (unless I do it in the engine.rb file).

An additional question I have then, IF "it requires each engine to register its assets" then how come I can use the JS, CSS, and FONT files without having to do the precompile part? It's only the images that are not working unless precompiled in the engine.rb.

@rafaelfranca
Copy link
Member

have you added config/engine_name_manifest.js in app.config.assets.precompile?

@rafaelfranca
Copy link
Member

An additional question I have then, IF "it requires each engine to register its assets" then how come I can use the JS, CSS, and FONT files without having to do the precompile part? It's only the images that are not working unless precompiled in the engine.rb.

Nothing should work if you don't add to precompile. If it is working, please create a minimal application reproducing this issue.

@iamdriz
Copy link
Author

iamdriz commented Jan 16, 2018

@rafaelfranca I hadn't. But I've added it now to my Engine's /lib/engine.rb

initializer "engine_name.assets.precompile" do |app|
  app.config.assets.precompile << "config/engine_name_manifest.js"
end

and the contents of the manifest file are:

//= link_tree ../images
//= link ../images/image.png
//= link ../images/engine_name/image.png
//= link ../images/home/image.png

So I've tried to register the whole images folder in the Engine as well as each image individually, but images are still not working unless I add them using app.config.assets.precompile.

I'm using Rails 5.1.4 and Sprockets 3.7.1 if that helps?

@iamdriz
Copy link
Author

iamdriz commented Jan 18, 2018

@rafaelfranca Should the above have worked?

@richpeck
Copy link
Contributor

richpeck commented Jun 20, 2018

I had this problem before and you are indeed correct in that it doesn't work with the manifest file.

The only way to get it to work is to explicitly require the top-level .css / .js files for your app in the config.assets.precompile hook, and within those files call the various link_tree resources you require.

We presently use:

# lib/engine.rb (don't need to wrap in initializer) 
config.assets.precompile << 'exception_handler.css' 

# app/assets/stylesheets/exception_handler.css (within engine file tree)
/*
  *= link_tree ../../images
*/

This works 100% as intended. Would obviously love to have the manifest working - but it doesn't at present (sprockets 4.0.0.beta8).

@brodjustice
Copy link

brodjustice commented Jul 28, 2018

@richpeck
Thanks for that. Your method seems to work. Some observations:

  1. The Rails documentation is very misleading, as it says "Assets within an engine work in an identical way to a full application..."
  2. With Rails 3 and the earlier version of sprockets it DID work automatically
  3. It does work automatically in development environment on Rails 4
  4. Much as I appreciate your workaround, it feels wrong to put paths to image and font assets inside a css "manifest"

@emilebosch
Copy link

👋 Wow, this took me 3 hours to figure out. Shall I update the rails documentation on this? I am. building a bunch of themes as engines, and it totally hurt my head, because the theme also has images.

@masylum
Copy link

masylum commented May 8, 2020

👋 Hi there. I'm probably late to the party but I've been debugging this very same issue in the last few days and I've finally discovered why this doesn't work.

First, sprockets-rails will include manifest.js by default (https://github.com/rails/sprockets-rails/blob/master/lib/sprockets/railtie.rb#L107) but this is only meant for the main Rails app. This means that you have to add each engine's manifest to the assets.precompile array.

If you look at this line: https://github.com/rails/sprockets-rails/blob/5f75d4196b046ec6bef573b9d5e3292df7afb5e8/lib/sprockets/railtie.rb#L60
you will see that sprockets will set config.assets.paths to include all the subdirectories of app/assets. This includes engine_name/app/assets/config. This means, that in order to specify the manifest for a given engine, you shouldn't add the config/ folder:

# engine_name/config/initializers/assets.rb
app.config.assets.precompile << "engine_name_manifest.js"

It's still important to namespace the manifest with the engine name or it will resolve wrongly.

@silviorelli
Copy link

I just spent half a day on this issue until I found @masylum's comment, and solved adding inside lib/engine_name/engine.rb:

initializer "engine_name.assets.precompile" do |app|
  app.config.assets.precompile << "engine_name_manifest.js"
end

Then the manifest gets loaded correctly by the main app that includes the engine.
I agree the documentation could be improved for sure!

@brendon
Copy link

brendon commented Jul 30, 2020

Is there any difference between these three?:

Within lib/engine_name/engine.rb

initializer "engine_name.assets.precompile" do |app|
  app.config.assets.precompile << "engine_name_manifest.js"
end

Within lib/engine_name/engine.rb

config.assets.precompile += %w( engine_name_manifest.js )

or in an initialiser engine_name/config/initializers/assets.rb

app.config.assets.precompile << "engine_name_manifest.js"

I've settled on #2 as it's the simplest but is there any ordering issues here?

@taylorthurlow
Copy link

Is there any difference between these three?

@brendon I don't think there's any functional difference but I'd go with number 3. I treat engine.rb as analogous to application.rb, and I think generally you should put things in their more-relevant initializer file rather than polluting application.rb.

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

9 participants