diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 01be33f2b85b7..37b9f15438a17 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -377,13 +377,13 @@ export type RoutesManifest = { dynamicRoutes: Array dataRoutes: Array i18n?: { - domains?: Array<{ + domains?: ReadonlyArray<{ http?: true domain: string - locales?: string[] + locales?: readonly string[] defaultLocale: string }> - locales: string[] + locales: readonly string[] defaultLocale: string localeDetection?: false } @@ -473,7 +473,11 @@ async function writeClientSsgManifest( buildId, distDir, locales, - }: { buildId: string; distDir: string; locales: string[] } + }: { + buildId: string + distDir: string + locales: readonly string[] | undefined + } ) { const ssgPages = new Set( [ @@ -3622,7 +3626,7 @@ export default async function build( await writeClientSsgManifest(prerenderManifest, { distDir, buildId, - locales: config.i18n?.locales || [], + locales: config.i18n?.locales, }) } else { await writePrerenderManifest(distDir, { diff --git a/packages/next/src/build/static-paths/pages.ts b/packages/next/src/build/static-paths/pages.ts index 3a5ad406cbc79..b684006fc95b3 100644 --- a/packages/next/src/build/static-paths/pages.ts +++ b/packages/next/src/build/static-paths/pages.ts @@ -19,7 +19,7 @@ export async function buildPagesStaticPaths({ page: string getStaticPaths: GetStaticPaths configFileName: string - locales?: string[] + locales?: readonly string[] defaultLocale?: string }): Promise { const prerenderedRoutes: PrerenderedRoute[] = [] @@ -28,7 +28,13 @@ export async function buildPagesStaticPaths({ // Get the default list of allowed params. const routeParameterKeys = Object.keys(_routeMatcher(page)) - const staticPathsResult = await getStaticPaths({ locales, defaultLocale }) + const staticPathsResult = await getStaticPaths({ + // We create a copy here to avoid having the types of `getStaticPaths` + // change. This ensures that users can't mutate this array and have it + // poison the reference. + locales: [...(locales ?? [])], + defaultLocale, + }) const expectedReturnVal = `Expected: { paths: [], fallback: boolean }\n` + diff --git a/packages/next/src/build/utils.ts b/packages/next/src/build/utils.ts index f9b78d79ea77a..ad6c70ce3d297 100644 --- a/packages/next/src/build/utils.ts +++ b/packages/next/src/build/utils.ts @@ -981,7 +981,7 @@ export async function isPageStatic({ configFileName: string runtimeEnvConfig: any httpAgentOptions: NextConfigComplete['httpAgentOptions'] - locales?: string[] + locales?: readonly string[] defaultLocale?: string parentId?: any edgeInfo?: any diff --git a/packages/next/src/client/get-domain-locale.ts b/packages/next/src/client/get-domain-locale.ts index 0924cb34b7779..aebe4ccec95fe 100644 --- a/packages/next/src/client/get-domain-locale.ts +++ b/packages/next/src/client/get-domain-locale.ts @@ -8,8 +8,8 @@ const basePath = (process.env.__NEXT_ROUTER_BASEPATH as string) || '' export function getDomainLocale( path: string, locale?: string | false, - locales?: string[], - domainLocales?: DomainLocale[] + locales?: readonly string[], + domainLocales?: readonly DomainLocale[] ) { if (process.env.__NEXT_I18N_SUPPORT) { const normalizeLocalePath: typeof NormalizeFn = diff --git a/packages/next/src/server/accept-header.ts b/packages/next/src/server/accept-header.ts index 071c48737ae1c..38284657914df 100644 --- a/packages/next/src/server/accept-header.ts +++ b/packages/next/src/server/accept-header.ts @@ -12,7 +12,7 @@ interface Options { function parse( raw: string, - preferences: string[] | undefined, + preferences: readonly string[] | undefined, options: Options ) { const lowers = new Map() @@ -127,7 +127,7 @@ function parse( return preferred } -export function acceptLanguage(header = '', preferences?: string[]) { +export function acceptLanguage(header = '', preferences?: readonly string[]) { return ( parse(header, preferences, { type: 'accept-language', diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index 3c23f8af11522..564ffc68f4aec 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -1437,7 +1437,7 @@ export default abstract class Server< } const normalizeResult = normalizeLocalePath( removePathPrefix(parsedUrl.pathname, this.nextConfig.basePath || ''), - this.nextConfig.i18n?.locales || [] + this.nextConfig.i18n?.locales ) if (normalizeResult.detectedLocale) { diff --git a/packages/next/src/server/config-shared.ts b/packages/next/src/server/config-shared.ts index 026f9c93fee64..629fb1e740c82 100644 --- a/packages/next/src/server/config-shared.ts +++ b/packages/next/src/server/config-shared.ts @@ -23,20 +23,20 @@ export type NextConfigComplete = Required & { configFileName: string } -export type I18NDomains = DomainLocale[] +export type I18NDomains = readonly DomainLocale[] export interface I18NConfig { defaultLocale: string domains?: I18NDomains localeDetection?: false - locales: string[] + locales: readonly string[] } export interface DomainLocale { defaultLocale: string domain: string http?: true - locales?: string[] + locales?: readonly string[] } export interface ESLintConfig { diff --git a/packages/next/src/server/dev/static-paths-worker.ts b/packages/next/src/server/dev/static-paths-worker.ts index 17b1330aebf53..c1978b515f6fc 100644 --- a/packages/next/src/server/dev/static-paths-worker.ts +++ b/packages/next/src/server/dev/static-paths-worker.ts @@ -55,7 +55,7 @@ export async function loadStaticPaths({ pathname: string config: RuntimeConfig httpAgentOptions: NextConfigComplete['httpAgentOptions'] - locales?: string[] + locales?: readonly string[] defaultLocale?: string isAppPath: boolean page: string diff --git a/packages/next/src/server/render.tsx b/packages/next/src/server/render.tsx index b2022d743f53d..ed33dde5a8c3a 100644 --- a/packages/next/src/server/render.tsx +++ b/packages/next/src/server/render.tsx @@ -142,9 +142,9 @@ class ServerRouter implements NextRouter { isFallback: boolean locale?: string isReady: boolean - locales?: string[] + locales?: readonly string[] defaultLocale?: string - domainLocales?: DomainLocale[] + domainLocales?: readonly DomainLocale[] isPreview: boolean isLocaleDomain: boolean @@ -156,9 +156,9 @@ class ServerRouter implements NextRouter { isReady: boolean, basePath: string, locale?: string, - locales?: string[], + locales?: readonly string[], defaultLocale?: string, - domainLocales?: DomainLocale[], + domainLocales?: readonly DomainLocale[], isPreview?: boolean, isLocaleDomain?: boolean ) { @@ -261,9 +261,9 @@ export type RenderOptsPartial = { nextFontManifest?: DeepReadonly distDir?: string locale?: string - locales?: string[] + locales?: readonly string[] defaultLocale?: string - domainLocales?: DomainLocale[] + domainLocales?: readonly DomainLocale[] disableOptimizedLoading?: boolean supportsDynamicResponse: boolean isBot?: boolean @@ -848,7 +848,7 @@ export async function renderToHTMLImpl( ...(isPreview ? { draftMode: true, preview: true, previewData: previewData } : undefined), - locales: renderOpts.locales, + locales: [...(renderOpts.locales ?? [])], locale: renderOpts.locale, defaultLocale: renderOpts.defaultLocale, revalidateReason: renderOpts.isOnDemandRevalidate @@ -1071,7 +1071,10 @@ export async function renderToHTMLImpl( ...(previewData !== false ? { draftMode: true, preview: true, previewData: previewData } : undefined), - locales: renderOpts.locales, + // We create a copy here to avoid having the types of + // `getServerSideProps` change. This ensures that users can't + // mutate this array and have it poison the reference. + locales: [...(renderOpts.locales ?? [])], locale: renderOpts.locale, defaultLocale: renderOpts.defaultLocale, }) diff --git a/packages/next/src/server/require.ts b/packages/next/src/server/require.ts index fda15e7c5e855..95e4e22a599e7 100644 --- a/packages/next/src/server/require.ts +++ b/packages/next/src/server/require.ts @@ -19,7 +19,7 @@ const pagePathCache = !isDev ? new LRUCache(1000) : null export function getMaybePagePath( page: string, distDir: string, - locales: string[] | undefined, + locales: readonly string[] | undefined, isAppPath: boolean ): string | null { const cacheKey = `${page}:${distDir}:${locales}:${isAppPath}` diff --git a/packages/next/src/shared/lib/i18n/detect-domain-locale.ts b/packages/next/src/shared/lib/i18n/detect-domain-locale.ts index a7ce3e07a0cd5..df9d9f35a5e88 100644 --- a/packages/next/src/shared/lib/i18n/detect-domain-locale.ts +++ b/packages/next/src/shared/lib/i18n/detect-domain-locale.ts @@ -1,7 +1,7 @@ import type { DomainLocale } from '../../../server/config-shared' export function detectDomainLocale( - domainItems?: DomainLocale[], + domainItems?: readonly DomainLocale[], hostname?: string, detectedLocale?: string ) { diff --git a/packages/next/src/shared/lib/i18n/normalize-locale-path.ts b/packages/next/src/shared/lib/i18n/normalize-locale-path.ts index d687605e14a28..146b854b35652 100644 --- a/packages/next/src/shared/lib/i18n/normalize-locale-path.ts +++ b/packages/next/src/shared/lib/i18n/normalize-locale-path.ts @@ -14,7 +14,7 @@ export interface PathLocale { */ export function normalizeLocalePath( pathname: string, - locales?: string[] + locales?: readonly string[] ): PathLocale { let detectedLocale: string | undefined // first item will be empty string from splitting at first char diff --git a/packages/next/src/shared/lib/router/router.ts b/packages/next/src/shared/lib/router/router.ts index e74ae88268b16..5d0fa2b7eed4f 100644 --- a/packages/next/src/shared/lib/router/router.ts +++ b/packages/next/src/shared/lib/router/router.ts @@ -348,9 +348,9 @@ export type BaseRouter = { asPath: string basePath: string locale?: string | undefined - locales?: string[] | undefined + locales?: readonly string[] | undefined defaultLocale?: string | undefined - domainLocales?: DomainLocale[] | undefined + domainLocales?: readonly DomainLocale[] | undefined isLocaleDomain: boolean } @@ -681,9 +681,9 @@ export default class Router implements BaseRouter { isSsr: boolean _inFlightRoute?: string | undefined _shallow?: boolean | undefined - locales?: string[] | undefined + locales?: readonly string[] | undefined defaultLocale?: string | undefined - domainLocales?: DomainLocale[] | undefined + domainLocales?: readonly DomainLocale[] | undefined isReady: boolean isLocaleDomain: boolean isFirstPopStateEvent = true @@ -735,9 +735,9 @@ export default class Router implements BaseRouter { err?: Error isFallback: boolean locale?: string - locales?: string[] + locales?: readonly string[] defaultLocale?: string - domainLocales?: DomainLocale[] + domainLocales?: readonly DomainLocale[] isPreview?: boolean } ) { diff --git a/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts b/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts index 71937afa6db63..7016cfe47e801 100644 --- a/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts +++ b/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts @@ -39,7 +39,7 @@ interface Options { */ nextConfig?: { basePath?: string - i18n?: { locales?: string[] } | null + i18n?: { locales?: readonly string[] } | null trailingSlash?: boolean } diff --git a/packages/next/src/shared/lib/router/utils/resolve-rewrites.ts b/packages/next/src/shared/lib/router/utils/resolve-rewrites.ts index 4c1bb545147b6..a628d474f223c 100644 --- a/packages/next/src/shared/lib/router/utils/resolve-rewrites.ts +++ b/packages/next/src/shared/lib/router/utils/resolve-rewrites.ts @@ -17,7 +17,7 @@ export default function resolveRewrites( }, query: ParsedUrlQuery, resolveHref: (path: string) => string, - locales?: string[] + locales?: readonly string[] ): { matchedPage: boolean parsedAs: ParsedRelativeUrl diff --git a/packages/next/src/shared/lib/utils.ts b/packages/next/src/shared/lib/utils.ts index ff1955ab2456c..abc5d6da627f3 100644 --- a/packages/next/src/shared/lib/utils.ts +++ b/packages/next/src/shared/lib/utils.ts @@ -107,9 +107,9 @@ export type NEXT_DATA = { gip?: boolean appGip?: boolean locale?: string - locales?: string[] + locales?: readonly string[] defaultLocale?: string - domainLocales?: DomainLocale[] + domainLocales?: readonly DomainLocale[] scriptLoader?: any[] isPreview?: boolean notFoundSrcPage?: string @@ -150,7 +150,7 @@ export interface NextPageContext { /** * All configured locales */ - locales?: string[] + locales?: readonly string[] /** * The configured default locale */