From 285f3644394fd5112b19d2312b3cdefa9af72a55 Mon Sep 17 00:00:00 2001 From: Tate Thurston Date: Wed, 20 Nov 2024 21:51:58 -0800 Subject: [PATCH] Fix #206 --- CHANGELOG.md | 1 + examples/app/@types/nextjs-routes.d.ts | 2 +- examples/cjs/types/nextjs-routes.d.ts | 2 +- examples/intl/@types/nextjs-routes.d.ts | 2 +- examples/typescript/types/nextjs-routes.d.ts | 2 +- packages/e2e/@types/nextjs-routes.d.ts | 2 +- packages/nextjs-routes/package.json | 2 +- .../src/__snapshots__/core.test.ts.snap | 26 ++-- packages/nextjs-routes/src/core.ts | 2 +- packages/nextjs-routes/src/index.test.ts | 121 ++++++------------ packages/nextjs-routes/src/index.ts | 4 +- 11 files changed, 62 insertions(+), 104 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d110d6..8630437 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 2.2.4 - CLI invocation now reads next.config.js or next.config.mjs. +- Fix `route`'s handling of query keys whose value is `undefined`. Fixes #206. Thanks @sleepdotexe! ## 2.2.3 diff --git a/examples/app/@types/nextjs-routes.d.ts b/examples/app/@types/nextjs-routes.d.ts index c615559..e1b4218 100644 --- a/examples/app/@types/nextjs-routes.d.ts +++ b/examples/app/@types/nextjs-routes.d.ts @@ -1,6 +1,6 @@ // 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore diff --git a/examples/cjs/types/nextjs-routes.d.ts b/examples/cjs/types/nextjs-routes.d.ts index fe494da..3167c2b 100644 --- a/examples/cjs/types/nextjs-routes.d.ts +++ b/examples/cjs/types/nextjs-routes.d.ts @@ -1,6 +1,6 @@ // 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore diff --git a/examples/intl/@types/nextjs-routes.d.ts b/examples/intl/@types/nextjs-routes.d.ts index 578f3ca..71c2a0f 100644 --- a/examples/intl/@types/nextjs-routes.d.ts +++ b/examples/intl/@types/nextjs-routes.d.ts @@ -1,6 +1,6 @@ // 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore diff --git a/examples/typescript/types/nextjs-routes.d.ts b/examples/typescript/types/nextjs-routes.d.ts index f6c40d6..66c029e 100644 --- a/examples/typescript/types/nextjs-routes.d.ts +++ b/examples/typescript/types/nextjs-routes.d.ts @@ -1,6 +1,6 @@ // 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore diff --git a/packages/e2e/@types/nextjs-routes.d.ts b/packages/e2e/@types/nextjs-routes.d.ts index 841f5ae..decfc9a 100644 --- a/packages/e2e/@types/nextjs-routes.d.ts +++ b/packages/e2e/@types/nextjs-routes.d.ts @@ -1,6 +1,6 @@ // 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore diff --git a/packages/nextjs-routes/package.json b/packages/nextjs-routes/package.json index a33239b..87d986d 100644 --- a/packages/nextjs-routes/package.json +++ b/packages/nextjs-routes/package.json @@ -1,6 +1,6 @@ { "name": "nextjs-routes", - "version": "2.2.4-rc.1", + "version": "2.2.4", "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 8ab97a9..18aaedf 100644 --- a/packages/nextjs-routes/src/__snapshots__/core.test.ts.snap +++ b/packages/nextjs-routes/src/__snapshots__/core.test.ts.snap @@ -6,7 +6,7 @@ exports[`route generation 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore @@ -261,7 +261,7 @@ exports[`route generation app directory handles intercept 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore @@ -510,7 +510,7 @@ exports[`route generation app directory handles parallel 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore @@ -760,7 +760,7 @@ exports[`route generation app directory handles 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.2 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore @@ -1009,7 +1009,7 @@ exports[`route generation configuration i18n 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore @@ -1209,7 +1209,7 @@ exports[`route generation configuration outDir 1`] = ` "src/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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore @@ -1385,7 +1385,7 @@ exports[`route generation configuration pageExtensions configured 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore @@ -1563,7 +1563,7 @@ exports[`route generation configuration pageExtensions default 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore @@ -1739,7 +1739,7 @@ exports[`route generation dedupes 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore @@ -1915,7 +1915,7 @@ exports[`route generation no 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore @@ -2091,7 +2091,7 @@ 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 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore @@ -2342,7 +2342,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.2 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore @@ -2518,7 +2518,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.2 +// nextjs-routes version: 2.2.4 /* eslint-disable */ // prettier-ignore diff --git a/packages/nextjs-routes/src/core.ts b/packages/nextjs-routes/src/core.ts index cd91afd..4d0833f 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.2", + version: "2.2.4", }; type QueryType = "dynamic" | "catch-all" | "optional-catch-all"; diff --git a/packages/nextjs-routes/src/index.test.ts b/packages/nextjs-routes/src/index.test.ts index 72adefb..fd282f5 100644 --- a/packages/nextjs-routes/src/index.test.ts +++ b/packages/nextjs-routes/src/index.test.ts @@ -1,92 +1,49 @@ import { route } from "./index.js"; describe(route, () => { - it("generates paths", () => { - // static - expect(route({ pathname: "/404" })).toEqual("/404"); - expect(route({ pathname: "/settings/about" })).toEqual("/settings/about"); - // dynamic - expect(route({ pathname: "/foos/[foo]", query: { foo: "bar" } })).toEqual( - "/foos/bar", - ); - expect( - route({ - pathname: "/foos/[foo]/bars/[bar]", - query: { foo: "bar", bar: "baz" }, - }), - ).toEqual("/foos/bar/bars/baz"); - expect( - route({ - pathname: "/[foo]/[bar]/[baz]", - query: { foo: "foo", bar: "bar", baz: "baz" }, - }), - ).toEqual("/foo/bar/baz"); - // catch all - expect( - route({ pathname: "/[...segments]", query: { segments: ["foo"] } }), - ).toEqual("/foo"); - expect( - route({ - pathname: "/[...segments]", - query: { segments: ["foo", "bar"] }, - }), - ).toEqual("/foo/bar"); - // optional catch all - expect( - route({ pathname: "/[[...segments]]", query: { segments: [] } }), - ).toEqual("/"); - expect( - route({ pathname: "/[[...segments]]", query: { segments: undefined } }), - ).toEqual("/"); - expect( - route({ - pathname: "/[[...segments]]/foos", - query: { segments: undefined }, - }), - ).toEqual("/foos"); - // query params - expect( - route({ pathname: "/foos/[foo]", query: { foo: "foo", bar: "bar" } }), - ).toEqual("/foos/foo?bar=bar"); - expect( - route({ - pathname: "/foos/[foo]", - query: { foo: "foo", bar: "bar", baz: ["1", "2", "3"] }, - }), - ).toEqual("/foos/foo?bar=bar&baz=1&baz=2&baz=3"); - expect( - route({ - pathname: "/foos/[foo]", - query: { foo: "foo", bar: "bar", baz: ["1", "2", "3"] }, - hash: "foo", - }), - ).toEqual("/foos/foo?bar=bar&baz=1&baz=2&baz=3#foo"); + describe("generates paths", () => { + // prettier-ignore + test.each([ + [{ pathname: "/404" }, "/404"], + [{ pathname: "/settings/about" }, "/settings/about"], + // dynamic + [{ pathname: "/foos/[foo]", query: { foo: "bar" } }, "/foos/bar"], + [{ pathname: "/foos/[foo]/bars/[bar]", query: { foo: "bar", bar: "baz" } }, "/foos/bar/bars/baz"], + [{ pathname: "/[foo]/[bar]/[baz]", query: { foo: "foo", bar: "bar", baz: "baz" } }, "/foo/bar/baz"], + // catch all + [{ pathname: "/[...segments]", query: { segments: ["foo"] } }, "/foo"], + [{ pathname: "/[...segments]", query: { segments: ["foo", "bar"] } }, "/foo/bar"], + // optional catch all + [{ pathname: "/[[...segments]]", query: { segments: [] } }, "/"], + [{ pathname: "/[[...segments]]", query: { segments: undefined } }, "/"], + [{ pathname: "/[[...segments]]/foos", query: { segments: undefined } }, "/foos"], + // query params + [{ pathname: "/foos/[foo]", query: { foo: "foo", bar: "bar" } }, "/foos/foo?bar=bar"], + [{ pathname: "/foos/[foo]", query: { foo: "foo", bar: "bar", baz: ["1", "2", "3"] } }, "/foos/foo?bar=bar&baz=1&baz=2&baz=3"], + [{ pathname: "/foos/[foo]", query: { foo: "foo", bar: "bar", baz: ["1", "2", "3"] }, hash: "foo" }, "/foos/foo?bar=bar&baz=1&baz=2&baz=3#foo"], + [{ pathname: "/foos/[foo]", query: { foo: "foo", bar: undefined, baz: '', foobar: '' } }, "/foos/foo?baz=&foobar="], + ])("generates paths for %o", (input, expected) => { + expect(route(input)).toEqual(expected); + }); }); describe("options", () => { describe("trailingSlash", () => { - it("when true", () => { - expect( - route({ pathname: "/settings/about" }, { trailingSlash: true }), - ).toEqual("/settings/about/"); - expect( - route( - { pathname: "/foos/[foo]", query: { foo: "bar" } }, - { trailingSlash: true }, - ), - ).toEqual("/foos/bar/"); - }); - it("when false", () => { - expect( - route({ pathname: "/settings/about" }, { trailingSlash: false }), - ).toEqual("/settings/about"); - expect( - route( - { pathname: "/foos/[foo]", query: { foo: "bar" } }, - { trailingSlash: false }, - ), - ).toEqual("/foos/bar"); - }); + describe.each([ + ["/settings/about", undefined, true, "/settings/about/"], + ["/settings/about", undefined, false, "/settings/about"], + ["/foos/[foo]", { foo: "bar" }, true, "/foos/bar/"], + ["/foos/[foo]", { foo: "bar" }, false, "/foos/bar"], + ])( + "route(%p, { trailingSlash: %p })", + (pathname, query, trailingSlash, expectedResult) => { + it(`returns ${expectedResult}`, () => { + expect(route({ pathname, query }, { trailingSlash })).toEqual( + expectedResult, + ); + }); + }, + ); }); }); }); diff --git a/packages/nextjs-routes/src/index.ts b/packages/nextjs-routes/src/index.ts index 0adb55d..e932462 100644 --- a/packages/nextjs-routes/src/index.ts +++ b/packages/nextjs-routes/src/index.ts @@ -64,8 +64,8 @@ export function route( const value = r.query[key]; if (Array.isArray(value)) { value.forEach((val) => search.append(key, val)); - } else { - search.append(key, value as string); + } else if (value !== undefined) { + search.append(key, value); } } }