diff --git a/README.md b/README.md index 6b9ce0b..be7e8f0 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,8 @@ If you want a type for all possible `pathname`s you can achieve this via `Route` ```ts import type { Route } from "nextjs-routes"; + +// '/' | '/foos/[foo]' | 'other-route' | ... type Pathname = Route["pathname"]; ``` @@ -192,9 +194,17 @@ type Pathname = Route["pathname"]; If you want to use the generated `Query` for a given `Route`, you can import it from `nextjs-routes`: ```ts +// Query | Query & { foo: string } | ... import type { RoutedQuery } from "nextjs-routes"; ``` +By default, `query` will be typed as the union of all possible query parameters defined by your application routes. If you'd like to narrow the type to fewer routes or a single page, you can supply the path as a type argument: + +```ts +// Query & { foo: string } +type FooRouteQuery = RoutedQuery<"/foos/[foo]">; +``` + ### GetServerSidePropsContext If you're using `getServerSideProps` consider using `GetServerSidePropsContext` from nextjs-routes. This is nearly identical to `GetServerSidePropsContext` from next, but further narrows types based on nextjs-route's route data. diff --git a/examples/app/@types/nextjs-routes.d.ts b/examples/app/@types/nextjs-routes.d.ts index 31e93c2..ade9588 100644 --- a/examples/app/@types/nextjs-routes.d.ts +++ b/examples/app/@types/nextjs-routes.d.ts @@ -175,3 +175,47 @@ declare module "next/router" { export function useRouter

(): NextRouter

; } + +// prettier-ignore +declare module "next/navigation" { + export * from "next/dist/client/components/navigation"; + import type { RoutedQuery, RouteLiteral } from "nextjs-routes"; + +/** + * A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook + * that lets you read the current URL's pathname. + * + * @example + * ```ts + * "use client" + * import { usePathname } from 'next/navigation' + * + * export default function Page() { + * const pathname = usePathname() // returns "/dashboard" on /dashboard?foo=bar + * // ... + * } + * ``` + * + * Read more: [Next.js Docs: `usePathname`](https://nextjs.org/docs/app/api-reference/functions/use-pathname) + */ + export function usePathname(): RouteLiteral; + +/** + * A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook + * that lets you read a route's dynamic params filled in by the current URL. + * + * @example + * ```ts + * "use client" + * import { useParams } from 'next/navigation' + * + * export default function Page() { + * // on /dashboard/[team] where pathname is /dashboard/nextjs + * const { team } = useParams() // team === "nextjs" + * } + * ``` + * + * Read more: [Next.js Docs: `useParams`](https://nextjs.org/docs/app/api-reference/functions/use-params) + */ + export function useParams(): RoutedQuery; +} diff --git a/packages/nextjs-routes/package.json b/packages/nextjs-routes/package.json index 82482a7..d878d56 100644 --- a/packages/nextjs-routes/package.json +++ b/packages/nextjs-routes/package.json @@ -1,6 +1,6 @@ { "name": "nextjs-routes", - "version": "2.2.2-rc.1", + "version": "2.2.2-rc.2", "description": "Type safe routing for Next.js", "license": "MIT", "author": "Tate ", diff --git a/packages/nextjs-routes/src/__snapshots__/core.test.ts.snap b/packages/nextjs-routes/src/__snapshots__/core.test.ts.snap index 6dd63e4..acb09b6 100644 --- a/packages/nextjs-routes/src/__snapshots__/core.test.ts.snap +++ b/packages/nextjs-routes/src/__snapshots__/core.test.ts.snap @@ -186,6 +186,50 @@ declare module "next/router" { export function useRouter

(): NextRouter

; } + +// prettier-ignore +declare module "next/navigation" { + export * from "next/dist/client/components/navigation"; + import type { RoutedQuery, RouteLiteral } from "nextjs-routes"; + +/** + * A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook + * that lets you read the current URL's pathname. + * + * @example + * \`\`\`ts + * "use client" + * import { usePathname } from 'next/navigation' + * + * export default function Page() { + * const pathname = usePathname() // returns "/dashboard" on /dashboard?foo=bar + * // ... + * } + * \`\`\` + * + * Read more: [Next.js Docs: \`usePathname\`](https://nextjs.org/docs/app/api-reference/functions/use-pathname) + */ + export function usePathname(): RouteLiteral; + +/** + * A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook + * that lets you read a route's dynamic params filled in by the current URL. + * + * @example + * \`\`\`ts + * "use client" + * import { useParams } from 'next/navigation' + * + * export default function Page() { + * // on /dashboard/[team] where pathname is /dashboard/nextjs + * const { team } = useParams() // team === "nextjs" + * } + * \`\`\` + * + * Read more: [Next.js Docs: \`useParams\`](https://nextjs.org/docs/app/api-reference/functions/use-params) + */ + export function useParams(): RoutedQuery; +} ", ], ] @@ -371,6 +415,50 @@ declare module "next/router" { export function useRouter

