)
+ | ({ isReady: true } & PaQ);
+
+ export type NextRouter =
+ BaseRouter> &
+ Omit<
+ Router,
+ | "defaultLocale"
+ | "domainLocales"
+ | "isReady"
+ | "locale"
+ | "locales"
+ | "pathname"
+ | "push"
+ | "query"
+ | "replace"
+ | "route"
+ > & {
+ defaultLocale?: undefined;
+ domainLocales?: undefined;
+ locale?: Locale;
+ locales?: undefined;
+ push(
+ url: Route | StaticRoute | Omit,
+ as?: string,
+ options?: TransitionOptions
+ ): Promise;
+ replace(
+ url: Route | StaticRoute | Omit,
+ as?: string,
+ options?: TransitionOptions
+ ): Promise;
+ route: P;
+ };
+
+ export function useRouter(): NextRouter
;
+}
+",
+ ],
+]
+`;
+
+exports[`route generation pages and app directory generates routes 1`] = `
+[
+ [
+ "@types/nextjs-routes.d.ts",
+ "// 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: 2.2.2
+/* eslint-disable */
+
+// prettier-ignore
+declare module "nextjs-routes" {
+ import type {
+ GetServerSidePropsContext as NextGetServerSidePropsContext,
+ GetServerSidePropsResult as NextGetServerSidePropsResult
+ } from "next";
+
+ export type Route =
+ | StaticRoute<"/">
+ | DynamicRoute<"/[foo]", { "foo": string }>
+ | StaticRoute<"/bar">;
+
+ interface StaticRoute {
+ pathname: Pathname;
+ query?: Query | undefined;
+ hash?: string | null | undefined;
+ }
+
+ interface DynamicRoute {
+ pathname: Pathname;
+ query: Parameters & Query;
+ hash?: string | null | undefined;
+ }
+
+ interface Query {
+ [key: string]: string | string[] | undefined;
+ };
+
+ export type RoutedQuery = Extract<
+ Route,
+ { pathname: P }
+ >["query"];
+
+ export type Locale = undefined;
+
+ type Brand = K & { __brand: T };
+
+ /**
+ * A string that is a valid application route.
+ */
+ export type RouteLiteral = Brand
+
/**
* A typesafe utility function for generating paths in your application.
*
* route({ pathname: "/foos/[foo]", query: { foo: "bar" }}) will produce "/foos/bar".
*/
- export declare function route(r: Route): string;
+ export declare function route(r: Route): RouteLiteral;
/**
* Nearly identical to GetServerSidePropsContext from next, but further narrows
@@ -1357,7 +1600,7 @@ declare module "nextjs-routes" {
// prettier-ignore
declare module "next/link" {
- import type { Route } from "nextjs-routes";
+ import type { Route, RouteLiteral } from "nextjs-routes";;
import type { LinkProps as NextLinkProps } from "next/dist/client/link";
import type {
AnchorHTMLAttributes,
@@ -1372,7 +1615,7 @@ declare module "next/link" {
export interface LinkProps
extends Omit,
AnchorHTMLAttributes {
- href: Route | StaticRoute | Omit
+ href: Route | StaticRoute | Omit | RouteLiteral;
locale?: false;
}
@@ -1462,7 +1705,7 @@ exports[`route generation transforms windows paths 1`] = `
"@types/nextjs-routes.d.ts",
"// 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: 2.2.1
+// nextjs-routes version: 2.2.2
/* eslint-disable */
// prettier-ignore
@@ -1498,12 +1741,19 @@ declare module "nextjs-routes" {
export type Locale = undefined;
+ type Brand = K & { __brand: T };
+
+ /**
+ * A string that is a valid application route.
+ */
+ export type RouteLiteral = Brand
+
/**
* A typesafe utility function for generating paths in your application.
*
* route({ pathname: "/foos/[foo]", query: { foo: "bar" }}) will produce "/foos/bar".
*/
- export declare function route(r: Route): string;
+ export declare function route(r: Route): RouteLiteral;
/**
* Nearly identical to GetServerSidePropsContext from next, but further narrows
@@ -1535,7 +1785,7 @@ declare module "nextjs-routes" {
// prettier-ignore
declare module "next/link" {
- import type { Route } from "nextjs-routes";
+ import type { Route } from "nextjs-routes";;
import type { LinkProps as NextLinkProps } from "next/dist/client/link";
import type {
AnchorHTMLAttributes,
@@ -1550,7 +1800,7 @@ declare module "next/link" {
export interface LinkProps
extends Omit,
AnchorHTMLAttributes {
- href: Route | StaticRoute | Omit
+ href: Route | StaticRoute | Omit;
locale?: false;
}
@@ -1640,7 +1890,7 @@ exports[`route generation typescript 1`] = `
"@types/nextjs-routes.d.ts",
"// 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: 2.2.1
+// nextjs-routes version: 2.2.2
/* eslint-disable */
// prettier-ignore
@@ -1693,12 +1943,19 @@ declare module "nextjs-routes" {
export type Locale = undefined;
+ type Brand = K & { __brand: T };
+
+ /**
+ * A string that is a valid application route.
+ */
+ export type RouteLiteral = Brand
+
/**
* A typesafe utility function for generating paths in your application.
*
* route({ pathname: "/foos/[foo]", query: { foo: "bar" }}) will produce "/foos/bar".
*/
- export declare function route(r: Route): string;
+ export declare function route(r: Route): RouteLiteral;
/**
* Nearly identical to GetServerSidePropsContext from next, but further narrows
@@ -1730,7 +1987,7 @@ declare module "nextjs-routes" {
// prettier-ignore
declare module "next/link" {
- import type { Route } from "nextjs-routes";
+ import type { Route } from "nextjs-routes";;
import type { LinkProps as NextLinkProps } from "next/dist/client/link";
import type {
AnchorHTMLAttributes,
@@ -1745,7 +2002,7 @@ declare module "next/link" {
export interface LinkProps
extends Omit,
AnchorHTMLAttributes {
- href: Route | StaticRoute | Omit
+ href: Route | StaticRoute | Omit;
locale?: false;
}
diff --git a/packages/nextjs-routes/src/core.test.ts b/packages/nextjs-routes/src/core.test.ts
index 25d808e..b83e47e 100644
--- a/packages/nextjs-routes/src/core.test.ts
+++ b/packages/nextjs-routes/src/core.test.ts
@@ -79,7 +79,7 @@ describe("route generation", () => {
expect(writeFileSyncMock.mock.calls).toMatchSnapshot();
});
- describe("app directory (experimental)", () => {
+ describe("app directory", () => {
it("generates routes", () => {
// getAppRoutes
existsSyncMock
@@ -111,6 +111,23 @@ describe("route generation", () => {
});
});
+ describe("pages and app directory", () => {
+ it("generates routes", () => {
+ existsSyncMock
+ // getPageRoutes
+ .mockImplementationOnce(() => true)
+ // getAppRoutes
+ .mockImplementationOnce(() => true);
+ findFilesMock
+ // page routes
+ .mockReturnValueOnce(["pages/[foo].ts"])
+ // app routes
+ .mockReturnValueOnce(["app/bar/page.ts", "app/page.ts"]);
+ writeNextJSRoutes({});
+ expect(writeFileSyncMock.mock.calls).toMatchSnapshot();
+ });
+ });
+
describe("configuration", () => {
describe("pageExtensions", () => {
it("default", () => {
diff --git a/packages/nextjs-routes/src/core.ts b/packages/nextjs-routes/src/core.ts
index 010f9c6..b5e451c 100644
--- a/packages/nextjs-routes/src/core.ts
+++ b/packages/nextjs-routes/src/core.ts
@@ -7,7 +7,7 @@ import { findFiles, getAppDirectory, getPagesDirectory } from "./utils.js";
// by node 17+
// import pkg from "../package.json" assert { type: "json" };
const pkg = {
- version: "2.2.1",
+ version: "2.2.2",
};
type QueryType = "dynamic" | "catch-all" | "optional-catch-all";
@@ -82,7 +82,12 @@ function getQueryInterface(
return [`{ ${keys} }`, requiredKeys, optionalCatchAll];
}
-function generate(routes: Route[], config: NextJSRoutesOptions): string {
+interface GenerateConfig extends NextJSRoutesOptions {
+ usingAppDirectory: boolean;
+ usingPagesDirectory: boolean;
+}
+
+function generate(routes: Route[], config: GenerateConfig): string {
const i18n = config.i18n ?? {
defaultLocale: "",
domains: [],
@@ -145,12 +150,19 @@ declare module "nextjs-routes" {
: `\n | ${i18n.locales.map((x) => `"${x}"`).join("\n | ")}`
};
+ type Brand = K & { __brand: T };
+
+ /**
+ * A string that is a valid application route.
+ */
+ export type RouteLiteral = Brand
+
/**
* A typesafe utility function for generating paths in your application.
*
* route({ pathname: "/foos/[foo]", query: { foo: "bar" }}) will produce "/foos/bar".
*/
- export declare function route(r: Route): string;
+ export declare function route(r: Route): RouteLiteral;
/**
* Nearly identical to GetServerSidePropsContext from next, but further narrows
@@ -186,7 +198,13 @@ declare module "nextjs-routes" {
// prettier-ignore
declare module "next/link" {
- import type { Route } from "nextjs-routes";
+ ${(() => {
+ if (config.usingAppDirectory) {
+ return 'import type { Route, RouteLiteral } from "nextjs-routes";';
+ } else {
+ return 'import type { Route } from "nextjs-routes";';
+ }
+ })()};
import type { LinkProps as NextLinkProps } from "next/dist/client/link";
import type {
AnchorHTMLAttributes,
@@ -201,7 +219,15 @@ declare module "next/link" {
export interface LinkProps
extends Omit,
AnchorHTMLAttributes {
- href: Route | StaticRoute | Omit
+ href: ${(() => {
+ if (config.usingPagesDirectory && config.usingAppDirectory) {
+ return 'Route | StaticRoute | Omit | RouteLiteral';
+ } else if (config.usingPagesDirectory) {
+ return 'Route | StaticRoute | Omit';
+ } else {
+ return "StaticRoute | RouteLiteral";
+ }
+ })()};
locale?: ${!i18n.locales.length ? "false" : `Locale | false`};
}
@@ -300,11 +326,6 @@ export const logger: Pick = {
};
export interface NextJSRoutesOptions {
- /**
- * The file path indicating the output directory where the generated route types
- * should be written to (e.g.: "types").
- */
- outDir?: string | undefined;
/**
* Location of the Next.js project. Defaults to the current working directory.
*
@@ -317,6 +338,11 @@ export interface NextJSRoutesOptions {
* const withRoutes = nextRoutes({ dir: __dirname });
*/
dir?: string | undefined;
+ /**
+ * The file path indicating the output directory where the generated route types
+ * should be written to (e.g.: "types").
+ */
+ outDir?: string | undefined;
/**
* NextJS config option.
* https://nextjs.org/docs/api-reference/next.config.js/custom-page-extensions
@@ -395,6 +421,8 @@ export function getPageRoutes(files: string[], opts: Opts): string[] {
export function writeNextJSRoutes(options: NextJSRoutesOptions): void {
const defaultOptions = {
+ usingPagesDirectory: false,
+ usingAppDirectory: false,
dir: process.cwd(),
outDir: join(options.dir ?? process.cwd(), "@types"),
pageExtensions: ["tsx", "ts", "jsx", "js"],
@@ -411,6 +439,7 @@ export function writeNextJSRoutes(options: NextJSRoutesOptions): void {
directory: pagesDirectory,
});
files.push(...routes);
+ opts.usingPagesDirectory = true;
}
const appDirectory = getAppDirectory(opts.dir);
if (appDirectory) {
@@ -419,6 +448,7 @@ export function writeNextJSRoutes(options: NextJSRoutesOptions): void {
directory: appDirectory,
});
files.push(...routes);
+ opts.usingAppDirectory = true;
}
const outputFilepath = join(opts.outDir, "nextjs-routes.d.ts");
if (opts.outDir && !existsSync(opts.outDir)) {