123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- "use strict";
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports.default = loadCustomRoutes;
- exports.normalizeRouteRegex = normalizeRouteRegex;
- exports.checkCustomRoutes = checkCustomRoutes;
- var _chalk = _interopRequireDefault(require("./chalk"));
- var _escapeRegexp = require("../shared/lib/escape-regexp");
- var _tryToParsePath = require("./try-to-parse-path");
- var _redirectStatus = require("./redirect-status");
- async function loadCustomRoutes(config) {
- var ref;
- const [headers, rewrites, redirects] = await Promise.all([
- loadHeaders(config),
- loadRewrites(config),
- loadRedirects(config),
- ]);
- const totalRewrites = rewrites.beforeFiles.length + rewrites.afterFiles.length + rewrites.fallback.length;
- const totalRoutes = headers.length + redirects.length + totalRewrites;
- if (totalRoutes > 1000) {
- console.warn(_chalk.default.bold.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`);
- }
- if (!((ref = config.experimental) == null ? void 0 : ref.skipTrailingSlashRedirect)) {
- if (config.trailingSlash) {
- redirects.unshift({
- source: "/:file((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+)/",
- destination: "/:file",
- permanent: true,
- locale: config.i18n ? false : undefined,
- internal: true
- }, {
- source: "/:notfile((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/\\.]+)",
- destination: "/:notfile/",
- permanent: true,
- locale: config.i18n ? false : undefined,
- internal: true
- });
- if (config.basePath) {
- redirects.unshift({
- source: config.basePath,
- destination: config.basePath + "/",
- permanent: true,
- basePath: false,
- locale: config.i18n ? false : undefined,
- internal: true
- });
- }
- } else {
- redirects.unshift({
- source: "/:path+/",
- destination: "/:path+",
- permanent: true,
- locale: config.i18n ? false : undefined,
- internal: true
- });
- if (config.basePath) {
- redirects.unshift({
- source: config.basePath + "/",
- destination: config.basePath,
- permanent: true,
- basePath: false,
- locale: config.i18n ? false : undefined,
- internal: true
- });
- }
- }
- }
- return {
- headers,
- rewrites,
- redirects
- };
- }
- function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : {
- default: obj
- };
- }
- 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 ref;
- for (const item of ((ref = config.i18n) == null ? void 0 : ref.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 ref1;
- const srcBasePath = config.basePath && r.basePath !== false ? config.basePath : "";
- const isExternal = !((ref1 = r.destination) == null ? void 0 : ref1.startsWith("/"));
- const destBasePath = srcBasePath && !isExternal ? srcBasePath : "";
- if (config.i18n && r.locale !== false) {
- var ref2;
- 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}`
- });
- });
- }
- r.source = `/:nextInternalLocale(${config.i18n.locales.map((locale)=>(0, _escapeRegexp).escapeStringRegexp(locale)).join("|")})${r.source === "/" && !config.trailingSlash ? "" : r.source}`;
- if (r.destination && ((ref2 = r.destination) == null ? void 0 : ref2.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");
- redirects = processRoutes(redirects, config, "redirect");
- checkCustomRoutes(redirects, "redirect");
- return redirects;
- }
- async function loadRewrites(config) {
- if (typeof config.rewrites !== "function") {
- return {
- beforeFiles: [],
- 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");
- beforeFiles = 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;
- }
- //# sourceMappingURL=load-custom-routes.js.map
|