vike
Version:
The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.
67 lines (66 loc) • 3.29 kB
JavaScript
import '../assertEnvServer.js';
export { logErrorServer };
export { hasAlreadyLogged };
import pc from '@brillout/picocolors';
import { assertWarning } from '../../utils/assert.js';
import { assertIsNotBrowser } from '../../utils/assertIsNotBrowser.js';
import { hasRed } from '../../utils/colorsServer.js';
import { isDebugError } from '../../utils/debug.js';
import { getGlobalObject } from '../../utils/getGlobalObject.js';
import { isObject } from '../../utils/isObject.js';
import { execHookOnError } from './renderPageServer/execHookOnError.js';
import { addErrorHint } from './renderPageServer/addErrorHint.js';
import { isAbortError } from '../../shared-server-client/route/abort.js';
assertIsNotBrowser();
const globalObject = getGlobalObject('server/runtime/logErrorServer.ts', {
wasAlreadyLogged: new WeakSet(),
});
function logErrorServer(err, pageContext) {
if (isAbortError(err) && !isDebugError())
return;
// I don't think there is a use case for printing the same error object twice? Reloading page throwing error => the same error is printed a second time but it's a different error object.
if (hasAlreadyLogged(err))
return;
warnIfErrorIsNotObject(err);
const errBetter = addErrorHint(err);
execHookOnError(errBetter, pageContext);
const errPrinted = getStackOrMessage(isDebugError() ? getOriginalError(errBetter) : errBetter);
console.error(hasRed(errPrinted) ? errPrinted : pc.red(errPrinted));
setAlreadyLogged(err);
}
function getOriginalError(err) {
// getOriginalError() is set by getBetterError()
// https://github.com/vikejs/vike/blob/c0dc090e64ca9daa516ebf884fef66f5531cae69/packages/vike/utils/getBetterError.ts#L32
return err?.getOriginalError?.() ?? err;
}
// We ensure we print a string; Cloudflare Workers doesn't seem to properly stringify `Error` objects.
// - TO-DO/eventually: is that still true? Let's eventually remove it and see if it crashes Cloudflare.
function getStackOrMessage(err) {
if (!isObject(err) || !err.stack)
return String(err);
if (err.hideStack)
return err.message;
return err.stack;
}
// It would be cleaner to:
// - Call assertUsageErrorIsObject() right after calling the user's hook
// - Attach the original error: assertUsageError.originalErrorValue = err
// - Show the original error in Vike's error handling
// - Use assertErrorIsObject() throughout Vike's source code
function warnIfErrorIsNotObject(err) {
if (!isObject(err)) {
console.warn('[vike] The thrown value is:');
console.warn(err);
assertWarning(false, `One of your hooks threw an error ${pc.cyan('throw someValue')} but ${pc.cyan('someValue')} isn't an object (it's ${pc.cyan(`typeof someValue === ${typeof err}`)} instead). Make sure thrown values are always wrapped with ${pc.cyan('new Error()')}, in other words: ${pc.cyan('throw someValue')} should be replaced with ${pc.cyan('throw new Error(someValue)')}. The thrown value is printed above.`, { onlyOnce: false });
}
}
function hasAlreadyLogged(err) {
if (!isObject(err))
return false;
return globalObject.wasAlreadyLogged.has(err);
}
function setAlreadyLogged(err) {
if (!isObject(err))
return;
globalObject.wasAlreadyLogged.add(err);
}