diff --git a/.changeset/404-root-with-path.md b/.changeset/404-root-with-path.md new file mode 100644 index 0000000000..207bb2b5b2 --- /dev/null +++ b/.changeset/404-root-with-path.md @@ -0,0 +1,5 @@ +--- +"@remix-run/router": patch +--- + +Allow 404 detection to leverage root route error boundary if path contains a URL segment diff --git a/contributors.yml b/contributors.yml index 36102cc500..8f0a2cfa64 100644 --- a/contributors.yml +++ b/contributors.yml @@ -237,5 +237,6 @@ - xcsnowcity - yionr - yuleicul +- yracnet - zheng-chuang - sgrishchenko diff --git a/packages/router/__tests__/router-test.ts b/packages/router/__tests__/router-test.ts index 8b05062659..edc7109df7 100644 --- a/packages/router/__tests__/router-test.ts +++ b/packages/router/__tests__/router-test.ts @@ -4823,6 +4823,85 @@ describe("a router", () => { }); }); + it("handles 404 routes when the root route contains a path (initialization)", () => { + let t = setup({ + routes: [ + { + id: "root", + path: "/path", + children: [ + { + index: true, + }, + ], + }, + ], + initialEntries: ["/junk"], + }); + expect(t.router.state).toMatchObject({ + errors: { + root: new ErrorResponseImpl( + 404, + "Not Found", + new Error('No route matches URL "/junk"'), + true + ), + }, + initialized: true, + location: { + pathname: "/junk", + }, + matches: [ + { + route: { + id: "root", + }, + }, + ], + }); + }); + + it("handles 404 routes when the root route contains a path (navigation)", () => { + let t = setup({ + routes: [ + { + id: "root", + path: "/path", + children: [ + { + index: true, + }, + ], + }, + ], + initialEntries: ["/path"], + }); + expect(t.router.state).toMatchObject({ + errors: null, + }); + t.navigate("/junk"); + expect(t.router.state).toMatchObject({ + errors: { + root: new ErrorResponseImpl( + 404, + "Not Found", + new Error('No route matches URL "/junk"'), + true + ), + }, + location: { + pathname: "/junk", + }, + matches: [ + { + route: { + id: "root", + }, + }, + ], + }); + }); + it("converts formData to URLSearchParams for unspecified formMethod", async () => { let t = setup({ routes: TASK_ROUTES, diff --git a/packages/router/router.ts b/packages/router/router.ts index a611128fe9..5e7c01518c 100644 --- a/packages/router/router.ts +++ b/packages/router/router.ts @@ -4074,9 +4074,12 @@ function getShortCircuitMatches(routes: AgnosticDataRouteObject[]): { route: AgnosticDataRouteObject; } { // Prefer a root layout route if present, otherwise shim in a route object - let route = routes.find((r) => r.index || !r.path || r.path === "/") || { - id: `__shim-error-route__`, - }; +let route = + routes.length === 1 + ? routes[0] + : routes.find((r) => r.index || !r.path || r.path === "/") || { + id: `__shim-error-route__`, + }; return { matches: [