next
Version:
The React Framework
334 lines (333 loc) • 17.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getPageHandler = getPageHandler;
var _url = require("url");
var _utils = require("../../../../shared/lib/utils");
var _sendPayload = require("../../../../server/send-payload");
var _utils1 = require("./utils");
var _render = require("../../../../server/render");
var _node = require("../../../../server/api-utils/node");
var _denormalizePagePath = require("../../../../server/denormalize-page-path");
var _apiUtils = require("../../../../server/api-utils");
var _loadCustomRoutes = require("../../../../lib/load-custom-routes");
var _getRouteFromAssetPath = _interopRequireDefault(require("../../../../shared/lib/router/utils/get-route-from-asset-path"));
var _constants = require("../../../../shared/lib/constants");
var _renderResult = _interopRequireDefault(require("../../../../server/render-result"));
var _isError = _interopRequireDefault(require("../../../../lib/is-error"));
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function getPageHandler(ctx) {
const { page , pageComponent , pageConfig , pageGetStaticProps , pageGetStaticPaths , pageGetServerSideProps , appModule , documentModule , errorModule , notFoundModule , encodedPreviewProps , pageIsDynamic , generateEtags , poweredByHeader , runtimeConfig , buildManifest , reactLoadableManifest , i18n , buildId , basePath , assetPrefix , canonicalBase , escapedBuildId , } = ctx;
const { handleLocale , handleRewrites , handleBasePath , defaultRouteRegex , dynamicRouteMatcher , interpolateDynamicPath , getParamsFromRouteMatches , normalizeDynamicRouteParams , normalizeVercelUrl , } = (0, _utils1).getUtils(ctx);
async function renderReqToHTML(req, res, renderMode, _renderOpts, _params) {
let Component;
let App;
let config;
let Document;
let Error;
let notFoundMod;
let getStaticProps;
let getStaticPaths;
let getServerSideProps;
[getStaticProps, getServerSideProps, getStaticPaths, Component, App, config, { default: Document }, { default: Error }, notFoundMod, ] = await Promise.all([
pageGetStaticProps,
pageGetServerSideProps,
pageGetStaticPaths,
pageComponent,
appModule,
pageConfig,
documentModule,
errorModule,
notFoundModule,
]);
const fromExport = renderMode === 'export' || renderMode === true;
const nextStartMode = renderMode === 'passthrough';
let hasValidParams = true;
(0, _apiUtils).setLazyProp({
req: req
}, 'cookies', (0, _apiUtils).getCookieParser(req.headers));
const options = {
App,
Document,
buildManifest,
getStaticProps,
getServerSideProps,
getStaticPaths,
reactLoadableManifest,
canonicalBase,
buildId,
assetPrefix,
runtimeConfig: (runtimeConfig || {}).publicRuntimeConfig || {},
previewProps: encodedPreviewProps,
env: process.env,
basePath,
supportsDynamicHTML: false,
..._renderOpts
};
let _nextData = false;
let defaultLocale = i18n === null || i18n === void 0 ? void 0 : i18n.defaultLocale;
let detectedLocale = i18n === null || i18n === void 0 ? void 0 : i18n.defaultLocale;
let parsedUrl;
try {
var ref;
// We need to trust the dynamic route params from the proxy
// to ensure we are using the correct values
const trustQuery = !getStaticProps && req.headers[_utils1.vercelHeader];
parsedUrl = (0, _url).parse(req.url, true);
let routeNoAssetPath = parsedUrl.pathname;
if (basePath) {
routeNoAssetPath = routeNoAssetPath.replace(new RegExp(`^${basePath}`), '') || '/';
}
const origQuery = Object.assign({}, parsedUrl.query);
parsedUrl = handleRewrites(req, parsedUrl);
handleBasePath(req, parsedUrl);
// remove ?amp=1 from request URL if rendering for export
if (fromExport && parsedUrl.query.amp) {
const queryNoAmp = Object.assign({}, origQuery);
delete queryNoAmp.amp;
req.url = (0, _url).format({
...parsedUrl,
search: undefined,
query: queryNoAmp
});
}
if (parsedUrl.pathname.match(/_next\/data/)) {
_nextData = page !== '/_error';
parsedUrl.pathname = (0, _getRouteFromAssetPath).default(parsedUrl.pathname.replace(new RegExp(`/_next/data/${escapedBuildId}/`), '/'), '.json');
routeNoAssetPath = parsedUrl.pathname;
}
const localeResult = handleLocale(req, res, parsedUrl, routeNoAssetPath, fromExport || nextStartMode);
defaultLocale = (localeResult === null || localeResult === void 0 ? void 0 : localeResult.defaultLocale) || defaultLocale;
detectedLocale = (localeResult === null || localeResult === void 0 ? void 0 : localeResult.detectedLocale) || detectedLocale;
routeNoAssetPath = (localeResult === null || localeResult === void 0 ? void 0 : localeResult.routeNoAssetPath) || routeNoAssetPath;
if (parsedUrl.query.nextInternalLocale) {
detectedLocale = parsedUrl.query.nextInternalLocale;
delete parsedUrl.query.nextInternalLocale;
}
const renderOpts = Object.assign({
Component,
pageConfig: config,
nextExport: fromExport,
isDataReq: _nextData,
locales: i18n === null || i18n === void 0 ? void 0 : i18n.locales,
locale: detectedLocale,
defaultLocale,
domainLocales: i18n === null || i18n === void 0 ? void 0 : i18n.domains,
optimizeCss: process.env.__NEXT_OPTIMIZE_CSS,
nextScriptWorkers: process.env.__NEXT_SCRIPT_WORKERS,
crossOrigin: process.env.__NEXT_CROSS_ORIGIN
}, options);
if (page === '/_error' && !res.statusCode) {
res.statusCode = 404;
}
let params = {};
if (!fromExport && pageIsDynamic) {
const result = normalizeDynamicRouteParams(trustQuery ? parsedUrl.query : dynamicRouteMatcher(parsedUrl.pathname));
hasValidParams = result.hasValidParams;
params = result.params;
}
let nowParams = null;
if (pageIsDynamic && !hasValidParams && ((ref = req.headers) === null || ref === void 0 ? void 0 : ref['x-now-route-matches'])) {
nowParams = getParamsFromRouteMatches(req, renderOpts, detectedLocale);
}
// make sure to set renderOpts to the correct params e.g. _params
// if provided from worker or params if we're parsing them here
renderOpts.params = _params || params;
normalizeVercelUrl(req, !!trustQuery);
// normalize request URL/asPath for fallback/revalidate pages since the
// proxy sets the request URL to the output's path for fallback pages
if (pageIsDynamic && nowParams && defaultRouteRegex) {
const _parsedUrl = (0, _url).parse(req.url);
_parsedUrl.pathname = interpolateDynamicPath(_parsedUrl.pathname, nowParams);
parsedUrl.pathname = _parsedUrl.pathname;
req.url = (0, _url).format(_parsedUrl);
}
// make sure to normalize asPath for revalidate and _next/data requests
// since the asPath should match what is shown on the client
if (!fromExport && (getStaticProps || getServerSideProps)) {
// don't include dynamic route params in query while normalizing
// asPath
if (pageIsDynamic && trustQuery && defaultRouteRegex) {
delete parsedUrl.search;
for (const param of Object.keys(defaultRouteRegex.groups)){
delete origQuery[param];
}
}
parsedUrl.pathname = (0, _denormalizePagePath).denormalizePagePath(parsedUrl.pathname);
renderOpts.resolvedUrl = (0, _url).format({
...parsedUrl,
query: origQuery
});
// For getServerSideProps we need to ensure we use the original URL
// and not the resolved URL to prevent a hydration mismatch on asPath
renderOpts.resolvedAsPath = getServerSideProps ? (0, _url).format({
...parsedUrl,
pathname: routeNoAssetPath,
query: origQuery
}) : renderOpts.resolvedUrl;
}
const isFallback = parsedUrl.query.__nextFallback;
const previewData = (0, _node).tryGetPreviewData(req, res, options.previewProps);
const isPreviewMode = previewData !== false;
if (process.env.__NEXT_OPTIMIZE_FONTS) {
renderOpts.optimizeFonts = true;
/**
* __webpack_require__.__NEXT_FONT_MANIFEST__ is added by
* font-stylesheet-gathering-plugin
*/ // @ts-ignore
renderOpts.fontManifest = __webpack_require__.__NEXT_FONT_MANIFEST__;
}
let result = await (0, _render).renderToHTML(req, res, page, Object.assign({}, getStaticProps ? {
...parsedUrl.query.amp ? {
amp: '1'
} : {}
} : parsedUrl.query, nowParams ? nowParams : params, _params, isFallback ? {
__nextFallback: 'true'
} : {}), renderOpts);
if (!renderMode) {
if (_nextData || getStaticProps || getServerSideProps) {
if (renderOpts.isNotFound) {
res.statusCode = 404;
if (_nextData) {
res.end('{"notFound":true}');
return null;
}
const NotFoundComponent = notFoundMod ? notFoundMod.default : Error;
const errPathname = notFoundMod ? '/404' : '/_error';
const result2 = await (0, _render).renderToHTML(req, res, errPathname, parsedUrl.query, Object.assign({}, options, {
getStaticProps: notFoundMod ? notFoundMod.getStaticProps : undefined,
getStaticPaths: undefined,
getServerSideProps: undefined,
Component: NotFoundComponent,
err: undefined,
locale: detectedLocale,
locales: i18n === null || i18n === void 0 ? void 0 : i18n.locales,
defaultLocale: i18n === null || i18n === void 0 ? void 0 : i18n.defaultLocale
}));
(0, _sendPayload).sendRenderResult({
req,
res,
result: result2 !== null && result2 !== void 0 ? result2 : _renderResult.default.empty,
type: 'html',
generateEtags,
poweredByHeader,
options: {
private: isPreviewMode || page === '/404',
stateful: !!getServerSideProps,
revalidate: renderOpts.revalidate
}
});
return null;
} else if (renderOpts.isRedirect && !_nextData) {
const redirect = {
destination: renderOpts.pageData.pageProps.__N_REDIRECT,
statusCode: renderOpts.pageData.pageProps.__N_REDIRECT_STATUS,
basePath: renderOpts.pageData.pageProps.__N_REDIRECT_BASE_PATH
};
const statusCode = (0, _loadCustomRoutes).getRedirectStatus(redirect);
if (basePath && redirect.basePath !== false && redirect.destination.startsWith('/')) {
redirect.destination = `${basePath}${redirect.destination}`;
}
if (statusCode === _constants.PERMANENT_REDIRECT_STATUS) {
res.setHeader('Refresh', `0;url=${redirect.destination}`);
}
res.statusCode = statusCode;
res.setHeader('Location', redirect.destination);
res.end(redirect.destination);
return null;
} else {
(0, _sendPayload).sendRenderResult({
req,
res,
result: _nextData ? _renderResult.default.fromStatic(JSON.stringify(renderOpts.pageData)) : result !== null && result !== void 0 ? result : _renderResult.default.empty,
type: _nextData ? 'json' : 'html',
generateEtags,
poweredByHeader,
options: {
private: isPreviewMode || renderOpts.is404Page,
stateful: !!getServerSideProps,
revalidate: renderOpts.revalidate
}
});
return null;
}
}
} else if (isPreviewMode) {
res.setHeader('Cache-Control', 'private, no-cache, no-store, max-age=0, must-revalidate');
}
if (renderMode) return {
html: result,
renderOpts
};
return result ? result.toUnchunkedString() : null;
} catch (err) {
if (!parsedUrl) {
parsedUrl = (0, _url).parse(req.url, true);
}
if ((0, _isError).default(err) && err.code === 'ENOENT') {
res.statusCode = 404;
} else if (err instanceof _utils.DecodeError) {
res.statusCode = 400;
} else {
console.error('Unhandled error during request:', err);
// Backwards compat (call getInitialProps in custom error):
try {
await (0, _render).renderToHTML(req, res, '/_error', parsedUrl.query, Object.assign({}, options, {
getStaticProps: undefined,
getStaticPaths: undefined,
getServerSideProps: undefined,
Component: Error,
err: err,
// Short-circuit rendering:
isDataReq: true
}));
} catch (underErrorErr) {
console.error('Failed call /_error subroutine, continuing to crash function:', underErrorErr);
}
// Throw the error to crash the serverless function
if ((0, _utils).isResSent(res)) {
console.error('!!! WARNING !!!');
console.error('Your function crashed, but closed the response before allowing the function to exit.\\n' + 'This may cause unexpected behavior for the next request.');
console.error('!!! WARNING !!!');
}
throw err;
}
const result2 = await (0, _render).renderToHTML(req, res, '/_error', parsedUrl.query, Object.assign({}, options, {
getStaticProps: undefined,
getStaticPaths: undefined,
getServerSideProps: undefined,
Component: Error,
err: res.statusCode === 404 ? undefined : err
}));
return result2 ? result2.toUnchunkedString() : null;
}
}
return {
renderReqToHTML,
render: async function render(req, res) {
try {
const html = await renderReqToHTML(req, res);
if (html) {
(0, _sendPayload).sendRenderResult({
req,
res,
result: _renderResult.default.fromStatic(html),
type: 'html',
generateEtags,
poweredByHeader
});
}
} catch (err) {
console.error(err);
// Throw the error to crash the serverless function
throw err;
}
}
};
}
//# sourceMappingURL=page-handler.js.map