jsbundling-rails: Can't load images in JavaScript files

Hi,

I’m trying to migrate my Rails + React application from Webpacker to Jsbundling + Esbuild following switch_from_webpacker guide to first replace webpacker for Jsbundling

Everything is bundled to “app/assets/builds” by webpack, images included.

application.js and application.css are working fine in application.html.erb but the images in jsx files aren’t loading.

My React component are trying to load “0bdd8103a525a17c3528e4f40d701b33.svg” the same as output in assets/builds folder but public/assets has the digested version of my svg “0bdd8103a525a17c3528e4f40d701b33-b8fb0ca2943e7724f64b3717f8a3e2b3ecb321adac1053107f2c89142efffd17.svg”

import React from "react";
import Logo from "Assets/logo-brand.svg";

export default function Img() {
  return <img height="46px" src={Logo}  />;
}

I’m not sure if the correct approach is to move all assets from assets/build to public/asssets with a rake or if it is possible to skip sprockets-rails digest for images and rely on webpack digested file or if there is another way to load imagens inside js files

Thanks for jsbundling effort!

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 2
  • Comments: 19 (6 by maintainers)

Commits related to this issue

Most upvoted comments

@richardkmiller,

This is what is happening with my file.svg: file.svg -> webpack build -> app/assets/builds/[webpack-hash].svg app/assets/builds/[webpack-hash].svg -> sprockets -> public/assets/[webpack-hash]-[sprockets-hash].svg

my JS component is resolving the image as assets/[webpack-hash].svg and can’t find it because only assets/[webpack-hash]-[sprockets-hash].svg is accessible.

Same general problem, but with rails 7.0 + propshaft + jsbundling + esbuild, specifically with react-leaflet. Using --loader:.png=file --public-path=/assets and am trying to override the paths as mentioned in https://github.com/PaulLeCam/react-leaflet/issues/453#issuecomment-731732137 and the file ends up as assets/builds/marker-icon-2V3QKKVC.png, but rails can’t find this file without it’s own digest. I’ve also tried using --entry-names=[name]-[hash].digested on esbuild, but this doesn’t appear to apply to the file loader.

Edit: Digging a little deeper into the esbuild config, I got this to work by using the asset-names parameter: --loader:.png=file --public-path=/assets --asset-names=[name]-[hash].digested

Same problem here. app/asset/builds is an intermediate space for the output from webpack. Sprocket moves js and images to public/assets and add hash to the tail of filename. So even we remove hash added by webpack can’t solve the problem.

For example, app/javascript/application.js import app/javascript/logo.svg(or other places) in JS way. After webpack building, we have app/assets/builds/application-abc.js and app/assets/builds/logo-abc.svg. Then sprocket produces public/assets/application-abc-123.js and public/assets/logo-abc-123.svg. We can resolve the application.js inside the public folder by javascript_include_tag "application". But the image lost due to the image filename inside the JS is logo-abc.svg rather than logo-abc-123.svg.

@jon-sully , It’s working with JSB-Rails + Webpack 5!

I’m using webpack with this config and now sprockets are not re-stamping the assets.

Also you need sprockets >4.0.3 -> https://github.com/rails/sprockets/blob/master/CHANGELOG.md

 {
        test: /\.(png|jpe?g|gif|svg)$/i,
        use: [
          {
            loader: "file-loader",
            options: {
              name: "[name]-[hash].digested.[ext]",
            },
          },
        ],
      },

@cirdes PR #58 should be helpful to you. The short answer is to open package.json under "scripts" and edit the esbuild script to include --public-path=/assets. In the PR I suggested --public-path=assets but after further use, I found an issue that was resolved by using the leading slash: --public-path=/assets. Let me know if that doesn’t work for you.

I’m having a similar, but maybe slightly different problem. After upgrading from Rails 6 and switching from Webpack to esbuild, I can load images in development, but not after deploying to production (Heroku). In production, it looks like an additional fingerprint is added to the asset path.

remote:        $ node esbuild.config.js
remote:        Done in 2.79s.
remote:        I, [2022-06-15T20:31:18.764370 #688]  INFO -- : Writing /tmp/build_ec2e3794/public/assets/logo-Y5WNYHYB.digested-6f90874fa6e725ac79c88da3fc17b3dd7739c0a66c44586d8d247951fe8c06d6.png

My esbuild.config.js looks like this:

const path = require("path")
const vuePlugin = require("esbuild-vue")

require("esbuild").build({
  entryPoints: ["application.js", "compartment.js"],
  bundle: true,
  loader: {".png": "file"},
  outdir: path.join(process.cwd(), "app/assets/builds"),
  absWorkingDir: path.join(process.cwd(), "app/javascript"),
  publicPath: "/assets",
  assetNames: "[name]-[hash].digested",
  watch: process.argv.includes("--watch"),
  plugins: [vuePlugin()],
}).catch(() => process.exit(1))

A note for people hitting the same problem as me: Your asset name must have a dash before the digest.

The default value for file-loader’s name option is [contenthash].[ext], so I changed it to [contenthash].digested.[ext]. However, sprockets uses a regex that looks for the hyphen.

After changing the option to [name]-[contenthash].digested.[ext] or asset-[contenthash].digested.[ext], the extra digest is no longer appended.

@richardkmiller, moving forward to JSB-Rails with Esbuild I had to point sprockets to https://github.com/rails/sprockets/pull/726 and use --public-path=/assets

Images assets were working but not using my CDN to serve the assets. So I came with this solution: “–public-path=$CDN_URL_FULL/assets”

esbuild app/javascript/*.* --bundle --outdir=app/assets/builds --loader:.png=file --public-path=$CDN_URL_FULL/assets --asset-names=[name]-[hash].digested

I’m not sure if esbuild should generate assets string with cdn domain or if sprockets should somehow be responsible to rewrite urls with CND.

any thoughts about that?

Wanted to comment back a solution to folks that are using JSB-Rails + Webpack 5 not ESBuild — specifically in my case how to setup Webpack to output the asset/resource files with the right name such that Propshaft doesn’t re-stamp them with a new digest. This should work for folks using Sprockets instead of Propshaft even without https://github.com/rails/sprockets/pull/726 merging (if I’m reading the diff correctly).

      {
        test: /\.(png|jpe?g|gif|eot|woff2|woff|ttf|svg)$/i,
        type: 'asset/resource',
        generator: {
          filename: '[name]-[hash].digested[ext][query]' // Setup asset filename so Propshaft doesn't re-digest
        }
      },

That helped fix things for us.