diff --git a/packages/qwik/src/core/error/assert.ts b/packages/qwik/src/core/error/assert.ts index db25bd2f858..50554f49459 100644 --- a/packages/qwik/src/core/error/assert.ts +++ b/packages/qwik/src/core/error/assert.ts @@ -1,6 +1,6 @@ import type { QwikElement, VirtualElement } from '../render/dom/virtual-element'; import { isElement, isQwikElement } from '../util/element'; -import { logErrorAndStop } from '../util/log'; +import { throwErrorAndStop } from '../util/log'; import { qDev } from '../util/qdev'; const ASSERT_DISCLAIMER = 'Internal assert, this is likely caused by a bug in Qwik: '; @@ -14,7 +14,7 @@ export function assertDefined( if (value != null) { return; } - throw logErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); + throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); } } @@ -28,14 +28,14 @@ export function assertEqual( if (value1 === value2) { return; } - throw logErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); + throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); } } export function assertFail(text: string, ...parts: any[]): never; export function assertFail(text: string, ...parts: any[]) { if (qDev) { - throw logErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); + throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); } } @@ -44,7 +44,7 @@ export function assertTrue(value1: any, text: string, ...parts: any[]): asserts if (value1 === true) { return; } - throw logErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); + throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); } } @@ -53,7 +53,7 @@ export function assertNumber(value1: any, text: string, ...parts: any[]): assert if (typeof value1 === 'number') { return; } - throw logErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); + throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); } } @@ -62,7 +62,7 @@ export function assertString(value1: any, text: string, ...parts: any[]): assert if (typeof value1 === 'string') { return; } - throw logErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); + throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); } } @@ -70,7 +70,7 @@ export function assertQwikElement(el: any): asserts el is QwikElement { if (qDev) { if (!isQwikElement(el)) { console.error('Not a Qwik Element, got', el); - throw logErrorAndStop(ASSERT_DISCLAIMER + 'Not a Qwik Element'); + throwErrorAndStop(ASSERT_DISCLAIMER + 'Not a Qwik Element'); } } } @@ -79,7 +79,7 @@ export function assertElement(el: Node | VirtualElement): asserts el is Element if (qDev) { if (!isElement(el)) { console.error('Not a Element, got', el); - throw logErrorAndStop(ASSERT_DISCLAIMER + 'Not an Element'); + throwErrorAndStop(ASSERT_DISCLAIMER + 'Not an Element'); } } } diff --git a/packages/qwik/src/core/state/common.ts b/packages/qwik/src/core/state/common.ts index 3aefd714000..b23b60b8c9a 100644 --- a/packages/qwik/src/core/state/common.ts +++ b/packages/qwik/src/core/state/common.ts @@ -13,7 +13,7 @@ import { } from '../use/use-task'; import type { QwikElement } from '../render/dom/virtual-element'; import { notifyChange } from '../render/dom/notify-render'; -import { createError, logError } from '../util/log'; +import { logError, throwErrorAndStop } from '../util/log'; import { tryGetContext } from './context'; import { QObjectFlagsSymbol, QObjectManagerSymbol, QOjectTargetSymbol } from './constants'; import type { Signal } from './signal'; @@ -101,7 +101,7 @@ const _verifySerializable = (value: T, seen: Set, ctx: string, preMessag )});\n\nPlease check out https://qwik.builder.io/docs/advanced/qrl/ for more information.`; } console.error('Trying to serialize', value); - throw createError(message); + throwErrorAndStop(message); } return value; }; diff --git a/packages/qwik/src/core/util/log.ts b/packages/qwik/src/core/util/log.ts index 6a272747295..6cdd0f156f6 100644 --- a/packages/qwik/src/core/util/log.ts +++ b/packages/qwik/src/core/util/log.ts @@ -1,26 +1,25 @@ import type { QwikElement } from '../render/dom/virtual-element'; import type { QContext } from '../state/context'; import { isElement, isNode } from './element'; -import { qDev } from './qdev'; +import { qDev, qTest } from './qdev'; const STYLE = qDev ? `background: #564CE0; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;` : ''; export const logError = (message?: any, ...optionalParams: any[]) => { - const err = message instanceof Error ? message : createError(message); - const messageStr = err.stack || err.message; - console.error('%cQWIK ERROR', STYLE, messageStr, ...printParams(optionalParams)); - return err; + return createAndLogError(true, message, ...optionalParams); }; -export const createError = (message?: string) => { - const err = new Error(message); - return err; +export const throwErrorAndStop = (message?: any, ...optionalParams: any[]): never => { + const error = createAndLogError(false, message, ...optionalParams); + // eslint-disable-next-line no-debugger + debugger; + throw error; }; export const logErrorAndStop = (message?: any, ...optionalParams: any[]) => { - const err = logError(message, ...optionalParams); + const err = createAndLogError(true, message, ...optionalParams); // eslint-disable-next-line no-debugger debugger; return err; @@ -79,3 +78,18 @@ const printElement = (el: Element) => { ctx: isServer ? undefined : ctx, }; }; + +const createAndLogError = (asyncThrow: boolean, message?: any, ...optionalParams: any[]) => { + const err = message instanceof Error ? message : new Error(message); + const messageStr = err.stack || err.message; + console.error('%cQWIK ERROR', STYLE, messageStr, ...printParams(optionalParams)); + asyncThrow && + !qTest && + setTimeout(() => { + // throwing error asynchronously to avoid breaking the current call stack. + // We throw so that the error is delivered to the global error handler for + // reporting it to a third-party tools such as Qwik Insights, Sentry or New Relic. + throw err; + }, 0); + return err; +};