Phx 1.5 to 1.6, ESbuild + SCSS roadblock

Greetings from new member with >1yr phoenix/elixir experience.

I’m working through the upgrade of my phoenix app from 1.5 to 1.6.6 and am stuck on the following error pertaining to local assets. After following the boilerplate instructions, I was getting the same type of error code for my SCSS file. But with the help of my weekly tutor/coach/chief roadblock-unblocker, we’ve been able to that to load. We’re stumped on this one, though. The app uses SCSS, but not particularly Tailwind or Dart.

'✘ [ERROR] No loader is configured for “.jpg” files: static/images/marketplace.jpg

css/app.scss:2600:20:
  2600 │   background-image: url(../static/images/marketplace.jpg);
       ╵                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

✘ [ERROR] No loader is configured for “.gif” files: static/images/loading_spinner.gif

css/app.scss:4071:24:
  4071 │   background-image: url("../static/images/loading_spinner.gif");
       ╵                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

2 errors

I suspect this is not enough information to troubleshoot the unique setup of the app, which was developed by a team of pros. So I think my ask is for strategic guidance on our options:

  1. Work to integrate something like Tailwind, which I’m told has its share of qwerks.
  2. Stay the course and try to figure out the current roadblock.
  3. Other thoughtful suggestions?

I really appreciate this community. The Phoenix/elixir framework makes web-development more accessible and fun, imo.

This sounds very similar to the issue discussed in this thread:

See the PR linked in that message for details; sounds like there’s a config option needed for esbuild.

3 Likes

Hi @al2o3cr

Thank you for giving some direction! That was very helpful.
Turns out we commented out the boilerplate config below for esbuild as part of the effort to get the SCSS to load at all. So the exact answer there doesn’t currently work for me, but it points me in the right direction. I’ll do some reading on the esbuild docs and share this insight with my coach. I’ll post again when we’ve solved it.

Cheers

config :esbuild,
version: “0.12.18”,
default: [
args:
~w(js/app.js --bundle --target=es2016 --outdir=…/priv/static/assets),
cd: Path.expand(“…/assets”, DIR),
env: %{“NODE_PATH” => Path.expand(“…/deps”, DIR)}
]

In the interim, I’ve removed the background image instructions from the CSS and now my app loads beautifully. Doesn’t solve the problem, but it was super helpful to know it was isolated to just that CSS command.

Huge cheers!

Esbuild is little different from webpack. Phoenix docs added a new section Asset Management in 1.6 and it recommends the following.

Finally, all other assets, that usually don’t have to be preprocessed, go directly to “priv/static”.

Note: You have to copy those images, fonts, etc directly to respective folders in priv/static folder instead of keeping them in assets folder.

Images, fonts and external files section details about how to deal with images, etc. It recommends using --external flag.

Few observations when using esbuild:

  • you can configure a loader for a file type in esbuild, example --loader:.gif=file if esbuild has to copy the file. esbuild - Content Types. (phoenix recommends that assets which don’t require preprocessing to be directly in /priv/static - in that case this might not be required)
watchers: [
    # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
    esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch --loader:.svg=text --loader:.eot=file --loader:.woff=file --loader:.woff2=file --loader:.ttf=file)]}
  ]
  • or use --external:/images/* and referring to them with absolute paths like /images/* - esbuild - API.

  • configure DartSass to process the SCSS file. EsBuild wont handle scss file and css file is generated by DartSass in priv/static folder.

config :dart_sass, 
  version: "1.49.0",
  default: [
    args: ~w(--error-css vendor/bootstrap_docs/scss/docs.scss ../priv/static/assets/docs.css),
    cd: Path.expand("../assets", __DIR__)
  ]

I would use DartSass if I am dealing with SCSS files.

Hi Kartheek,
Your follow-up was timely. I am indeed facing an issue that I think pertains to this topic of external files and loaders. My setup is different than the default and I’m not sure how to adapt it.

Backtracking for context…

  1. We followed the instructions here to enable esbuild plugins: https://hexdocs.pm/phoenix/asset_management.html#esbuild-plugins

  2. Then we installed the esbuild sass plugin:
    https://www.npmjs.com/package/esbuild-sass-plugin

Everything fires up nicely on my local machine, but we’ve hit some roadblocks on the deployment side.

  1. First we hit the ~‘no loader for xyz image type’ errors. We fixed those by updating the build.js script created in step 1. above to include the file types:
const loader = {
  // Add loaders for images/fonts/etc, e.g. { '.svg': 'file' }
  '.gif': 'file',
  '.png': 'file',
  '.jpg': 'file',
  '.svg': 'file'
}

and that solved the loader errors. Now, jpg files are loading nicely, but the pngs, gifs, and svgs are not. The app compiles and deploys, but these image types show their placeholder info and the console gives me errors messages that read, “Failed to load resource: the server responded with a status of 404 ().” I’m not sure why the jpgs would load while the others will not, given they are housed in the same folder. Maybe I’m referencing the loaders incorrectly?

I’ve also experimented with the external flag, though I can’t pass it in the same manner as your example, because we use this build.js script. Here is the snippet from the build.js script that I experimented with by adding the “external: [‘/images/*’],”. This did not seem to have an explicit effect.

let opts = {
  entryPoints: ['js/app.js'],
  bundle: true,
  target: 'es2017',
  outdir: '../priv/static/assets',
  external: ['/images/*'],
  logLevel: 'info',
  loader,
  plugins
}

At this point I’m stumped and would be grateful for a nudge in the right direction.
Cheers

Correction. Some static jpgs are loading, while others are not. I’ll focus on comparing the underlying code and file locations between the success/fail examples below. Forgive my ignorance if the cause is obvious from these examples. The success case is in the css and appears to be cache-busted, while the failure cases are in the html template and do not appear to be cache-busted.

Nav jpg image in background works
in the css:

background-image: url(./nav_background-GYKWV2ZH-3df62fc….jpg?vsn=d);

leaf.png in nav does not work
in the html:

<img src="/images/leaf.png" alt="leaf">

partner_logo.jpg in an html template does not work

in the html: <img src="/images/partner_logo.jpg" alt="">

Thank you very much for any insights :slight_smile:

<img src="/images/partner_logo.jpg" alt=""> 

You should be using Routes.static_path(conn, “path_to_static_img_etc”) rather than using url directly.


If you are still facing issues, you may have to create a sample project and replicate your issues. Upload this to github, so that others can share some insights.

Ok, problems appear to be solved. I am still learning many of the basics and I often don’t immediately undestand the documentation.

I should have clarified in my last note that the ‘in the css’ and ‘in the html’ paths were taken from the output in chrome inspect, not the code. The paths in the html templates are as you suggest. In the css, it is using a url(), which I’m thinking is because the css gets set up by the sass plugin such that the static path doesn’t work.

So to close the loop, the one step that I hadn’t done was move the images folder over to priv/assets. This is because it was already there. Or at least it was already there on my local machine. The jpgs that were working did so because of the url() path that referenced the legacy assets folder, but everythign else was using the static_paths pointing to /priv/static/ which is no longer being built at compilation automatically. So I deleted the legacy priv/static/ and copied it newly from the legacy assets/static. Then I updated the .gitignore to include the /priv/static but exclude /priv/static/assets/, as I’ve found noted in a couple posts here and there.

This experience definitely helped me to better understand more of how the Phoenix framework comes together. Thank you very much for the help!

1 Like