I am trying to import an image from JS in production. I have rails 6 with esbuild
and jsbundling
, and it works fine in development environment, but in production, images being imported from JS has no fingerprint. I've seen a Stack Overflow post like this one (Specific Image Not Loading After Rails 7 ESBuilt Update), but their solution was to move those image files to public/images folder. It's a solution, but this requires changing all JS files to load from a different location....
esbuild.config.js
const path = require('path')
require("esbuild").build({
entryPoints: ['app/javascript/application.js'],
bundle: true,
tsconfig: path.join(process.cwd(), "tsconfig.json"),
outdir: path.join(process.cwd(), "app/assets/builds"),
absWorkingDir: path.join(process.cwd(), "app/javascript"),
watch: process.argv.includes("--watch"),
incremental: process.argv.includes("--watch"),
assetNames: "[name]-[hash].digested",
publicPath: "/assets",
plugins: [],
loader: {
".js": "jsx",
".locale.json": "file",
".json": "json",
".png": "file",
".jpeg": "file",
".jpg": "file",
".svg": "file",
}
}).catch(() => process.exit(1))
Importing Image, where JavaScript file is under app/javascript/
, and image directory is app/javascript/images
.
import Logo from './images/logo.svg';
Logo file in app/assets/builds
in development
➜ work git:(master) ls -la app/assets/builds/logo*
-rw-r--r-- 1 yang staff 4710 Jun 26 16:30 app/assets/builds/logo-M5AMKDMO.digested.svg
Logo file referenced in HTML after the page was rendered in DEVELOPMENT
<img class="logo" src="/assets/logo-M5AMKDMO.svg">
Logo file in public/assets
in production
-rw------- 1 u18958 dyno 4710 Jun 24 02:48 public/assets/logo-M5AMKDMO.digested-abddddc51310100c173ac88db35b27e1492e730c91cb447b346f772532ac85ba.svg
Logo file referenced in HTML after the page was rendered in PRODUCTION
<img class="logo" src="/assets/logo-M5AMKDMO.svg">
The img tag in production is totally missing the fingerprint by asset precompile. The server is deployed in Heroku, and they do precompile during deployment.
One more thing: I did one test by setting config.assets.compile
to true
, and that actually worked, but you shouldn't do that per this post (config.assets.compile=true in Rails production, why not?), so I feel quite stuck.
I'm guessing you have an old version of sprockets. v4.1.0
is when .digested
thing works properly:
# Gemfile
gem "sprockets", ">= 4.1.0"
// esbuild.config.js
require('esbuild').build({
watch: process.argv.includes('--watch'),
entryPoints: ['app/javascript/application.js'],
outdir: 'app/assets/builds',
bundle: true,
publicPath: '/assets',
assetNames: '[name]-[hash].digested',
loader: {
'.svg': 'file'
}
})
// package.json
"scripts": {
"build": "node esbuild.config.js",
...
I'm not sure how you're getting /assets/logo-M5AMKDMO.svg
path, with this config it should be /assets/logo-M5AMKDMO.digested.svg
.
// app/javascript/application.js
import logo from './images/logo.svg'
console.log(logo) // => "/assets/logo-2RBQBF7Y.digested.svg"
That's the url generated by esbuild, it doesn't change in different rails environments. .digested
is a recent addition, it's meant to skip the extra sprockets digest:
# gem "sprockets", "4.0.3"
>> helper.asset_path("logo-2RBQBF7Y.digested.svg")
=> "/assets/logo-2RBQBF7Y.digested-0c3d0155537d0e4f72356e8d9c7b41e8aa779b007b82f88d5af2766ba6166f45.svg"
# gem "sprockets", "4.1.0"
>> helper.asset_path("logo-2RBQBF7Y.digested.svg")
=> "/assets/logo-2RBQBF7Y.digested.svg"
$ RAILS_ENV=production bin/rails assets:precompile
$ ls public/assets/logo*
public/assets/logo-2RBQBF7Y.digested.svg
public/assets/logo-2RBQBF7Y.digested.svg.gz
You can also just rename files after precompilation:
>> URI.open("http://localhost:3000/assets/logo-2RBQBF7Y.digested.svg").read
/home/alex/.rbenv/versions/3.1.2/lib/ruby/3.1.0/open-uri.rb:364:in 'open_http': 404 Not Found (OpenURI::HTTPError)
>> Rails.root.join("public/assets").glob("*.digested*").each do |f|
f.rename(f.to_s.remove(/\.digested\K-\w+/))
end
>> URI.open("http://localhost:3000/assets/logo-2RBQBF7Y.digested.svg").read
=> "<svg>i am image</svg>\n"