UNPKG

vike

Version:

The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.

179 lines (178 loc) 8.05 kB
"use strict"; 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)}`; }