Rails webpacker is not reading app/javascript/images folder - ruby-on-rails

Using the default configuration of rails/webpacker I could not read a file through the <%= asset_pack_path 'images/calendar.png' %> helper in a view.
Reading the docs it seems easy to do, but something goes wrong trying access the page:
Webpacker can't find application.css in /path/manifest.json. Possible causes:
You want to set webpacker.yml value of compile to true for your environment
unless you are using the webpack -w or the webpack-dev-server.
Webpack has not yet re-run to reflect updates.
You have misconfigured Webpacker's config/webpacker.yml file.
Your Webpack configuration is not creating a manifest.
Your manifest contains:
Am I missing some configuration?

It seems a docs misundertand as the answer:
asset_pack_path only references assets used as pack (i.e. JS packs file) or assets used inside a pack like a font, image or styles referenced that's why it's not present inside manifest.json. asset_pack_path doesn't look inside any folder like rails view helpers for assets.
Perhaps use Rails image_path helper if you want to reference images that’s not used inside packs.

The webpacker documention tells us about using:
require.context('../images', true)
in an entrypoint (app/javascript/packs), to include all the images under app/javascript/images into the exported assets.
If you are using webpacker6.pre/beta, there is currently a bug which flattens nested folders, though.

Related

Rails - why is /public/assets gitignored when I need this for static imaged

In my Rails (Rails 7) app, I have some small assets (a logo and a favicon), which I want to render in production as well as in development.
I put the assets in /app/assets/images.
Since setting # config.assets.compile = true is not recommended, i run RAILS_ENV=production bundle exec rake assets:precompile. It then builds a whole lot of files (it runs esbuild app/javascript/application.tsx since I have a typescript react app running in this rails app.) and puts them in /public/assets/. But this folder is .gitignored by default. Since the folder is approx 5.5 Mb I can see why. Now, heroku docs tell me to add /public/assets/ to git and then my assets should show, but why then is this directory gitignored by default?
Am I missing something? Should I just remove the dir from my .gitignore file?
Or could I just put the assets in the public folder directly? If so, how do I add an image referencing the public folder in an erb file?
The home for static assets is the /public folder. In a default Rails 7 app you will find some error pages (e.g. /public/404.html) and icons (e.g. /public/favicon.ico) already in that folder. Files in this folder will be available both directly, example.com/favicon.ico, and under the public path, example.com/public/favicon.ico. Serving public files can be disabled, see below.
To answer your sub-question, "how do I add an image referencing the public folder in an erb file?" you need to make sure you tell Rails it's an absolute path, not a relative one somewhere in the asset pipeline. This is done with a leading forward slash.
The following compares including a logo called logo.png when it's stored in app/assets/images vs /public
<%= image_tag "logo.png" %>
<!-- becomes <img src="/assets/logo-1f04fdc3ec379029cee88d56e008774df299be776f88e7a9fe5.png"> or similar -->
<%= image_tag "/logo.png" %>
<!-- note the leading slash. This becomes <img src="/logo.png"> -->
<img src="/logo.png">
<!-- or use plain HTML if you don't need/want to use the helper -->
You can also use sub-directories; /public/images/logo.png would be available at /images/logo.png (or image_tag "/images/logo.png").
The second paragraph of chapter 2 of The Asset Pipeline Rails Guide contians more information. It mentions that this functionality depends on whether config.public_file_server.enabled is set.
In Rails 7 that config defaults to ENV["RAILS_SERVE_STATIC_FILES"].present? in config/production.rb. If you're using Heroku you can check this variable with heroku config:get RAILS_SERVE_STATIC_FILES.
The guide also has more information on how to adjust all this behaviour to have these files served by your upstream web server (e.g. nginx) or from a CDN.
Thanks for the comments, I went with adding the assets to the public folder directly and this works fine. It would be nice though if Heroku would either support a way to generate the assets after deployment by default, or describe an option to do it like I did in their docs.

RAILS 6 + Webpacker -- route error for packs/css|js/application-* in production environment

In development mode, all looks great. But when I switch over to production, bootstrap isn't found. When I look at the logs, I see errors like:
ActionController::RoutingError (No route matches [GET] "/packs/js/application-2a5806e943e281221741.js"):
This is true for both CSS and JS. When I look inside public/packs the files do exist in the corresponding subdirectory (js for the .js file, etc). So, webpacker is did it's job but puma isn't finding it. Any ideas? This is an old rails app that was migrated up several versions of rails, so I'm sure there is something I missed when tying everything together.
To expand on Tenzin's comment which I believe is the correct answer to the issue, Rails in development will always re-render assets and serve them. This is slow, but useful for seeing quick changes while developing.
When we want to deploy to production, the issue is Rails begins to serve static files which means that the server is no longer going to render them on every request. It will render them once and serve them forever. With webpacker, you can find the places these assets are served in webpacker.yml
public_root_path: public
public_output_path: packs
So if you are deploying or trying to run production locally, first be sure to compile your assets for production so these static files are created.
rake assets:precompile RAILS_ENV=production
Then make sure that wherever the server is running it has an ENV variable RAILS_SERVE_STATIC_FILES set to anything. As you can see from production.rb,
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
Is simply checking if this variable is present. If it is then it will not re-render as we discussed and it will look for the static files shown in webpacker.yml.
As another small note, if you are using something like a React UI or Bootstrap and the CSS is missing, be sure that you are importing the CSS according to the library's documentation.
For example, in Bootstrap, you will need to create an application.scss file in app/javascript/packs/stylesheets (really wherever in the /javascript folder) and import that in /javascript/packs/application.js.
In application.scss you'll want to
import "~bootstrap/scss/bootstrap"
This is of course after you have added Bootstrap to your package.json with something like
yarn add bootstrap

