357 lines
16 KiB
JavaScript
357 lines
16 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
default: null,
|
|
makeResolver: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
default: function() {
|
|
return Router;
|
|
},
|
|
makeResolver: function() {
|
|
return makeResolver;
|
|
}
|
|
});
|
|
const _requestmeta = require("./request-meta");
|
|
const _isapiroute = require("../lib/is-api-route");
|
|
const _pathmatch = require("../shared/lib/router/utils/path-match");
|
|
const _preparedestination = require("../shared/lib/router/utils/prepare-destination");
|
|
const _removepathprefix = require("../shared/lib/router/utils/remove-path-prefix");
|
|
const _formatnextpathnameinfo = require("../shared/lib/router/utils/format-next-pathname-info");
|
|
const _getnextpathnameinfo = require("../shared/lib/router/utils/get-next-pathname-info");
|
|
const _removetrailingslash = require("../shared/lib/router/utils/remove-trailing-slash");
|
|
const _tracer = require("./lib/trace/tracer");
|
|
const _constants = require("./lib/trace/constants");
|
|
class Router {
|
|
constructor({ headers =[] , fsRoutes =[] , rewrites ={
|
|
beforeFiles: [],
|
|
afterFiles: [],
|
|
fallback: []
|
|
} , redirects =[] , catchAllRoute , catchAllMiddleware =[] , matchers , useFileSystemPublicRoutes , nextConfig , i18nProvider }){
|
|
this.nextConfig = nextConfig;
|
|
this.headers = headers;
|
|
this.fsRoutes = [
|
|
...fsRoutes
|
|
];
|
|
this.rewrites = rewrites;
|
|
this.redirects = redirects;
|
|
this.catchAllRoute = catchAllRoute;
|
|
this.catchAllMiddleware = catchAllMiddleware;
|
|
this.matchers = matchers;
|
|
this.useFileSystemPublicRoutes = useFileSystemPublicRoutes;
|
|
this.i18nProvider = i18nProvider;
|
|
// Perform the initial route compilation.
|
|
this.compiledRoutes = this.compileRoutes();
|
|
this.needsRecompilation = false;
|
|
}
|
|
get basePath() {
|
|
return this.nextConfig.basePath || "";
|
|
}
|
|
/**
|
|
* True when the router has catch-all middleware routes configured.
|
|
*/ get hasMiddleware() {
|
|
return this.catchAllMiddleware.length > 0;
|
|
}
|
|
setCatchallMiddleware(catchAllMiddleware) {
|
|
this.catchAllMiddleware = catchAllMiddleware;
|
|
this.needsRecompilation = true;
|
|
}
|
|
setRewrites(rewrites) {
|
|
this.rewrites = rewrites;
|
|
this.needsRecompilation = true;
|
|
}
|
|
addFsRoute(fsRoute) {
|
|
// We use unshift so that we're sure the routes is defined before Next's
|
|
// default routes.
|
|
this.fsRoutes.unshift(fsRoute);
|
|
this.needsRecompilation = true;
|
|
}
|
|
compileRoutes() {
|
|
/*
|
|
Desired routes order
|
|
- headers
|
|
- redirects
|
|
- Check filesystem (including pages), if nothing found continue
|
|
- User rewrites (checking filesystem and pages each match)
|
|
*/ const [middlewareCatchAllRoute] = this.catchAllMiddleware;
|
|
return [
|
|
...middlewareCatchAllRoute ? this.fsRoutes.filter((route)=>route.name === "_next/data catchall").map((route)=>({
|
|
...route,
|
|
name: "_next/data normalizing",
|
|
check: false
|
|
})) : [],
|
|
...this.headers,
|
|
...this.redirects,
|
|
...this.useFileSystemPublicRoutes && middlewareCatchAllRoute ? [
|
|
middlewareCatchAllRoute
|
|
] : [],
|
|
...this.rewrites.beforeFiles,
|
|
...this.fsRoutes,
|
|
// We only check the catch-all route if public page routes hasn't been
|
|
// disabled
|
|
...this.useFileSystemPublicRoutes ? [
|
|
{
|
|
type: "route",
|
|
matchesLocale: true,
|
|
name: "page checker",
|
|
match: (0, _pathmatch.getPathMatch)("/:path*"),
|
|
fn: async (req, res, params, parsedUrl, upgradeHead)=>{
|
|
var _this_i18nProvider;
|
|
// Next.js performs all route matching without the trailing slash.
|
|
const pathname = (0, _removetrailingslash.removeTrailingSlash)(parsedUrl.pathname || "/");
|
|
// Normalize and detect the locale on the pathname.
|
|
const options = {
|
|
// We need to skip dynamic route matching because the next
|
|
// step we're processing the afterFiles rewrites which must
|
|
// not include dynamic matches.
|
|
skipDynamic: true,
|
|
i18n: (_this_i18nProvider = this.i18nProvider) == null ? void 0 : _this_i18nProvider.analyze(pathname)
|
|
};
|
|
// If the locale was inferred from the default, we should mark
|
|
// it in the match options.
|
|
if (options.i18n && parsedUrl.query.__nextInferredLocaleFromDefault) {
|
|
options.i18n.inferredFromDefault = true;
|
|
}
|
|
const match = await this.matchers.match(pathname, options);
|
|
if (!match) return {
|
|
finished: false
|
|
};
|
|
// Add the match so we can get it later.
|
|
(0, _requestmeta.addRequestMeta)(req, "_nextMatch", match);
|
|
return this.catchAllRoute.fn(req, res, params, parsedUrl, upgradeHead);
|
|
}
|
|
}
|
|
] : [],
|
|
...this.rewrites.afterFiles,
|
|
...this.rewrites.fallback.length ? [
|
|
{
|
|
type: "route",
|
|
name: "dynamic route/page check",
|
|
match: (0, _pathmatch.getPathMatch)("/:path*"),
|
|
fn: async (req, res, _params, parsedCheckerUrl, upgradeHead)=>{
|
|
return {
|
|
finished: await this.checkFsRoutes(req, res, parsedCheckerUrl, upgradeHead)
|
|
};
|
|
}
|
|
},
|
|
...this.rewrites.fallback
|
|
] : [],
|
|
// We only check the catch-all route if public page routes hasn't been
|
|
// disabled
|
|
...this.useFileSystemPublicRoutes ? [
|
|
this.catchAllRoute
|
|
] : []
|
|
].map((route)=>{
|
|
if (route.fn) {
|
|
return {
|
|
...route,
|
|
fn: (0, _tracer.getTracer)().wrap(_constants.RouterSpan.executeRoute, {
|
|
attributes: {
|
|
"next.route": route.name
|
|
}
|
|
}, route.fn)
|
|
};
|
|
}
|
|
return route;
|
|
});
|
|
}
|
|
async checkFsRoutes(req, res, parsedUrl, upgradeHead) {
|
|
var _this_i18nProvider;
|
|
const fsPathname = (0, _removepathprefix.removePathPrefix)(parsedUrl.pathname, this.basePath);
|
|
for (const route of this.fsRoutes){
|
|
const params = route.match(fsPathname);
|
|
if (!params) continue;
|
|
const { finished } = await route.fn(req, res, params, {
|
|
...parsedUrl,
|
|
pathname: fsPathname
|
|
});
|
|
if (finished) {
|
|
return true;
|
|
}
|
|
}
|
|
// Normalize and detect the locale on the pathname.
|
|
const options = {
|
|
i18n: (_this_i18nProvider = this.i18nProvider) == null ? void 0 : _this_i18nProvider.analyze(fsPathname)
|
|
};
|
|
const match = await this.matchers.test(fsPathname, options);
|
|
if (!match) return false;
|
|
// Matched a page or dynamic route so render it using catchAllRoute
|
|
const params = this.catchAllRoute.match(parsedUrl.pathname);
|
|
if (!params) {
|
|
throw new Error(`Invariant: could not match params, this is an internal error please open an issue.`);
|
|
}
|
|
const { finished } = await this.catchAllRoute.fn(req, res, params, {
|
|
...parsedUrl,
|
|
pathname: fsPathname,
|
|
query: {
|
|
...parsedUrl.query,
|
|
_nextBubbleNoFallback: "1"
|
|
}
|
|
}, upgradeHead);
|
|
return finished;
|
|
}
|
|
async execute(req, res, parsedUrl, upgradeHead) {
|
|
// Only recompile if the routes need to be recompiled, this should only
|
|
// happen in development.
|
|
if (this.needsRecompilation) {
|
|
this.compiledRoutes = this.compileRoutes();
|
|
this.needsRecompilation = false;
|
|
}
|
|
// Create a deep copy of the parsed URL.
|
|
const parsedUrlUpdated = {
|
|
...parsedUrl,
|
|
query: {
|
|
...parsedUrl.query
|
|
}
|
|
};
|
|
// when x-invoke-path is specified we can short short circuit resolving
|
|
// we only honor this header if we are inside of a render worker to
|
|
// prevent external users coercing the routing path
|
|
const matchedPath = req.headers["x-invoke-path"];
|
|
let curRoutes = this.compiledRoutes;
|
|
if (process.env.NEXT_RUNTIME !== "edge" && process.env.__NEXT_PRIVATE_RENDER_WORKER && matchedPath) {
|
|
curRoutes = this.compiledRoutes.filter((r)=>{
|
|
return r.name === "Catchall render" || r.name === "_next/data catchall";
|
|
});
|
|
const parsedMatchedPath = new URL(matchedPath || "/", "http://n");
|
|
const pathnameInfo = (0, _getnextpathnameinfo.getNextPathnameInfo)(parsedMatchedPath.pathname, {
|
|
nextConfig: this.nextConfig,
|
|
parseData: false
|
|
});
|
|
if (pathnameInfo.locale) {
|
|
parsedUrlUpdated.query.__nextLocale = pathnameInfo.locale;
|
|
}
|
|
if (parsedUrlUpdated.pathname !== parsedMatchedPath.pathname) {
|
|
parsedUrlUpdated.pathname = parsedMatchedPath.pathname;
|
|
(0, _requestmeta.addRequestMeta)(req, "_nextRewroteUrl", pathnameInfo.pathname);
|
|
(0, _requestmeta.addRequestMeta)(req, "_nextDidRewrite", true);
|
|
}
|
|
for (const key of Object.keys(parsedUrlUpdated.query)){
|
|
if (!key.startsWith("__next") && !key.startsWith("_next")) {
|
|
delete parsedUrlUpdated.query[key];
|
|
}
|
|
}
|
|
const invokeQuery = req.headers["x-invoke-query"];
|
|
if (typeof invokeQuery === "string") {
|
|
Object.assign(parsedUrlUpdated.query, JSON.parse(decodeURIComponent(invokeQuery)));
|
|
}
|
|
}
|
|
for (const route of curRoutes){
|
|
var _this_nextConfig_i18n;
|
|
// only process rewrites for upgrade request
|
|
if (upgradeHead && route.type !== "rewrite") {
|
|
continue;
|
|
}
|
|
const originalPathname = parsedUrlUpdated.pathname;
|
|
const pathnameInfo = (0, _getnextpathnameinfo.getNextPathnameInfo)(originalPathname, {
|
|
nextConfig: this.nextConfig,
|
|
parseData: false
|
|
});
|
|
// If the request has a locale and the route is an api route that doesn't
|
|
// support matching locales, skip the route.
|
|
if (pathnameInfo.locale && !route.matchesLocaleAPIRoutes && (0, _isapiroute.isAPIRoute)(pathnameInfo.pathname)) {
|
|
continue;
|
|
}
|
|
// Restore the `basePath` if the request had a `basePath`.
|
|
if ((0, _requestmeta.getRequestMeta)(req, "_nextHadBasePath")) {
|
|
pathnameInfo.basePath = this.basePath;
|
|
}
|
|
// Create a copy of the `basePath` so we can modify it for the next
|
|
// request if the route doesn't match with the `basePath`.
|
|
const basePath = pathnameInfo.basePath;
|
|
if (!route.matchesBasePath) {
|
|
pathnameInfo.basePath = undefined;
|
|
}
|
|
// Add the locale to the information if the route supports matching
|
|
// locales and the locale is not present in the info.
|
|
const locale = parsedUrlUpdated.query.__nextLocale;
|
|
if (route.matchesLocale && locale && !pathnameInfo.locale) {
|
|
pathnameInfo.locale = locale;
|
|
}
|
|
// If the route doesn't support matching locales and the locale is the
|
|
// default locale then remove it from the info.
|
|
if (!route.matchesLocale && pathnameInfo.locale === ((_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.defaultLocale) && pathnameInfo.locale) {
|
|
pathnameInfo.locale = undefined;
|
|
}
|
|
// If the route doesn't support trailing slashes and the request had a
|
|
// trailing slash then remove it from the info.
|
|
if (route.matchesTrailingSlash && (0, _requestmeta.getRequestMeta)(req, "__nextHadTrailingSlash")) {
|
|
pathnameInfo.trailingSlash = true;
|
|
}
|
|
// Construct a new pathname based on the info.
|
|
const matchPathname = (0, _formatnextpathnameinfo.formatNextPathnameInfo)({
|
|
ignorePrefix: true,
|
|
...pathnameInfo
|
|
});
|
|
let params = route.match(matchPathname);
|
|
if ((route.has || route.missing) && params) {
|
|
const hasParams = (0, _preparedestination.matchHas)(req, parsedUrlUpdated.query, route.has, route.missing);
|
|
if (hasParams) {
|
|
Object.assign(params, hasParams);
|
|
} else {
|
|
params = false;
|
|
}
|
|
}
|
|
// If it is a matcher that doesn't match the basePath (like the public
|
|
// directory) but Next.js is configured to use a basePath that was
|
|
// never there, we consider this an invalid match and keep routing.
|
|
if (params && this.basePath && !route.matchesBasePath && !(0, _requestmeta.getRequestMeta)(req, "_nextDidRewrite") && !basePath) {
|
|
continue;
|
|
}
|
|
if (params) {
|
|
const isNextDataNormalizing = route.name === "_next/data normalizing";
|
|
if (isNextDataNormalizing) {
|
|
(0, _requestmeta.addRequestMeta)(req, "_nextDataNormalizing", true);
|
|
}
|
|
parsedUrlUpdated.pathname = matchPathname;
|
|
const result = await route.fn(req, res, params, parsedUrlUpdated, upgradeHead);
|
|
if (isNextDataNormalizing) {
|
|
(0, _requestmeta.addRequestMeta)(req, "_nextDataNormalizing", false);
|
|
}
|
|
if (result.finished) {
|
|
return true;
|
|
}
|
|
// If the result includes a pathname then we need to update the
|
|
// parsed url pathname to continue routing.
|
|
if (result.pathname) {
|
|
parsedUrlUpdated.pathname = result.pathname;
|
|
} else {
|
|
// since the fs route didn't finish routing we need to re-add the
|
|
// basePath to continue checking with the basePath present
|
|
parsedUrlUpdated.pathname = originalPathname;
|
|
}
|
|
// Copy over only internal query parameters from the original query and
|
|
// merge with the result query.
|
|
if (result.query) {
|
|
parsedUrlUpdated.query = {
|
|
...(0, _requestmeta.getNextInternalQuery)(parsedUrlUpdated.query),
|
|
...result.query
|
|
};
|
|
}
|
|
// check filesystem
|
|
if (route.check && await this.checkFsRoutes(req, res, parsedUrlUpdated)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
// All routes were tested, none were found.
|
|
return false;
|
|
}
|
|
}
|
|
let _makeResolver = ()=>{};
|
|
if (// ensure this isn't bundled for edge runtime
|
|
process.env.NEXT_RUNTIME !== "edge" && // only load if we are inside of the turbopack handler
|
|
process.argv.some((arg)=>arg.endsWith("router.js"))) {
|
|
_makeResolver = require("./lib/route-resolver").makeResolver;
|
|
}
|
|
const makeResolver = _makeResolver;
|
|
|
|
//# sourceMappingURL=router.js.map
|