Enhancing Esbuild Error Handling in a Rails App.
Table of Contents
- The Problem
- The Solution
- Step 1: Ignore Error Files in Git
- Step 2: Modify ApplicationController
- Step 3: Create EsbuildErrorRendering Concern
- Step 4: Configure Esbuild to Log Errors
- Step 5: Update package.json
- How Errors Appear in the Browser
- Final Thoughts
- π Bonus Track: Rails Template for Easy Setup
When working on a Rails app using esbuild, encountering build errors can be frustrating β especially when they don't surface in a way that makes debugging easier. To improve the developer experience, I implemented an automatic error rendering system that displays esbuild errors directly in the browser during development. Here's how I did it.
The Problem
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.
By default, esbuild logs errors to the terminal, which can be easy to miss when working with multiple tabs. Instead, I wanted a way for these errors to be displayed directly in the browser, making debugging more efficient.
The Solution
I created a system that:
- Captures esbuild errors and writes them to a file.
- Reads the error file in a Rails before-action.
- Renders errors in the browser if present.
Step 1: Ignore Error Files in Git
Since esbuild errors will be logged to a file, we should prevent them from being committed. Add the following to .gitignore
:
esbuild_error_*.txt
Step 2: Modify ApplicationController
To ensure errors are displayed only in development, I included a new concern in ApplicationController
:
class ApplicationController < ActionController::Base
include EsbuildErrorRendering if Rails.env.development?
Step 3: Create EsbuildErrorRendering Concern
This concern reads the error file and renders it in the browser. esbuild_error_rendering.rb
module EsbuildErrorRendering
ESBUILD_ERROR = Rails.root.join("esbuild_error_#{Rails.env}.txt")
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 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.file? && !ESBUILD_ERROR.zero?
end
end
Step 4: Configure Esbuild to Log Errors
To capture errors, I created esbuild.config.mjs
and added a custom plugin:
import esbuild from 'esbuild'
import fs from 'fs'
const watch = process.argv.includes('--watch')
const errorLogFile = `esbuild_error_${process.env.RAILS_ENV}.txt`
const onEndPlugin = {
name: 'onEnd',
setup (build) {
build.onEnd((result) => {
if (result.errors.length > 0) {
console.log(result.errors)
const errors = result.errors.map(error => `${error.location.file}:${error.location.line}\n${error.text}`).join('\n\n')
fs.writeFileSync(
errorLogFile,
`esbuild ended with ${result.errors.length} error${result.errors.length !== 1 ? 's' : ''}\n${errors}`
)
} else if (fs.existsSync(errorLogFile)) {
fs.truncate(errorLogFile, 0, () => {})
}
})
}
}
await esbuild
.context({
entryPoints: ['app/javascript/**/*.*'],
bundle: true,
sourcemap: true,
format: 'esm',
outdir: 'app/assets/builds',
publicPath: '/assets',
logLevel: 'info',
plugins: [onEndPlugin]
})
.then(context => {
if (watch) {
context.watch()
} else {
context.rebuild().then(result => {
context.dispose()
})
}
}).catch(() => process.exit(1))
Step 5: Update package.json
Finally, I replaced the default esbuild script with my custom configuration:
"scripts": {
"build": "node esbuild.config.mjs",
How Errors Appear in the Browser
With this setup, when an esbuild error occurs, it will be displayed directly in the browser instead of just the terminal. Below is an example of how an error message might look:
This makes debugging much faster by allowing you to immediately see the issue in the browser, rather than hunting for it in the terminal.
Final Thoughts
This setup improves error visibility by displaying esbuild errors directly in the browser, reducing the time spent switching between the terminal and browser. It also keeps concerns separate and integrates seamlessly with Rails' development environment.
If you're using esbuild with Rails, give this approach a try β your future self will thank you, and your browser will finally show some respect for esbuild errors.
π Bonus Track: Rails Template for Easy Setup
If you want to enhance esbuild error handling in your Rails app without manually applying the changes, Iβve created a Rails app template that does all the setup for you!
This template:
β
Adds an esbuild error logging mechanism
β
Displays errors in the browser when esbuild fails
β
Creates the necessary files and configurations automatically
Instead of manually adding the concern and esbuild config, you can run this simple command:
rails app:template LOCATION='https://railsbytes.com/script/XnJsOq'
Make sure to:
- Update your
package.json
to usenode esbuild.config.mjs
as the build command. - Adjust entryPoints, publicPath or outdir inside
esbuild.config.mjs
if needed.
With this, you'll have esbuild error handling up and running in seconds! π―
Check out the template here: RailsBytes Link