vitejs / vite

Next generation frontend tooling. It's fast!
http://vite.dev
MIT License
68.6k stars 6.19k forks source link

[Feature request] Show browser error overlay for runtime errors #2076

Open martinszeltins opened 3 years ago

martinszeltins commented 3 years ago

I really like the browser error overlay that we get for compile errors (server.hmr.overlay). But runtime errors still appear only in console. It would be nice if runtime errors could also be displayed using the nice browser error overlay.

For example, it would be nice to get the overlay if I forgot to import ref:

<script setup>
    import HelloWorld from './components/HelloWorld.vue'

    const name = ref('Martin')
</script>

I'm talking about this overlay:

ygj6 commented 3 years ago

Sometimes runtime errors occur after most of the page is loaded normally and the page can be used normally. In this case, will the page be replaced with the browser error overlay?

jugglingcats commented 3 years ago

In React they prevent partial page from being rendered if there is an error, so error overlay is appropriate in all cases. It would be a nice enhancement particularly for new users and is present in CRA, NX, Snowpack etc.

janwirth commented 3 years ago

The overlay is an overlay so it's not replaced, it's overlaid or am I missing something here?

This does the trick for me:

// REGISTER ERROR OVERLAY
const showErrorOverlay = err => {
    // must be within function call because that's when the element is defined for sure.
    const ErrorOverlay = customElements.get('vite-error-overlay')
    // don't open outside vite environment
    if (!ErrorOverlay) {return}
    console.log(err)
    const overlay = new ErrorOverlay(err)
    document.body.appendChild(overlay)
}

window.addEventListener('error', showErrorOverlay)
window.addEventListener('unhandledrejection', ({reason}) => showErrorOverlay(reason))
andrew-knack commented 3 years ago

@FranzSkuffka Almost but the overlay needs the frame to be defined on the error to get correct formatting.

The code you posted will essentially show

Error: boom
    at http://localhost:3000/src/index.tsx:17:7

vs what I think most people expect

Runtime error at [filepath]
[error message]
/[filepath]:17:0
17 |      throw new Error('boom')
18 |      ^  

at [stacktrace]

On an unrelated note small bug in the code you posted, I think you meant to pass in the error not the event.

window.addEventListener('error', ({error}) => showErrorOverlay(error));
TechAkayy commented 2 years ago

Please prioritise this one, we are all spoilt by our previous experiences with webpack and other dev-servers. Really hard to live without the run-time overlay. Thanks!

TechAkayy commented 2 years ago

Thanks @arbassett for the related PR (#6274) you are working on. Will this PR cover showing all runtime errors in the overlay? Would be great if it does.

Here is example where prop validations fail (validator fn & type check). At the moment, we are forced to keep the browser console always open to even notice these important errors.

image

Inviz commented 1 year ago

I thought i was doing something wrong, but it really is a problem. I would really love to see this improved

uwaisdev commented 1 year ago
// REGISTER ERROR OVERLAY
const showErrorOverlay = err => {
    // must be within function call because that's when the element is defined for sure.
    const ErrorOverlay = customElements.get('vite-error-overlay')
    // don't open outside vite environment
    if (!ErrorOverlay) {return}
    console.log(err)
    const overlay = new ErrorOverlay(err)
    document.body.appendChild(overlay)
}

window.addEventListener('error', showErrorOverlay)
window.addEventListener('unhandledrejection', ({reason}) => showErrorOverlay(reason))

Fantastic, seems to have solved it for now.

vKongv commented 1 year ago

The overlay is an overlay so it's not replaced, it's overlaid or am I missing something here?

This does the trick for me:

// REGISTER ERROR OVERLAY
const showErrorOverlay = err => {
    // must be within function call because that's when the element is defined for sure.
    const ErrorOverlay = customElements.get('vite-error-overlay')
    // don't open outside vite environment
    if (!ErrorOverlay) {return}
    console.log(err)
    const overlay = new ErrorOverlay(err)
    document.body.appendChild(overlay)
}

window.addEventListener('error', showErrorOverlay)
window.addEventListener('unhandledrejection', ({reason}) => showErrorOverlay(reason))

I'm able to make the overlay more usable with information such as stacktrace using window.onerror and pass err object from 5th param. I also check if it's dev env to make sure it doesn't include this code in prod in case causing any side effect

if (import.meta.env.DEV) {
  window.onerror = (event, source, lineno, colno, err) => {
    // must be within function call because that's when the element is defined for sure.
    const ErrorOverlay = customElements.get('vite-error-overlay');
    // don't open outside vite environment
    if (!ErrorOverlay) {
      return;
    }
    const overlay = new ErrorOverlay(err);
    document.body.appendChild(overlay);
  };
}

Example using window.onerror

image

addEventListener('error', ...) only emit the error event which doesn't include stacktrace, see: https://developer.mozilla.org/en-US/docs/Web/API/Window/error_event#syntax

c10b10 commented 1 year ago

Where do you precisely add this code @vKongv for it to work?

I've tried adding it in the index.tsx of a React app to make it work. It does work for errors in that file, but fails for any other file.

krzkaczor commented 1 year ago

@c10b10 you can dump it into <script> tag in your index.html. However I am also wondering what's the best way to avoid bundling this code 🤔

lxe commented 1 year ago

Not sure if there's a better way to do this at this point, but for ErrorEvents, you also need to pass the error property into ErrorOverlay:

window.addEventListener('error', ({error}) => showErrorOverlay(error)); // here
Walidoux commented 1 year ago

A little hint for those who are using typescript and would like to switch the prop err from any to something more accurate reading their code from hmrPayload.ts, ref: https://github.com/vitejs/vite/blob/main/packages/vite/types/hmrPayload.d.ts#L45

import type { ErrorPayload } from 'vite'

export const showErrorOverlay = (err: Partial<ErrorPayload['err']>) => {
  const ErrorOverlay = customElements.get('vite-error-overlay')
  if (ErrorOverlay == null) return
  document.body.appendChild(new ErrorOverlay(err))
}
gaurav21r commented 5 months ago
window.onerror = (event, source, lineno, colno, err) => {
    // must be within function call because that's when the element is defined for sure.
    const ErrorOverlay = customElements.get('vite-error-overlay');
    // don't open outside vite environment
    if (!ErrorOverlay) {
      return;
    }
    const overlay = new ErrorOverlay(err);
    document.body.appendChild(overlay);
  };

@vKongv This is absolutely wonderful! However, I'm getting the wrong Line Numbers, even though the console shows the right ones. Shown here:

image

The Actual offending Line Number is 14, however the error shows Line 27.

@martinszeltins @yyx990803 @ygj6 @jugglingcats @janwirth @andrew-knack @TechAkayy @arbassett @TechAkayy @sapphi-red @mattrunyon @sapphi-red @Inviz @uwaisdev @vKongv @geoffrich @c10b10 @krzkaczor @lxe @Walidoux @lifeart

Any ideas how to resolve this and any update on this getting added to the core?

This is the same Code in a Next.JS ebvironment and the DX is so ❤️ Would love to have this in Vite!

image
JanderSilva-Acerta commented 3 days ago

Any solution for stack trace showing different lines than actual error position?

BlueManiac commented 2 days ago

Any solution for stack trace showing different lines than actual error position?

I did some work on this using a vite plugin instead. Check rewriteStacktrace and generateCodeFrame in https://github.com/BlueManiac/DotnetViteVueTemplate/blob/f6996afb8ddda8d56b36042a63cab8a194e91011/src/Web/Util/Plugins/vite-runtime-error-plugin.ts