router.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _requestMeta = require("./request-meta");
  7. var _pathMatch = require("../shared/lib/router/utils/path-match");
  8. var _removeTrailingSlash = require("../shared/lib/router/utils/remove-trailing-slash");
  9. var _normalizeLocalePath = require("../shared/lib/i18n/normalize-locale-path");
  10. var _prepareDestination = require("../shared/lib/router/utils/prepare-destination");
  11. var _removePathPrefix = require("../shared/lib/router/utils/remove-path-prefix");
  12. var _formatNextPathnameInfo = require("../shared/lib/router/utils/format-next-pathname-info");
  13. var _getNextPathnameInfo = require("../shared/lib/router/utils/get-next-pathname-info");
  14. class Router {
  15. /**
  16. * context stores information used by the router.
  17. */ context = new WeakMap();
  18. constructor({ headers =[] , fsRoutes =[] , rewrites ={
  19. beforeFiles: [],
  20. afterFiles: [],
  21. fallback: []
  22. } , redirects =[] , catchAllRoute , catchAllMiddleware =[] , dynamicRoutes =[] , pageChecker , useFileSystemPublicRoutes , nextConfig }){
  23. this.nextConfig = nextConfig;
  24. this.headers = headers;
  25. this.fsRoutes = [
  26. ...fsRoutes
  27. ];
  28. this.rewrites = rewrites;
  29. this.redirects = redirects;
  30. this.pageChecker = pageChecker;
  31. this.catchAllRoute = catchAllRoute;
  32. this.catchAllMiddleware = catchAllMiddleware;
  33. this.dynamicRoutes = dynamicRoutes;
  34. this.useFileSystemPublicRoutes = useFileSystemPublicRoutes;
  35. // Perform the initial route compilation.
  36. this.compiledRoutes = this.compileRoutes();
  37. this.needsRecompilation = false;
  38. }
  39. async checkPage(req, pathname) {
  40. pathname = (0, _normalizeLocalePath).normalizeLocalePath(pathname, this.locales).pathname;
  41. const context = this.context.get(req);
  42. if (!context) {
  43. throw new Error("Invariant: request is not available inside the context, this is an internal error please open an issue.");
  44. }
  45. if (context.pageChecks[pathname] !== undefined) {
  46. return context.pageChecks[pathname];
  47. }
  48. const result = await this.pageChecker(pathname);
  49. context.pageChecks[pathname] = result;
  50. return result;
  51. }
  52. get locales() {
  53. var ref;
  54. return ((ref = this.nextConfig.i18n) == null ? void 0 : ref.locales) || [];
  55. }
  56. get basePath() {
  57. return this.nextConfig.basePath || "";
  58. }
  59. setDynamicRoutes(dynamicRoutes) {
  60. this.dynamicRoutes = dynamicRoutes;
  61. this.needsRecompilation = true;
  62. }
  63. setCatchallMiddleware(catchAllMiddleware) {
  64. this.catchAllMiddleware = catchAllMiddleware;
  65. this.needsRecompilation = true;
  66. }
  67. addFsRoute(fsRoute) {
  68. // We use unshift so that we're sure the routes is defined before Next's
  69. // default routes.
  70. this.fsRoutes.unshift(fsRoute);
  71. this.needsRecompilation = true;
  72. }
  73. compileRoutes() {
  74. /*
  75. Desired routes order
  76. - headers
  77. - redirects
  78. - Check filesystem (including pages), if nothing found continue
  79. - User rewrites (checking filesystem and pages each match)
  80. */ const [middlewareCatchAllRoute] = this.catchAllMiddleware;
  81. return [
  82. ...middlewareCatchAllRoute ? this.fsRoutes.filter((route)=>route.name === "_next/data catchall").map((route)=>({
  83. ...route,
  84. name: "_next/data normalizing",
  85. check: false
  86. })) : [],
  87. ...this.headers,
  88. ...this.redirects,
  89. ...this.useFileSystemPublicRoutes && middlewareCatchAllRoute ? [
  90. middlewareCatchAllRoute
  91. ] : [],
  92. ...this.rewrites.beforeFiles,
  93. ...this.fsRoutes,
  94. // We only check the catch-all route if public page routes hasn't been
  95. // disabled
  96. ...this.useFileSystemPublicRoutes ? [
  97. {
  98. type: "route",
  99. name: "page checker",
  100. match: (0, _pathMatch).getPathMatch("/:path*"),
  101. fn: async (req, res, params, parsedUrl, upgradeHead)=>{
  102. const pathname = (0, _removeTrailingSlash).removeTrailingSlash(parsedUrl.pathname || "/");
  103. if (!pathname) {
  104. return {
  105. finished: false
  106. };
  107. }
  108. if (await this.checkPage(req, pathname)) {
  109. return this.catchAllRoute.fn(req, res, params, parsedUrl, upgradeHead);
  110. }
  111. return {
  112. finished: false
  113. };
  114. }
  115. },
  116. ] : [],
  117. ...this.rewrites.afterFiles,
  118. ...this.rewrites.fallback.length ? [
  119. {
  120. type: "route",
  121. name: "dynamic route/page check",
  122. match: (0, _pathMatch).getPathMatch("/:path*"),
  123. fn: async (req, res, _params, parsedCheckerUrl, upgradeHead)=>{
  124. return {
  125. finished: await this.checkFsRoutes(req, res, parsedCheckerUrl, upgradeHead)
  126. };
  127. }
  128. },
  129. ...this.rewrites.fallback,
  130. ] : [],
  131. // We only check the catch-all route if public page routes hasn't been
  132. // disabled
  133. ...this.useFileSystemPublicRoutes ? [
  134. this.catchAllRoute
  135. ] : [],
  136. ];
  137. }
  138. async checkFsRoutes(req, res, parsedUrl, upgradeHead) {
  139. const originalFsPathname = parsedUrl.pathname;
  140. const fsPathname = (0, _removePathPrefix).removePathPrefix(originalFsPathname, this.basePath);
  141. for (const route of this.fsRoutes){
  142. const params = route.match(fsPathname);
  143. if (params) {
  144. parsedUrl.pathname = fsPathname;
  145. const { finished } = await route.fn(req, res, params, parsedUrl);
  146. if (finished) {
  147. return true;
  148. }
  149. parsedUrl.pathname = originalFsPathname;
  150. }
  151. }
  152. let matchedPage = await this.checkPage(req, fsPathname);
  153. // If we didn't match a page check dynamic routes
  154. if (!matchedPage) {
  155. const normalizedFsPathname = (0, _normalizeLocalePath).normalizeLocalePath(fsPathname, this.locales).pathname;
  156. for (const dynamicRoute of this.dynamicRoutes){
  157. if (dynamicRoute.match(normalizedFsPathname)) {
  158. matchedPage = true;
  159. }
  160. }
  161. }
  162. // Matched a page or dynamic route so render it using catchAllRoute
  163. if (matchedPage) {
  164. const params = this.catchAllRoute.match(parsedUrl.pathname);
  165. if (!params) {
  166. throw new Error(`Invariant: could not match params, this is an internal error please open an issue.`);
  167. }
  168. parsedUrl.pathname = fsPathname;
  169. parsedUrl.query._nextBubbleNoFallback = "1";
  170. const { finished } = await this.catchAllRoute.fn(req, res, params, parsedUrl, upgradeHead);
  171. return finished;
  172. }
  173. return false;
  174. }
  175. async execute(req, res, parsedUrl, upgradeHead) {
  176. // Only recompile if the routes need to be recompiled, this should only
  177. // happen in development.
  178. if (this.needsRecompilation) {
  179. this.compiledRoutes = this.compileRoutes();
  180. this.needsRecompilation = false;
  181. }
  182. if (this.context.has(req)) {
  183. throw new Error(`Invariant: request has already been processed: ${req.url}, this is an internal error please open an issue.`);
  184. }
  185. this.context.set(req, {
  186. pageChecks: {}
  187. });
  188. try {
  189. // Create a deep copy of the parsed URL.
  190. const parsedUrlUpdated = {
  191. ...parsedUrl,
  192. query: {
  193. ...parsedUrl.query
  194. }
  195. };
  196. for (const route of this.compiledRoutes){
  197. var ref;
  198. // only process rewrites for upgrade request
  199. if (upgradeHead && route.type !== "rewrite") {
  200. continue;
  201. }
  202. const originalPathname = parsedUrlUpdated.pathname;
  203. const pathnameInfo = (0, _getNextPathnameInfo).getNextPathnameInfo(originalPathname, {
  204. nextConfig: this.nextConfig,
  205. parseData: false
  206. });
  207. if (pathnameInfo.locale && !route.matchesLocaleAPIRoutes && pathnameInfo.pathname.match(/^\/api(?:\/|$)/)) {
  208. continue;
  209. }
  210. if ((0, _requestMeta).getRequestMeta(req, "_nextHadBasePath")) {
  211. pathnameInfo.basePath = this.basePath;
  212. }
  213. const basePath = pathnameInfo.basePath;
  214. if (!route.matchesBasePath) {
  215. pathnameInfo.basePath = "";
  216. }
  217. if (route.matchesLocale && parsedUrlUpdated.query.__nextLocale && !pathnameInfo.locale) {
  218. pathnameInfo.locale = parsedUrlUpdated.query.__nextLocale;
  219. }
  220. if (!route.matchesLocale && pathnameInfo.locale === ((ref = this.nextConfig.i18n) == null ? void 0 : ref.defaultLocale) && pathnameInfo.locale) {
  221. pathnameInfo.locale = undefined;
  222. }
  223. if (route.matchesTrailingSlash && (0, _requestMeta).getRequestMeta(req, "__nextHadTrailingSlash")) {
  224. pathnameInfo.trailingSlash = true;
  225. }
  226. const matchPathname = (0, _formatNextPathnameInfo).formatNextPathnameInfo({
  227. ignorePrefix: true,
  228. ...pathnameInfo
  229. });
  230. let params = route.match(matchPathname);
  231. if ((route.has || route.missing) && params) {
  232. const hasParams = (0, _prepareDestination).matchHas(req, parsedUrlUpdated.query, route.has, route.missing);
  233. if (hasParams) {
  234. Object.assign(params, hasParams);
  235. } else {
  236. params = false;
  237. }
  238. }
  239. /**
  240. * If it is a matcher that doesn't match the basePath (like the public
  241. * directory) but Next.js is configured to use a basePath that was
  242. * never there, we consider this an invalid match and keep routing.
  243. */ if (params && this.basePath && !route.matchesBasePath && !(0, _requestMeta).getRequestMeta(req, "_nextDidRewrite") && !basePath) {
  244. continue;
  245. }
  246. if (params) {
  247. const isNextDataNormalizing = route.name === "_next/data normalizing";
  248. if (isNextDataNormalizing) {
  249. (0, _requestMeta).addRequestMeta(req, "_nextDataNormalizing", true);
  250. }
  251. parsedUrlUpdated.pathname = matchPathname;
  252. const result = await route.fn(req, res, params, parsedUrlUpdated, upgradeHead);
  253. if (isNextDataNormalizing) {
  254. (0, _requestMeta).addRequestMeta(req, "_nextDataNormalizing", false);
  255. }
  256. if (result.finished) {
  257. return true;
  258. }
  259. if (result.pathname) {
  260. parsedUrlUpdated.pathname = result.pathname;
  261. } else {
  262. // since the fs route didn't finish routing we need to re-add the
  263. // basePath to continue checking with the basePath present
  264. parsedUrlUpdated.pathname = originalPathname;
  265. }
  266. if (result.query) {
  267. parsedUrlUpdated.query = {
  268. ...(0, _requestMeta).getNextInternalQuery(parsedUrlUpdated.query),
  269. ...result.query
  270. };
  271. }
  272. // check filesystem
  273. if (route.check && await this.checkFsRoutes(req, res, parsedUrlUpdated)) {
  274. return true;
  275. }
  276. }
  277. }
  278. // All routes were tested, none were found.
  279. return false;
  280. } finally{
  281. this.context.delete(req);
  282. }
  283. }
  284. }
  285. exports.default = Router;
  286. //# sourceMappingURL=router.js.map