Skip to content

Commit

Permalink
feat: Add support for permanentRedirectin navigation APIs (#850 by @…
Browse files Browse the repository at this point in the history
…polvallverdu)

Fixes #715

---------

Co-authored-by: Jan Amann <[email protected]>
  • Loading branch information
polvallverdu and amannn authored Feb 9, 2024
1 parent 5cb9506 commit 6508ddc
Show file tree
Hide file tree
Showing 11 changed files with 408 additions and 22 deletions.
4 changes: 4 additions & 0 deletions docs/pages/docs/routing/navigation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,10 @@ redirect({
</Tab>
</Tabs>

<Callout>
[`permanentRedirect`](https://nextjs.org/docs/app/api-reference/functions/permanentRedirect) is supported too.
</Callout>

### `getPathname`

If you need to construct a particular pathname based on a locale, you can call the `getPathname` function. This can for example be useful to retrieve a [canonical link](https://nextjs.org/docs/app/api-reference/functions/generate-metadata#alternates) for a page that accepts search params.
Expand Down
4 changes: 2 additions & 2 deletions packages/next-intl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@
},
{
"path": "dist/production/navigation.react-client.js",
"limit": "2.84 KB"
"limit": "2.89 KB"
},
{
"path": "dist/production/navigation.react-server.js",
"limit": "2.95 KB"
"limit": "3.01 KB"
},
{
"path": "dist/production/server.react-client.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import useLocale from '../../react-client/useLocale';
import {LocalePrefix, ParametersExceptFirst} from '../../shared/types';
import basePermanentRedirect from '../shared/basePermanentRedirect';

export default function clientPermanentRedirect(
params: {localePrefix?: LocalePrefix; pathname: string},
...args: ParametersExceptFirst<typeof basePermanentRedirect>
) {
let locale;
try {
// eslint-disable-next-line react-hooks/rules-of-hooks -- Reading from context here is fine, since `redirect` should be called during render
locale = useLocale();
} catch (e) {
throw new Error(
process.env.NODE_ENV !== 'production'
? '`permanentRedirect()` can only be called during render. To redirect in an event handler or similar, you can use `useRouter()` instead.'
: undefined
);
}

return basePermanentRedirect({...params, locale}, ...args);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
HrefOrUrlObjectWithParams
} from '../shared/utils';
import ClientLink from './ClientLink';
import clientPermanentRedirect from './clientPermanentRedirect';
import clientRedirect from './clientRedirect';
import useBasePathname from './useBasePathname';
import useBaseRouter from './useBaseRouter';
Expand Down Expand Up @@ -89,6 +90,16 @@ export default function createLocalizedPathnamesNavigation<
return clientRedirect({...opts, pathname: resolvedHref}, ...args);
}

function permanentRedirect<Pathname extends keyof PathnamesConfig>(
href: HrefOrHrefWithParams<Pathname>,
...args: ParametersExceptFirst<typeof clientPermanentRedirect>
) {
// eslint-disable-next-line react-hooks/rules-of-hooks -- Reading from context here is fine, since `redirect` should be called during render
const locale = useTypedLocale();
const resolvedHref = getPathname({href, locale});
return clientPermanentRedirect({...opts, pathname: resolvedHref}, ...args);
}

function useRouter() {
const baseRouter = useBaseRouter();
const defaultLocale = useTypedLocale();
Expand Down Expand Up @@ -156,6 +167,7 @@ export default function createLocalizedPathnamesNavigation<
return {
Link: LinkWithRef,
redirect,
permanentRedirect,
usePathname,
useRouter,
getPathname
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ParametersExceptFirst
} from '../../shared/types';
import ClientLink from './ClientLink';
import clientPermanentRedirect from './clientPermanentRedirect';
import clientRedirect from './clientRedirect';
import useBasePathname from './useBasePathname';
import useBaseRouter from './useBaseRouter';
Expand Down Expand Up @@ -37,6 +38,13 @@ export default function createSharedPathnamesNavigation<
return clientRedirect({...opts, pathname}, ...args);
}

function permanentRedirect(
pathname: string,
...args: ParametersExceptFirst<typeof clientPermanentRedirect>
) {
return clientPermanentRedirect({...opts, pathname}, ...args);
}

function usePathname(): string {
// @ts-expect-error -- Mirror the behavior from Next.js, where `null` is returned when `usePathname` is used outside of Next, but the types indicate that a string is always returned.
return useBasePathname();
Expand All @@ -45,6 +53,7 @@ export default function createSharedPathnamesNavigation<
return {
Link: LinkWithRef,
redirect,
permanentRedirect,
usePathname,
useRouter: useBaseRouter
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
normalizeNameOrNameWithParams
} from '../shared/utils';
import ServerLink from './ServerLink';
import serverPermanentRedirect from './serverPermanentRedirect';
import serverRedirect from './serverRedirect';

export default function createLocalizedPathnamesNavigation<
Expand Down Expand Up @@ -68,6 +69,15 @@ export default function createLocalizedPathnamesNavigation<
return serverRedirect({localePrefix, pathname}, ...args);
}

function permanentRedirect<Pathname extends keyof PathnamesConfig>(
href: HrefOrHrefWithParams<Pathname>,
...args: ParametersExceptFirst<typeof serverPermanentRedirect>
) {
const locale = getRequestLocale();
const pathname = getPathname({href, locale});
return serverPermanentRedirect({localePrefix, pathname}, ...args);
}

function getPathname({
href,
locale
Expand All @@ -93,6 +103,7 @@ export default function createLocalizedPathnamesNavigation<
return {
Link,
redirect,
permanentRedirect,
getPathname,
usePathname: notSupported('usePathname'),
useRouter: notSupported('useRouter')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ParametersExceptFirst
} from '../../shared/types';
import ServerLink from './ServerLink';
import serverPermanentRedirect from './serverPermanentRedirect';
import serverRedirect from './serverRedirect';

export default function createSharedPathnamesNavigation<
Expand All @@ -29,9 +30,17 @@ export default function createSharedPathnamesNavigation<
return serverRedirect({...opts, pathname}, ...args);
}

function permanentRedirect(
pathname: string,
...args: ParametersExceptFirst<typeof serverPermanentRedirect>
) {
return serverPermanentRedirect({...opts, pathname}, ...args);
}

return {
Link,
redirect,
permanentRedirect,
usePathname: notSupported('usePathname'),
useRouter: notSupported('useRouter')
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {getRequestLocale} from '../../server/react-server/RequestLocale';
import {LocalePrefix, ParametersExceptFirst} from '../../shared/types';
import basePermanentRedirect from '../shared/basePermanentRedirect';

export default function serverPermanentRedirect(
params: {pathname: string; localePrefix?: LocalePrefix},
...args: ParametersExceptFirst<typeof basePermanentRedirect>
) {
const locale = getRequestLocale();
return basePermanentRedirect({...params, locale}, ...args);
}
22 changes: 22 additions & 0 deletions packages/next-intl/src/navigation/shared/basePermanentRedirect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {permanentRedirect as nextPermanentRedirect} from 'next/navigation';
import {
AllLocales,
LocalePrefix,
ParametersExceptFirst
} from '../../shared/types';
import {prefixPathname} from '../../shared/utils';

export default function basePermanentRedirect(
params: {
pathname: string;
locale: AllLocales[number];
localePrefix?: LocalePrefix;
},
...args: ParametersExceptFirst<typeof nextPermanentRedirect>
) {
const localizedPathname =
params.localePrefix === 'never'
? params.pathname
: prefixPathname(params.locale, params.pathname);
return nextPermanentRedirect(localizedPathname, ...args);
}
Loading

0 comments on commit 6508ddc

Please sign in to comment.