UNPKG

@nimpl/router

Version:

Edge router for next.js apps (i18n, basePath, rewrites, redirects)

177 lines (176 loc) 7.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Router = void 0; const server_1 = require("next/server"); const headers_1 = require("next/headers"); const format_destination_1 = require("./utils/format-destination"); const test_rule_1 = require("./utils/test-rule"); class Router { rewrites; redirects; basePath; i18n; constructor(opts) { this.redirects = opts.redirects; this.rewrites = opts.rewrites; this.basePath = opts.basePath?.replace(/\/$/, ""); this.i18n = opts.i18n; } getRequestLocale = async (url) => { if (!this.i18n) return null; const domainI18n = this.i18n.domains?.find((domainData) => domainData.domain === url.host); const locales = domainI18n?.locales || this.i18n.locales; const defaultLocale = domainI18n?.defaultLocale || this.i18n.defaultLocale; const pathnameLocale = url.pathname.match(new RegExp(`/(?<locale>${locales.join("|")})(?:/|$)`)); let preferredLocale = pathnameLocale?.groups?.locale; if (!preferredLocale) { const cookiesStore = await (0, headers_1.cookies)(); preferredLocale = cookiesStore.get("NEXT_LOCALE")?.value; } if (preferredLocale) return preferredLocale; const detectedLocale = this.i18n.localeDetector?.(url); return detectedLocale || defaultLocale; }; findChanger = async (changers, url, routeData) => { for (const changer of changers) { let targetLocale = null; let targetBasePath = null; if (this.basePath && changer.basePath !== false) { if (routeData.basePath) { targetBasePath = this.basePath; } else { continue; } } if (changer.locale !== false && routeData.locale) { targetLocale = routeData.locale; } const match = url.pathname.match(`^${targetBasePath || ""}${targetLocale ? `/${routeData.locale}` : ""}${changer.source.replace(/([^/])$/, "$1/")}?$`); if (!match) continue; const groups = match.groups || {}; const testRule = (0, test_rule_1.createRuleTester)(); if (changer.has) { let validRules = true; for (const rule of changer.has) { const { match, groups: ruleGroups } = await testRule(url, rule); if (!match) { validRules = false; break; } Object.assign(groups, ruleGroups); } if (!validRules) continue; } if (changer.missing) { let validRules = true; for (const rule of changer.missing) { const { match, groups: ruleGroups } = await testRule(url, rule); if (match) { validRules = false; break; } Object.assign(groups, ruleGroups); } if (!validRules) continue; } return { changer: changer, groups, }; } }; parsePathname(pathname) { const routeData = { basePath: null, locale: null, pathname, }; const basePathMatch = this.basePath && pathname.match(new RegExp(`^${this.basePath}(/|$)`)); if (basePathMatch) { routeData.basePath = this.basePath; routeData.pathname = routeData.pathname.replace(new RegExp(`^${this.basePath}(/|$)`), "/"); } const localeMatch = this.i18n && routeData.pathname.match(new RegExp(`^/(${this.i18n.locales.join("|")})(/|$)`)); if (localeMatch) { routeData.locale = localeMatch[1]; routeData.pathname = routeData.pathname.replace(new RegExp(`^/${routeData.locale}(/|$)`), "/"); } return routeData; } async navigate(nextUrl) { const originRouteData = this.parsePathname(nextUrl.pathname); const nextRouteData = { basePath: this.basePath, locale: originRouteData.locale, pathname: originRouteData.pathname, type: "none", status: 200, }; if (!nextRouteData.locale) nextRouteData.locale = await this.getRequestLocale(nextUrl); if (this.redirects) { const targetChanger = await this.findChanger(this.redirects, nextUrl, originRouteData); if (targetChanger) { const destination = (0, format_destination_1.formatDestination)(targetChanger.changer.destination, targetChanger.groups); nextRouteData.pathname = destination; if (targetChanger.changer.locale === false) nextRouteData.locale = null; if (targetChanger.changer.basePath === false) nextRouteData.basePath = null; nextRouteData.type = "redirect"; nextRouteData.status = targetChanger.changer.permanent ? 308 : 307; } } if (this.rewrites && nextRouteData.type !== "redirect") { const targetChanger = await this.findChanger(this.rewrites, nextUrl, originRouteData); if (targetChanger) { const destination = (0, format_destination_1.formatDestination)(targetChanger.changer.destination, targetChanger.groups); nextRouteData.pathname = destination; if (targetChanger.changer.locale === false) nextRouteData.locale = null; if (targetChanger.changer.basePath === false) nextRouteData.basePath = null; nextRouteData.type = "rewrite"; } } if (this.basePath && nextRouteData.basePath && nextRouteData.type === "none") { if (originRouteData.basePath) { nextRouteData.type = "rewrite"; } else { const newNextUrl = new URL(nextUrl); newNextUrl.pathname = "/404/"; return server_1.NextResponse.rewrite(newNextUrl, { status: 404 }); } } // if i18n configurated and locale wasnt disabled by rewrite or redirect if (this.i18n && nextRouteData.locale && nextRouteData.type !== "redirect") { // if in original route was default locale - redirect to pathname without default locale if (originRouteData.locale === this.i18n.defaultLocale) { nextRouteData.type = "redirect"; nextRouteData.status = 307; } else { nextRouteData.type = "rewrite"; } } if (nextRouteData.type === "redirect") { const newNextUrl = new URL(nextUrl); newNextUrl.pathname = `${nextRouteData.basePath || ""}${!this.i18n || !nextRouteData.locale || nextRouteData.locale === this.i18n.defaultLocale ? "" : `/${nextRouteData.locale}`}${nextRouteData.pathname}`; return server_1.NextResponse.redirect(newNextUrl, { status: nextRouteData.status }); } if (nextRouteData.type === "rewrite") { const newNextUrl = new URL(nextUrl); newNextUrl.pathname = `${nextRouteData.locale ? `/${nextRouteData.locale}` : ""}${nextRouteData.pathname}`; return server_1.NextResponse.rewrite(newNextUrl, { status: nextRouteData.status }); } return server_1.NextResponse.next(); } } exports.Router = Router;