From 3de862a7d52fd427b7408298992f49e631b80942 Mon Sep 17 00:00:00 2001 From: osaton Date: Mon, 4 Nov 2024 14:15:43 +0200 Subject: [PATCH 1/7] test: Add type export tests --- pnpm-lock.yaml | 12 ++-- pnpm-workspace.yaml | 7 +- type-export-test/.gitignore | 7 ++ type-export-test/README.md | 12 ++++ type-export-test/eslint.config.mjs | 3 + type-export-test/package.json | 18 +++++ type-export-test/src/index.ts | 42 ++++++++++++ type-export-test/src/middleware.ts | 14 ++++ .../createLocalizedPathnamesNavigation.ts | 23 +++++++ .../src/navigation/createNavigation.ts | 11 +++ .../createSharedPathnamesNavigation.ts | 7 ++ type-export-test/src/routing.ts | 63 +++++++++++++++++ type-export-test/src/server.ts | 67 +++++++++++++++++++ type-export-test/tsconfig.json | 27 ++++++++ 14 files changed, 304 insertions(+), 9 deletions(-) create mode 100644 type-export-test/.gitignore create mode 100644 type-export-test/README.md create mode 100644 type-export-test/eslint.config.mjs create mode 100644 type-export-test/package.json create mode 100644 type-export-test/src/index.ts create mode 100644 type-export-test/src/middleware.ts create mode 100644 type-export-test/src/navigation/createLocalizedPathnamesNavigation.ts create mode 100644 type-export-test/src/navigation/createNavigation.ts create mode 100644 type-export-test/src/navigation/createSharedPathnamesNavigation.ts create mode 100644 type-export-test/src/routing.ts create mode 100644 type-export-test/src/server.ts create mode 100644 type-export-test/tsconfig.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 851f93f28..7cd15338e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22902,7 +22902,7 @@ snapshots: debug: 4.3.7(supports-color@6.1.0) enhanced-resolve: 5.17.1 eslint: 9.13.0(jiti@2.3.3) - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@9.13.0(jiti@2.3.3)))(eslint@9.13.0(jiti@2.3.3)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0(jiti@2.3.3)) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-bun-module: 1.2.1 @@ -22921,7 +22921,7 @@ snapshots: debug: 4.3.7(supports-color@6.1.0) enhanced-resolve: 5.17.1 eslint: 9.13.0(jiti@2.3.3) - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@9.13.0(jiti@2.3.3)))(eslint@9.13.0(jiti@2.3.3)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0(jiti@2.3.3)) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-bun-module: 1.2.1 @@ -22934,7 +22934,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@9.13.0(jiti@2.3.3)))(eslint@9.13.0(jiti@2.3.3)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0(jiti@2.3.3)): dependencies: debug: 3.2.7(supports-color@6.1.0) optionalDependencies: @@ -22945,7 +22945,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.9.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@9.13.0(jiti@2.3.3)))(eslint@9.13.0(jiti@2.3.3)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.9.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0(jiti@2.3.3)): dependencies: debug: 3.2.7(supports-color@6.1.0) optionalDependencies: @@ -22973,7 +22973,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.13.0(jiti@2.3.3) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@9.13.0(jiti@2.3.3)))(eslint@9.13.0(jiti@2.3.3)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0(jiti@2.3.3)) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -23002,7 +23002,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.13.0(jiti@2.3.3) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@9.13.0(jiti@2.3.3)))(eslint@9.13.0(jiti@2.3.3)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0(jiti@2.3.3)) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4bfe0befb..e28f30509 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,5 @@ packages: - - "packages/*" - - "examples/*" - - "docs" + - 'packages/*' + - 'export-test' + - 'examples/*' + - 'docs' diff --git a/type-export-test/.gitignore b/type-export-test/.gitignore new file mode 100644 index 000000000..d61873784 --- /dev/null +++ b/type-export-test/.gitignore @@ -0,0 +1,7 @@ +/node_modules +/.next/ +.DS_Store +tsconfig.tsbuildinfo +*storybook.log +storybook-static +test-results diff --git a/type-export-test/README.md b/type-export-test/README.md new file mode 100644 index 000000000..af51d10a6 --- /dev/null +++ b/type-export-test/README.md @@ -0,0 +1,12 @@ +# Test type exports + +Ensure that exported types work as expected for library creators. + +## Motivation + +When setting `compilerOptions.declaration` to `true`, TypeScript attempts to +generate declaration files for all exported variables. This can fail with the +following error if there are missing exports in the library: + +> The inferred type of 'SomeType' cannot be named without a reference to +> '../some/path' diff --git a/type-export-test/eslint.config.mjs b/type-export-test/eslint.config.mjs new file mode 100644 index 000000000..8a4bf6954 --- /dev/null +++ b/type-export-test/eslint.config.mjs @@ -0,0 +1,3 @@ +import {getPresets} from 'eslint-config-molindo'; + +export default await getPresets('typescript', 'react', 'jest'); diff --git a/type-export-test/package.json b/type-export-test/package.json new file mode 100644 index 000000000..4eaf089ee --- /dev/null +++ b/type-export-test/package.json @@ -0,0 +1,18 @@ +{ + "name": "type-export-test", + "private": true, + "scripts": { + "lint": "eslint src && tsc && prettier src --check", + "test": "tsc" + }, + "dependencies": { + "next": "^14.2.4", + "next-intl": "^3.0.0" + }, + "devDependencies": { + "eslint": "^9.11.1", + "eslint-config-molindo": "^8.0.0", + "typescript": "^5.5.3" + }, + "prettier": "eslint-config-molindo/.prettierrc.json" +} diff --git a/type-export-test/src/index.ts b/type-export-test/src/index.ts new file mode 100644 index 000000000..a92b6a500 --- /dev/null +++ b/type-export-test/src/index.ts @@ -0,0 +1,42 @@ +import { + createFormatter, + createTranslator, + initializeConfig, + useFormatter, + useLocale, + useMessages, + useNow, + useTimeZone, + useTranslations +} from 'next-intl'; + +export function useExports() { + const messages = useMessages(); + const now = useNow(); + const locale = useLocale(); + const timezone = useTimeZone(); + const formatter = useFormatter(); + const translations = useTranslations(); + + return { + messages, + now, + locale, + timezone, + formatter, + translations + }; +} + +export const config = initializeConfig({ + locale: 'en' +}); + +export const translator = createTranslator({ + locale: 'en' +}); + +export const formatter = createFormatter({ + locale: 'en', + now: new Date(2022, 10, 6, 20, 20, 0, 0) +}); diff --git a/type-export-test/src/middleware.ts b/type-export-test/src/middleware.ts new file mode 100644 index 000000000..e87a1de5d --- /dev/null +++ b/type-export-test/src/middleware.ts @@ -0,0 +1,14 @@ +import createMiddleware from 'next-intl/middleware'; +import {routing} from './routing'; + +export default createMiddleware(routing); + +export const config = { + matcher: [ + // Skip all paths that should not be internationalized + '/((?!_next|.*\\..*).*)', + + // Necessary for base path to work + '/' + ] +}; diff --git a/type-export-test/src/navigation/createLocalizedPathnamesNavigation.ts b/type-export-test/src/navigation/createLocalizedPathnamesNavigation.ts new file mode 100644 index 000000000..b4b804bc0 --- /dev/null +++ b/type-export-test/src/navigation/createLocalizedPathnamesNavigation.ts @@ -0,0 +1,23 @@ +import {createLocalizedPathnamesNavigation} from 'next-intl/navigation'; + +export const { + Link, + getPathname, + permanentRedirect, + redirect, + usePathname, + useRouter +} = createLocalizedPathnamesNavigation({ + locales: ['en', 'de'], + defaultLocale: 'en', + pathnames: { + '/': { + en: '/', + de: '/de/' + }, + '/about': { + en: '/about', + de: '/ueber' + } + } +}); diff --git a/type-export-test/src/navigation/createNavigation.ts b/type-export-test/src/navigation/createNavigation.ts new file mode 100644 index 000000000..9f69f9292 --- /dev/null +++ b/type-export-test/src/navigation/createNavigation.ts @@ -0,0 +1,11 @@ +import {createNavigation} from 'next-intl/navigation'; +import {routing} from '../routing'; + +export const { + Link, + getPathname, + permanentRedirect, + redirect, + usePathname, + useRouter +} = createNavigation(routing); diff --git a/type-export-test/src/navigation/createSharedPathnamesNavigation.ts b/type-export-test/src/navigation/createSharedPathnamesNavigation.ts new file mode 100644 index 000000000..63253bdac --- /dev/null +++ b/type-export-test/src/navigation/createSharedPathnamesNavigation.ts @@ -0,0 +1,7 @@ +import {createSharedPathnamesNavigation} from 'next-intl/navigation'; + +export const {Link, permanentRedirect, redirect, usePathname, useRouter} = + createSharedPathnamesNavigation({ + locales: ['en'], + defaultLocale: 'en' + }); diff --git a/type-export-test/src/routing.ts b/type-export-test/src/routing.ts new file mode 100644 index 000000000..5ee31f247 --- /dev/null +++ b/type-export-test/src/routing.ts @@ -0,0 +1,63 @@ +import {defineRouting} from 'next-intl/routing'; + +export const routing = defineRouting({ + locales: ['en', 'de', 'es', 'ja'], + defaultLocale: 'en', + localePrefix: + process.env.NEXT_PUBLIC_USE_CASE === 'locale-prefix-never' + ? 'never' + : { + mode: 'as-needed', + prefixes: { + es: '/spain' + } + }, + domains: + process.env.NEXT_PUBLIC_USE_CASE === 'domains' + ? [ + { + domain: 'example.com', + defaultLocale: 'en' + }, + { + domain: 'example.de', + defaultLocale: 'de' + } + ] + : undefined, + pathnames: { + '/': '/', + '/client': '/client', + '/about': '/about', + '/client/redirect': '/client/redirect', + '/nested': { + en: '/nested', + de: '/verschachtelt', + es: '/anidada', + ja: '/ネスト' + }, + '/redirect': '/redirect', + '/news/[articleId]': { + en: '/news/[articleId]', + de: '/neuigkeiten/[articleId]', + es: '/noticias/[articleId]', + ja: '/ニュース/[articleId]' + }, + '/news/just-in': { + en: '/news/just-in', + de: '/neuigkeiten/aktuell', + es: '/noticias/justo-en', + ja: '/ニュース/現在' + } + }, + localeCookie: + process.env.NEXT_PUBLIC_USE_CASE === 'locale-cookie-false' + ? false + : { + // 200 days + maxAge: 200 * 24 * 60 * 60 + } +}); + +export type Pathnames = keyof typeof routing.pathnames; +export type Locale = (typeof routing.locales)[number]; diff --git a/type-export-test/src/server.ts b/type-export-test/src/server.ts new file mode 100644 index 000000000..a2652a0d3 --- /dev/null +++ b/type-export-test/src/server.ts @@ -0,0 +1,67 @@ +import {headers} from 'next/headers'; +import {Formats} from 'next-intl'; +import {getRequestConfig} from 'next-intl/server'; +import {routing} from './routing'; + +export const formats = { + dateTime: { + medium: { + dateStyle: 'medium', + timeStyle: 'short', + hour12: false + } + }, + number: { + precise: { + maximumFractionDigits: 5 + } + }, + list: { + enumeration: { + style: 'long', + type: 'conjunction' + } + } +} satisfies Formats; + +export default getRequestConfig(async ({requestLocale}) => { + // This typically corresponds to the `[locale]` segment + let locale = await requestLocale; + + // Ensure that the incoming locale is valid + if (!locale || !routing.locales.includes(locale as any)) { + locale = routing.defaultLocale; + } + + const now = headers().get('x-now'); + const timeZone = headers().get('x-time-zone') ?? 'Europe/Vienna'; + const localeMessages = (await import(`../../messages/${locale}.json`)) + .default; + const messages = localeMessages; + + return { + locale, + now: now ? new Date(now) : undefined, + timeZone, + messages, + formats, + onError(error) { + if ( + error.message === + (process.env.NODE_ENV === 'production' + ? 'MISSING_MESSAGE' + : 'MISSING_MESSAGE: Could not resolve `missing` in `Index`.') + ) { + // Do nothing, this error is triggered on purpose + } else { + console.error(JSON.stringify(error.message)); + } + }, + getMessageFallback({key, namespace}) { + return ( + '`getMessageFallback` called for ' + + [namespace, key].filter((part) => part != null).join('.') + ); + } + }; +}); diff --git a/type-export-test/tsconfig.json b/type-export-test/tsconfig.json new file mode 100644 index 000000000..b886b28b8 --- /dev/null +++ b/type-export-test/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "eslint-config-molindo/tsconfig.json", + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "skipLibCheck": true, + "noEmit": true, + "declaration": true, + "esModuleInterop": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["**/*.ts"], + "exclude": ["node_modules"] +} From 4f2ca10beebebaee553b266e7620bfec15b162af Mon Sep 17 00:00:00 2001 From: osaton Date: Mon, 4 Nov 2024 14:20:35 +0200 Subject: [PATCH 2/7] fix: Add missing type exports to improve portability --- .../next-intl/src/navigation/react-client/index.tsx | 7 +++++++ .../next-intl/src/navigation/shared/StrictParams.tsx | 1 + packages/next-intl/src/routing/defineRouting.tsx | 1 + packages/next-intl/src/routing/index.tsx | 11 +++++++++-- packages/next-intl/src/server/react-server/index.tsx | 6 +++++- packages/use-intl/src/core/index.tsx | 2 ++ 6 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/next-intl/src/navigation/react-client/index.tsx b/packages/next-intl/src/navigation/react-client/index.tsx index c4183b53b..0b101c5b1 100644 --- a/packages/next-intl/src/navigation/react-client/index.tsx +++ b/packages/next-intl/src/navigation/react-client/index.tsx @@ -2,6 +2,13 @@ export {default as createSharedPathnamesNavigation} from './createSharedPathname export {default as createLocalizedPathnamesNavigation} from './createLocalizedPathnamesNavigation'; export {default as createNavigation} from './createNavigation'; +export type {StrictParams} from '../shared/StrictParams'; +export type { + HrefOrHrefWithParams, + HrefOrUrlObjectWithParams, + QueryParams +} from '../shared/utils'; + import type { Locales, Pathnames as PathnamesDeprecatedExport diff --git a/packages/next-intl/src/navigation/shared/StrictParams.tsx b/packages/next-intl/src/navigation/shared/StrictParams.tsx index 7606054c0..3d7e4d24a 100644 --- a/packages/next-intl/src/navigation/shared/StrictParams.tsx +++ b/packages/next-intl/src/navigation/shared/StrictParams.tsx @@ -25,3 +25,4 @@ type StrictParams = Pathname extends `${string}[${string}` : never; export default StrictParams; +export type {StrictParams}; diff --git a/packages/next-intl/src/routing/defineRouting.tsx b/packages/next-intl/src/routing/defineRouting.tsx index d470db867..f2adc09d4 100644 --- a/packages/next-intl/src/routing/defineRouting.tsx +++ b/packages/next-intl/src/routing/defineRouting.tsx @@ -1,6 +1,7 @@ import {RoutingConfig} from './config'; import {DomainsConfig, LocalePrefixMode, Locales, Pathnames} from './types'; +export type {RoutingConfig}; export default function defineRouting< const AppLocales extends Locales, const AppLocalePrefixMode extends LocalePrefixMode = 'always', diff --git a/packages/next-intl/src/routing/index.tsx b/packages/next-intl/src/routing/index.tsx index d0832e45d..32352c719 100644 --- a/packages/next-intl/src/routing/index.tsx +++ b/packages/next-intl/src/routing/index.tsx @@ -1,2 +1,9 @@ -export type {Pathnames, LocalePrefix, DomainsConfig} from './types'; -export {default as defineRouting} from './defineRouting'; +export type { + Pathnames, + DomainsConfig, + DomainConfig, + LocalePrefixMode, + LocalePrefixes +} from './types'; +export type {InitializedLocaleCookieConfig} from './config'; +export {default as defineRouting, type RoutingConfig} from './defineRouting'; diff --git a/packages/next-intl/src/server/react-server/index.tsx b/packages/next-intl/src/server/react-server/index.tsx index 258707b90..3b368f52e 100644 --- a/packages/next-intl/src/server/react-server/index.tsx +++ b/packages/next-intl/src/server/react-server/index.tsx @@ -2,7 +2,11 @@ * Server-only APIs available via `next-intl/server`. */ -export {default as getRequestConfig} from './getRequestConfig'; +export { + default as getRequestConfig, + type GetRequestConfigParams, + type RequestConfig +} from './getRequestConfig'; export {default as getFormatter} from './getFormatter'; export {default as getNow} from './getNow'; export {default as getTimeZone} from './getTimeZone'; diff --git a/packages/use-intl/src/core/index.tsx b/packages/use-intl/src/core/index.tsx index cfc3cc0e4..a298caf02 100644 --- a/packages/use-intl/src/core/index.tsx +++ b/packages/use-intl/src/core/index.tsx @@ -8,6 +8,8 @@ export type {default as Formats} from './Formats'; export type {default as IntlConfig} from './IntlConfig'; export type {default as DateTimeFormatOptions} from './DateTimeFormatOptions'; export type {default as NumberFormatOptions} from './NumberFormatOptions'; +export type {default as RelativeTimeFormatOptions} from './RelativeTimeFormatOptions'; +export type {default as Timezone} from './TimeZone'; export {default as IntlError, IntlErrorCode} from './IntlError'; export {default as createTranslator} from './createTranslator'; export {default as createFormatter} from './createFormatter'; From e906baddc61bfc55ebbfb8f0bd7c311d58afd6fd Mon Sep 17 00:00:00 2001 From: osaton Date: Mon, 4 Nov 2024 15:33:20 +0200 Subject: [PATCH 3/7] chore: Add type export tests as a dependency for `next-intl` tests --- pnpm-workspace.yaml | 2 +- turbo.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e28f30509..2fb8451fc 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,5 @@ packages: - 'packages/*' - - 'export-test' + - 'type-export-test' - 'examples/*' - 'docs' diff --git a/turbo.json b/turbo.json index 49eabfe80..77b457cb4 100644 --- a/turbo.json +++ b/turbo.json @@ -1,6 +1,9 @@ { "$schema": "https://turbo.build/schema.json", "tasks": { + "next-intl#test": { + "dependsOn": ["type-export-test#test"] + }, "build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**", "!.next/cache/**", "build/**"] From 7a4b17876be663bfec3c16537d32b860e1827529 Mon Sep 17 00:00:00 2001 From: osaton Date: Tue, 5 Nov 2024 11:09:49 +0200 Subject: [PATCH 4/7] chore: Update pnpm-lock.yaml --- pnpm-lock.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7cd15338e..832291d33 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -931,6 +931,25 @@ importers: specifier: ^2.0.2 version: 2.1.3(@edge-runtime/vm@4.0.3)(@types/node@20.17.0)(jsdom@25.0.1)(terser@5.36.0) + type-export-test: + dependencies: + next: + specifier: ^14.2.4 + version: 14.2.16(@babel/core@7.25.9)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next-intl: + specifier: ^3.0.0 + version: link:../packages/next-intl + devDependencies: + eslint: + specifier: ^9.11.1 + version: 9.13.0(jiti@2.3.3) + eslint-config-molindo: + specifier: ^8.0.0 + version: 8.0.0(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint@9.13.0(jiti@2.3.3))(jest@29.7.0(@types/node@22.7.9))(tailwindcss@3.4.14)(typescript@5.6.3)(vitest@2.1.3(@edge-runtime/vm@4.0.3)(@types/node@22.7.9)(jsdom@25.0.1)(terser@5.36.0)) + typescript: + specifier: ^5.5.3 + version: 5.6.3 + packages: '@aashutoshrathi/word-wrap@1.2.6': From afa327672e843cf5c67135deb0b4065ba5df1db1 Mon Sep 17 00:00:00 2001 From: osaton Date: Wed, 6 Nov 2024 11:08:54 +0200 Subject: [PATCH 5/7] fix: Remove unnecessary type exports --- packages/next-intl/src/navigation/react-client/index.tsx | 6 +----- packages/next-intl/src/routing/index.tsx | 5 ++--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/next-intl/src/navigation/react-client/index.tsx b/packages/next-intl/src/navigation/react-client/index.tsx index 0b101c5b1..3612fb62d 100644 --- a/packages/next-intl/src/navigation/react-client/index.tsx +++ b/packages/next-intl/src/navigation/react-client/index.tsx @@ -3,11 +3,7 @@ export {default as createLocalizedPathnamesNavigation} from './createLocalizedPa export {default as createNavigation} from './createNavigation'; export type {StrictParams} from '../shared/StrictParams'; -export type { - HrefOrHrefWithParams, - HrefOrUrlObjectWithParams, - QueryParams -} from '../shared/utils'; +export type {QueryParams} from '../shared/utils'; import type { Locales, diff --git a/packages/next-intl/src/routing/index.tsx b/packages/next-intl/src/routing/index.tsx index 32352c719..9101d2cdd 100644 --- a/packages/next-intl/src/routing/index.tsx +++ b/packages/next-intl/src/routing/index.tsx @@ -1,9 +1,8 @@ export type { Pathnames, DomainsConfig, - DomainConfig, - LocalePrefixMode, - LocalePrefixes + LocalePrefix, + LocalePrefixMode } from './types'; export type {InitializedLocaleCookieConfig} from './config'; export {default as defineRouting, type RoutingConfig} from './defineRouting'; From 0ae6e9b72f9868961b51f896195bef4ea5e431b2 Mon Sep 17 00:00:00 2001 From: osaton Date: Thu, 7 Nov 2024 17:35:12 +0200 Subject: [PATCH 6/7] chore: Move portability tests to `example-app-router-playground` --- .../src/type-portability-test.ts | 5 ++ .../tsconfig.json | 1 + pnpm-lock.yaml | 19 ------ pnpm-workspace.yaml | 1 - turbo.json | 2 +- type-export-test/.gitignore | 7 -- type-export-test/README.md | 12 ---- type-export-test/eslint.config.mjs | 3 - type-export-test/package.json | 18 ----- type-export-test/src/middleware.ts | 14 ---- .../createLocalizedPathnamesNavigation.ts | 23 ------- .../src/navigation/createNavigation.ts | 11 --- .../createSharedPathnamesNavigation.ts | 7 -- type-export-test/src/routing.ts | 63 ----------------- type-export-test/src/server.ts | 67 ------------------- type-export-test/tsconfig.json | 27 -------- 16 files changed, 7 insertions(+), 273 deletions(-) rename type-export-test/src/index.ts => examples/example-app-router-playground/src/type-portability-test.ts (77%) delete mode 100644 type-export-test/.gitignore delete mode 100644 type-export-test/README.md delete mode 100644 type-export-test/eslint.config.mjs delete mode 100644 type-export-test/package.json delete mode 100644 type-export-test/src/middleware.ts delete mode 100644 type-export-test/src/navigation/createLocalizedPathnamesNavigation.ts delete mode 100644 type-export-test/src/navigation/createNavigation.ts delete mode 100644 type-export-test/src/navigation/createSharedPathnamesNavigation.ts delete mode 100644 type-export-test/src/routing.ts delete mode 100644 type-export-test/src/server.ts delete mode 100644 type-export-test/tsconfig.json diff --git a/type-export-test/src/index.ts b/examples/example-app-router-playground/src/type-portability-test.ts similarity index 77% rename from type-export-test/src/index.ts rename to examples/example-app-router-playground/src/type-portability-test.ts index a92b6a500..b22efdce0 100644 --- a/type-export-test/src/index.ts +++ b/examples/example-app-router-playground/src/type-portability-test.ts @@ -1,3 +1,8 @@ +// Ensure that exported types function as expected for library creators. +// +// Most functionality is already tested through usage in this application. +// This file includes exports for any that are not yet covered. + import { createFormatter, createTranslator, diff --git a/examples/example-app-router-playground/tsconfig.json b/examples/example-app-router-playground/tsconfig.json index 8d6bca754..5e96a894f 100644 --- a/examples/example-app-router-playground/tsconfig.json +++ b/examples/example-app-router-playground/tsconfig.json @@ -6,6 +6,7 @@ "allowJs": true, "skipLibCheck": true, "noEmit": true, + "declaration": true, "esModuleInterop": true, "module": "ESNext", "moduleResolution": "Bundler", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 832291d33..7cd15338e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -931,25 +931,6 @@ importers: specifier: ^2.0.2 version: 2.1.3(@edge-runtime/vm@4.0.3)(@types/node@20.17.0)(jsdom@25.0.1)(terser@5.36.0) - type-export-test: - dependencies: - next: - specifier: ^14.2.4 - version: 14.2.16(@babel/core@7.25.9)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - next-intl: - specifier: ^3.0.0 - version: link:../packages/next-intl - devDependencies: - eslint: - specifier: ^9.11.1 - version: 9.13.0(jiti@2.3.3) - eslint-config-molindo: - specifier: ^8.0.0 - version: 8.0.0(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint@9.13.0(jiti@2.3.3))(jest@29.7.0(@types/node@22.7.9))(tailwindcss@3.4.14)(typescript@5.6.3)(vitest@2.1.3(@edge-runtime/vm@4.0.3)(@types/node@22.7.9)(jsdom@25.0.1)(terser@5.36.0)) - typescript: - specifier: ^5.5.3 - version: 5.6.3 - packages: '@aashutoshrathi/word-wrap@1.2.6': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2fb8451fc..dd8cb5185 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,4 @@ packages: - 'packages/*' - - 'type-export-test' - 'examples/*' - 'docs' diff --git a/turbo.json b/turbo.json index 77b457cb4..27e12775f 100644 --- a/turbo.json +++ b/turbo.json @@ -2,7 +2,7 @@ "$schema": "https://turbo.build/schema.json", "tasks": { "next-intl#test": { - "dependsOn": ["type-export-test#test"] + "dependsOn": ["example-app-router-playground#lint"] }, "build": { "dependsOn": ["^build"], diff --git a/type-export-test/.gitignore b/type-export-test/.gitignore deleted file mode 100644 index d61873784..000000000 --- a/type-export-test/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -/node_modules -/.next/ -.DS_Store -tsconfig.tsbuildinfo -*storybook.log -storybook-static -test-results diff --git a/type-export-test/README.md b/type-export-test/README.md deleted file mode 100644 index af51d10a6..000000000 --- a/type-export-test/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Test type exports - -Ensure that exported types work as expected for library creators. - -## Motivation - -When setting `compilerOptions.declaration` to `true`, TypeScript attempts to -generate declaration files for all exported variables. This can fail with the -following error if there are missing exports in the library: - -> The inferred type of 'SomeType' cannot be named without a reference to -> '../some/path' diff --git a/type-export-test/eslint.config.mjs b/type-export-test/eslint.config.mjs deleted file mode 100644 index 8a4bf6954..000000000 --- a/type-export-test/eslint.config.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import {getPresets} from 'eslint-config-molindo'; - -export default await getPresets('typescript', 'react', 'jest'); diff --git a/type-export-test/package.json b/type-export-test/package.json deleted file mode 100644 index 4eaf089ee..000000000 --- a/type-export-test/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "type-export-test", - "private": true, - "scripts": { - "lint": "eslint src && tsc && prettier src --check", - "test": "tsc" - }, - "dependencies": { - "next": "^14.2.4", - "next-intl": "^3.0.0" - }, - "devDependencies": { - "eslint": "^9.11.1", - "eslint-config-molindo": "^8.0.0", - "typescript": "^5.5.3" - }, - "prettier": "eslint-config-molindo/.prettierrc.json" -} diff --git a/type-export-test/src/middleware.ts b/type-export-test/src/middleware.ts deleted file mode 100644 index e87a1de5d..000000000 --- a/type-export-test/src/middleware.ts +++ /dev/null @@ -1,14 +0,0 @@ -import createMiddleware from 'next-intl/middleware'; -import {routing} from './routing'; - -export default createMiddleware(routing); - -export const config = { - matcher: [ - // Skip all paths that should not be internationalized - '/((?!_next|.*\\..*).*)', - - // Necessary for base path to work - '/' - ] -}; diff --git a/type-export-test/src/navigation/createLocalizedPathnamesNavigation.ts b/type-export-test/src/navigation/createLocalizedPathnamesNavigation.ts deleted file mode 100644 index b4b804bc0..000000000 --- a/type-export-test/src/navigation/createLocalizedPathnamesNavigation.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {createLocalizedPathnamesNavigation} from 'next-intl/navigation'; - -export const { - Link, - getPathname, - permanentRedirect, - redirect, - usePathname, - useRouter -} = createLocalizedPathnamesNavigation({ - locales: ['en', 'de'], - defaultLocale: 'en', - pathnames: { - '/': { - en: '/', - de: '/de/' - }, - '/about': { - en: '/about', - de: '/ueber' - } - } -}); diff --git a/type-export-test/src/navigation/createNavigation.ts b/type-export-test/src/navigation/createNavigation.ts deleted file mode 100644 index 9f69f9292..000000000 --- a/type-export-test/src/navigation/createNavigation.ts +++ /dev/null @@ -1,11 +0,0 @@ -import {createNavigation} from 'next-intl/navigation'; -import {routing} from '../routing'; - -export const { - Link, - getPathname, - permanentRedirect, - redirect, - usePathname, - useRouter -} = createNavigation(routing); diff --git a/type-export-test/src/navigation/createSharedPathnamesNavigation.ts b/type-export-test/src/navigation/createSharedPathnamesNavigation.ts deleted file mode 100644 index 63253bdac..000000000 --- a/type-export-test/src/navigation/createSharedPathnamesNavigation.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {createSharedPathnamesNavigation} from 'next-intl/navigation'; - -export const {Link, permanentRedirect, redirect, usePathname, useRouter} = - createSharedPathnamesNavigation({ - locales: ['en'], - defaultLocale: 'en' - }); diff --git a/type-export-test/src/routing.ts b/type-export-test/src/routing.ts deleted file mode 100644 index 5ee31f247..000000000 --- a/type-export-test/src/routing.ts +++ /dev/null @@ -1,63 +0,0 @@ -import {defineRouting} from 'next-intl/routing'; - -export const routing = defineRouting({ - locales: ['en', 'de', 'es', 'ja'], - defaultLocale: 'en', - localePrefix: - process.env.NEXT_PUBLIC_USE_CASE === 'locale-prefix-never' - ? 'never' - : { - mode: 'as-needed', - prefixes: { - es: '/spain' - } - }, - domains: - process.env.NEXT_PUBLIC_USE_CASE === 'domains' - ? [ - { - domain: 'example.com', - defaultLocale: 'en' - }, - { - domain: 'example.de', - defaultLocale: 'de' - } - ] - : undefined, - pathnames: { - '/': '/', - '/client': '/client', - '/about': '/about', - '/client/redirect': '/client/redirect', - '/nested': { - en: '/nested', - de: '/verschachtelt', - es: '/anidada', - ja: '/ネスト' - }, - '/redirect': '/redirect', - '/news/[articleId]': { - en: '/news/[articleId]', - de: '/neuigkeiten/[articleId]', - es: '/noticias/[articleId]', - ja: '/ニュース/[articleId]' - }, - '/news/just-in': { - en: '/news/just-in', - de: '/neuigkeiten/aktuell', - es: '/noticias/justo-en', - ja: '/ニュース/現在' - } - }, - localeCookie: - process.env.NEXT_PUBLIC_USE_CASE === 'locale-cookie-false' - ? false - : { - // 200 days - maxAge: 200 * 24 * 60 * 60 - } -}); - -export type Pathnames = keyof typeof routing.pathnames; -export type Locale = (typeof routing.locales)[number]; diff --git a/type-export-test/src/server.ts b/type-export-test/src/server.ts deleted file mode 100644 index a2652a0d3..000000000 --- a/type-export-test/src/server.ts +++ /dev/null @@ -1,67 +0,0 @@ -import {headers} from 'next/headers'; -import {Formats} from 'next-intl'; -import {getRequestConfig} from 'next-intl/server'; -import {routing} from './routing'; - -export const formats = { - dateTime: { - medium: { - dateStyle: 'medium', - timeStyle: 'short', - hour12: false - } - }, - number: { - precise: { - maximumFractionDigits: 5 - } - }, - list: { - enumeration: { - style: 'long', - type: 'conjunction' - } - } -} satisfies Formats; - -export default getRequestConfig(async ({requestLocale}) => { - // This typically corresponds to the `[locale]` segment - let locale = await requestLocale; - - // Ensure that the incoming locale is valid - if (!locale || !routing.locales.includes(locale as any)) { - locale = routing.defaultLocale; - } - - const now = headers().get('x-now'); - const timeZone = headers().get('x-time-zone') ?? 'Europe/Vienna'; - const localeMessages = (await import(`../../messages/${locale}.json`)) - .default; - const messages = localeMessages; - - return { - locale, - now: now ? new Date(now) : undefined, - timeZone, - messages, - formats, - onError(error) { - if ( - error.message === - (process.env.NODE_ENV === 'production' - ? 'MISSING_MESSAGE' - : 'MISSING_MESSAGE: Could not resolve `missing` in `Index`.') - ) { - // Do nothing, this error is triggered on purpose - } else { - console.error(JSON.stringify(error.message)); - } - }, - getMessageFallback({key, namespace}) { - return ( - '`getMessageFallback` called for ' + - [namespace, key].filter((part) => part != null).join('.') - ); - } - }; -}); diff --git a/type-export-test/tsconfig.json b/type-export-test/tsconfig.json deleted file mode 100644 index b886b28b8..000000000 --- a/type-export-test/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "extends": "eslint-config-molindo/tsconfig.json", - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "skipLibCheck": true, - "noEmit": true, - "declaration": true, - "esModuleInterop": true, - "module": "ESNext", - "moduleResolution": "Bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["**/*.ts"], - "exclude": ["node_modules"] -} From 1d91e6d135f2e6fe6418ecc196cc1b775b4cec94 Mon Sep 17 00:00:00 2001 From: Jan Amann Date: Fri, 8 Nov 2024 10:48:47 +0100 Subject: [PATCH 7/7] cleanup: - Revert change in turbo.json - Add tests for async APIs and createNextIntlPlugin - Remove not needed type exports: StrictParams, InitializedLocaleCookieConfig --- .../src/type-portability-test.ts | 34 +++++++++++++++---- .../tsconfig.json | 6 ++-- .../src/navigation/react-client/index.tsx | 1 - .../src/navigation/shared/StrictParams.tsx | 1 - .../next-intl/src/routing/defineRouting.tsx | 1 - packages/next-intl/src/routing/index.tsx | 4 +-- turbo.json | 3 -- 7 files changed, 34 insertions(+), 16 deletions(-) diff --git a/examples/example-app-router-playground/src/type-portability-test.ts b/examples/example-app-router-playground/src/type-portability-test.ts index b22efdce0..18facf87d 100644 --- a/examples/example-app-router-playground/src/type-portability-test.ts +++ b/examples/example-app-router-playground/src/type-portability-test.ts @@ -14,6 +14,15 @@ import { useTimeZone, useTranslations } from 'next-intl'; +import createNextIntlPlugin from 'next-intl/plugin'; +import { + getFormatter, + getLocale, + getMessages, + getNow, + getTimeZone, + getTranslations +} from 'next-intl/server'; export function useExports() { const messages = useMessages(); @@ -33,14 +42,27 @@ export function useExports() { }; } -export const config = initializeConfig({ - locale: 'en' -}); +export async function asyncApis() { + const messages = await getMessages(); + const now = await getNow(); + const locale = await getLocale(); + const timezone = await getTimeZone(); + const formatter = await getFormatter(); + const translations = await getTranslations(); -export const translator = createTranslator({ - locale: 'en' -}); + return { + messages, + now, + locale, + timezone, + formatter, + translations + }; +} +export const withNextIntl = createNextIntlPlugin(); +export const config = initializeConfig({locale: 'en'}); +export const translator = createTranslator({locale: 'en'}); export const formatter = createFormatter({ locale: 'en', now: new Date(2022, 10, 6, 20, 20, 0, 0) diff --git a/examples/example-app-router-playground/tsconfig.json b/examples/example-app-router-playground/tsconfig.json index 5e96a894f..710349cbe 100644 --- a/examples/example-app-router-playground/tsconfig.json +++ b/examples/example-app-router-playground/tsconfig.json @@ -6,7 +6,6 @@ "allowJs": true, "skipLibCheck": true, "noEmit": true, - "declaration": true, "esModuleInterop": true, "module": "ESNext", "moduleResolution": "Bundler", @@ -21,7 +20,10 @@ ], "paths": { "@/*": ["./src/*"] - } + }, + + // See https://github.com/amannn/next-intl/pull/1509 + "declaration": true }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] diff --git a/packages/next-intl/src/navigation/react-client/index.tsx b/packages/next-intl/src/navigation/react-client/index.tsx index 3612fb62d..7d0372b79 100644 --- a/packages/next-intl/src/navigation/react-client/index.tsx +++ b/packages/next-intl/src/navigation/react-client/index.tsx @@ -2,7 +2,6 @@ export {default as createSharedPathnamesNavigation} from './createSharedPathname export {default as createLocalizedPathnamesNavigation} from './createLocalizedPathnamesNavigation'; export {default as createNavigation} from './createNavigation'; -export type {StrictParams} from '../shared/StrictParams'; export type {QueryParams} from '../shared/utils'; import type { diff --git a/packages/next-intl/src/navigation/shared/StrictParams.tsx b/packages/next-intl/src/navigation/shared/StrictParams.tsx index 3d7e4d24a..7606054c0 100644 --- a/packages/next-intl/src/navigation/shared/StrictParams.tsx +++ b/packages/next-intl/src/navigation/shared/StrictParams.tsx @@ -25,4 +25,3 @@ type StrictParams = Pathname extends `${string}[${string}` : never; export default StrictParams; -export type {StrictParams}; diff --git a/packages/next-intl/src/routing/defineRouting.tsx b/packages/next-intl/src/routing/defineRouting.tsx index f2adc09d4..d470db867 100644 --- a/packages/next-intl/src/routing/defineRouting.tsx +++ b/packages/next-intl/src/routing/defineRouting.tsx @@ -1,7 +1,6 @@ import {RoutingConfig} from './config'; import {DomainsConfig, LocalePrefixMode, Locales, Pathnames} from './types'; -export type {RoutingConfig}; export default function defineRouting< const AppLocales extends Locales, const AppLocalePrefixMode extends LocalePrefixMode = 'always', diff --git a/packages/next-intl/src/routing/index.tsx b/packages/next-intl/src/routing/index.tsx index 9101d2cdd..ac10d4159 100644 --- a/packages/next-intl/src/routing/index.tsx +++ b/packages/next-intl/src/routing/index.tsx @@ -4,5 +4,5 @@ export type { LocalePrefix, LocalePrefixMode } from './types'; -export type {InitializedLocaleCookieConfig} from './config'; -export {default as defineRouting, type RoutingConfig} from './defineRouting'; +export type {RoutingConfig} from './config'; +export {default as defineRouting} from './defineRouting'; diff --git a/turbo.json b/turbo.json index 27e12775f..49eabfe80 100644 --- a/turbo.json +++ b/turbo.json @@ -1,9 +1,6 @@ { "$schema": "https://turbo.build/schema.json", "tasks": { - "next-intl#test": { - "dependsOn": ["example-app-router-playground#lint"] - }, "build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**", "!.next/cache/**", "build/**"]