From fd33c9f7e1897bf519442001147e05d297d5ed12 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 31 Dec 2020 02:07:51 -0600 Subject: [PATCH] Ensure href is updated for locale domain (#20631) This ensures we render the locale domain on the `href` when using `next/link` previously the provided `href` was stilling being rendered which differed from the resulting `href` that was navigated to. Fixes: https://github.com/vercel/next.js/issues/20612 --- packages/next/client/link.tsx | 19 +++++++--- .../next/next-server/lib/router/router.ts | 23 +++++++++++ packages/next/next-server/server/render.tsx | 8 +++- test/integration/i18n-support/test/shared.js | 38 +++++++++++++++++++ 4 files changed, 80 insertions(+), 8 deletions(-) diff --git a/packages/next/client/link.tsx b/packages/next/client/link.tsx index 4447abb16a..0ea989d872 100644 --- a/packages/next/client/link.tsx +++ b/packages/next/client/link.tsx @@ -3,6 +3,7 @@ import { UrlObject } from 'url' import { addBasePath, addLocale, + getDomainLocale, isLocalURL, NextRouter, PrefetchOptions, @@ -297,13 +298,19 @@ function Link(props: React.PropsWithChildren) { // If child is an tag and doesn't have a href attribute, or if the 'passHref' property is // defined, we specify the current 'href', so that repetition is not needed by the user if (props.passHref || (child.type === 'a' && !('href' in child.props))) { - childProps.href = addBasePath( - addLocale( - as, - typeof locale !== 'undefined' ? locale : router && router.locale, - router && router.defaultLocale - ) + const curLocale = + typeof locale !== 'undefined' ? locale : router && router.locale + + const localeDomain = getDomainLocale( + as, + curLocale, + router && router.locales, + router && router.domainLocales ) + + childProps.href = + localeDomain || + addBasePath(addLocale(as, curLocale, router && router.defaultLocale)) } return React.cloneElement(child, childProps) diff --git a/packages/next/next-server/lib/router/router.ts b/packages/next/next-server/lib/router/router.ts index 59bad4242b..9b141348f8 100644 --- a/packages/next/next-server/lib/router/router.ts +++ b/packages/next/next-server/lib/router/router.ts @@ -74,6 +74,28 @@ function addPathPrefix(path: string, prefix?: string) { : path } +export function getDomainLocale( + path: string, + locale?: string | false, + locales?: string[], + domainLocales?: DomainLocales +) { + if (process.env.__NEXT_I18N_SUPPORT) { + locale = locale || normalizeLocalePath(path, locales).detectedLocale + + const detectedDomain = detectDomainLocale(domainLocales, undefined, locale) + + if (detectedDomain) { + return `http${detectedDomain.http ? '' : 's'}://${detectedDomain.domain}${ + basePath || '' + }${locale === detectedDomain.defaultLocale ? '' : `/${locale}`}${path}` + } + return false + } + + return false +} + export function addLocale( path: string, locale?: string | false, @@ -290,6 +312,7 @@ export type BaseRouter = { locale?: string locales?: string[] defaultLocale?: string + domainLocales?: DomainLocales } export type NextRouter = BaseRouter & diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index 74e57abde7..25ebe5c47a 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -74,6 +74,7 @@ class ServerRouter implements NextRouter { locale?: string locales?: string[] defaultLocale?: string + domainLocales?: DomainLocales // TODO: Remove in the next major version, as this would mean the user is adding event listeners in server-side `render` method static events: MittEmitter = mitt() @@ -85,7 +86,8 @@ class ServerRouter implements NextRouter { basePath: string, locale?: string, locales?: string[], - defaultLocale?: string + defaultLocale?: string, + domainLocales?: DomainLocales ) { this.route = pathname.replace(/\/$/, '') || '/' this.pathname = pathname @@ -96,6 +98,7 @@ class ServerRouter implements NextRouter { this.locale = locale this.locales = locales this.defaultLocale = defaultLocale + this.domainLocales = domainLocales } push(): any { noRouter() @@ -533,7 +536,8 @@ export async function renderToHTML( basePath, renderOpts.locale, renderOpts.locales, - renderOpts.defaultLocale + renderOpts.defaultLocale, + renderOpts.domainLocales ) const ctx = { err, diff --git a/test/integration/i18n-support/test/shared.js b/test/integration/i18n-support/test/shared.js index b0a92b1b5f..0b4f9fe6a6 100644 --- a/test/integration/i18n-support/test/shared.js +++ b/test/integration/i18n-support/test/shared.js @@ -74,6 +74,44 @@ export function runTests(ctx) { ) }) + it('should render the correct href for locale domain', async () => { + let browser = await webdriver( + ctx.appPort, + `${ctx.basePath || ''}/links?nextLocale=go` + ) + + for (const [element, pathname] of [ + ['#to-another', '/another'], + ['#to-gsp', '/gsp'], + ['#to-fallback-first', '/gsp/fallback/first'], + ['#to-fallback-hello', '/gsp/fallback/hello'], + ['#to-gssp', '/gssp'], + ['#to-gssp-slug', '/gssp/first'], + ]) { + const href = await browser.elementByCss(element).getAttribute('href') + expect(href).toBe(`https://example.com${ctx.basePath || ''}${pathname}`) + } + + browser = await webdriver( + ctx.appPort, + `${ctx.basePath || ''}/links?nextLocale=go-BE` + ) + + for (const [element, pathname] of [ + ['#to-another', '/another'], + ['#to-gsp', '/gsp'], + ['#to-fallback-first', '/gsp/fallback/first'], + ['#to-fallback-hello', '/gsp/fallback/hello'], + ['#to-gssp', '/gssp'], + ['#to-gssp-slug', '/gssp/first'], + ]) { + const href = await browser.elementByCss(element).getAttribute('href') + expect(href).toBe( + `https://example.com${ctx.basePath || ''}/go-BE${pathname}` + ) + } + }) + it('should navigate through history with query correctly', async () => { const browser = await webdriver(ctx.appPort, `${ctx.basePath || '/'}`) -- GitLab