roxiness / routify-starter

https://example.routify.dev/
198 stars 55 forks source link

Need a better way to debug SSR errors #97

Closed mizzao closed 3 years ago

mizzao commented 3 years ago

I'm having a hard time tracking down errors in SSR code right now. For example, if there is a runtime error on the SSR function, both options don't work particularly well:

With npm run dev:ssr, tossr appears to be fine, but has actually actually died and no longer responds to connections. The error does not appear anywhere.

Local data is > 24hrs old. Refreshing...
Successfully wiped local storage
User is logged out
Successfull wiped local firestore cache
LATxo0jkXeOji1KCpvCHpqfD2PE3 has logged in
Getting new data from firestore...
[tossr] /recipe/favicon_bw.png - 3503ms (rebuilt bundle)

with npm run serve the error appears but the spassr just dies, and does not print out the whole error. It is also annoying to debug because it requires a new npm run build to try again, which can take a while.

Local data is > 24hrs old. Refreshing...
Successfully wiped local storage
User is logged out
Successfull wiped local firestore cache
B2CkgahGccWKtzbzlhwJZZdS7m32 has logged in
Getting new data from firestore...
[tossr] /recipe/favicon_bw.png - 3317ms
node:internal/process/promises:227
          triggerUncaughtException(err, true /* fromPromise */);
          ^

[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "TypeError: Cannot read property 'phases' of undefined".] {

Happy to help with improvements to tossr, spassr or anything else to make this work.

braebo commented 3 years ago

Jake and Ghost will be able to provide more insight into this- but in the meantime, some things that help me:

mizzao commented 3 years ago

Not sure any of that helps me debug what is going on...

More info: If I manually run routify in one terminal and rollup -cw in another terminal, then the rollup thread running tossr / spassr just dies silently when the error is thrown.

Currently, it seems that if I amend the code a bit from here: https://github.com/roxiness/spassr/blob/master/src/spassr.js#L37:

            const foo = await tossr(entrypoint, script, req.url, ssrOptions);
            console.log(foo);
            return res.send(foo)

The value of foo gets printed to the console, but then express just abruptly quits. It is very different than a route that is handled properly. I have no idea why...it's very mysterious because the tossr promise seems to return a value, not an error.

mizzao commented 3 years ago

I think I have narrowed this down to the use of jsdom in tossr: https://github.com/jsdom/jsdom/issues/2346.

Basically, as the DOM is getting torn down, after SSR is complete, an async error is unhandled and this causes node to quit. The async error is in my frontend code, of course, but it's very hard to debug when everything just exits and nothing prints out. The browser is a lot more forgiving of errors like these since it is generally running async code, but Node does not like them. Perhaps there is a way to force unhandled rejections to be just printed out: https://thecodebarbarian.com/unhandled-promise-rejections-in-node.js.html

This seems to be a useful clue: https://nodejs.org/api/process.html#process_event_rejectionhandled

The built version of the code above also shows part of the error.

jakobrosenberg commented 3 years ago

Thanks for looking into this @mizzao. Would you be interested in submitting a PR for this?

mizzao commented 3 years ago

Beat you to it: https://github.com/roxiness/tossr/pull/11/. Happy to take any feedback there.