framework7io / framework7

Full featured HTML framework for building iOS & Android apps
http://framework7.io
MIT License
17.97k stars 3.24k forks source link

Wrong stack trace in case of error in the router component #4250

Closed slenz closed 4 months ago

slenz commented 4 months ago

Describe the bug

When error happens in the custom router component during the loading of the component (for example Type Error happens), then stack information of the original error is lost. I attached the "unhandledrejection" handler to "window" and expect to get correct stack trace but stack trace started at the "component-loader.js" itself. I have proposition how to make a workaround for this problem, please look at the patch below in the "Proposition" section.

To Reproduce

  1. Make some typing error in the some page (i call not existed function "ttt()" in the example below).
  2. Attach ("unhandledrejection" event handler to window.
    window.addEventListener("unhandledrejection", function (e) {
    console.log("window.unhandledrejection", e);
    };
  3. Navigate to that page.
  4. See the console log, it will show something like this. No stack of original error.
    window.unhandledrejection: Error: ReferenceError: ttt is not defined
    at component-loader.js:121:17

Expected behavior

Stack with correct error location.

window.unhandledrejection: ReferenceError: ttt is not defined
    at Component.renderFunction (home.jsx:17:21)
    at Component.render (component-class.js:361:17)
    at component-class.js:173:27

Actual Behavior

Stack with incorrect error location.

window.unhandledrejection: Error: ReferenceError: ttt is not defined
    at component-loader.js:121:17

Proposition

This happens because stack is lost when error re-thrown in the modules/router/component-loader.js. I propose to add the original error object to the new Error thrown (for example as "cause" object). This will allow us to check for the "cause" object in the Error and get the correct stack frame in the "unhandledrejection" handler. I propose to make small patch in the modules/router/async-component.js and modules/router/component-loader.js:

--- async-component.js.old  2024-02-05 13:28:24.000000000 +0300
+++ async-component.js  2024-02-16 12:49:17.980356045 +0300
@@ -7,7 +7,7 @@
       })
       .catch((err) => {
         reject();
-        throw new Error(err);
+        throw new Error(err, {cause: err});
       });
   }
   if (component instanceof Promise) {

--- component-loader.js.old     2024-02-05 13:28:24.000000000 +0300
+++ component-loader.js 2024-02-16 12:47:39.380287616 +0300
@@ -117,7 +117,7 @@
           })
           .catch((err) => {
             reject(err);
-            throw new Error(err);
+            throw new Error(err, {cause: err});
           });
       }
       let cachedComponent;

Then in the "unhandledrejection" handler we can check for the "cause" and it will contain the original error's stack.

window.addEventListener("unhandledrejection", function (e) {
    if (e.reason.cause) {
        console.log("window.unhandledrejection:", e.reason.cause);
    } else {
        console.log("window.unhandledrejection:", e.reason);
    }
});