From e8de3a0bd8d701cc7b40f1ecccaacd6e7a5ace81 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Fri, 9 Oct 2020 23:35:06 -0500 Subject: [PATCH 1/2] Add handling for domain to locale mapping --- packages/next/build/index.ts | 14 +- .../webpack/loaders/next-serverless-loader.ts | 27 +++- packages/next/client/index.tsx | 19 +-- packages/next/client/page-loader.ts | 29 +--- .../lib/i18n/detect-domain-locales.ts | 37 +++++ .../next/next-server/lib/router/router.ts | 3 +- packages/next/next-server/server/config.ts | 41 ++++++ .../next/next-server/server/next-server.ts | 66 +++++++-- packages/next/next-server/server/render.tsx | 2 + test/integration/i18n-support/next.config.js | 14 +- .../i18n-support/test/index.test.js | 137 ++++++++++++++++-- 11 files changed, 310 insertions(+), 79 deletions(-) create mode 100644 packages/next/next-server/lib/i18n/detect-domain-locales.ts diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index e826708786e4378..1ddb4f3d3c9815a 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -780,7 +780,6 @@ export default async function build( const isFallback = isSsg && ssgStaticFallbackPages.has(page) for (const locale of i18n.locales) { - if (!isSsg && locale === i18n.defaultLocale) continue // skip fallback generation for SSG pages without fallback mode if (isSsg && isDynamic && !isFallback) continue const outputPath = `/${locale}${page === '/' ? '' : page}` @@ -869,22 +868,19 @@ export default async function build( // for SSG files with i18n the non-prerendered variants are // output with the locale prefixed so don't attempt moving // without the prefix - if (!i18n || !isSsg || additionalSsgFile) { + if (!i18n || additionalSsgFile) { await promises.mkdir(path.dirname(dest), { recursive: true }) await promises.rename(orig, dest) + } else if (i18n && !isSsg) { + // this will be updated with the locale prefixed variant + // since all files are output with the locale prefix + delete pagesManifest[page] } if (i18n) { if (additionalSsgFile) return for (const locale of i18n.locales) { - // auto-export default locale files exist at root - // TODO: should these always be prefixed with locale - // similar to SSG prerender/fallback files? - if (!isSsg && locale === i18n.defaultLocale) { - continue - } - const localeExt = page === '/' ? path.extname(file) : '' const relativeDestNoPages = relativeDest.substr('pages/'.length) diff --git a/packages/next/build/webpack/loaders/next-serverless-loader.ts b/packages/next/build/webpack/loaders/next-serverless-loader.ts index 587bb631bf02a54..bd5b923bc4917f4 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader.ts @@ -222,24 +222,33 @@ const nextServerlessLoader: loader.Loader = function () { const i18n = ${i18n} const accept = require('@hapi/accept') const { detectLocaleCookie } = require('next/dist/next-server/lib/i18n/detect-locale-cookie') + const { detectDomainLocales } = require('next/dist/next-server/lib/i18n/detect-domain-locales') const { normalizeLocalePath } = require('next/dist/next-server/lib/i18n/normalize-locale-path') let detectedLocale = detectLocaleCookie(req, i18n.locales) + const { defaultLocale, locales } = detectDomainLocales( + req, + i18n.domains, + i18n.locales, + i18n.defaultLocale, + ) + if (!detectedLocale) { detectedLocale = accept.language( req.headers['accept-language'], - i18n.locales + locales ) } const denormalizedPagePath = denormalizePagePath(parsedUrl.pathname || '/') - const detectedDefaultLocale = detectedLocale === i18n.defaultLocale + const detectedDefaultLocale = !detectedLocale || detectedLocale === defaultLocale const shouldStripDefaultLocale = detectedDefaultLocale && - denormalizedPagePath === \`/\${i18n.defaultLocale}\` + denormalizedPagePath === \`/\${defaultLocale}\` const shouldAddLocalePrefix = !detectedDefaultLocale && denormalizedPagePath === '/' - detectedLocale = detectedLocale || i18n.defaultLocale + + detectedLocale = detectedLocale || defaultLocale if ( !fromExport && @@ -261,7 +270,7 @@ const nextServerlessLoader: loader.Loader = function () { } // TODO: domain based locales (domain to locale mapping needs to be provided in next.config.js) - const localePathResult = normalizeLocalePath(parsedUrl.pathname, i18n.locales) + const localePathResult = normalizeLocalePath(parsedUrl.pathname, locales) if (localePathResult.detectedLocale) { detectedLocale = localePathResult.detectedLocale @@ -272,11 +281,13 @@ const nextServerlessLoader: loader.Loader = function () { parsedUrl.pathname = localePathResult.pathname } - detectedLocale = detectedLocale || i18n.defaultLocale + detectedLocale = detectedLocale || defaultLocale ` : ` const i18n = {} const detectedLocale = undefined + const defaultLocale = undefined + const locales = undefined ` if (page.match(API_ROUTE)) { @@ -468,8 +479,8 @@ const nextServerlessLoader: loader.Loader = function () { nextExport: fromExport, isDataReq: _nextData, locale: detectedLocale, - locales: i18n.locales, - defaultLocale: i18n.defaultLocale, + locales, + defaultLocale, }, options, ) diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index e511d8abb294940..22e926dd8c73df3 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -11,11 +11,7 @@ import type { AppProps, PrivateRouteInfo, } from '../next-server/lib/router/router' -import { - delBasePath, - hasBasePath, - delLocale, -} from '../next-server/lib/router/router' +import { delBasePath, hasBasePath } from '../next-server/lib/router/router' import { isDynamicRoute } from '../next-server/lib/router/utils/is-dynamic' import * as querystring from '../next-server/lib/router/utils/querystring' import * as envConfig from '../next-server/lib/runtime-config' @@ -65,10 +61,9 @@ const { isFallback, head: initialHeadData, locales, - defaultLocale, } = data -let { locale } = data +let { locale, defaultLocale } = data const prefix = assetPrefix || '' @@ -88,19 +83,21 @@ if (hasBasePath(asPath)) { asPath = delBasePath(asPath) } -asPath = delLocale(asPath, locale) - if (process.env.__NEXT_i18n_SUPPORT) { const { normalizeLocalePath, } = require('../next-server/lib/i18n/normalize-locale-path') - if (isFallback && locales) { + if (locales) { const localePathResult = normalizeLocalePath(asPath, locales) if (localePathResult.detectedLocale) { asPath = asPath.substr(localePathResult.detectedLocale.length + 1) - locale = localePathResult.detectedLocale + } else { + // derive the default locale if it wasn't detected in the asPath + // since we don't prerender static pages with all possible default + // locales + defaultLocale = locale } } } diff --git a/packages/next/client/page-loader.ts b/packages/next/client/page-loader.ts index f53dc9a1a61713e..e7a339dc12c986d 100644 --- a/packages/next/client/page-loader.ts +++ b/packages/next/client/page-loader.ts @@ -203,23 +203,13 @@ export default class PageLoader { * @param {string} href the route href (file-system path) * @param {string} asPath the URL as shown in browser (virtual path); used for dynamic routes */ - getDataHref( - href: string, - asPath: string, - ssg: boolean, - locale?: string, - defaultLocale?: string - ) { + getDataHref(href: string, asPath: string, ssg: boolean, locale?: string) { const { pathname: hrefPathname, query, search } = parseRelativeUrl(href) const { pathname: asPathname } = parseRelativeUrl(asPath) const route = normalizeRoute(hrefPathname) const getHrefForSlug = (path: string) => { - const dataRoute = addLocale( - getAssetPathFromRoute(path, '.json'), - locale, - defaultLocale - ) + const dataRoute = addLocale(getAssetPathFromRoute(path, '.json'), locale) return addBasePath( `/_next/data/${this.buildId}${dataRoute}${ssg ? '' : search}` ) @@ -239,12 +229,7 @@ export default class PageLoader { * @param {string} href the route href (file-system path) * @param {string} asPath the URL as shown in browser (virtual path); used for dynamic routes */ - prefetchData( - href: string, - asPath: string, - locale?: string, - defaultLocale?: string - ) { + prefetchData(href: string, asPath: string, locale?: string) { const { pathname: hrefPathname } = parseRelativeUrl(href) const route = normalizeRoute(hrefPathname) return this.promisedSsgManifest!.then( @@ -252,13 +237,7 @@ export default class PageLoader { // Check if the route requires a data file s.has(route) && // Try to generate data href, noop when falsy - (_dataHref = this.getDataHref( - href, - asPath, - true, - locale, - defaultLocale - )) && + (_dataHref = this.getDataHref(href, asPath, true, locale)) && // noop when data has already been prefetched (dedupe) !document.querySelector( `link[rel="${relPrefetch}"][href^="${_dataHref}"]` diff --git a/packages/next/next-server/lib/i18n/detect-domain-locales.ts b/packages/next/next-server/lib/i18n/detect-domain-locales.ts new file mode 100644 index 000000000000000..f91870643efc410 --- /dev/null +++ b/packages/next/next-server/lib/i18n/detect-domain-locales.ts @@ -0,0 +1,37 @@ +import { IncomingMessage } from 'http' + +export function detectDomainLocales( + req: IncomingMessage, + domainItems: + | Array<{ + domain: string + locales: string[] + defaultLocale: string + }> + | undefined, + locales: string[], + defaultLocale: string +) { + let curDefaultLocale = defaultLocale + let curLocales = locales + + const { host } = req.headers + + if (host && domainItems) { + // remove port from host and remove port if present + const hostname = host.split(':')[0].toLowerCase() + + for (const item of domainItems) { + if (hostname === item.domain.toLowerCase()) { + curDefaultLocale = item.defaultLocale + curLocales = item.locales + break + } + } + } + + return { + defaultLocale: curDefaultLocale, + locales: curLocales, + } +} diff --git a/packages/next/next-server/lib/router/router.ts b/packages/next/next-server/lib/router/router.ts index c6d0916aedd2f06..4a0bf3fd6a413ae 100644 --- a/packages/next/next-server/lib/router/router.ts +++ b/packages/next/next-server/lib/router/router.ts @@ -974,8 +974,7 @@ export default class Router implements BaseRouter { formatWithValidation({ pathname, query }), delBasePath(as), __N_SSG, - this.locale, - this.defaultLocale + this.locale ) } diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index 867433dfbd160e5..e765cddb20b7564 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -227,6 +227,47 @@ function assignDefaults(userConfig: { [key: string]: any }) { throw new Error(`Specified i18n.defaultLocale should be a string`) } + if (typeof i18n.domains !== 'undefined' && !Array.isArray(i18n.domains)) { + throw new Error( + `Specified i18n.domains must be an array of domain objects e.g. [ { domain: 'example.fr', defaultLocale: 'fr', locales: ['fr'] } ] received ${typeof i18n.domains}` + ) + } + + if (i18n.domains) { + const invalidDomainItems = i18n.domains.filter((item: any) => { + if (!item || typeof item !== 'object') return true + if (!item.defaultLocale) return true + if (!item.domain || typeof item.domain !== 'string') return true + if (!item.locales || !Array.isArray(item.locales)) return true + + const invalidLocales = item.locales.filter( + (locale: string) => !i18n.locales.includes(locale) + ) + + if (invalidLocales.length > 0) { + console.error( + `i18n.domains item "${ + item.domain + }" has the following locales (${invalidLocales.join( + ', ' + )}) that aren't provided in the main i18n.locales. Add them to the i18n.locales list or remove them from the domains item locales to continue.\n` + ) + return true + } + return false + }) + + if (invalidDomainItems.length > 0) { + throw new Error( + `Invalid i18n.domains values:\n${invalidDomainItems + .map((item: any) => JSON.stringify(item)) + .join( + '\n' + )}\n\ndomains value must follow format { domain: 'example.fr', defaultLocale: 'fr', locales: ['fr'] }` + ) + } + } + if (!Array.isArray(i18n.locales)) { throw new Error( `Specified i18n.locales must be an array of locale strings e.g. ["en-US", "nl-NL"] received ${typeof i18n.locales}` diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index dfdba9b637a3254..96e429fb4b3ff50 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -79,6 +79,7 @@ import accept from '@hapi/accept' import { normalizeLocalePath } from '../lib/i18n/normalize-locale-path' import { detectLocaleCookie } from '../lib/i18n/detect-locale-cookie' import * as Log from '../../build/output/log' +import { detectDomainLocales } from '../lib/i18n/detect-domain-locales' const getCustomRouteMatcher = pathMatch(true) @@ -193,8 +194,6 @@ export default class Server { ? requireFontManifest(this.distDir, this._isLikeServerless) : null, optimizeImages: this.nextConfig.experimental.optimizeImages, - locales: this.nextConfig.experimental.i18n?.locales, - defaultLocale: this.nextConfig.experimental.i18n?.defaultLocale, } // Only the `publicRuntimeConfig` key is exposed to the client side @@ -309,21 +308,29 @@ export default class Server { const { pathname, ...parsed } = parseUrl(req.url || '/') let detectedLocale = detectLocaleCookie(req, i18n.locales) + const { defaultLocale, locales } = detectDomainLocales( + req, + i18n.domains, + i18n.locales, + i18n.defaultLocale + ) + if (!detectedLocale) { detectedLocale = accept.language( req.headers['accept-language'], - i18n.locales + locales ) } const denormalizedPagePath = denormalizePagePath(pathname || '/') - const detectedDefaultLocale = detectedLocale === i18n.defaultLocale + const detectedDefaultLocale = + !detectedLocale || detectedLocale === defaultLocale const shouldStripDefaultLocale = - detectedDefaultLocale && - denormalizedPagePath === `/${i18n.defaultLocale}` + detectedDefaultLocale && denormalizedPagePath === `/${defaultLocale}` const shouldAddLocalePrefix = !detectedDefaultLocale && denormalizedPagePath === '/' - detectedLocale = detectedLocale || i18n.defaultLocale + + detectedLocale = detectedLocale || defaultLocale if ( i18n.localeDetection !== false && @@ -342,8 +349,7 @@ export default class Server { return } - // TODO: domain based locales (domain to locale mapping needs to be provided in next.config.js) - const localePathResult = normalizeLocalePath(pathname!, i18n.locales) + const localePathResult = normalizeLocalePath(pathname!, locales) if (localePathResult.detectedLocale) { detectedLocale = localePathResult.detectedLocale @@ -354,7 +360,12 @@ export default class Server { parsedUrl.pathname = localePathResult.pathname } - parsedUrl.query.__nextLocale = detectedLocale || i18n.defaultLocale + // TODO: render with domain specific locales and defaultLocale also? + // Currently locale specific domains will have all locales populated + // under router.locales instead of only the domain specific ones + parsedUrl.query.__nextLocales = i18n.locales + // parsedUrl.query.__nextDefaultLocale = defaultLocale + parsedUrl.query.__nextLocale = detectedLocale || defaultLocale } res.statusCode = 200 @@ -504,13 +515,21 @@ export default class Server { if (i18n) { const localePathResult = normalizeLocalePath(pathname, i18n.locales) - let detectedLocale = detectLocaleCookie(req, i18n.locales) + const { defaultLocale } = detectDomainLocales( + req, + i18n.domains, + i18n.locales, + i18n.defaultLocale + ) + let detectedLocale = defaultLocale if (localePathResult.detectedLocale) { pathname = localePathResult.pathname detectedLocale = localePathResult.detectedLocale } - _parsedUrl.query.__nextLocale = detectedLocale || i18n.defaultLocale + _parsedUrl.query.__nextLocales = i18n.locales + _parsedUrl.query.__nextLocale = detectedLocale + // _parsedUrl.query.__nextDefaultLocale = defaultLocale } pathname = getRouteFromAssetPath(pathname, '.json') @@ -1037,6 +1056,18 @@ export default class Server { pagePath!, !this.renderOpts.dev && this._isLikeServerless ) + // if loading an static HTML file the locale is required + // to be present since all HTML files are output under their locale + if ( + query.__nextLocale && + typeof components.Component === 'string' && + !pagePath?.startsWith(`/${query.__nextLocale}`) + ) { + const err = new Error('NOT_FOUND') + ;(err as any).code = 'ENOENT' + throw err + } + return { components, query: { @@ -1045,6 +1076,8 @@ export default class Server { amp: query.amp, _nextDataReq: query._nextDataReq, __nextLocale: query.__nextLocale, + __nextLocales: query.__nextLocales, + // __nextDefaultLocale: query.__nextDefaultLocale, } : query), ...(params || {}), @@ -1156,7 +1189,11 @@ export default class Server { } const locale = query.__nextLocale as string + const locales = query.__nextLocales as string[] + // const defaultLocale = query.__nextDefaultLocale as string delete query.__nextLocale + delete query.__nextLocales + // delete query.__nextDefaultLocale const ssgCacheKey = isPreviewMode || !isSSG @@ -1230,7 +1267,8 @@ export default class Server { { fontManifest: this.renderOpts.fontManifest, locale, - locales: this.renderOpts.locales, + locales, + // defaultLocale, } ) @@ -1251,6 +1289,8 @@ export default class Server { isDataReq, resolvedUrl, locale, + locales, + // defaultLocale, // For getServerSideProps we need to ensure we use the original URL // and not the resolved URL to prevent a hydration mismatch on // asPath diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index 332823e52612af0..590a008ac0e09cb 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -414,6 +414,8 @@ export async function renderToHTML( const isFallback = !!query.__nextFallback delete query.__nextFallback delete query.__nextLocale + delete query.__nextLocales + delete query.__nextDefaultLocale const isSSG = !!getStaticProps const isBuildTimeSSG = isSSG && renderOpts.nextExport diff --git a/test/integration/i18n-support/next.config.js b/test/integration/i18n-support/next.config.js index d759a91263b9fb8..f07b32b6c9a0d0a 100644 --- a/test/integration/i18n-support/next.config.js +++ b/test/integration/i18n-support/next.config.js @@ -2,8 +2,20 @@ module.exports = { // target: 'experimental-serverless-trace', experimental: { i18n: { - locales: ['nl-NL', 'nl-BE', 'nl', 'en-US', 'en'], + locales: ['nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en-US', 'en'], defaultLocale: 'en-US', + domains: [ + { + domain: 'example.be', + defaultLocale: 'nl-BE', + locales: ['nl-BE', 'fr-BE'], + }, + { + domain: 'example.fr', + locales: ['fr', 'fr-BE'], + defaultLocale: 'fr', + }, + ], }, }, } diff --git a/test/integration/i18n-support/test/index.test.js b/test/integration/i18n-support/test/index.test.js index 117fb7bc9ffcdb3..9c795b7837ddd3e 100644 --- a/test/integration/i18n-support/test/index.test.js +++ b/test/integration/i18n-support/test/index.test.js @@ -24,9 +24,115 @@ let app let appPort // let buildId -const locales = ['en-US', 'nl-NL', 'nl-BE', 'nl', 'en'] +const locales = ['en-US', 'nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en'] function runTests() { + it('should use correct default locale for locale domains', async () => { + const res = await fetchViaHTTP(appPort, '/', undefined, { + headers: { + host: 'example.fr', + }, + }) + + expect(res.status).toBe(200) + + const html = await res.text() + const $ = cheerio.load(html) + + expect($('html').attr('lang')).toBe('fr') + expect($('#router-locale').text()).toBe('fr') + expect($('#router-as-path').text()).toBe('/') + expect($('#router-pathname').text()).toBe('/') + // expect(JSON.parse($('#router-locales').text())).toEqual(['fr','fr-BE']) + expect(JSON.parse($('#router-locales').text())).toEqual(locales) + + const res2 = await fetchViaHTTP(appPort, '/', undefined, { + headers: { + host: 'example.be', + }, + }) + + expect(res2.status).toBe(200) + + const html2 = await res2.text() + const $2 = cheerio.load(html2) + + expect($2('html').attr('lang')).toBe('nl-BE') + expect($2('#router-locale').text()).toBe('nl-BE') + expect($2('#router-as-path').text()).toBe('/') + expect($2('#router-pathname').text()).toBe('/') + // expect(JSON.parse($2('#router-locales').text())).toEqual(['nl-BE','fr-BE']) + expect(JSON.parse($2('#router-locales').text())).toEqual(locales) + }) + + it('should strip locale prefix for default locale with locale domains', async () => { + const res = await fetchViaHTTP(appPort, '/fr', undefined, { + headers: { + host: 'example.fr', + }, + redirect: 'manual', + }) + + expect(res.status).toBe(307) + + const result = url.parse(res.headers.get('location'), true) + expect(result.pathname).toBe('/') + expect(result.query).toEqual({}) + + const res2 = await fetchViaHTTP(appPort, '/nl-BE', undefined, { + headers: { + host: 'example.be', + }, + redirect: 'manual', + }) + + expect(res2.status).toBe(307) + + const result2 = url.parse(res2.headers.get('location'), true) + expect(result2.pathname).toBe('/') + expect(result2.query).toEqual({}) + }) + + it('should only handle the domain specific locales', async () => { + const checkDomainLocales = async ( + domainLocales = [], + domainDefault = '', + domain = '' + ) => { + for (const locale of locales) { + const res = await fetchViaHTTP( + appPort, + `/${locale === domainDefault ? '' : locale}`, + undefined, + { + headers: { + host: domain, + }, + redirect: 'manual', + } + ) + + const isDomainLocale = domainLocales.includes(locale) + + expect(res.status).toBe(isDomainLocale ? 200 : 404) + + if (isDomainLocale) { + const html = await res.text() + const $ = cheerio.load(html) + + expect($('html').attr('lang')).toBe(locale) + expect($('#router-locale').text()).toBe(locale) + // expect(JSON.parse($('#router-locales').text())).toEqual(domainLocales) + expect(JSON.parse($('#router-locales').text())).toEqual(locales) + } + } + } + + await checkDomainLocales(['nl-BE', 'fr-BE'], 'nl-BE', 'example.be') + + await checkDomainLocales(['fr', 'fr-BE'], 'fr', 'example.fr') + }) + it('should generate fallbacks with all locales', async () => { for (const locale of locales) { const html = await renderViaHTTP( @@ -175,15 +281,20 @@ function runTests() { expect(parsedUrl2.query).toEqual({ hello: 'world' }) }) - it('should redirect to default locale route for / without accept-language', async () => { + it('should use default locale for / without accept-language', async () => { const res = await fetchViaHTTP(appPort, '/', undefined, { redirect: 'manual', }) - expect(res.status).toBe(307) + expect(res.status).toBe(200) - const parsedUrl = url.parse(res.headers.get('location'), true) - expect(parsedUrl.pathname).toBe('/en-US') - expect(parsedUrl.query).toEqual({}) + const html = await res.text() + const $ = cheerio.load(html) + + expect($('#router-locale').text()).toBe('en-US') + expect(JSON.parse($('#router-locales').text())).toEqual(locales) + expect(JSON.parse($('#router-query').text())).toEqual({}) + expect($('#router-pathname').text()).toBe('/') + expect($('#router-as-path').text()).toBe('/') const res2 = await fetchViaHTTP( appPort, @@ -193,11 +304,17 @@ function runTests() { redirect: 'manual', } ) - expect(res2.status).toBe(307) + expect(res2.status).toBe(200) - const parsedUrl2 = url.parse(res2.headers.get('location'), true) - expect(parsedUrl2.pathname).toBe('/en-US') - expect(parsedUrl2.query).toEqual({ hello: 'world' }) + const html2 = await res2.text() + const $2 = cheerio.load(html2) + + expect($2('#router-locale').text()).toBe('en-US') + expect(JSON.parse($2('#router-locales').text())).toEqual(locales) + // page is auto-export so query isn't hydrated until client + expect(JSON.parse($2('#router-query').text())).toEqual({}) + expect($2('#router-pathname').text()).toBe('/') + expect($2('#router-as-path').text()).toBe('/') }) it('should load getStaticProps page correctly SSR', async () => { From e93f17a48d70f11af1378186fb82e1cc57e87a4b Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Fri, 9 Oct 2020 23:37:51 -0500 Subject: [PATCH 2/2] remove old todo --- packages/next/build/webpack/loaders/next-serverless-loader.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/next/build/webpack/loaders/next-serverless-loader.ts b/packages/next/build/webpack/loaders/next-serverless-loader.ts index bd5b923bc4917f4..39bc53e13dcb899 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader.ts @@ -269,7 +269,6 @@ const nextServerlessLoader: loader.Loader = function () { return } - // TODO: domain based locales (domain to locale mapping needs to be provided in next.config.js) const localePathResult = normalizeLocalePath(parsedUrl.pathname, locales) if (localePathResult.detectedLocale) {