From 072311f8b00d165bd81ea4fbe3678c07abe187d7 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Mon, 5 Dec 2022 15:56:01 -0500 Subject: [PATCH 1/5] fix URL creation in cloudflare pages --- .changeset/new-kiwis-confess.md | 5 ++++ packages/router/history.ts | 6 +++-- packages/router/router.ts | 47 +++++++++++++++++++++------------ 3 files changed, 39 insertions(+), 19 deletions(-) create mode 100644 .changeset/new-kiwis-confess.md diff --git a/.changeset/new-kiwis-confess.md b/.changeset/new-kiwis-confess.md new file mode 100644 index 0000000000..8816d1b758 --- /dev/null +++ b/.changeset/new-kiwis-confess.md @@ -0,0 +1,5 @@ +--- +"@remix-run/router": patch +--- + +Fix URL creation in Cloudflare Pages or other non-browser-environment diff --git a/packages/router/history.ts b/packages/router/history.ts index 56779c8f9d..0826c6542f 100644 --- a/packages/router/history.ts +++ b/packages/router/history.ts @@ -544,7 +544,7 @@ export function parsePath(path: string): Partial { return parsedPath; } -export function createURL(location: Location | string): URL { +export function createClientSideURL(location: Location | string): URL { // window.location.origin is "null" (the literal string value) in Firefox // under certain conditions, notably when serving from a local HTML file // See https://bugzilla.mozilla.org/show_bug.cgi?id=878297 @@ -643,7 +643,9 @@ function getUrlBasedHistory( }, encodeLocation(to) { // Encode a Location the same way window.location would - let url = createURL(typeof to === "string" ? to : createPath(to)); + let url = createClientSideURL( + typeof to === "string" ? to : createPath(to) + ); return { pathname: url.pathname, search: url.search, diff --git a/packages/router/router.ts b/packages/router/router.ts index 7d3b1d3f76..c142843e42 100644 --- a/packages/router/router.ts +++ b/packages/router/router.ts @@ -3,7 +3,7 @@ import { Action as HistoryAction, createLocation, createPath, - createURL, + createClientSideURL, parsePath, } from "./history"; import type { @@ -913,7 +913,7 @@ export function createRouter(init: RouterInit): Router { // Create a controller/Request for this navigation pendingNavigationController = new AbortController(); - let request = createRequest( + let request = createClientSideRequest( location, pendingNavigationController.signal, opts && opts.submission @@ -954,7 +954,7 @@ export function createRouter(init: RouterInit): Router { loadingNavigation = navigation; // Create a GET request for the loaders - request = createRequest(request.url, request.signal); + request = new Request(request.url, { signal: request.signal }); } // Call loaders @@ -1299,7 +1299,11 @@ export function createRouter(init: RouterInit): Router { // Call the action for the fetcher let abortController = new AbortController(); - let fetchRequest = createRequest(path, abortController.signal, submission); + let fetchRequest = createClientSideRequest( + path, + abortController.signal, + submission + ); fetchControllers.set(key, abortController); let actionResult = await callLoaderOrAction( @@ -1346,7 +1350,7 @@ export function createRouter(init: RouterInit): Router { // Start the data load for current matches, or the next location if we're // in the middle of a navigation let nextLocation = state.navigation.location || state.location; - let revalidationRequest = createRequest( + let revalidationRequest = createClientSideRequest( nextLocation, abortController.signal ); @@ -1501,7 +1505,7 @@ export function createRouter(init: RouterInit): Router { // Call the loader for this fetcher route match let abortController = new AbortController(); - let fetchRequest = createRequest(path, abortController.signal); + let fetchRequest = createClientSideRequest(path, abortController.signal); fetchControllers.set(key, abortController); let result: DataResult = await callLoaderOrAction( "loader", @@ -1675,7 +1679,7 @@ export function createRouter(init: RouterInit): Router { ...fetchersToLoad.map(([, href, match, fetchMatches]) => callLoaderOrAction( "loader", - createRequest(href, request.signal), + createClientSideRequest(href, request.signal), match, fetchMatches, router.basename @@ -2120,7 +2124,7 @@ export function unstable_createStaticHandler( if (!actionMatch.route.action) { let error = getInternalRouterError(405, { method: request.method, - pathname: createURL(request.url).pathname, + pathname: new URL(request.url).pathname, routeId: actionMatch.route.id, }); if (isRouteRequest) { @@ -2206,7 +2210,7 @@ export function unstable_createStaticHandler( } // Create a GET request for the loaders - let loaderRequest = createRequest(request.url, request.signal); + let loaderRequest = new Request(request.url, { signal: request.signal }); let context = await loadRouteData(loaderRequest, matches); return { @@ -2240,7 +2244,7 @@ export function unstable_createStaticHandler( if (isRouteRequest && !routeMatch?.route.loader) { throw getInternalRouterError(400, { method: request.method, - pathname: createURL(request.url).pathname, + pathname: new URL(request.url).pathname, routeId: routeMatch?.route.id, }); } @@ -2531,9 +2535,9 @@ function shouldRevalidateLoader( isRevalidationRequired: boolean, actionResult: DataResult | undefined ) { - let currentUrl = createURL(currentLocation); + let currentUrl = createClientSideURL(currentLocation); let currentParams = currentMatch.params; - let nextUrl = createURL(location); + let nextUrl = createClientSideURL(location); let nextParams = match.params; // This is the default implementation as to when we revalidate. If the route @@ -2624,7 +2628,10 @@ async function callLoaderOrAction( ); // Check if this an external redirect that goes to a new origin - let external = createURL(location).origin !== createURL("/").origin; + let currentUrl = new URL(request.url); + let currentOrigin = currentUrl.origin; + let newOrigin = new URL(location, currentOrigin).origin; + let external = newOrigin !== currentOrigin; // Support relative routing in internal redirects if (!external) { @@ -2632,8 +2639,11 @@ async function callLoaderOrAction( let routePathnames = getPathContributingMatches(activeMatches).map( (match) => match.pathnameBase ); - let requestPath = createURL(request.url).pathname; - let resolvedLocation = resolveTo(location, routePathnames, requestPath); + let resolvedLocation = resolveTo( + location, + routePathnames, + currentUrl.pathname + ); invariant( createPath(resolvedLocation), `Unable to resolve redirect location: ${location}` @@ -2713,12 +2723,15 @@ async function callLoaderOrAction( return { type: ResultType.data, data: result }; } -function createRequest( +// Utility method for creating the Request instances for loaders/actions during +// client-side navigations and fetches. During SSR we will always have a +// Request instance from the static handler (query/queryRoute) +function createClientSideRequest( location: string | Location, signal: AbortSignal, submission?: Submission ): Request { - let url = createURL(stripHashFromPath(location)).toString(); + let url = createClientSideURL(stripHashFromPath(location)).toString(); let init: RequestInit = { signal }; if (submission) { From 03a362ab88af44b51c5ebc217cbb2486a1c681dc Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Mon, 5 Dec 2022 16:02:06 -0500 Subject: [PATCH 2/5] Remove fallback url origin in favor of invariant --- packages/router/history.ts | 22 ++++++++++++++++++++-- packages/router/index.ts | 3 +-- packages/router/router.ts | 2 +- packages/router/utils.ts | 16 +--------------- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/packages/router/history.ts b/packages/router/history.ts index 0826c6542f..6d8973392d 100644 --- a/packages/router/history.ts +++ b/packages/router/history.ts @@ -447,6 +447,20 @@ export function createHashHistory( //#region UTILS //////////////////////////////////////////////////////////////////////////////// +/** + * @private + */ +export function invariant(value: boolean, message?: string): asserts value; +export function invariant( + value: T | null | undefined, + message?: string +): asserts value is T; +export function invariant(value: any, message?: string) { + if (value === false || value === null || typeof value === "undefined") { + throw new Error(message); + } +} + function warning(cond: any, message: string) { if (!cond) { // eslint-disable-next-line no-console @@ -553,9 +567,13 @@ export function createClientSideURL(location: Location | string): URL { typeof window.location !== "undefined" && window.location.origin !== "null" ? window.location.origin - : "unknown://unknown"; + : null; let href = typeof location === "string" ? location : createPath(location); - return new URL(href, base); + invariant( + base, + `No window.location.origin available to create URL for href: ${href}` + ); + return new URL(href, window.location.origin); } export interface UrlHistory extends History {} diff --git a/packages/router/index.ts b/packages/router/index.ts index f9739ba566..23bfca95b1 100644 --- a/packages/router/index.ts +++ b/packages/router/index.ts @@ -32,7 +32,6 @@ export { defer, generatePath, getToPathname, - invariant, isRouteErrorResponse, joinPaths, json, @@ -59,13 +58,13 @@ export type { Path, To, } from "./history"; - export { Action, createBrowserHistory, createPath, createHashHistory, createMemoryHistory, + invariant, parsePath, } from "./history"; diff --git a/packages/router/router.ts b/packages/router/router.ts index c142843e42..8bd5ae9f55 100644 --- a/packages/router/router.ts +++ b/packages/router/router.ts @@ -4,6 +4,7 @@ import { createLocation, createPath, createClientSideURL, + invariant, parsePath, } from "./history"; import type { @@ -28,7 +29,6 @@ import { ResultType, convertRoutesToDataRoutes, getPathContributingMatches, - invariant, isRouteErrorResponse, joinPaths, matchRoutes, diff --git a/packages/router/utils.ts b/packages/router/utils.ts index cd35742733..53c970f823 100644 --- a/packages/router/utils.ts +++ b/packages/router/utils.ts @@ -1,5 +1,5 @@ import type { Location, Path, To } from "./history"; -import { parsePath } from "./history"; +import { invariant, parsePath } from "./history"; /** * Map of routeId -> data returned from a loader/action/error @@ -771,20 +771,6 @@ export function stripBasename( return pathname.slice(startIndex) || "/"; } -/** - * @private - */ -export function invariant(value: boolean, message?: string): asserts value; -export function invariant( - value: T | null | undefined, - message?: string -): asserts value is T; -export function invariant(value: any, message?: string) { - if (value === false || value === null || typeof value === "undefined") { - throw new Error(message); - } -} - /** * @private */ From 0b6ade29a690cd7b1e6e992617636e528a3eaed6 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Mon, 5 Dec 2022 16:22:44 -0500 Subject: [PATCH 3/5] Fallback on window.location.href for firefox --- packages/router/history.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/router/history.ts b/packages/router/history.ts index 6d8973392d..90aece0a49 100644 --- a/packages/router/history.ts +++ b/packages/router/history.ts @@ -567,11 +567,11 @@ export function createClientSideURL(location: Location | string): URL { typeof window.location !== "undefined" && window.location.origin !== "null" ? window.location.origin - : null; + : window.location.href; let href = typeof location === "string" ? location : createPath(location); invariant( base, - `No window.location.origin available to create URL for href: ${href}` + `No window.location.(origin|href) available to create URL for href: ${href}` ); return new URL(href, window.location.origin); } From 06f4674d9e61230ed989f9efb74a1df08c0f0867 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Mon, 5 Dec 2022 17:13:41 -0500 Subject: [PATCH 4/5] Bump bundle --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d359a2a285..d7b6afc2db 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ }, "filesize": { "packages/router/dist/router.umd.min.js": { - "none": "35 kB" + "none": "35.5 kB" }, "packages/react-router/dist/react-router.production.min.js": { "none": "12.5 kB" From d518c724370a3bc7b852a4b74b26f4c5832e25c6 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Mon, 5 Dec 2022 17:14:41 -0500 Subject: [PATCH 5/5] doh --- packages/router/history.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/router/history.ts b/packages/router/history.ts index 90aece0a49..e5117817b7 100644 --- a/packages/router/history.ts +++ b/packages/router/history.ts @@ -573,7 +573,7 @@ export function createClientSideURL(location: Location | string): URL { base, `No window.location.(origin|href) available to create URL for href: ${href}` ); - return new URL(href, window.location.origin); + return new URL(href, base); } export interface UrlHistory extends History {}