diff --git a/.changeset/dull-files-warn.md b/.changeset/dull-files-warn.md
new file mode 100644
index 00000000000..6f0394ef53a
--- /dev/null
+++ b/.changeset/dull-files-warn.md
@@ -0,0 +1,5 @@
+---
+"@remix-run/react": patch
+---
+
+Properly handle interrupted \_\_manifest requests in lazy route discovery
diff --git a/integration/fog-of-war-test.ts b/integration/fog-of-war-test.ts
index 6dc66ad62ed..fa4c2ebfe86 100644
--- a/integration/fog-of-war-test.ts
+++ b/integration/fog-of-war-test.ts
@@ -1448,4 +1448,66 @@ test.describe("Fog of War", () => {
),
]);
});
+
+ test("handles interruptions from back to back navigations", async ({
+ page,
+ }) => {
+ let fixture = await createFixture({
+ config: {
+ future: {
+ v3_lazyRouteDiscovery: true,
+ },
+ },
+ files: {
+ ...getFiles(),
+ "app/routes/a.tsx": js`
+ import { Link, Outlet, useLoaderData, useNavigate } from "@remix-run/react";
+
+ export function loader({ request }) {
+ return { message: "A LOADER" };
+ }
+
+ export default function Index() {
+ let data = useLoaderData();
+ let navigate = useNavigate();
+ return (
+ <>
+
A: {data.message}
+
+
+ >
+ )
+ }
+ `,
+ },
+ });
+ let appFixture = await createAppFixture(fixture);
+ let app = new PlaywrightFixture(appFixture, page);
+
+ await app.goto("/a", true);
+ expect(
+ await page.evaluate(() =>
+ Object.keys((window as any).__remixManifest.routes)
+ )
+ ).toEqual(["root", "routes/a", "routes/_index"]);
+
+ // /a/b gets discovered on click
+ await app.clickElement("[data-link]");
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+ expect(await (await page.$("body"))?.textContent()).not.toContain(
+ "Not Found"
+ );
+ await page.waitForSelector("#b");
+
+ expect(
+ await page.evaluate(() =>
+ Object.keys((window as any).__remixManifest.routes)
+ )
+ ).toEqual(["root", "routes/a", "routes/_index", "routes/a.b"]);
+ });
});
diff --git a/integration/package.json b/integration/package.json
index d36352dc104..aaebef4d883 100644
--- a/integration/package.json
+++ b/integration/package.json
@@ -14,7 +14,7 @@
"@remix-run/dev": "workspace:*",
"@remix-run/express": "workspace:*",
"@remix-run/node": "workspace:*",
- "@remix-run/router": "1.21.2-pre-v6.0",
+ "@remix-run/router": "1.22.0-pre-v6.1",
"@remix-run/server-runtime": "workspace:*",
"@types/express": "^4.17.9",
"@vanilla-extract/css": "^1.10.0",
diff --git a/packages/remix-dev/package.json b/packages/remix-dev/package.json
index 0a8c56b2c70..f00d59e7eb3 100644
--- a/packages/remix-dev/package.json
+++ b/packages/remix-dev/package.json
@@ -32,7 +32,7 @@
"@mdx-js/mdx": "^2.3.0",
"@npmcli/package-json": "^4.0.1",
"@remix-run/node": "workspace:*",
- "@remix-run/router": "1.21.2-pre-v6.0",
+ "@remix-run/router": "1.22.0-pre-v6.1",
"@remix-run/server-runtime": "workspace:*",
"@types/mdx": "^2.0.5",
"@vanilla-extract/integration": "^6.2.0",
diff --git a/packages/remix-react/fog-of-war.ts b/packages/remix-react/fog-of-war.ts
index ad4020d1faf..4092368206f 100644
--- a/packages/remix-react/fog-of-war.ts
+++ b/packages/remix-react/fog-of-war.ts
@@ -74,7 +74,7 @@ export function getPatchRoutesOnNavigationFunction(
if (!isFogOfWarEnabled(future, isSpaMode)) {
return undefined;
}
- return async ({ path, patch }) => {
+ return async ({ path, patch, signal }) => {
if (discoveredPaths.has(path)) {
return;
}
@@ -85,7 +85,8 @@ export function getPatchRoutesOnNavigationFunction(
future,
isSpaMode,
basename,
- patch
+ patch,
+ signal
);
};
}
@@ -207,7 +208,8 @@ export async function fetchAndApplyManifestPatches(
future: FutureConfig,
isSpaMode: boolean,
basename: string | undefined,
- patchRoutes: Router["patchRoutes"]
+ patchRoutes: Router["patchRoutes"],
+ signal?: AbortSignal
): Promise {
let manifestPath = `${basename ?? "/"}/__manifest`.replace(/\/+/g, "/");
let url = new URL(manifestPath, window.location.origin);
@@ -222,15 +224,21 @@ export async function fetchAndApplyManifestPatches(
return;
}
- let res = await fetch(url);
+ let serverPatches: AssetsManifest["routes"];
+ try {
+ let res = await fetch(url, { signal });
- if (!res.ok) {
- throw new Error(`${res.status} ${res.statusText}`);
- } else if (res.status >= 400) {
- throw new Error(await res.text());
- }
+ if (!res.ok) {
+ throw new Error(`${res.status} ${res.statusText}`);
+ } else if (res.status >= 400) {
+ throw new Error(await res.text());
+ }
- let serverPatches = (await res.json()) as AssetsManifest["routes"];
+ serverPatches = (await res.json()) as AssetsManifest["routes"];
+ } catch (e) {
+ if (signal?.aborted) return;
+ throw e;
+ }
// Patch routes we don't know about yet into the manifest
let knownRoutes = new Set(Object.keys(manifest.routes));
diff --git a/packages/remix-react/package.json b/packages/remix-react/package.json
index 14b7af832f6..6d2c9376f9b 100644
--- a/packages/remix-react/package.json
+++ b/packages/remix-react/package.json
@@ -19,10 +19,10 @@
"tsc": "tsc"
},
"dependencies": {
- "@remix-run/router": "1.21.2-pre-v6.0",
+ "@remix-run/router": "1.22.0-pre-v6.1",
"@remix-run/server-runtime": "workspace:*",
- "react-router": "6.28.3-pre-v6.1",
- "react-router-dom": "6.28.3-pre-v6.1",
+ "react-router": "6.29.0-pre-v6.2",
+ "react-router-dom": "6.29.0-pre-v6.2",
"turbo-stream": "2.4.0"
},
"devDependencies": {
diff --git a/packages/remix-server-runtime/package.json b/packages/remix-server-runtime/package.json
index 16e7da6541b..33e60891209 100644
--- a/packages/remix-server-runtime/package.json
+++ b/packages/remix-server-runtime/package.json
@@ -19,7 +19,7 @@
"tsc": "tsc"
},
"dependencies": {
- "@remix-run/router": "1.21.2-pre-v6.0",
+ "@remix-run/router": "1.22.0-pre-v6.1",
"@types/cookie": "^0.6.0",
"@web3-storage/multipart-parser": "^1.0.0",
"cookie": "^0.6.0",
diff --git a/packages/remix-testing/package.json b/packages/remix-testing/package.json
index ce1b40b6aab..b1d6d83c30a 100644
--- a/packages/remix-testing/package.json
+++ b/packages/remix-testing/package.json
@@ -21,8 +21,8 @@
"dependencies": {
"@remix-run/node": "workspace:*",
"@remix-run/react": "workspace:*",
- "@remix-run/router": "1.21.2-pre-v6.0",
- "react-router-dom": "6.28.3-pre-v6.1"
+ "@remix-run/router": "1.22.0-pre-v6.1",
+ "react-router-dom": "6.29.0-pre-v6.2"
},
"devDependencies": {
"@remix-run/server-runtime": "workspace:*",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a4a1c6fe8a1..35d537fdc5f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -323,8 +323,8 @@ importers:
specifier: workspace:*
version: link:../packages/remix-node
'@remix-run/router':
- specifier: 1.21.2-pre-v6.0
- version: 1.21.2-pre-v6.0
+ specifier: 1.22.0-pre-v6.1
+ version: 1.22.0-pre-v6.1
'@remix-run/server-runtime':
specifier: workspace:*
version: link:../packages/remix-server-runtime
@@ -883,8 +883,8 @@ importers:
specifier: ^2.15.3-pre.0
version: link:../remix-react
'@remix-run/router':
- specifier: 1.21.2-pre-v6.0
- version: 1.21.2-pre-v6.0
+ specifier: 1.22.0-pre-v6.1
+ version: 1.22.0-pre-v6.1
'@remix-run/server-runtime':
specifier: workspace:*
version: link:../remix-server-runtime
@@ -1247,17 +1247,17 @@ importers:
packages/remix-react:
dependencies:
'@remix-run/router':
- specifier: 1.21.2-pre-v6.0
- version: 1.21.2-pre-v6.0
+ specifier: 1.22.0-pre-v6.1
+ version: 1.22.0-pre-v6.1
'@remix-run/server-runtime':
specifier: workspace:*
version: link:../remix-server-runtime
react-router:
- specifier: 6.28.3-pre-v6.1
- version: 6.28.3-pre-v6.1(react@18.2.0)
+ specifier: 6.29.0-pre-v6.2
+ version: 6.29.0-pre-v6.2(react@18.2.0)
react-router-dom:
- specifier: 6.28.3-pre-v6.1
- version: 6.28.3-pre-v6.1(react-dom@18.2.0)(react@18.2.0)
+ specifier: 6.29.0-pre-v6.2
+ version: 6.29.0-pre-v6.2(react-dom@18.2.0)(react@18.2.0)
turbo-stream:
specifier: 2.4.0
version: 2.4.0
@@ -1364,8 +1364,8 @@ importers:
packages/remix-server-runtime:
dependencies:
'@remix-run/router':
- specifier: 1.21.2-pre-v6.0
- version: 1.21.2-pre-v6.0
+ specifier: 1.22.0-pre-v6.1
+ version: 1.22.0-pre-v6.1
'@types/cookie':
specifier: ^0.6.0
version: 0.6.0
@@ -1401,11 +1401,11 @@ importers:
specifier: workspace:*
version: link:../remix-react
'@remix-run/router':
- specifier: 1.21.2-pre-v6.0
- version: 1.21.2-pre-v6.0
+ specifier: 1.22.0-pre-v6.1
+ version: 1.22.0-pre-v6.1
react-router-dom:
- specifier: 6.28.3-pre-v6.1
- version: 6.28.3-pre-v6.1(react-dom@18.2.0)(react@18.2.0)
+ specifier: 6.29.0-pre-v6.2
+ version: 6.29.0-pre-v6.2(react-dom@18.2.0)(react@18.2.0)
devDependencies:
'@remix-run/server-runtime':
specifier: workspace:*
@@ -4267,8 +4267,8 @@ packages:
- encoding
dev: false
- /@remix-run/router@1.21.2-pre-v6.0:
- resolution: {integrity: sha512-wbnSFjjHmAOYArpveJ4IYAxhPRzBW4onkjcukKUC8xCUXleteSP1On+YcqFeXnB0FEUzSD9cT2s/0y77w28Slg==}
+ /@remix-run/router@1.22.0-pre-v6.1:
+ resolution: {integrity: sha512-SotUMuK2uvVsJVBXa98R2UlYtniGuSMAKhOmiVu+efpSPCPf3G4npT32pSGYfZYVZ/7RqTxlghfgQkh2URKIqg==}
engines: {node: '>=14.0.0'}
dev: false
@@ -12840,26 +12840,26 @@ packages:
engines: {node: '>=0.10.0'}
dev: false
- /react-router-dom@6.28.3-pre-v6.1(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-qPrKJZ8VX83vQZ5RxIh+GKbeA1ZV3aR7zEOCBewFnexMdiQBwmDGiq+39NZ6ts7KuyAqFTSsRtiPznTopeQ0Ow==}
+ /react-router-dom@6.29.0-pre-v6.2(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-tq8W0OJ/wHgRcTRGPoQleRFIJnwy9/kynfvo3BYvjE0YzEHdQag8e8liAkRMJDZ4laZZWyM+r55cGlv9kt4JtA==}
engines: {node: '>=14.0.0'}
peerDependencies:
react: '>=16.8'
react-dom: '>=16.8'
dependencies:
- '@remix-run/router': 1.21.2-pre-v6.0
+ '@remix-run/router': 1.22.0-pre-v6.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
- react-router: 6.28.3-pre-v6.1(react@18.2.0)
+ react-router: 6.29.0-pre-v6.2(react@18.2.0)
dev: false
- /react-router@6.28.3-pre-v6.1(react@18.2.0):
- resolution: {integrity: sha512-CqBA2PYs4m6CbVZ18pm3fpq/Jww4M1L9UODnxnthZe5c5/23QAwS6PU9EWLTa5WWATrO83+GK4qOpZjV98Y6cA==}
+ /react-router@6.29.0-pre-v6.2(react@18.2.0):
+ resolution: {integrity: sha512-8MQITnj3tFxiBs/U+letrIyMcapbnkjL/jM0Gte4FmkMxToO7PO806eYiRfJxRL6VeBg4JPPRe1vuXs4qzbJsQ==}
engines: {node: '>=14.0.0'}
peerDependencies:
react: '>=16.8'
dependencies:
- '@remix-run/router': 1.21.2-pre-v6.0
+ '@remix-run/router': 1.22.0-pre-v6.1
react: 18.2.0
dev: false