(): NextRouter

; } + +// prettier-ignore +declare module "next/navigation" { + export * from "next/dist/client/components/navigation"; + import type { RoutedQuery, RouteLiteral } from "nextjs-routes"; + +/** + * A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook + * that lets you read the current URL's pathname. + * + * @example + * \`\`\`ts + * "use client" + * import { usePathname } from 'next/navigation' + * + * export default function Page() { + * const pathname = usePathname() // returns "/dashboard" on /dashboard?foo=bar + * // ... + * } + * \`\`\` + * + * Read more: [Next.js Docs: \`usePathname\`](https://nextjs.org/docs/app/api-reference/functions/use-pathname) + */ + export function usePathname(): RouteLiteral; + +/** + * A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook + * that lets you read a route's dynamic params filled in by the current URL. + * + * @example + * \`\`\`ts + * "use client" + * import { useParams } from 'next/navigation' + * + * export default function Page() { + * // on /dashboard/[team] where pathname is /dashboard/nextjs + * const { team } = useParams() // team === "nextjs" + * } + * \`\`\` + * + * Read more: [Next.js Docs: \`useParams\`](https://nextjs.org/docs/app/api-reference/functions/use-params) + */ + export function useParams(): RoutedQuery; +} ", ], ] @@ -1694,6 +1782,50 @@ declare module "next/router" { export function useRouter

(): NextRouter

; } + +// prettier-ignore +declare module "next/navigation" { + export * from "next/dist/client/components/navigation"; + import type { RoutedQuery, RouteLiteral } from "nextjs-routes"; + +/** + * A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook + * that lets you read the current URL's pathname. + * + * @example + * \`\`\`ts + * "use client" + * import { usePathname } from 'next/navigation' + * + * export default function Page() { + * const pathname = usePathname() // returns "/dashboard" on /dashboard?foo=bar + * // ... + * } + * \`\`\` + * + * Read more: [Next.js Docs: \`usePathname\`](https://nextjs.org/docs/app/api-reference/functions/use-pathname) + */ + export function usePathname(): RouteLiteral; + +/** + * A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook + * that lets you read a route's dynamic params filled in by the current URL. + * + * @example + * \`\`\`ts + * "use client" + * import { useParams } from 'next/navigation' + * + * export default function Page() { + * // on /dashboard/[team] where pathname is /dashboard/nextjs + * const { team } = useParams() // team === "nextjs" + * } + * \`\`\` + * + * Read more: [Next.js Docs: \`useParams\`](https://nextjs.org/docs/app/api-reference/functions/use-params) + */ + export function useParams(): RoutedQuery; +} ", ], ] diff --git a/packages/nextjs-routes/src/core.ts b/packages/nextjs-routes/src/core.ts index b5e451c..ba22232 100644 --- a/packages/nextjs-routes/src/core.ts +++ b/packages/nextjs-routes/src/core.ts @@ -94,7 +94,7 @@ function generate(routes: Route[], config: GenerateConfig): string { localeDetection: false, locales: [], }; - return `\ + let output = `\ // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. // This file will be automatically regenerated when your Next.js server is running. // nextjs-routes version: ${pkg.version} @@ -313,6 +313,56 @@ declare module "next/router" { export function useRouter

(): NextRouter

; } `; + + if (config.usingAppDirectory) { + output += `\ + +// prettier-ignore +declare module "next/navigation" { + export * from "next/dist/client/components/navigation"; + import type { RoutedQuery, RouteLiteral } from "nextjs-routes"; + +/** + * A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook + * that lets you read the current URL's pathname. + * + * @example + * \`\`\`ts + * "use client" + * import { usePathname } from 'next/navigation' + * + * export default function Page() { + * const pathname = usePathname() // returns "/dashboard" on /dashboard?foo=bar + * // ... + * } + * \`\`\` + * + * Read more: [Next.js Docs: \`usePathname\`](https://nextjs.org/docs/app/api-reference/functions/use-pathname) + */ + export function usePathname(): RouteLiteral; + +/** + * A [Client Component](https://nextjs.org/docs/app/building-your-application/rendering/client-components) hook + * that lets you read a route's dynamic params filled in by the current URL. + * + * @example + * \`\`\`ts + * "use client" + * import { useParams } from 'next/navigation' + * + * export default function Page() { + * // on /dashboard/[team] where pathname is /dashboard/nextjs + * const { team } = useParams() // team === "nextjs" + * } + * \`\`\` + * + * Read more: [Next.js Docs: \`useParams\`](https://nextjs.org/docs/app/api-reference/functions/use-params) + */ + export function useParams(): RoutedQuery; +} +`; + } + return output; } function print(x: unknown, indent: number): string {