Trigger webpacker compile when file outside javascript folder changes

I've run into an issue with webpacker in my rails app during development. Webpacker should also compile when some of the related config files change, but doesn't since they aren't in the app/javascript folder.
Is there to have webpacker also watch additional files for changes?
More detailed:
I load my css through webpacker. For this I'm using TailwindCSS and postCSS. I tend to change the tailwind config file (located in the application root) quite a bit, to automatically generate additional utilities and/or variant.
However, when I change the tailwind config file it doesn't trigger webpacker to recompile on the next page load. It only recompiles when I change one of the files in the app/javascript folder - even if it is just adding an extra blank line. I would like to avoid having to manually trigger webpacker to recompile.
I have changed nothing to the Rails defaults for webpacker.
In config/webpacker.yml, you want to add files and directories to the resolved_paths section.
resolved_paths:
- app/assets
- tailwind.config.js
There’s more information in this article https://rossta.net/blog/why-does-rails-install-both-webpacker-and-sprockets.html

Ruby on Rails - setting specific path to directory in rails app

Hello I am attempting to implement Trumbowyg emojis plugin in my ruby on rails application. So I have referred to the below link for guidance on how to implement the plugin.
https://github.com/Alex-D/Trumbowyg/blob/master/examples/plugins/emoji.html
$('#editor').trumbowyg({
btns: [['emoji']]
});
function initEmoji() {
emojify.setConfig({
img_dir : '../../bower_components/emojify.js/dist/images/basic/',
});
emojify.run();
}
$('.trumbowyg-editor').bind('input propertychange', function() {
initEmoji();
});
initEmoji();
How do I store the images in a directory and make reference to the directory under img_dir (as shown above) in a rails app?
Option 1: Embedding into Asset Pipeline
Without knowing exactly how this Javascript library works, the image path needs to be added to the Asset Pipeline. Otherwise rake assets:precompile will not include all of the emoji images in the manifest file it generates and will not know how to serve those assets in the production environment (assuming default Production environment configuration).
See #config/initializers/assets.rb
# Add additional assets to the asset load path
Rails.application.config.assets.paths << Rails.root.join('path', 'to', 'emoji', 'image', 'directory')
Option 2: Serve static images from /public
Another option is to not serve these files from the Asset Pipeline at all. It may be too much trouble to coerce the Asset Pipeline into serving these assets correctly without modifying the Javascript library to use asset-url in all the JS and CSS files.
Instead, copy the files to a directory under /public so the Rails app can serve them. Then adjust the img_dir in your Javascript configuration to be the path where the files are in /public. Ex: /emoji_images if you put the files in /public/emoji_images.
Depending on how you're hosting the app, you may have to configure serve_static_files in config/environments/production.rb.

Middleman images/fonts from bower

I'm wondering what the best way is to use images & fonts from bower packages with middleman. As an example, I'm trying to add the slick.js carousel to my project. It's on bower and includes css, js, images, and fonts in the bower code.
With middleman, I have things set up where I've added the bower_components directory to the path for sprockets and compass, so the scss and js files are getting compiled correctly and working fine.
But the images and fonts aren't getting put anywhere where they'll be used. The slick.js library uses scss and is set up to use the compass image-url and font-url functions if they exist, meaning I need to somehow get the assets from the bower_components directory to be served from the same place as all my own images and fonts, and in a way that works in both the development middleman server mode as well as when running build.
How do I do this?
Obviously possible solutions are just to vendorize the slick.js library directly into my code or include it from the cdn where it's already hosted and not worry about not having it compiled into my single css and js files. Either could work fine but I'm wondering about the general case, surely this is common scenario for anyone using bower and middleman.
I figured it out - I thought compass was for requiring scss files and sprockets was just for the js, but middleman also uses sprockets (the middleman-sprockets library) for copying arbitrary static assets.
It's a bit manual and verbose (if there were a lot more files middleman suggests writing a script to auto-discover them by file extension types and import them) but my solution is to include the following in the config.rb file:
# set local vars I'll need to access later
images_dir = 'images'
set :images_dir, images_dir
# ... other config
sprockets.import_asset('slick-carousel/slick/ajax-loader.gif') {|p| "#{images_dir}/ajax-loader.gif"}
I use grunt, but it's the same issue. Generally you have the following options:
-Commit what you need in the bower_components directory right in to source control and reference your resources from there (somewhat recommended especially if something external is down when you are doing a build), or if you don't like exposing bower_components in URLs, create a route that directs to your bower_components folder
-Copy components on build/middleman script execution to a specified path. There will be no resources to check in for this option, you just choose a destination to reference in your code and have middleman copy your components out there.

Resources