Skip to content

Instantly share code, notes, and snippets.

@kiliman
Created February 25, 2022 19:03
Show Gist options
  • Select an option

  • Save kiliman/f03d9e015ece72b9c91ba1527982cbaf to your computer and use it in GitHub Desktop.

Select an option

Save kiliman/f03d9e015ece72b9c91ba1527982cbaf to your computer and use it in GitHub Desktop.
ErrorBoundary that displays the actual source file and highlights the line
import { LoaderFunction } from 'remix'
import { fs } from '~/utils/node.server'
export const loader: LoaderFunction = ({ request }) => {
const url = new URL(request.url)
const file = url.searchParams.get('file')
if (file) {
const source = fs.readFileSync(file, 'utf8')
return new Response(source, {
status: 200,
headers: { 'Content-Type': 'text/plain' },
})
}
return new Response('', { status: 404 })
}
export const ErrorBoundary = ({ error }: any) => {
const [source, setSource] = useState('')
const lines = error.stack.split('\n')
const match = lines[1].match(/(?<path>.+):(?<line>\d+):(?<column>\d+)/)
let path = ''
let linenumber = 0
let column = 0
if (match) {
path = match.groups.path
linenumber = Number(match.groups.line)
column = Number(match.groups.column)
}
const fetcher = useFetcher()
useEffect(() => {
if (
!source &&
path &&
fetcher.state === 'idle' &&
fetcher.type !== 'done'
) {
fetcher.load(`/api/source?file=${path}`)
}
}, [])
if (path && !source) {
if (typeof window === 'undefined') {
// server
setSource(fs.readFileSync(path, 'utf8'))
} else if (fetcher.data) {
setSource(fetcher.data)
}
}
if (source) {
const sourceLines: string[] = source.split('\n')
return (
<Document>
<div>
{error.message} ({path} {linenumber}: {column})
</div>
<div style={{ display: 'flex', gap: '2rem' }}>
<pre style={{ maxWidth: '45vw', overflowX: 'scroll' }}>
{error.stack}
</pre>
<div style={{ fontFamily: 'monospace', lineHeight: '1' }}>
{sourceLines.map((line, index) =>
index + 1 === linenumber ? (
<pre
key={index}
style={{
backgroundColor: '#ddd',
fontWeight: 'bold',
margin: 0,
}}
>
{index + 1}: {line}
</pre>
) : (
<pre key={index} style={{ margin: 0 }}>
{index + 1}: {line}
</pre>
),
)}
</div>
</div>
</Document>
)
}
return <pre>{error.stack}</pre>
}
@danthareja
Copy link
Copy Markdown

Thanks for this!

I had to update the regex slightly to get this working for me (on MacOS, remix 1.3.4)

- const match = lines[1].match(/(?<path>.+):(?<line>\d+):(?<column>\d+)/)
+ const match = lines[1].match(/:(?<path>.+):(?<line>\d+):(?<column>\d+)/)

A very slight change, but I noticed the path in the stacktrace has an extra prefix (in the example below it's /Users/dan/Code/Projects/schnerp/api/route: so I added the extra : in the diff above.

Error: This is an error
    at loader3 (/Users/dan/Code/Projects/schnerp/api/route:/Users/dan/Code/Projects/schnerp/app/routes/errors/loader.jsx:1:1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment