diff --git a/src/components/error/ErrorInfo.tsx b/src/components/error/ErrorInfo.tsx
index d2bc50d..8043afd 100644
--- a/src/components/error/ErrorInfo.tsx
+++ b/src/components/error/ErrorInfo.tsx
@@ -11,6 +11,7 @@ type Props = {
message: string;
statusCode?: number;
originalPath?: string;
+ stack?: string;
/** props specific to error boundary. */
misc?: {
subtext: string;
@@ -19,7 +20,9 @@ type Props = {
};
};
-const ErrorInfo = ({ message, statusCode, misc, originalPath }: Props) => {
+const isDev = process.env.NODE_ENV === 'development';
+
+const ErrorInfo = ({ message, statusCode, misc, originalPath, stack }: Props) => {
const title = statusCode ? `${message} (${statusCode})` : message;
return (
<>
@@ -39,6 +42,11 @@ const ErrorInfo = ({ message, statusCode, misc, originalPath }: Props) => {
{title}
+ {Boolean(stack && isDev) && (
+
+ {stack}
+
+ )}
{misc ? (
<>
{misc.subtext}
@@ -64,6 +72,13 @@ const ErrorInfo = ({ message, statusCode, misc, originalPath }: Props) => {
.
)}
+
+ If you think this shouldn't happen,{' '}
+
+ let it be known
+
+ .
+
>
);
diff --git a/src/interfaces/shared/error.ts b/src/interfaces/shared/error.ts
index 9beabf8..1d2d82f 100644
--- a/src/interfaces/shared/error.ts
+++ b/src/interfaces/shared/error.ts
@@ -1,3 +1,3 @@
import { AppError as AppErrorClass } from 'src/utils/helpers';
-export type AppError = Omit, 'name'>;
+export type AppError = Pick, 'message' | 'statusCode' | 'stack'>;
diff --git a/src/pages/find/index.tsx b/src/pages/find/index.tsx
index 7d8dd0a..444d176 100644
--- a/src/pages/find/index.tsx
+++ b/src/pages/find/index.tsx
@@ -67,13 +67,13 @@ export const getServerSideProps: GetServerSideProps = asy
props: { data: { title: query, results: res }, error: null, originalPath },
};
} catch (error) {
- const { message, statusCode } = getErrorProperties(error);
- ctx.res.statusCode = statusCode;
- ctx.res.statusMessage = message;
+ const err = getErrorProperties(error);
+ ctx.res.statusCode = err.statusCode;
+ ctx.res.statusMessage = err.message;
return {
props: {
- error: { message, statusCode },
+ error: { message: err.message, statusCode: err.statusCode, stack: err.format() },
data: { title: query, results: null },
originalPath,
},
diff --git a/src/pages/name/[nameId]/index.tsx b/src/pages/name/[nameId]/index.tsx
index b3967d9..73f2060 100644
--- a/src/pages/name/[nameId]/index.tsx
+++ b/src/pages/name/[nameId]/index.tsx
@@ -55,11 +55,17 @@ export const getServerSideProps: GetServerSideProps = async ctx =>
return { props: { data, error: null, originalPath } };
} catch (error) {
- const { message, statusCode } = getErrorProperties(error);
- ctx.res.statusCode = statusCode;
- ctx.res.statusMessage = message;
+ const err = getErrorProperties(error);
+ ctx.res.statusCode = err.statusCode;
+ ctx.res.statusMessage = err.message;
- return { props: { error: { message, statusCode }, data: null, originalPath } };
+ return {
+ props: {
+ error: { message: err.message, statusCode: err.statusCode, stack: err.format() },
+ data: null,
+ originalPath,
+ },
+ };
}
};
diff --git a/src/pages/title/[titleId]/index.tsx b/src/pages/title/[titleId]/index.tsx
index 51ea836..a6ed616 100644
--- a/src/pages/title/[titleId]/index.tsx
+++ b/src/pages/title/[titleId]/index.tsx
@@ -5,7 +5,7 @@ import ErrorInfo from 'src/components/error/ErrorInfo';
import Media from 'src/components/media/Media';
import { Basic, Cast, DidYouKnow, Info, MoreLikeThis, Reviews } from 'src/components/title';
import Title from 'src/interfaces/shared/title';
-import { AppError } from 'src/interfaces/shared/error';
+import type { AppError } from 'src/interfaces/shared/error';
import getOrSetApiCache from 'src/utils/getOrSetApiCache';
import title from 'src/utils/fetchers/title';
import { getErrorProperties, getProxiedIMDbImgUrl } from 'src/utils/helpers';
@@ -63,12 +63,18 @@ export const getServerSideProps: GetServerSideProps = async ctx =>
const data = await getOrSetApiCache(titleKey(titleId), title, titleId);
return { props: { data, error: null, originalPath } };
- } catch (error) {
- const { message, statusCode } = getErrorProperties(error);
- ctx.res.statusCode = statusCode;
- ctx.res.statusMessage = message;
+ } catch (e) {
+ const err = getErrorProperties(e);
+ ctx.res.statusCode = err.statusCode;
+ ctx.res.statusMessage = err.message;
- return { props: { error: { message, statusCode }, data: null, originalPath } };
+ const error = {
+ message: err.message,
+ statusCode: err.statusCode,
+ stack: err.format(),
+ };
+ console.error(err);
+ return { props: { error, data: null, originalPath } };
}
};
diff --git a/src/styles/modules/components/error/error-info.module.scss b/src/styles/modules/components/error/error-info.module.scss
index 1cfa76b..51f3533 100644
--- a/src/styles/modules/components/error/error-info.module.scss
+++ b/src/styles/modules/components/error/error-info.module.scss
@@ -8,7 +8,7 @@
display: grid;
justify-content: center;
justify-items: center;
- gap: var(--spacer-1);
+ gap: var(--comp-whitespace);
@include helper.bp('bp-700') {
--doc-whitespace: var(--spacer-5);
@@ -31,6 +31,19 @@
text-align: center;
}
+.stack {
+ max-width: 90%;
+ max-height: 20rem;
+ padding: var(--spacer-3);
+ white-space: pre-wrap;
+ overflow: scroll;
+
+ user-select: all;
+
+ border-radius: var(--spacer-1);
+ background-color: var(--clr-bg-muted);
+}
+
.button {
align-self: end;
diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts
index 2a483ed..a19b133 100644
--- a/src/utils/helpers.ts
+++ b/src/utils/helpers.ts
@@ -74,12 +74,33 @@ export const getProxiedIMDbImgUrl = (url: string) => {
};
export const AppError = class extends Error {
- constructor(message: string, public statusCode: number, errorOptions?: unknown) {
- const saneErrorOptions = getErrorOptions(errorOptions);
- super(message, saneErrorOptions);
+ constructor(message: string, public statusCode: number, cause?: unknown) {
+ const _cause = cause ? AppError.toError(cause) : undefined;
+ super(message, { cause: _cause });
Error.captureStackTrace(this, AppError);
- if (process.env.NODE_ENV === 'development') console.error(this);
+ }
+
+ static toError(err: unknown) {
+ if (err instanceof Error) return err;
+ return new Error(`Unexpected: ${JSON.stringify(err)}`);
+ }
+
+ format() {
+ let str = '';
+ let cur: Error | null = this;
+ let depth = 0;
+
+ while (cur && depth <= 4) {
+ if (cur.stack) str += `${cur.stack}\n`;
+ else str += `${cur.name}: ${cur.message}\n`;
+
+ cur = cur.cause instanceof Error ? cur.cause : null;
+ if (cur) str += 'Caused by:\n';
+ depth++;
+ }
+
+ return str.trimEnd();
}
};
@@ -110,19 +131,6 @@ export const isLocalStorageAvailable = () => {
}
};
-const getErrorOptions = (error: unknown): ErrorOptions | undefined => {
- if (!error || typeof error !== 'object') return undefined;
-
- let cause: unknown;
- // @ts-expect-error it's not an error! just that project's ts version is old, which can't be upgraded
- if ('cause' in error) cause = error.cause;
- // @ts-expect-error it's not an error! just that project's ts version is old, which can't be upgraded
- else if ('stack' in error) cause = error.stack;
-
- // @ts-expect-error it's not an error! just that project's ts version is old, which can't be upgraded
- return { cause };
-};
-
export const getErrorProperties = (
error: unknown,
message = 'Something went very wrong',
@@ -130,4 +138,4 @@ export const getErrorProperties = (
) => {
if (error instanceof AppError) return error;
return new AppError(message, statusCode, error);
-};
\ No newline at end of file
+};