Posted 7 months ago. Visible to the public.

esbuild: Make your Rails application show build errors

Building application assets with esbuild is the new way to do it, and it's great, especially in combination with Sprockets (or Propshaft on Rails 7).
You might be missing some convenience features, though.

Here we cover one specific issue:
Once you have started your development Rails server and esbuild with the --watch option (if you used jsbundling-rails to set up, you probably use bin/dev), esbuild will recompile your assets upon change, but build errors will only be printed to the terminal. Your application won't complain about them -- instead it just delivers the files that were previously built and you might not notice something went wrong.

Cry no more, here is how to solve that.

  1. Have esbuild write errors to a file,
  2. make the Rails app render such errors,
  3. automatically reload to show errors immediately (optional).

Make esbuild write errors to a file

In your esbuild.config.js, you probably have something like this:

Copy
const watch = process.argv.includes('--watch') require('esbuild').build({ ... watch: watch, ... })

Now tell it to write errors to a file esbuild_error_development.txt in your project root:

Copy
const watch = process.argv.includes('--watch') const railsEnv = process.env.RAILS_ENV || 'development' const errorFilePath = `esbuild_error_${railsEnv}.txt` const path = require('path') const fs = require('fs') function handleError(error) { if (error) fs.writeFileSync(errorFilePath, error.toString()) else if (fs.existsSync(errorFilePath)) fs.truncate(errorFilePath, 0, () => {}) } require('esbuild').build({ ... watch: watch && { onRebuild: handleError }, ... })

Tell your Rails application about esbuild errors

We now tell our Rails app to render contents from that file, if there are any.
That should only happen in development, since tests or production environments will fail when trying to compile their assets up front.

Copy
class ApplicationController < ActionController::Base include EsbuildErrorRendering if Rails.env.development? end
Copy
module ApplicationController::EsbuildErrorRendering ESBUILD_ERROR = Rails.root.join("esbuild_error_#{Rails.env}.txt") # see esbuild.config.js def self.included(base) base.before_action :render_esbuild_error, if: :render_esbuild_error? end private def render_esbuild_error heading, errors = ESBUILD_ERROR.read.split("\n", 2) # Render error as HTML so rack-livereload can inject its code into <head> # and refresh the error page when assets are modified. render html: <<~HTML.html_safe, layout: false <html> <head></head> <body> <h1>#{ERB::Util.html_escape(heading)}</h1> <pre>#{ERB::Util.html_escape(errors)}</pre> </body> </html> HTML end def render_esbuild_error? ESBUILD_ERROR.exist? && ESBUILD_ERROR.size > 0 end end

Note how we are rendering HTML. We don't do that because it's pretty, but to allow tools like Rack::LiveReload to inject themselves into the HTML response, and trigger a reload when the error page is shown (and show the application when the error has been fixed).

Optional: Configure Guard / Rack::LiveReload

If you use Guard with Rack::LiveReload to reload your application when assets change, tell it about that new file. Add to your Guardfile:

Copy
watch(%r(^esbuild_error_development\.txt$))

Note that the file must exist when Guard starts, but we'll take care of that in the next step.

Configure git

As mentioned above, the esbuild_error_development.txt file must exist for Guard to watch it. [1]

Sadly, Git does not offer a way to add a file and ignore further changes, at least not in a way where this information is shared with the upstream repo Archive . With bin/setup, Rails offers a convention to prepare your application for use (e.g. to make it work for a new developer), so we simply leverage that. If your project uses a different approach, adjust accordingly.

  1. Add to your project's .gitignore:
    Copy
    /esbuild_error_*
  2. Add to your project's bin/setup:
    Copy
    FileUtils.touch('esbuild_error_development.txt')

Try it out

That's it! Restart your application and esbuild, and break stuff. You should see error messages be delivered by your application.

Demo


[1] Side note: Our implementation of the controller module also expects the file to exist. If you don't use Guard and consider that requirement annoying, you could change render_esbuild_error? to just check for ESBUILD_ERROR.exist? and not add an empty error file to your repo.

Growing Rails Applications in Practice
Check out our new e-book:
Learn to structure large Ruby on Rails codebases with the tools you already know and love.

Owner of this card:

Avatar
Arne Hartherz
Last edit:
7 months ago
by Arne Hartherz
Attachments:
esbuild-error-demo.gif
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Arne Hartherz to makandra dev
This website uses short-lived cookies to improve usability.
Accept or learn more