vike
Version:
The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.
179 lines (178 loc) • 8.05 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.redirect = redirect;
exports.render = render;
exports.RenderErrorPage = RenderErrorPage;
exports.isAbortError = isAbortError;
exports.isAbortPageContext = isAbortPageContext;
exports.logAbortErrorHandled = logAbortErrorHandled;
exports.getPageContextFromAllRewrites = getPageContextFromAllRewrites;
exports.AbortRender = AbortRender;
exports.assertNoInfiniteAbortLoop = assertNoInfiniteAbortLoop;
const execHook_js_1 = require("../hooks/execHook.js");
const utils_js_1 = require("./utils.js");
const picocolors_1 = __importDefault(require("@brillout/picocolors"));
/**
* Abort the rendering of the current page, and redirect the user to another URL instead.
*
* https://vike.dev/redirect
*
* @param url The URL to redirect to.
* @param statusCode By default a temporary redirection (`302`) is performed. For permanent redirections (`301`), use `config.redirects` https://vike.dev/redirects instead or, alternatively, set the `statusCode` argument to `301`.
*/
function redirect(url, statusCode) {
const abortCaller = 'throw redirect()';
(0, utils_js_1.assertUsageUrlRedirectTarget)(url, getErrPrefix(abortCaller));
const args = [JSON.stringify(url)];
if (!statusCode) {
statusCode = 302;
}
else {
assertStatusCode(statusCode, [301, 302], 'redirect');
args.push(String(statusCode));
}
const pageContextAbort = {};
(0, utils_js_1.objectAssign)(pageContextAbort, {
_abortCaller: abortCaller,
_abortCall: `redirect(${args.join(', ')})`,
_urlRedirect: {
url,
statusCode,
},
});
return AbortRender(pageContextAbort);
}
function render(urlOrStatusCode, abortReason) {
const args = [typeof urlOrStatusCode === 'number' ? String(urlOrStatusCode) : JSON.stringify(urlOrStatusCode)];
if (abortReason !== undefined)
args.push((0, utils_js_1.truncateString)(JSON.stringify(abortReason), 30));
const abortCaller = 'throw render()';
const abortCall = `render(${args.join(', ')})`;
return render_(urlOrStatusCode, abortReason, abortCall, abortCaller);
}
function render_(urlOrStatusCode, abortReason, abortCall, abortCaller, pageContextAddendum) {
const pageContextAbort = {
abortReason,
_abortCaller: abortCaller,
_abortCall: abortCall,
};
if (pageContextAddendum) {
(0, utils_js_1.assert)(pageContextAddendum._isLegacyRenderErrorPage === true);
(0, utils_js_1.objectAssign)(pageContextAbort, pageContextAddendum);
}
if (typeof urlOrStatusCode === 'string') {
const url = urlOrStatusCode;
(0, utils_js_1.assertUsageUrlPathnameAbsolute)(url, getErrPrefix(abortCaller));
(0, utils_js_1.objectAssign)(pageContextAbort, {
_urlRewrite: url,
});
return AbortRender(pageContextAbort);
}
else {
const abortStatusCode = urlOrStatusCode;
assertStatusCode(urlOrStatusCode, [401, 403, 404, 410, 429, 500, 503], 'render');
(0, utils_js_1.objectAssign)(pageContextAbort, {
abortStatusCode,
is404: abortStatusCode === 404,
});
return AbortRender(pageContextAbort);
}
}
function AbortRender(pageContextAbort) {
const err = new Error('AbortRender');
(0, utils_js_1.objectAssign)(err, { _pageContextAbort: pageContextAbort, [stamp]: true });
(0, utils_js_1.checkType)(err);
return err;
}
// TODO/v1-release: remove
/**
* @deprecated Use `throw render()` or `throw redirect()` instead, see https://vike.dev/render'
*/
function RenderErrorPage({ pageContext = {} } = {}) {
(0, utils_js_1.assertWarning)(false, `${picocolors_1.default.cyan('throw RenderErrorPage()')} is deprecated and will be removed in the next major release. Use ${picocolors_1.default.cyan('throw render()')} or ${picocolors_1.default.cyan('throw redirect()')} instead, see https://vike.dev/render`, { onlyOnce: false });
let abortStatusCode = 404;
let abortReason = 'Page Not Found';
if (pageContext.is404 === false || pageContext.pageProps?.is404 === false) {
abortStatusCode = 500;
abortReason = 'Something went wrong';
}
(0, utils_js_1.objectAssign)(pageContext, { _isLegacyRenderErrorPage: true });
return render_(abortStatusCode, abortReason, 'RenderErrorPage()', 'throw RenderErrorPage()', pageContext);
}
const stamp = '_isAbortError';
function isAbortError(thing) {
return typeof thing === 'object' && thing !== null && stamp in thing;
}
function isAbortPageContext(pageContext) {
if (!(pageContext._urlRewrite || pageContext._urlRedirect || pageContext.abortStatusCode)) {
return false;
}
(0, utils_js_1.assert)((0, utils_js_1.hasProp)(pageContext, '_abortCall', 'string'));
/* Isn't needed and is missing on the client-side
assert(hasProp(pageContext, '_abortCaller', 'string'))
*/
(0, utils_js_1.checkType)(pageContext);
return true;
}
function logAbortErrorHandled(err, isProduction, pageContext) {
if (isProduction)
return;
const urlCurrent = pageContext._urlRewrite ?? pageContext.urlOriginal;
(0, utils_js_1.assert)(urlCurrent);
const abortCall = err._pageContextAbort._abortCall;
(0, utils_js_1.assert)(abortCall);
const hookLoc = (0, execHook_js_1.isUserHookError)(err);
let thrownBy = '';
if (hookLoc) {
thrownBy = ` by ${picocolors_1.default.cyan(`${hookLoc.hookName}()`)} hook defined at ${hookLoc.hookFilePath}`;
}
else {
// hookLoc is missing when serializing abort errors from server to client
}
(0, utils_js_1.assertInfo)(false, `${picocolors_1.default.cyan(abortCall)} thrown${thrownBy} while rendering ${picocolors_1.default.cyan(urlCurrent)}`, {
onlyOnce: false,
});
}
function assertStatusCode(statusCode, expected, caller) {
const expectedEnglish = (0, utils_js_1.joinEnglish)(expected.map((s) => s.toString()), 'or');
(0, utils_js_1.assertWarning)(expected.includes(statusCode), `Unepexected status code ${statusCode} passed to ${caller}(), we recommend ${expectedEnglish} instead. (Or reach out at https://github.com/vikejs/vike/issues/1008 if you believe ${statusCode} should be added.)`, { onlyOnce: true });
}
function getPageContextFromAllRewrites(pageContextsFromRewrite) {
assertNoInfiniteLoop(pageContextsFromRewrite);
const pageContextFromAllRewrites = { _urlRewrite: null };
pageContextsFromRewrite.forEach((pageContextFromRewrite) => {
Object.assign(pageContextFromAllRewrites, pageContextFromRewrite);
});
return pageContextFromAllRewrites;
}
function assertNoInfiniteLoop(pageContextsFromRewrite) {
const urlRewrites = [];
pageContextsFromRewrite.forEach((pageContext) => {
const urlRewrite = pageContext._urlRewrite;
{
const idx = urlRewrites.indexOf(urlRewrite);
if (idx !== -1) {
const loop = [...urlRewrites.slice(idx), urlRewrite].map((url) => `render('${url}')`).join(' => ');
(0, utils_js_1.assertUsage)(false, `Infinite loop of render() calls: ${loop}`);
}
}
urlRewrites.push(urlRewrite);
});
}
function assertNoInfiniteAbortLoop(rewriteCount, redirectCount) {
const abortCalls = [
// prettier-ignore
// biome-ignore format:
rewriteCount > 0 && picocolors_1.default.cyan("throw render('/some-url')"),
redirectCount > 0 && picocolors_1.default.cyan("throw redirect('/some-url')"),
]
.filter(Boolean)
.join(' and ');
(0, utils_js_1.assertUsage)(rewriteCount + redirectCount <= 7, `Maximum chain length of 7 ${abortCalls} exceeded. Did you define an infinite loop of ${abortCalls}?`);
}
function getErrPrefix(abortCaller) {
return `URL passed to ${picocolors_1.default.code(abortCaller)}`;
}