Spaces:
Running
Running
| ; | |
| Object.defineProperty(exports, "__esModule", { | |
| value: true | |
| }); | |
| 0 && (module.exports = { | |
| checkCustomRoutes: null, | |
| default: null, | |
| normalizeRouteRegex: null | |
| }); | |
| function _export(target, all) { | |
| for(var name in all)Object.defineProperty(target, name, { | |
| enumerable: true, | |
| get: all[name] | |
| }); | |
| } | |
| _export(exports, { | |
| checkCustomRoutes: function() { | |
| return checkCustomRoutes; | |
| }, | |
| default: function() { | |
| return loadCustomRoutes; | |
| }, | |
| normalizeRouteRegex: function() { | |
| return normalizeRouteRegex; | |
| } | |
| }); | |
| const _picocolors = require("./picocolors"); | |
| const _escaperegexp = require("../shared/lib/escape-regexp"); | |
| const _trytoparsepath = require("./try-to-parse-path"); | |
| const _redirectstatus = require("./redirect-status"); | |
| const _url = require("./url"); | |
| const _constants = require("./constants"); | |
| const allowedHasTypes = new Set([ | |
| 'header', | |
| 'cookie', | |
| 'query', | |
| 'host' | |
| ]); | |
| const namedGroupsRegex = /\(\?<([a-zA-Z][a-zA-Z0-9]*)>/g; | |
| function normalizeRouteRegex(regex) { | |
| // clean up un-necessary escaping from regex.source which turns / into \\/ | |
| return regex.replace(/\\\//g, '/'); | |
| } | |
| function checkRedirect(route) { | |
| const invalidParts = []; | |
| let hadInvalidStatus = false; | |
| if (route.statusCode && !_redirectstatus.allowedStatusCodes.has(route['statusCode'])) { | |
| hadInvalidStatus = true; | |
| invalidParts.push(`\`statusCode\` is not undefined or valid statusCode`); | |
| } | |
| if (typeof route.permanent !== 'boolean' && !route['statusCode']) { | |
| invalidParts.push(`\`permanent\` is not set to \`true\` or \`false\``); | |
| } | |
| return { | |
| invalidParts, | |
| hadInvalidStatus | |
| }; | |
| } | |
| function checkHeader(route) { | |
| const invalidParts = []; | |
| if (!Array.isArray(route.headers)) { | |
| invalidParts.push('`headers` field must be an array'); | |
| } else if (route.headers.length === 0) { | |
| invalidParts.push('`headers` field cannot be empty'); | |
| } else { | |
| for (const header of route.headers){ | |
| if (!header || typeof header !== 'object') { | |
| invalidParts.push("`headers` items must be object with { key: '', value: '' }"); | |
| break; | |
| } | |
| if (typeof header.key !== 'string') { | |
| invalidParts.push('`key` in header item must be string'); | |
| break; | |
| } | |
| if (typeof header.value !== 'string') { | |
| invalidParts.push('`value` in header item must be string'); | |
| break; | |
| } | |
| } | |
| } | |
| return invalidParts; | |
| } | |
| function checkCustomRoutes(routes, type) { | |
| if (!Array.isArray(routes)) { | |
| console.error(`Error: ${type}s must return an array, received ${typeof routes}.\n` + `See here for more info: https://nextjs.org/docs/messages/routes-must-be-array`); | |
| process.exit(1); | |
| } | |
| let numInvalidRoutes = 0; | |
| let hadInvalidStatus = false; | |
| let hadInvalidHas = false; | |
| let hadInvalidMissing = false; | |
| const allowedKeys = new Set([ | |
| 'source', | |
| 'locale', | |
| 'has', | |
| 'missing' | |
| ]); | |
| if (type === 'rewrite') { | |
| allowedKeys.add('basePath'); | |
| allowedKeys.add('destination'); | |
| } | |
| if (type === 'redirect') { | |
| allowedKeys.add('basePath'); | |
| allowedKeys.add('statusCode'); | |
| allowedKeys.add('permanent'); | |
| allowedKeys.add('destination'); | |
| } | |
| if (type === 'header') { | |
| allowedKeys.add('basePath'); | |
| allowedKeys.add('headers'); | |
| } | |
| for (const route of routes){ | |
| if (!route || typeof route !== 'object') { | |
| console.error(`The route ${JSON.stringify(route)} is not a valid object with \`source\`${type !== 'middleware' ? ` and \`${type === 'header' ? 'headers' : 'destination'}\`` : ''}`); | |
| numInvalidRoutes++; | |
| continue; | |
| } | |
| if (type === 'rewrite' && route.basePath === false && !(route.destination.startsWith('http://') || route.destination.startsWith('https://'))) { | |
| console.error(`The route ${route.source} rewrites urls outside of the basePath. Please use a destination that starts with \`http://\` or \`https://\` https://nextjs.org/docs/messages/invalid-external-rewrite`); | |
| numInvalidRoutes++; | |
| continue; | |
| } | |
| const keys = Object.keys(route); | |
| const invalidKeys = keys.filter((key)=>!allowedKeys.has(key)); | |
| const invalidParts = []; | |
| if ('basePath' in route && typeof route.basePath !== 'undefined' && route.basePath !== false) { | |
| invalidParts.push('`basePath` must be undefined or false'); | |
| } | |
| if (typeof route.locale !== 'undefined' && route.locale !== false) { | |
| invalidParts.push('`locale` must be undefined or false'); | |
| } | |
| const checkInvalidHasMissing = (items, fieldName)=>{ | |
| let hadInvalidItem = false; | |
| if (typeof items !== 'undefined' && !Array.isArray(items)) { | |
| invalidParts.push(`\`${fieldName}\` must be undefined or valid has object`); | |
| hadInvalidItem = true; | |
| } else if (items) { | |
| const invalidHasItems = []; | |
| for (const hasItem of items){ | |
| let invalidHasParts = []; | |
| if (!allowedHasTypes.has(hasItem.type)) { | |
| invalidHasParts.push(`invalid type "${hasItem.type}"`); | |
| } | |
| if (typeof hasItem.key !== 'string' && hasItem.type !== 'host') { | |
| invalidHasParts.push(`invalid key "${hasItem.key}"`); | |
| } | |
| if (typeof hasItem.value !== 'undefined' && typeof hasItem.value !== 'string') { | |
| invalidHasParts.push(`invalid value "${hasItem.value}"`); | |
| } | |
| if (typeof hasItem.value === 'undefined' && hasItem.type === 'host') { | |
| invalidHasParts.push(`value is required for "host" type`); | |
| } | |
| if (invalidHasParts.length > 0) { | |
| invalidHasItems.push(`${invalidHasParts.join(', ')} for ${JSON.stringify(hasItem)}`); | |
| } | |
| } | |
| if (invalidHasItems.length > 0) { | |
| hadInvalidItem = true; | |
| const itemStr = `item${invalidHasItems.length === 1 ? '' : 's'}`; | |
| console.error(`Invalid \`${fieldName}\` ${itemStr}:\n` + invalidHasItems.join('\n')); | |
| console.error(); | |
| invalidParts.push(`invalid \`${fieldName}\` ${itemStr} found`); | |
| } | |
| } | |
| return hadInvalidItem; | |
| }; | |
| if (checkInvalidHasMissing(route.has, 'has')) { | |
| hadInvalidHas = true; | |
| } | |
| if (checkInvalidHasMissing(route.missing, 'missing')) { | |
| hadInvalidMissing = true; | |
| } | |
| if (!route.source) { | |
| invalidParts.push('`source` is missing'); | |
| } else if (typeof route.source !== 'string') { | |
| invalidParts.push('`source` is not a string'); | |
| } else if (!route.source.startsWith('/')) { | |
| invalidParts.push('`source` does not start with /'); | |
| } | |
| if (type === 'header') { | |
| invalidParts.push(...checkHeader(route)); | |
| } else if (type !== 'middleware') { | |
| let _route = route; | |
| if (!_route.destination) { | |
| invalidParts.push('`destination` is missing'); | |
| } else if (typeof _route.destination !== 'string') { | |
| invalidParts.push('`destination` is not a string'); | |
| } else if (type === 'rewrite' && !_route.destination.match(/^(\/|https:\/\/|http:\/\/)/)) { | |
| invalidParts.push('`destination` does not start with `/`, `http://`, or `https://`'); | |
| } | |
| } | |
| if (type === 'redirect') { | |
| const result = checkRedirect(route); | |
| hadInvalidStatus = hadInvalidStatus || result.hadInvalidStatus; | |
| invalidParts.push(...result.invalidParts); | |
| } | |
| let sourceTokens; | |
| if (typeof route.source === 'string' && route.source.startsWith('/')) { | |
| // only show parse error if we didn't already show error | |
| // for not being a string | |
| const { tokens, error, regexStr } = (0, _trytoparsepath.tryToParsePath)(route.source); | |
| if (error) { | |
| invalidParts.push('`source` parse failed'); | |
| } | |
| if (regexStr && regexStr.length > 4096) { | |
| invalidParts.push('`source` exceeds max built length of 4096'); | |
| } | |
| sourceTokens = tokens; | |
| } | |
| const hasSegments = new Set(); | |
| if (route.has) { | |
| for (const hasItem of route.has){ | |
| if (!hasItem.value && hasItem.key) { | |
| hasSegments.add(hasItem.key); | |
| } | |
| if (hasItem.value) { | |
| for (const match of hasItem.value.matchAll(namedGroupsRegex)){ | |
| if (match[1]) { | |
| hasSegments.add(match[1]); | |
| } | |
| } | |
| if (hasItem.type === 'host') { | |
| hasSegments.add('host'); | |
| } | |
| } | |
| } | |
| } | |
| // make sure no unnamed patterns are attempted to be used in the | |
| // destination as this can cause confusion and is not allowed | |
| if (typeof route.destination === 'string') { | |
| if (route.destination.startsWith('/') && Array.isArray(sourceTokens)) { | |
| const unnamedInDest = new Set(); | |
| for (const token of sourceTokens){ | |
| if (typeof token === 'object' && typeof token.name === 'number') { | |
| const unnamedIndex = new RegExp(`:${token.name}(?!\\d)`); | |
| if (route.destination.match(unnamedIndex)) { | |
| unnamedInDest.add(`:${token.name}`); | |
| } | |
| } | |
| } | |
| if (unnamedInDest.size > 0) { | |
| invalidParts.push(`\`destination\` has unnamed params ${[ | |
| ...unnamedInDest | |
| ].join(', ')}`); | |
| } else { | |
| const { tokens: destTokens, regexStr: destRegexStr, error: destinationParseFailed } = (0, _trytoparsepath.tryToParsePath)(route.destination, { | |
| handleUrl: true | |
| }); | |
| if (destRegexStr && destRegexStr.length > 4096) { | |
| invalidParts.push('`destination` exceeds max built length of 4096'); | |
| } | |
| if (destinationParseFailed) { | |
| invalidParts.push('`destination` parse failed'); | |
| } else { | |
| const sourceSegments = new Set(sourceTokens.map((item)=>typeof item === 'object' && item.name).filter(Boolean)); | |
| const invalidDestSegments = new Set(); | |
| for (const token of destTokens){ | |
| if (typeof token === 'object' && !sourceSegments.has(token.name) && !hasSegments.has(token.name)) { | |
| invalidDestSegments.add(token.name); | |
| } | |
| } | |
| if (invalidDestSegments.size) { | |
| invalidParts.push(`\`destination\` has segments not in \`source\` or \`has\` (${[ | |
| ...invalidDestSegments | |
| ].join(', ')})`); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| const hasInvalidKeys = invalidKeys.length > 0; | |
| const hasInvalidParts = invalidParts.length > 0; | |
| if (hasInvalidKeys || hasInvalidParts) { | |
| console.error(`${invalidParts.join(', ')}${invalidKeys.length ? (hasInvalidParts ? ',' : '') + ` invalid field${invalidKeys.length === 1 ? '' : 's'}: ` + invalidKeys.join(',') : ''} for route ${JSON.stringify(route)}`); | |
| console.error(); | |
| numInvalidRoutes++; | |
| } | |
| } | |
| if (numInvalidRoutes > 0) { | |
| if (hadInvalidStatus) { | |
| console.error(`\nValid redirect statusCode values are ${[ | |
| ..._redirectstatus.allowedStatusCodes | |
| ].join(', ')}`); | |
| } | |
| if (hadInvalidHas) { | |
| console.error(`\nValid \`has\` object shape is ${JSON.stringify({ | |
| type: [ | |
| ...allowedHasTypes | |
| ].join(', '), | |
| key: 'the key to check for', | |
| value: 'undefined or a value string to match against' | |
| }, null, 2)}`); | |
| } | |
| if (hadInvalidMissing) { | |
| console.error(`\nValid \`missing\` object shape is ${JSON.stringify({ | |
| type: [ | |
| ...allowedHasTypes | |
| ].join(', '), | |
| key: 'the key to check for', | |
| value: 'undefined or a value string to match against' | |
| }, null, 2)}`); | |
| } | |
| console.error(); | |
| console.error(`Error: Invalid ${type}${numInvalidRoutes === 1 ? '' : 's'} found`); | |
| process.exit(1); | |
| } | |
| } | |
| function processRoutes(routes, config, type) { | |
| const _routes = routes; | |
| const newRoutes = []; | |
| const defaultLocales = []; | |
| if (config.i18n && type === 'redirect') { | |
| var _config_i18n; | |
| for (const item of ((_config_i18n = config.i18n) == null ? void 0 : _config_i18n.domains) || []){ | |
| defaultLocales.push({ | |
| locale: item.defaultLocale, | |
| base: `http${item.http ? '' : 's'}://${item.domain}` | |
| }); | |
| } | |
| defaultLocales.push({ | |
| locale: config.i18n.defaultLocale, | |
| base: '' | |
| }); | |
| } | |
| for (const r of _routes){ | |
| var _r_destination; | |
| const srcBasePath = config.basePath && r.basePath !== false ? config.basePath : ''; | |
| const isExternal = !((_r_destination = r.destination) == null ? void 0 : _r_destination.startsWith('/')); | |
| const destBasePath = srcBasePath && !isExternal ? srcBasePath : ''; | |
| if (config.i18n && r.locale !== false) { | |
| var _r_destination1; | |
| if (!isExternal) { | |
| defaultLocales.forEach((item)=>{ | |
| let destination; | |
| if (r.destination) { | |
| destination = item.base ? `${item.base}${destBasePath}${r.destination}` : `${destBasePath}${r.destination}`; | |
| } | |
| newRoutes.push({ | |
| ...r, | |
| destination, | |
| source: `${srcBasePath}/${item.locale}${r.source === '/' && !config.trailingSlash ? '' : r.source}` | |
| }); | |
| }); | |
| } | |
| r.source = `/:nextInternalLocale(${config.i18n.locales.map((locale)=>(0, _escaperegexp.escapeStringRegexp)(locale)).join('|')})${r.source === '/' && !config.trailingSlash ? '' : r.source}`; | |
| if (r.destination && ((_r_destination1 = r.destination) == null ? void 0 : _r_destination1.startsWith('/'))) { | |
| r.destination = `/:nextInternalLocale${r.destination === '/' && !config.trailingSlash ? '' : r.destination}`; | |
| } | |
| } | |
| r.source = `${srcBasePath}${r.source === '/' && srcBasePath ? '' : r.source}`; | |
| if (r.destination) { | |
| r.destination = `${destBasePath}${r.destination === '/' && destBasePath ? '' : r.destination}`; | |
| } | |
| newRoutes.push(r); | |
| } | |
| return newRoutes; | |
| } | |
| async function loadRedirects(config) { | |
| if (typeof config.redirects !== 'function') { | |
| return []; | |
| } | |
| let redirects = await config.redirects(); | |
| // check before we process the routes and after to ensure | |
| // they are still valid | |
| checkCustomRoutes(redirects, 'redirect'); | |
| // save original redirects before transforms | |
| if (Array.isArray(redirects)) { | |
| config._originalRedirects = redirects.map((r)=>({ | |
| ...r | |
| })); | |
| } | |
| redirects = processRoutes(redirects, config, 'redirect'); | |
| checkCustomRoutes(redirects, 'redirect'); | |
| return redirects; | |
| } | |
| async function loadRewrites(config) { | |
| // If assetPrefix is set, add a rewrite for `/${assetPrefix}/_next/*` | |
| // requests so that they are handled in any of dev, start, or deploy | |
| // automatically without the user having to configure this. | |
| // If the assetPrefix is an absolute URL, we still consider the path for automatic rewrite. | |
| // but hostname routing must be handled by the user | |
| let maybeAssetPrefixRewrite = []; | |
| if (config.assetPrefix) { | |
| let prefix = config.assetPrefix; | |
| if ((0, _url.isFullStringUrl)(config.assetPrefix) && URL.canParse(config.assetPrefix)) { | |
| prefix = new URL(config.assetPrefix).pathname; | |
| } | |
| if (prefix && prefix !== '/') { | |
| const assetPrefix = prefix.startsWith('/') ? prefix : `/${prefix}`; | |
| const basePath = config.basePath || ''; | |
| // If these are the same, then this would result in an infinite rewrite. | |
| if (assetPrefix !== basePath) { | |
| maybeAssetPrefixRewrite.push({ | |
| source: `${assetPrefix}/_next/:path+`, | |
| destination: `${basePath}/_next/:path+` | |
| }); | |
| } | |
| } | |
| } | |
| if (typeof config.rewrites !== 'function') { | |
| return { | |
| beforeFiles: [ | |
| ...maybeAssetPrefixRewrite | |
| ], | |
| afterFiles: [], | |
| fallback: [] | |
| }; | |
| } | |
| const _rewrites = await config.rewrites(); | |
| let beforeFiles = []; | |
| let afterFiles = []; | |
| let fallback = []; | |
| if (!Array.isArray(_rewrites) && typeof _rewrites === 'object' && Object.keys(_rewrites).every((key)=>key === 'beforeFiles' || key === 'afterFiles' || key === 'fallback')) { | |
| beforeFiles = _rewrites.beforeFiles || []; | |
| afterFiles = _rewrites.afterFiles || []; | |
| fallback = _rewrites.fallback || []; | |
| } else { | |
| afterFiles = _rewrites; | |
| } | |
| // check before we process the routes and after to ensure | |
| // they are still valid | |
| checkCustomRoutes(beforeFiles, 'rewrite'); | |
| checkCustomRoutes(afterFiles, 'rewrite'); | |
| checkCustomRoutes(fallback, 'rewrite'); | |
| // save original rewrites before transforms | |
| config._originalRewrites = { | |
| beforeFiles: beforeFiles.map((r)=>({ | |
| ...r | |
| })), | |
| afterFiles: afterFiles.map((r)=>({ | |
| ...r | |
| })), | |
| fallback: fallback.map((r)=>({ | |
| ...r | |
| })) | |
| }; | |
| beforeFiles = [ | |
| ...maybeAssetPrefixRewrite, | |
| ...processRoutes(beforeFiles, config, 'rewrite') | |
| ]; | |
| afterFiles = processRoutes(afterFiles, config, 'rewrite'); | |
| fallback = processRoutes(fallback, config, 'rewrite'); | |
| checkCustomRoutes(beforeFiles, 'rewrite'); | |
| checkCustomRoutes(afterFiles, 'rewrite'); | |
| checkCustomRoutes(fallback, 'rewrite'); | |
| return { | |
| beforeFiles, | |
| afterFiles, | |
| fallback | |
| }; | |
| } | |
| async function loadHeaders(config) { | |
| if (typeof config.headers !== 'function') { | |
| return []; | |
| } | |
| let headers = await config.headers(); | |
| // check before we process the routes and after to ensure | |
| // they are still valid | |
| checkCustomRoutes(headers, 'header'); | |
| headers = processRoutes(headers, config, 'header'); | |
| checkCustomRoutes(headers, 'header'); | |
| return headers; | |
| } | |
| async function loadCustomRoutes(config) { | |
| const [headers, rewrites, redirects] = await Promise.all([ | |
| loadHeaders(config), | |
| loadRewrites(config), | |
| loadRedirects(config) | |
| ]); | |
| const onMatchHeaders = []; | |
| const totalRewrites = rewrites.beforeFiles.length + rewrites.afterFiles.length + rewrites.fallback.length; | |
| const totalRoutes = headers.length + redirects.length + totalRewrites; | |
| if (totalRoutes > 1000) { | |
| console.warn((0, _picocolors.bold)((0, _picocolors.yellow)(`Warning: `)) + `total number of custom routes exceeds 1000, this can reduce performance. Route counts:\n` + `headers: ${headers.length}\n` + `rewrites: ${totalRewrites}\n` + `redirects: ${redirects.length}\n` + `See more info: https://nextjs.org/docs/messages/max-custom-routes-reached`); | |
| } | |
| const cacheControlSources = []; | |
| for (const headerRoute of headers){ | |
| if (!headerRoute.source.startsWith('/_next/')) { | |
| continue; | |
| } | |
| for (const header of headerRoute.headers){ | |
| if (header.key.toLowerCase() === 'cache-control') { | |
| cacheControlSources.push(headerRoute.source); | |
| break; | |
| } | |
| } | |
| } | |
| if (cacheControlSources.length > 0) { | |
| console.warn((0, _picocolors.bold)((0, _picocolors.yellow)(`Warning: `)) + `Custom Cache-Control headers detected for the following routes:\n` + cacheControlSources.map((source)=>` - ${source}`).join('\n') + `\n\nSetting a custom Cache-Control header can break Next.js development behavior.`); | |
| } | |
| if (config.deploymentId) { | |
| var _config_experimental; | |
| if ((_config_experimental = config.experimental) == null ? void 0 : _config_experimental.useSkewCookie) { | |
| headers.unshift({ | |
| source: '/:path*', | |
| headers: [ | |
| { | |
| key: 'Set-Cookie', | |
| value: `__vdpl=${config.deploymentId}; Path=/; HttpOnly` | |
| } | |
| ] | |
| }); | |
| } | |
| onMatchHeaders.push({ | |
| source: '/:path*', | |
| has: [ | |
| { | |
| type: 'header', | |
| key: 'rsc', | |
| value: '1' | |
| } | |
| ], | |
| headers: [ | |
| { | |
| key: _constants.NEXT_NAV_DEPLOYMENT_ID_HEADER, | |
| value: config.deploymentId | |
| } | |
| ] | |
| }, { | |
| source: '/_next/data/(.*)', | |
| headers: [ | |
| { | |
| key: _constants.NEXT_NAV_DEPLOYMENT_ID_HEADER, | |
| value: config.deploymentId | |
| } | |
| ] | |
| }); | |
| } | |
| if (!config.skipTrailingSlashRedirect) { | |
| if (config.trailingSlash) { | |
| redirects.unshift({ | |
| source: '/:file((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+)/', | |
| destination: '/:file', | |
| permanent: true, | |
| locale: config.i18n ? false : undefined, | |
| internal: true, | |
| priority: true, | |
| // don't run this redirect for _next/data requests | |
| missing: [ | |
| { | |
| type: 'header', | |
| key: 'x-nextjs-data' | |
| } | |
| ] | |
| }, { | |
| source: '/:notfile((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/\\.]+)', | |
| destination: '/:notfile/', | |
| permanent: true, | |
| locale: config.i18n ? false : undefined, | |
| internal: true, | |
| priority: true | |
| }); | |
| if (config.basePath) { | |
| redirects.unshift({ | |
| source: config.basePath, | |
| destination: config.basePath + '/', | |
| permanent: true, | |
| basePath: false, | |
| locale: config.i18n ? false : undefined, | |
| internal: true, | |
| priority: true | |
| }); | |
| } | |
| } else { | |
| redirects.unshift({ | |
| source: '/:path+/', | |
| destination: '/:path+', | |
| permanent: true, | |
| locale: config.i18n ? false : undefined, | |
| internal: true, | |
| priority: true | |
| }); | |
| if (config.basePath) { | |
| redirects.unshift({ | |
| source: config.basePath + '/', | |
| destination: config.basePath, | |
| permanent: true, | |
| basePath: false, | |
| locale: config.i18n ? false : undefined, | |
| internal: true, | |
| priority: true | |
| }); | |
| } | |
| } | |
| } | |
| return { | |
| headers, | |
| onMatchHeaders, | |
| rewrites, | |
| redirects | |
| }; | |
| } | |
| //# sourceMappingURL=load-custom-routes.js.map |