next
Version:
The React Framework
329 lines (328 loc) • 16.5 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getUtils = getUtils;
exports.vercelHeader = void 0;
var _url = require("url");
var _querystring = require("querystring");
var _normalizeLocalePath = require("../../../../shared/lib/i18n/normalize-locale-path");
var _pathMatch = _interopRequireDefault(require("../../../../shared/lib/router/utils/path-match"));
var _routeRegex = require("../../../../shared/lib/router/utils/route-regex");
var _routeMatcher = require("../../../../shared/lib/router/utils/route-matcher");
var _prepareDestination = require("../../../../shared/lib/router/utils/prepare-destination");
var _acceptHeader = require("../../../../server/accept-header");
var _detectLocaleCookie = require("../../../../shared/lib/i18n/detect-locale-cookie");
var _detectDomainLocale = require("../../../../shared/lib/i18n/detect-domain-locale");
var _denormalizePagePath = require("../../../../server/denormalize-page-path");
var _cookie = _interopRequireDefault(require("next/dist/compiled/cookie"));
var _constants = require("../../../../shared/lib/constants");
var _requestMeta = require("../../../../server/request-meta");
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
const getCustomRouteMatcher = (0, _pathMatch).default(true);
const vercelHeader = 'x-vercel-id';
exports.vercelHeader = vercelHeader;
function getUtils({ page , i18n , basePath , rewrites , pageIsDynamic }) {
let defaultRouteRegex;
let dynamicRouteMatcher;
let defaultRouteMatches;
if (pageIsDynamic) {
defaultRouteRegex = (0, _routeRegex).getRouteRegex(page);
dynamicRouteMatcher = (0, _routeMatcher).getRouteMatcher(defaultRouteRegex);
defaultRouteMatches = dynamicRouteMatcher(page);
}
function handleRewrites(req, parsedUrl) {
for (const rewrite of rewrites){
const matcher = getCustomRouteMatcher(rewrite.source);
let params = matcher(parsedUrl.pathname);
if (rewrite.has && params) {
const hasParams = (0, _prepareDestination).matchHas(req, rewrite.has, parsedUrl.query);
if (hasParams) {
Object.assign(params, hasParams);
} else {
params = false;
}
}
if (params) {
const { parsedDestination } = (0, _prepareDestination).prepareDestination({
appendParamsToQuery: true,
destination: rewrite.destination,
params: params,
query: parsedUrl.query
});
Object.assign(parsedUrl.query, parsedDestination.query);
delete parsedDestination.query;
Object.assign(parsedUrl, parsedDestination);
let fsPathname = parsedUrl.pathname;
if (basePath) {
fsPathname = fsPathname.replace(new RegExp(`^${basePath}`), '') || '/';
}
if (i18n) {
const destLocalePathResult = (0, _normalizeLocalePath).normalizeLocalePath(fsPathname, i18n.locales);
fsPathname = destLocalePathResult.pathname;
parsedUrl.query.nextInternalLocale = destLocalePathResult.detectedLocale || params.nextInternalLocale;
}
if (fsPathname === page) {
break;
}
if (pageIsDynamic && dynamicRouteMatcher) {
const dynamicParams = dynamicRouteMatcher(fsPathname);
if (dynamicParams) {
parsedUrl.query = {
...parsedUrl.query,
...dynamicParams
};
break;
}
}
}
}
return parsedUrl;
}
function handleBasePath(req, parsedUrl) {
// always strip the basePath if configured since it is required
req.url = req.url.replace(new RegExp(`^${basePath}`), '') || '/';
parsedUrl.pathname = parsedUrl.pathname.replace(new RegExp(`^${basePath}`), '') || '/';
}
function getParamsFromRouteMatches(req, renderOpts, detectedLocale) {
return (0, _routeMatcher).getRouteMatcher(function() {
const { groups , routeKeys } = defaultRouteRegex;
return {
re: {
// Simulate a RegExp match from the \`req.url\` input
exec: (str)=>{
const obj = (0, _querystring).parse(str);
const matchesHasLocale = i18n && detectedLocale && obj['1'] === detectedLocale;
// favor named matches if available
const routeKeyNames = Object.keys(routeKeys || {});
const filterLocaleItem = (val)=>{
if (i18n) {
// locale items can be included in route-matches
// for fallback SSG pages so ensure they are
// filtered
const isCatchAll = Array.isArray(val);
const _val = isCatchAll ? val[0] : val;
if (typeof _val === 'string' && i18n.locales.some((item)=>{
if (item.toLowerCase() === _val.toLowerCase()) {
detectedLocale = item;
renderOpts.locale = detectedLocale;
return true;
}
return false;
})) {
// remove the locale item from the match
if (isCatchAll) {
val.splice(0, 1);
}
// the value is only a locale item and
// shouldn't be added
return isCatchAll ? val.length === 0 : true;
}
}
return false;
};
if (routeKeyNames.every((name)=>obj[name]
)) {
return routeKeyNames.reduce((prev, keyName)=>{
const paramName = routeKeys === null || routeKeys === void 0 ? void 0 : routeKeys[keyName];
if (paramName && !filterLocaleItem(obj[keyName])) {
prev[groups[paramName].pos] = obj[keyName];
}
return prev;
}, {});
}
return Object.keys(obj).reduce((prev, key)=>{
if (!filterLocaleItem(obj[key])) {
let normalizedKey = key;
if (matchesHasLocale) {
normalizedKey = parseInt(key, 10) - 1 + '';
}
return Object.assign(prev, {
[normalizedKey]: obj[key]
});
}
return prev;
}, {});
}
},
groups
};
}())(req.headers['x-now-route-matches']);
}
function interpolateDynamicPath(pathname, params) {
if (!defaultRouteRegex) return pathname;
for (const param of Object.keys(defaultRouteRegex.groups)){
const { optional , repeat } = defaultRouteRegex.groups[param];
let builtParam = `[${repeat ? '...' : ''}${param}]`;
if (optional) {
builtParam = `[${builtParam}]`;
}
const paramIdx = pathname.indexOf(builtParam);
if (paramIdx > -1) {
let paramValue;
if (Array.isArray(params[param])) {
paramValue = params[param].map((v)=>v && encodeURIComponent(v)
).join('/');
} else {
paramValue = params[param] && encodeURIComponent(params[param]);
}
pathname = pathname.slice(0, paramIdx) + (paramValue || '') + pathname.slice(paramIdx + builtParam.length);
}
}
return pathname;
}
function normalizeVercelUrl(req, trustQuery) {
// make sure to normalize req.url on Vercel to strip dynamic params
// from the query which are added during routing
if (pageIsDynamic && trustQuery && defaultRouteRegex) {
const _parsedUrl = (0, _url).parse(req.url, true);
delete _parsedUrl.search;
for (const param of Object.keys(defaultRouteRegex.groups)){
delete _parsedUrl.query[param];
}
req.url = (0, _url).format(_parsedUrl);
}
}
function normalizeDynamicRouteParams(params) {
let hasValidParams = true;
if (!defaultRouteRegex) return {
params,
hasValidParams: false
};
params = Object.keys(defaultRouteRegex.groups).reduce((prev, key)=>{
let value = params[key];
// if the value matches the default value we can't rely
// on the parsed params, this is used to signal if we need
// to parse x-now-route-matches or not
const defaultValue = defaultRouteMatches[key];
const isDefaultValue = Array.isArray(defaultValue) ? defaultValue.some((defaultVal)=>{
return Array.isArray(value) ? value.some((val)=>val.includes(defaultVal)
) : value === null || value === void 0 ? void 0 : value.includes(defaultVal);
}) : value === null || value === void 0 ? void 0 : value.includes(defaultValue);
if (isDefaultValue || typeof value === 'undefined') {
hasValidParams = false;
}
// non-provided optional values should be undefined so normalize
// them to undefined
if (defaultRouteRegex.groups[key].optional && (!value || Array.isArray(value) && value.length === 1 && // fallback optional catch-all SSG pages have
// [[...paramName]] for the root path on Vercel
(value[0] === 'index' || value[0] === `[[...${key}]]`))) {
value = undefined;
delete params[key];
}
// query values from the proxy aren't already split into arrays
// so make sure to normalize catch-all values
if (value && typeof value === 'string' && defaultRouteRegex.groups[key].repeat) {
value = value.split('/');
}
if (value) {
prev[key] = value;
}
return prev;
}, {});
return {
params,
hasValidParams
};
}
function handleLocale(req, res, parsedUrl, routeNoAssetPath, shouldNotRedirect) {
if (!i18n) return;
const pathname = parsedUrl.pathname || '/';
let defaultLocale = i18n.defaultLocale;
let detectedLocale = (0, _detectLocaleCookie).detectLocaleCookie(req, i18n.locales);
let acceptPreferredLocale;
try {
acceptPreferredLocale = i18n.localeDetection !== false ? (0, _acceptHeader).acceptLanguage(req.headers['accept-language'], i18n.locales) : detectedLocale;
} catch (_) {
acceptPreferredLocale = detectedLocale;
}
const { host } = req.headers || {};
// remove port from host and remove port if present
const hostname = host && host.split(':')[0].toLowerCase();
const detectedDomain = (0, _detectDomainLocale).detectDomainLocale(i18n.domains, hostname);
if (detectedDomain) {
defaultLocale = detectedDomain.defaultLocale;
detectedLocale = defaultLocale;
(0, _requestMeta).addRequestMeta(req, '__nextIsLocaleDomain', true);
}
// if not domain specific locale use accept-language preferred
detectedLocale = detectedLocale || acceptPreferredLocale;
let localeDomainRedirect;
const localePathResult = (0, _normalizeLocalePath).normalizeLocalePath(pathname, i18n.locales);
routeNoAssetPath = (0, _normalizeLocalePath).normalizeLocalePath(routeNoAssetPath, i18n.locales).pathname;
if (localePathResult.detectedLocale) {
detectedLocale = localePathResult.detectedLocale;
req.url = (0, _url).format({
...parsedUrl,
pathname: localePathResult.pathname
});
(0, _requestMeta).addRequestMeta(req, '__nextStrippedLocale', true);
parsedUrl.pathname = localePathResult.pathname;
}
// If a detected locale is a domain specific locale and we aren't already
// on that domain and path prefix redirect to it to prevent duplicate
// content from multiple domains
if (detectedDomain) {
const localeToCheck = localePathResult.detectedLocale ? detectedLocale : acceptPreferredLocale;
const matchedDomain = (0, _detectDomainLocale).detectDomainLocale(i18n.domains, undefined, localeToCheck);
if (matchedDomain && matchedDomain.domain !== detectedDomain.domain) {
localeDomainRedirect = `http${matchedDomain.http ? '' : 's'}://${matchedDomain.domain}/${localeToCheck === matchedDomain.defaultLocale ? '' : localeToCheck}`;
}
}
const denormalizedPagePath = (0, _denormalizePagePath).denormalizePagePath(pathname);
const detectedDefaultLocale = !detectedLocale || detectedLocale.toLowerCase() === defaultLocale.toLowerCase();
const shouldStripDefaultLocale = false;
// detectedDefaultLocale &&
// denormalizedPagePath.toLowerCase() === \`/\${i18n.defaultLocale.toLowerCase()}\`
const shouldAddLocalePrefix = !detectedDefaultLocale && denormalizedPagePath === '/';
detectedLocale = detectedLocale || i18n.defaultLocale;
if (!shouldNotRedirect && !req.headers[vercelHeader] && i18n.localeDetection !== false && (localeDomainRedirect || shouldAddLocalePrefix || shouldStripDefaultLocale)) {
// set the NEXT_LOCALE cookie when a user visits the default locale
// with the locale prefix so that they aren't redirected back to
// their accept-language preferred locale
if (shouldStripDefaultLocale && acceptPreferredLocale !== defaultLocale) {
const previous = res.getHeader('set-cookie');
res.setHeader('set-cookie', [
...typeof previous === 'string' ? [
previous
] : Array.isArray(previous) ? previous : [],
_cookie.default.serialize('NEXT_LOCALE', defaultLocale, {
httpOnly: true,
path: '/'
}),
]);
}
res.setHeader('Location', (0, _url).format({
// make sure to include any query values when redirecting
...parsedUrl,
pathname: localeDomainRedirect ? localeDomainRedirect : shouldStripDefaultLocale ? basePath || '/' : `${basePath}/${detectedLocale}`
}));
res.statusCode = _constants.TEMPORARY_REDIRECT_STATUS;
res.end();
return;
}
detectedLocale = localePathResult.detectedLocale || detectedDomain && detectedDomain.defaultLocale || defaultLocale;
return {
defaultLocale,
detectedLocale,
routeNoAssetPath
};
}
return {
handleLocale,
handleRewrites,
handleBasePath,
defaultRouteRegex,
normalizeVercelUrl,
dynamicRouteMatcher,
defaultRouteMatches,
interpolateDynamicPath,
getParamsFromRouteMatches,
normalizeDynamicRouteParams
};
}
//# sourceMappingURL=utils.js.map
;