diff --git a/packages/next-intl/.size-limit.ts b/packages/next-intl/.size-limit.ts index 1e129f675..29b3fc507 100644 --- a/packages/next-intl/.size-limit.ts +++ b/packages/next-intl/.size-limit.ts @@ -9,7 +9,7 @@ const config: SizeLimitConfig = [ { name: "import * from 'next-intl' (react-server)", path: 'dist/production/index.react-server.js', - limit: '14.735 KB' + limit: '15.355 KB' }, { name: "import {createSharedPathnamesNavigation} from 'next-intl/navigation' (react-client)", @@ -21,13 +21,13 @@ const config: SizeLimitConfig = [ name: "import {createLocalizedPathnamesNavigation} from 'next-intl/navigation' (react-client)", path: 'dist/production/navigation.react-client.js', import: '{createLocalizedPathnamesNavigation}', - limit: '4.115 KB' + limit: '4.125 KB' }, { name: "import {createNavigation} from 'next-intl/navigation' (react-client)", path: 'dist/production/navigation.react-client.js', import: '{createNavigation}', - limit: '4.115 KB' + limit: '4.125 KB' }, { name: "import {createSharedPathnamesNavigation} from 'next-intl/navigation' (react-server)", @@ -55,7 +55,7 @@ const config: SizeLimitConfig = [ { name: "import * from 'next-intl/server' (react-server)", path: 'dist/production/server.react-server.js', - limit: '14.035 KB' + limit: '14.635 KB' }, { name: "import createMiddleware from 'next-intl/middleware'", diff --git a/packages/next-intl/__mocks__/react.tsx b/packages/next-intl/__mocks__/react.tsx index 5a6eef33a..c70f628f1 100644 --- a/packages/next-intl/__mocks__/react.tsx +++ b/packages/next-intl/__mocks__/react.tsx @@ -1,10 +1,12 @@ +import {isPromise} from '../src/shared/utils'; + // @ts-expect-error -- React uses CJS export * from 'react'; export {default} from 'react'; export function use(promise: Promise & {value?: unknown}) { - if (!(promise instanceof Promise)) { + if (!isPromise(promise)) { throw new Error('Expected a promise, got ' + typeof promise); } diff --git a/packages/next-intl/src/navigation/shared/createSharedNavigationFns.tsx b/packages/next-intl/src/navigation/shared/createSharedNavigationFns.tsx index aa05d2a68..a415b72b8 100644 --- a/packages/next-intl/src/navigation/shared/createSharedNavigationFns.tsx +++ b/packages/next-intl/src/navigation/shared/createSharedNavigationFns.tsx @@ -16,7 +16,7 @@ import { Pathnames } from '../../routing/types'; import {ParametersExceptFirst, Prettify} from '../../shared/types'; -import {isLocalizableHref} from '../../shared/utils'; +import {isLocalizableHref, isPromise} from '../../shared/utils'; import BaseLink from './BaseLink'; import { HrefOrHrefWithParams, @@ -111,10 +111,9 @@ export default function createSharedNavigationFns< const isLocalizable = isLocalizableHref(href); const localePromiseOrValue = getLocale(); - const curLocale = - localePromiseOrValue instanceof Promise - ? use(localePromiseOrValue) - : localePromiseOrValue; + const curLocale = isPromise(localePromiseOrValue) + ? use(localePromiseOrValue) + : localePromiseOrValue; const finalPathname = isLocalizable ? getPathname( diff --git a/packages/next-intl/src/react-server/index.test.tsx b/packages/next-intl/src/react-server/index.test.tsx index ab84710ce..36c74a3df 100644 --- a/packages/next-intl/src/react-server/index.test.tsx +++ b/packages/next-intl/src/react-server/index.test.tsx @@ -1,6 +1,7 @@ import React from 'react'; import {describe, expect, it, vi} from 'vitest'; import {getTranslations} from '../server.react-server'; +import {isPromise} from '../shared/utils'; import {renderToStream} from './testUtils'; import { _createCache, @@ -73,7 +74,7 @@ describe('performance', () => { try { useTranslations('Component'); } catch (promiseOrError) { - if (promiseOrError instanceof Promise) { + if (isPromise(promiseOrError)) { await promiseOrError; useTranslations('Component'); } else { diff --git a/packages/next-intl/src/server/react-server/RequestLocale.tsx b/packages/next-intl/src/server/react-server/RequestLocale.tsx index 2a62aeaaa..e0cd06085 100644 --- a/packages/next-intl/src/server/react-server/RequestLocale.tsx +++ b/packages/next-intl/src/server/react-server/RequestLocale.tsx @@ -1,15 +1,14 @@ import {headers} from 'next/headers'; import {cache} from 'react'; import {HEADER_LOCALE_NAME} from '../../shared/constants'; +import {isPromise} from '../../shared/utils'; import {getCachedRequestLocale} from './RequestLocaleCache'; async function getHeadersImpl(): Promise { const promiseOrValue = headers(); // Compatibility with Next.js <15 - return promiseOrValue instanceof Promise - ? await promiseOrValue - : promiseOrValue; + return isPromise(promiseOrValue) ? await promiseOrValue : promiseOrValue; } const getHeaders = cache(getHeadersImpl); diff --git a/packages/next-intl/src/server/react-server/getConfig.tsx b/packages/next-intl/src/server/react-server/getConfig.tsx index 362dc915f..482302b53 100644 --- a/packages/next-intl/src/server/react-server/getConfig.tsx +++ b/packages/next-intl/src/server/react-server/getConfig.tsx @@ -6,6 +6,7 @@ import { _createIntlFormatters, initializeConfig } from 'use-intl/core'; +import {isPromise} from '../../shared/utils'; import {getRequestLocale} from './RequestLocale'; import {getRequestLocale as getRequestLocaleLegacy} from './RequestLocaleLegacy'; import createRequestConfig from './createRequestConfig'; @@ -72,7 +73,7 @@ See also: https://next-intl.dev/docs/usage/configuration#i18n-request }; let result = getConfig(params); - if (result instanceof Promise) { + if (isPromise(result)) { result = await result; } diff --git a/packages/next-intl/src/shared/utils.tsx b/packages/next-intl/src/shared/utils.tsx index bd6b28065..82ba81343 100644 --- a/packages/next-intl/src/shared/utils.tsx +++ b/packages/next-intl/src/shared/utils.tsx @@ -233,3 +233,10 @@ function comparePathnamePairs(a: string, b: string): number { export function getSortedPathnames(pathnames: Array) { return pathnames.sort(comparePathnamePairs); } + +export function isPromise( + value: Value | Promise +): value is Promise { + // https://github.com/amannn/next-intl/issues/1711 + return typeof (value as any).then === 'function'; +}