react-router 8.4.0 → 8.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +94 -1
- package/dist/development/dom-export.d.ts +2 -1
- package/dist/development/dom-export.js +1 -1
- package/dist/development/index-react-server-client.js +2 -2
- package/dist/development/index-react-server.d.ts +237 -165
- package/dist/development/index-react-server.js +69 -33
- package/dist/development/index.d.ts +9 -8
- package/dist/development/index.js +5 -4
- package/dist/development/lib/actions.js +1 -1
- package/dist/development/lib/await.js +58 -0
- package/dist/development/lib/components.d.ts +41 -5
- package/dist/development/lib/components.js +82 -5
- package/dist/development/lib/context.d.ts +8 -3
- package/dist/development/lib/context.js +1 -1
- package/dist/development/lib/dom/dom.d.ts +3 -0
- package/dist/development/lib/dom/dom.js +1 -1
- package/dist/development/lib/dom/lib.d.ts +34 -9
- package/dist/development/lib/dom/lib.js +17 -6
- package/dist/development/lib/dom/server.js +2 -2
- package/dist/development/lib/dom/ssr/components.js +12 -9
- package/dist/development/lib/dom/ssr/data.js +1 -1
- package/dist/development/lib/dom/ssr/entry.d.ts +9 -2
- package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/development/lib/dom/ssr/fallback.js +1 -1
- package/dist/development/lib/dom/ssr/fog-of-war.d.ts +3 -2
- package/dist/development/lib/dom/ssr/fog-of-war.js +25 -13
- package/dist/development/lib/dom/ssr/hydration.js +1 -1
- package/dist/development/lib/dom/ssr/invariant.js +1 -1
- package/dist/development/lib/dom/ssr/links.js +1 -1
- package/dist/development/lib/dom/ssr/markup.js +1 -1
- package/dist/development/lib/dom/ssr/route-discovery.d.ts +120 -0
- package/dist/development/lib/dom/ssr/route-discovery.js +415 -0
- package/dist/development/lib/dom/ssr/routeModules.js +1 -1
- package/dist/development/lib/dom/ssr/routes-test-stub.js +2 -2
- package/dist/development/lib/dom/ssr/routes.d.ts +2 -2
- package/dist/development/lib/dom/ssr/routes.js +8 -8
- package/dist/development/lib/dom/ssr/server.js +3 -1
- package/dist/development/lib/dom/ssr/single-fetch.d.ts +28 -2
- package/dist/development/lib/dom/ssr/single-fetch.js +73 -55
- package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/development/lib/dom-export/hydrated-router.d.ts +28 -0
- package/dist/development/lib/dom-export/hydrated-router.js +32 -12
- package/dist/development/lib/errors.js +1 -1
- package/dist/development/lib/hooks.d.ts +3 -6
- package/dist/development/lib/hooks.js +8 -9
- package/dist/development/lib/href.js +1 -1
- package/dist/development/lib/router/history.js +1 -1
- package/dist/development/lib/router/instrumentation.js +1 -1
- package/dist/development/lib/router/matcher-route-pattern.js +1 -1
- package/dist/development/lib/router/matcher-route-pattern.preload.js +1 -1
- package/dist/development/lib/router/matcher.js +6 -1
- package/dist/development/lib/router/navigation.js +1 -1
- package/dist/development/lib/router/router.d.ts +8 -0
- package/dist/development/lib/router/router.js +213 -91
- package/dist/development/lib/router/url.js +1 -1
- package/dist/development/lib/router/utils.d.ts +31 -4
- package/dist/development/lib/router/utils.js +4 -3
- package/dist/development/lib/rsc/browser.js +4 -4
- package/dist/development/lib/rsc/errorBoundaries.js +1 -1
- package/dist/development/lib/rsc/html-stream/browser.js +1 -1
- package/dist/development/lib/rsc/html-stream/server.js +24 -12
- package/dist/development/lib/rsc/route-modules.js +1 -1
- package/dist/development/lib/rsc/server.rsc.d.ts +3 -2
- package/dist/development/lib/rsc/server.ssr.js +1 -1
- package/dist/development/lib/server-runtime/build.d.ts +14 -0
- package/dist/development/lib/server-runtime/cookies.js +1 -1
- package/dist/development/lib/server-runtime/crypto.js +1 -1
- package/dist/development/lib/server-runtime/data.js +1 -1
- package/dist/development/lib/server-runtime/dev.js +1 -1
- package/dist/development/lib/server-runtime/entry.js +1 -1
- package/dist/development/lib/server-runtime/errors.js +1 -1
- package/dist/development/lib/server-runtime/headers.js +1 -1
- package/dist/development/lib/server-runtime/invariant.js +1 -1
- package/dist/development/lib/server-runtime/mode.js +1 -1
- package/dist/development/lib/server-runtime/routeMatching.js +1 -1
- package/dist/development/lib/server-runtime/routes.d.ts +1 -1
- package/dist/development/lib/server-runtime/routes.js +1 -1
- package/dist/development/lib/server-runtime/server.js +24 -8
- package/dist/development/lib/server-runtime/serverHandoff.d.ts +17 -0
- package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/development/lib/server-runtime/sessions/cookieStorage.js +1 -1
- package/dist/development/lib/server-runtime/sessions/memoryStorage.js +1 -1
- package/dist/development/lib/server-runtime/sessions.js +2 -2
- package/dist/development/lib/server-runtime/single-fetch.js +1 -1
- package/dist/development/lib/server-runtime/urls.js +1 -1
- package/dist/development/lib/server-runtime/warnings.js +1 -1
- package/dist/development/lib/types/internal.js +1 -1
- package/dist/development/lib/types/route-data.d.ts +2 -2
- package/dist/development/lib/types/route-module-annotations.d.ts +1 -1
- package/dist/development/route-pattern.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/unflatten.js +23 -3
- package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
- package/dist/production/dom-export.d.ts +2 -1
- package/dist/production/dom-export.js +1 -1
- package/dist/production/index-react-server-client.js +2 -2
- package/dist/production/index-react-server.d.ts +237 -165
- package/dist/production/index-react-server.js +69 -33
- package/dist/production/index.d.ts +9 -8
- package/dist/production/index.js +5 -4
- package/dist/production/lib/actions.js +1 -1
- package/dist/production/lib/await.js +58 -0
- package/dist/production/lib/components.d.ts +41 -5
- package/dist/production/lib/components.js +82 -5
- package/dist/production/lib/context.d.ts +8 -3
- package/dist/production/lib/context.js +1 -1
- package/dist/production/lib/dom/dom.d.ts +3 -0
- package/dist/production/lib/dom/dom.js +1 -1
- package/dist/production/lib/dom/lib.d.ts +34 -9
- package/dist/production/lib/dom/lib.js +17 -6
- package/dist/production/lib/dom/server.js +2 -2
- package/dist/production/lib/dom/ssr/components.js +12 -9
- package/dist/production/lib/dom/ssr/data.js +1 -1
- package/dist/production/lib/dom/ssr/entry.d.ts +9 -2
- package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/production/lib/dom/ssr/fallback.js +1 -1
- package/dist/production/lib/dom/ssr/fog-of-war.d.ts +3 -2
- package/dist/production/lib/dom/ssr/fog-of-war.js +25 -13
- package/dist/production/lib/dom/ssr/hydration.js +1 -1
- package/dist/production/lib/dom/ssr/invariant.js +1 -1
- package/dist/production/lib/dom/ssr/links.js +1 -1
- package/dist/production/lib/dom/ssr/markup.js +1 -1
- package/dist/production/lib/dom/ssr/route-discovery.d.ts +120 -0
- package/dist/production/lib/dom/ssr/route-discovery.js +415 -0
- package/dist/production/lib/dom/ssr/routeModules.js +1 -1
- package/dist/production/lib/dom/ssr/routes-test-stub.js +2 -2
- package/dist/production/lib/dom/ssr/routes.d.ts +2 -2
- package/dist/production/lib/dom/ssr/routes.js +8 -8
- package/dist/production/lib/dom/ssr/server.js +3 -1
- package/dist/production/lib/dom/ssr/single-fetch.d.ts +28 -2
- package/dist/production/lib/dom/ssr/single-fetch.js +73 -55
- package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/production/lib/dom-export/hydrated-router.d.ts +28 -0
- package/dist/production/lib/dom-export/hydrated-router.js +32 -12
- package/dist/production/lib/errors.js +1 -1
- package/dist/production/lib/hooks.d.ts +3 -6
- package/dist/production/lib/hooks.js +7 -8
- package/dist/production/lib/href.js +1 -1
- package/dist/production/lib/router/history.js +1 -1
- package/dist/production/lib/router/instrumentation.js +1 -1
- package/dist/production/lib/router/matcher-route-pattern.js +1 -1
- package/dist/production/lib/router/matcher-route-pattern.preload.js +1 -1
- package/dist/production/lib/router/matcher.js +6 -1
- package/dist/production/lib/router/navigation.js +1 -1
- package/dist/production/lib/router/router.d.ts +8 -0
- package/dist/production/lib/router/router.js +213 -91
- package/dist/production/lib/router/url.js +1 -1
- package/dist/production/lib/router/utils.d.ts +31 -4
- package/dist/production/lib/router/utils.js +4 -3
- package/dist/production/lib/rsc/browser.js +4 -4
- package/dist/production/lib/rsc/errorBoundaries.js +1 -1
- package/dist/production/lib/rsc/html-stream/browser.js +1 -1
- package/dist/production/lib/rsc/html-stream/server.js +24 -12
- package/dist/production/lib/rsc/route-modules.js +1 -1
- package/dist/production/lib/rsc/server.rsc.d.ts +3 -2
- package/dist/production/lib/rsc/server.ssr.js +1 -1
- package/dist/production/lib/server-runtime/build.d.ts +14 -0
- package/dist/production/lib/server-runtime/cookies.js +1 -1
- package/dist/production/lib/server-runtime/crypto.js +1 -1
- package/dist/production/lib/server-runtime/data.js +1 -1
- package/dist/production/lib/server-runtime/dev.js +1 -1
- package/dist/production/lib/server-runtime/entry.js +1 -1
- package/dist/production/lib/server-runtime/errors.js +1 -1
- package/dist/production/lib/server-runtime/headers.js +1 -1
- package/dist/production/lib/server-runtime/invariant.js +1 -1
- package/dist/production/lib/server-runtime/mode.js +1 -1
- package/dist/production/lib/server-runtime/routeMatching.js +1 -1
- package/dist/production/lib/server-runtime/routes.d.ts +1 -1
- package/dist/production/lib/server-runtime/routes.js +1 -1
- package/dist/production/lib/server-runtime/server.js +24 -8
- package/dist/production/lib/server-runtime/serverHandoff.d.ts +17 -0
- package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/production/lib/server-runtime/sessions/cookieStorage.js +1 -1
- package/dist/production/lib/server-runtime/sessions/memoryStorage.js +1 -1
- package/dist/production/lib/server-runtime/sessions.js +2 -2
- package/dist/production/lib/server-runtime/single-fetch.js +1 -1
- package/dist/production/lib/server-runtime/urls.js +1 -1
- package/dist/production/lib/server-runtime/warnings.js +1 -1
- package/dist/production/lib/types/internal.js +1 -1
- package/dist/production/lib/types/route-data.d.ts +2 -2
- package/dist/production/lib/types/route-module-annotations.d.ts +1 -1
- package/dist/production/route-pattern.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/unflatten.js +23 -3
- package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
- package/docs/explanation/backend-for-frontend.md +0 -3
- package/docs/explanation/code-splitting.md +0 -3
- package/docs/explanation/concurrency.md +0 -3
- package/docs/explanation/hot-module-replacement.md +0 -3
- package/docs/explanation/lazy-route-discovery.md +4 -3
- package/docs/explanation/progressive-enhancement.md +0 -3
- package/docs/explanation/race-conditions.md +0 -3
- package/docs/explanation/react-transitions.md +0 -3
- package/docs/explanation/sessions-and-cookies.md +109 -5
- package/docs/explanation/state-management.md +0 -3
- package/docs/explanation/styling.md +0 -3
- package/docs/explanation/type-safety.md +0 -3
- package/docs/how-to/accessibility.md +0 -6
- package/docs/how-to/client-data.md +0 -3
- package/docs/how-to/custom-fetch.md +218 -0
- package/docs/how-to/data-strategy.md +23 -4
- package/docs/how-to/error-boundary.md +0 -3
- package/docs/how-to/error-reporting.md +0 -3
- package/docs/how-to/fetchers.md +0 -3
- package/docs/how-to/file-route-conventions.md +0 -3
- package/docs/how-to/file-uploads.md +0 -3
- package/docs/how-to/form-validation.md +0 -3
- package/docs/how-to/headers.md +0 -3
- package/docs/how-to/instrumentation.md +0 -3
- package/docs/how-to/middleware.md +7 -3
- package/docs/how-to/navigation-blocking.md +0 -3
- package/docs/how-to/optimize-revalidation.md +2 -5
- package/docs/how-to/pre-rendering.md +23 -4
- package/docs/how-to/presets.md +0 -3
- package/docs/how-to/react-server-components.md +0 -3
- package/docs/how-to/resource-routes.md +0 -3
- package/docs/how-to/route-discovery.md +147 -0
- package/docs/how-to/route-module-type-safety.md +0 -3
- package/docs/how-to/security.md +0 -3
- package/docs/how-to/server-bundles.md +0 -3
- package/docs/how-to/spa.md +2 -3
- package/docs/how-to/status.md +0 -3
- package/docs/how-to/suspense.md +22 -6
- package/docs/how-to/using-handle.md +0 -3
- package/docs/how-to/view-transitions.md +79 -15
- package/docs/start/framework/data-loading.md +9 -0
- package/docs/start/framework/navigating.md +5 -9
- package/docs/start/framework/rendering.md +37 -0
- package/docs/upgrading/future.md +0 -12
- package/docs/upgrading/v7.md +3 -33
- package/package.json +9 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.5.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -27,7 +27,13 @@ function getPathsWithAncestors(paths) {
|
|
|
27
27
|
return Array.from(result);
|
|
28
28
|
}
|
|
29
29
|
function isFogOfWarEnabled(routeDiscovery, ssr) {
|
|
30
|
-
return routeDiscovery.mode === "lazy" && ssr
|
|
30
|
+
return routeDiscovery.mode === "lazy" && ssr;
|
|
31
|
+
}
|
|
32
|
+
const MANIFEST_MATCH_COST_LIMIT = 5e4;
|
|
33
|
+
const MANIFEST_MATCH_COST_SEGMENT_THRESHOLD = 10;
|
|
34
|
+
function shouldUseInitialRouteDiscovery(pathname, matchCost = 0) {
|
|
35
|
+
let segmentCount = pathname.split("/").length - 1;
|
|
36
|
+
return Math.max(segmentCount - 1, 0) * pathname.length + 1 > 7680 || segmentCount > MANIFEST_MATCH_COST_SEGMENT_THRESHOLD && segmentCount * matchCost > MANIFEST_MATCH_COST_LIMIT;
|
|
31
37
|
}
|
|
32
38
|
function getPartialManifest({ sri, ...manifest }, router) {
|
|
33
39
|
let routeIds = new Set(router.state.matches.map((m) => m.route.id));
|
|
@@ -49,15 +55,15 @@ function getPartialManifest({ sri, ...manifest }, router) {
|
|
|
49
55
|
sri: sri ? true : void 0
|
|
50
56
|
};
|
|
51
57
|
}
|
|
52
|
-
function getPatchRoutesOnNavigationFunction(getRouter, manifest, routeModules, ssr, routeDiscovery, isSpaMode, basename) {
|
|
58
|
+
function getPatchRoutesOnNavigationFunction(getRouter, manifest, routeModules, ssr, routeDiscovery, isSpaMode, basename, fetchImplementation = (request) => fetch(request), serverOrigin, isApiOnly = false) {
|
|
53
59
|
if (!isFogOfWarEnabled(routeDiscovery, ssr)) return;
|
|
54
60
|
return async ({ path, patch, signal, fetcherKey }) => {
|
|
55
61
|
if (discoveredPaths.has(path)) return;
|
|
56
62
|
let { state } = getRouter();
|
|
57
|
-
await fetchAndApplyManifestPatches([path], fetcherKey ? window.location.href : createPath(state.navigation.location || state.location), manifest, routeModules, ssr, isSpaMode, basename, routeDiscovery.manifestPath, patch, signal);
|
|
63
|
+
await fetchAndApplyManifestPatches([path], fetcherKey ? window.location.href : createPath(state.navigation.location || state.location), manifest, routeModules, ssr, isSpaMode, basename, routeDiscovery.manifestPath, patch, signal, fetchImplementation, serverOrigin, isApiOnly);
|
|
58
64
|
};
|
|
59
65
|
}
|
|
60
|
-
function useFogOFWarDiscovery(router, manifest, routeModules, ssr, routeDiscovery, isSpaMode) {
|
|
66
|
+
function useFogOFWarDiscovery(router, manifest, routeModules, ssr, routeDiscovery, isSpaMode, fetchImplementation = (request) => fetch(request), serverOrigin, isApiOnly = false) {
|
|
61
67
|
React$1.useEffect(() => {
|
|
62
68
|
if (!isFogOfWarEnabled(routeDiscovery, ssr) || window.navigator?.connection?.saveData === true) return;
|
|
63
69
|
function registerElement(el) {
|
|
@@ -77,7 +83,7 @@ function useFogOFWarDiscovery(router, manifest, routeModules, ssr, routeDiscover
|
|
|
77
83
|
});
|
|
78
84
|
if (lazyPaths.length === 0) return;
|
|
79
85
|
try {
|
|
80
|
-
await fetchAndApplyManifestPatches(lazyPaths, null, manifest, routeModules, ssr, isSpaMode, router.basename, routeDiscovery.manifestPath, router.patchRoutes);
|
|
86
|
+
await fetchAndApplyManifestPatches(lazyPaths, null, manifest, routeModules, ssr, isSpaMode, router.basename, routeDiscovery.manifestPath, router.patchRoutes, void 0, fetchImplementation, serverOrigin, isApiOnly);
|
|
81
87
|
} catch (e) {
|
|
82
88
|
console.error("Failed to fetch manifest patches", e);
|
|
83
89
|
}
|
|
@@ -102,9 +108,15 @@ function useFogOFWarDiscovery(router, manifest, routeModules, ssr, routeDiscover
|
|
|
102
108
|
manifest,
|
|
103
109
|
routeModules,
|
|
104
110
|
router,
|
|
105
|
-
routeDiscovery
|
|
111
|
+
routeDiscovery,
|
|
112
|
+
fetchImplementation,
|
|
113
|
+
serverOrigin,
|
|
114
|
+
isApiOnly
|
|
106
115
|
]);
|
|
107
116
|
}
|
|
117
|
+
function getManifestPathsParam(paths) {
|
|
118
|
+
return paths.slice().sort().map((path) => path.replace(/,/g, "%2C")).join(",");
|
|
119
|
+
}
|
|
108
120
|
function getManifestPath(_manifestPath, basename) {
|
|
109
121
|
let manifestPath = _manifestPath || "/__manifest";
|
|
110
122
|
return basename == null ? manifestPath : joinPaths([basename, manifestPath]);
|
|
@@ -154,12 +166,12 @@ async function handleClientVersionMismatch(needsReload, version, errorReloadPath
|
|
|
154
166
|
}
|
|
155
167
|
return reloadPromise;
|
|
156
168
|
}
|
|
157
|
-
async function fetchAndApplyManifestPatches(paths, errorReloadPath, manifest, routeModules, ssr, isSpaMode, basename, manifestPath, patchRoutes, signal) {
|
|
169
|
+
async function fetchAndApplyManifestPatches(paths, errorReloadPath, manifest, routeModules, ssr, isSpaMode, basename, manifestPath, patchRoutes, signal, fetchImplementation = (request) => fetch(request), serverOrigin, isApiOnly = false) {
|
|
158
170
|
paths = getPathsWithAncestors(paths);
|
|
159
171
|
const searchParams = new URLSearchParams();
|
|
160
|
-
searchParams.set("paths", paths
|
|
172
|
+
searchParams.set("paths", getManifestPathsParam(paths));
|
|
161
173
|
searchParams.set("version", manifest.version);
|
|
162
|
-
let url = new URL(getManifestPath(manifestPath, basename), window.location.origin);
|
|
174
|
+
let url = new URL(getManifestPath(manifestPath, basename), serverOrigin ?? window.location.origin);
|
|
163
175
|
url.search = searchParams.toString();
|
|
164
176
|
if (url.toString().length > 7680) {
|
|
165
177
|
nextPaths.clear();
|
|
@@ -167,7 +179,7 @@ async function fetchAndApplyManifestPatches(paths, errorReloadPath, manifest, ro
|
|
|
167
179
|
}
|
|
168
180
|
let serverPatches;
|
|
169
181
|
try {
|
|
170
|
-
let res = await
|
|
182
|
+
let res = await fetchImplementation(new Request(url, { signal }), { type: "manifest" });
|
|
171
183
|
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
|
|
172
184
|
if (await handleClientVersionMismatch(res.status === 204 && res.headers.has("X-Remix-Reload-Document"), manifest.version, errorReloadPath)) return;
|
|
173
185
|
serverPatches = await res.json();
|
|
@@ -187,7 +199,7 @@ async function fetchAndApplyManifestPatches(paths, errorReloadPath, manifest, ro
|
|
|
187
199
|
Object.values(patches).forEach((patch) => {
|
|
188
200
|
if (patch && (!patch.parentId || !patches[patch.parentId])) parentIds.add(patch.parentId);
|
|
189
201
|
});
|
|
190
|
-
parentIds.forEach((parentId) => patchRoutes(parentId || null, createClientRoutes(patches, routeModules, null, ssr, isSpaMode, parentId)));
|
|
202
|
+
parentIds.forEach((parentId) => patchRoutes(parentId || null, createClientRoutes(patches, routeModules, null, ssr, isSpaMode, parentId, void 0, void 0, isApiOnly)));
|
|
191
203
|
}
|
|
192
204
|
function addToFifoQueue(path, queue) {
|
|
193
205
|
if (queue.size >= discoveredPathsMaxSize) {
|
|
@@ -204,4 +216,4 @@ function debounce(callback, wait) {
|
|
|
204
216
|
};
|
|
205
217
|
}
|
|
206
218
|
//#endregion
|
|
207
|
-
export { URL_LIMIT, getManifestPath, getPartialManifest, getPatchRoutesOnNavigationFunction, getPathsWithAncestors, handleClientVersionMismatch, isFogOfWarEnabled, useFogOFWarDiscovery };
|
|
219
|
+
export { URL_LIMIT, getManifestPath, getManifestPathsParam, getPartialManifest, getPatchRoutesOnNavigationFunction, getPathsWithAncestors, handleClientVersionMismatch, isFogOfWarEnabled, shouldUseInitialRouteDiscovery, useFogOFWarDiscovery };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
|
|
2
|
+
import { PatchRoutesOnNavigationFunction, RouteObject } from "../../router/utils.js";
|
|
3
|
+
import { Router } from "../../router/router.js";
|
|
4
|
+
import { ClientOnErrorFunction } from "../../components.js";
|
|
5
|
+
import { RouteModules } from "./routeModules.js";
|
|
6
|
+
import { RouterFetch } from "./single-fetch.js";
|
|
7
|
+
import { ServerHandoff } from "../../server-runtime/serverHandoff.js";
|
|
8
|
+
import { AssetsManifest } from "./entry.js";
|
|
9
|
+
|
|
10
|
+
//#region lib/dom/ssr/route-discovery.d.ts
|
|
11
|
+
/** A discovery response from a different build. */
|
|
12
|
+
type ManifestMismatchEvent = {
|
|
13
|
+
source: "eager" | "navigation" | "fetcher" | "imperative";
|
|
14
|
+
version: string;
|
|
15
|
+
reloadUrl: string | null;
|
|
16
|
+
loadAllRoutes(): Promise<void>;
|
|
17
|
+
stopPropagation(): void; /** Run built-in recovery for this source, at most once per mismatch event. */
|
|
18
|
+
defaultBehavior(): Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
/** The result of imperative route discovery. */
|
|
21
|
+
type DiscoveryResult = {
|
|
22
|
+
type: "success";
|
|
23
|
+
} | {
|
|
24
|
+
type: "version-mismatch";
|
|
25
|
+
} | {
|
|
26
|
+
type: "aborted";
|
|
27
|
+
};
|
|
28
|
+
/** Options for {@link unstable_useRouteDiscovery}. */
|
|
29
|
+
type RouteDiscoveryOptions = {
|
|
30
|
+
onManifestMismatch?: (event: ManifestMismatchEvent) => void | Promise<void>;
|
|
31
|
+
};
|
|
32
|
+
type Registration = {
|
|
33
|
+
current: RouteDiscoveryOptions;
|
|
34
|
+
};
|
|
35
|
+
type DiscoveryState = "partial" | "loading" | "complete";
|
|
36
|
+
type RouteDiscoverySsrInfo = {
|
|
37
|
+
manifest: AssetsManifest;
|
|
38
|
+
routeModules: RouteModules;
|
|
39
|
+
router: Router | undefined;
|
|
40
|
+
context: Pick<ServerHandoff, "ssr" | "routeDiscovery" | "isSpaMode" | "basename" | "unstable_apiServerOrigin" | "unstable_apiOnly">;
|
|
41
|
+
};
|
|
42
|
+
declare class RouteDiscoveryRuntime {
|
|
43
|
+
private ssrInfo;
|
|
44
|
+
readonly enabled: boolean;
|
|
45
|
+
readonly discoveredPaths: Set<string>;
|
|
46
|
+
readonly nextPaths: Set<string>;
|
|
47
|
+
state: DiscoveryState;
|
|
48
|
+
ready: boolean;
|
|
49
|
+
private registrations;
|
|
50
|
+
private listeners;
|
|
51
|
+
private fullLoadErrorListeners;
|
|
52
|
+
private fullLoad?;
|
|
53
|
+
private loadedManifest?;
|
|
54
|
+
private mismatch?;
|
|
55
|
+
private fetchImplementation;
|
|
56
|
+
private loadManifest;
|
|
57
|
+
onError?: ClientOnErrorFunction;
|
|
58
|
+
constructor(ssrInfo: RouteDiscoverySsrInfo, {
|
|
59
|
+
fetch: fetchImplementation,
|
|
60
|
+
loadManifest
|
|
61
|
+
}?: {
|
|
62
|
+
fetch?: RouterFetch;
|
|
63
|
+
loadManifest?: () => Promise<AssetsManifest>;
|
|
64
|
+
});
|
|
65
|
+
private get router();
|
|
66
|
+
private loadManifestAsset;
|
|
67
|
+
subscribe: (listener: () => void) => () => void;
|
|
68
|
+
private update;
|
|
69
|
+
markReady: () => void;
|
|
70
|
+
register(registration: Registration): () => void;
|
|
71
|
+
private isComplete;
|
|
72
|
+
loadAllRoutes: () => Promise<void>;
|
|
73
|
+
reportError(error: unknown): void;
|
|
74
|
+
private dispatchMismatch;
|
|
75
|
+
private requestUrl;
|
|
76
|
+
discoverRoutes: (paths: string[], options?: {
|
|
77
|
+
signal?: AbortSignal;
|
|
78
|
+
}) => Promise<DiscoveryResult>;
|
|
79
|
+
discover(paths: string[], source: ManifestMismatchEvent["source"], reloadUrl: string | null, signal?: AbortSignal, patch?: (routeId: string | null, children: RouteObject[], unstable_allowElementMutations?: boolean) => void): Promise<DiscoveryResult>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Control lazy route discovery in Framework Mode with `future.unstable_customRouteDiscovery`
|
|
83
|
+
* enabled. This allows you to imperatively discover routes as well as add custom behavior
|
|
84
|
+
* when a manifest version mismatch is detected. Calling `loadAllRoutes` will load the entire
|
|
85
|
+
* route manifest for the current (potentially stale) client version - this requires that prior
|
|
86
|
+
* client artifacts are still available on your CDN _and_ you have not made breaking changes to
|
|
87
|
+
* the API contracts of your loaders and actions.
|
|
88
|
+
*
|
|
89
|
+
* See the [Route Discovery guide](../../how-to/route-discovery) for more information and examples.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* let { state, discoverRoutes, loadAllRoutes } = unstable_useRouteDiscovery({
|
|
93
|
+
* onManifestMismatch: async (event) => {
|
|
94
|
+
* // Eagerly load the complete route tree for this client without requiring a hard reload
|
|
95
|
+
* await event.loadAllRoutes();
|
|
96
|
+
* },
|
|
97
|
+
* });
|
|
98
|
+
*
|
|
99
|
+
* // Eagerly discover specific routes
|
|
100
|
+
* discoverRoutes(["/a", "/b"]);
|
|
101
|
+
*
|
|
102
|
+
* @name unstable_useRouteDiscovery
|
|
103
|
+
* @public
|
|
104
|
+
* @category Hooks
|
|
105
|
+
* @mode framework
|
|
106
|
+
* @param options An awaited manifest-mismatch handler.
|
|
107
|
+
* @returns The client version, full-manifest loading state, and discovery operations.
|
|
108
|
+
*/
|
|
109
|
+
declare function useRouteDiscovery(options?: RouteDiscoveryOptions): {
|
|
110
|
+
version: string;
|
|
111
|
+
state: DiscoveryState;
|
|
112
|
+
loadAllRoutes: () => Promise<void>;
|
|
113
|
+
discoverRoutes: (paths: string[], options?: {
|
|
114
|
+
signal?: AbortSignal;
|
|
115
|
+
}) => Promise<DiscoveryResult>;
|
|
116
|
+
};
|
|
117
|
+
declare function getCustomPatchRoutesOnNavigationFunction(getRouter: () => Router, runtime: RouteDiscoveryRuntime): PatchRoutesOnNavigationFunction | undefined;
|
|
118
|
+
declare function useCustomRouteDiscovery(runtime: RouteDiscoveryRuntime, onError?: ClientOnErrorFunction): "partial" | "loading" | "complete";
|
|
119
|
+
//#endregion
|
|
120
|
+
export { DiscoveryResult, DiscoveryState, ManifestMismatchEvent, RouteDiscoveryOptions, RouteDiscoveryRuntime, getCustomPatchRoutesOnNavigationFunction, useCustomRouteDiscovery, useRouteDiscovery };
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* react-router v8.5.1
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) Remix Software Inc.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE.md file in the root directory of this source tree.
|
|
8
|
+
*
|
|
9
|
+
* @license MIT
|
|
10
|
+
*/
|
|
11
|
+
import { createPath, invariant } from "../../router/history.js";
|
|
12
|
+
import { DiscoveryCancelledError, getRoutePattern, prependBasename } from "../../router/utils.js";
|
|
13
|
+
import { createClientRoutes } from "./routes.js";
|
|
14
|
+
import { getManifestPath, getManifestPathsParam, getPathsWithAncestors, handleClientVersionMismatch, isFogOfWarEnabled } from "./fog-of-war.js";
|
|
15
|
+
import { useFrameworkContext } from "./components.js";
|
|
16
|
+
import * as React$1 from "react";
|
|
17
|
+
//#region lib/dom/ssr/route-discovery.ts
|
|
18
|
+
var RouteDiscoveryRuntime = class {
|
|
19
|
+
ssrInfo;
|
|
20
|
+
enabled;
|
|
21
|
+
discoveredPaths = /* @__PURE__ */ new Set();
|
|
22
|
+
nextPaths = /* @__PURE__ */ new Set();
|
|
23
|
+
state;
|
|
24
|
+
ready = false;
|
|
25
|
+
registrations = /* @__PURE__ */ new Set();
|
|
26
|
+
listeners = /* @__PURE__ */ new Set();
|
|
27
|
+
fullLoadErrorListeners = /* @__PURE__ */ new Set();
|
|
28
|
+
fullLoad;
|
|
29
|
+
loadedManifest;
|
|
30
|
+
mismatch;
|
|
31
|
+
fetchImplementation;
|
|
32
|
+
loadManifest;
|
|
33
|
+
onError;
|
|
34
|
+
constructor(ssrInfo, { fetch: fetchImplementation = (request) => fetch(request), loadManifest } = {}) {
|
|
35
|
+
this.ssrInfo = ssrInfo;
|
|
36
|
+
this.fetchImplementation = fetchImplementation;
|
|
37
|
+
this.loadManifest = loadManifest ?? (() => this.loadManifestAsset());
|
|
38
|
+
let { manifest, context } = ssrInfo;
|
|
39
|
+
this.enabled = isFogOfWarEnabled(context.routeDiscovery, context.ssr);
|
|
40
|
+
this.state = this.enabled && !manifest.hmr ? "partial" : "complete";
|
|
41
|
+
}
|
|
42
|
+
get router() {
|
|
43
|
+
invariant(this.ssrInfo.router, "Router unavailable for route discovery");
|
|
44
|
+
return this.ssrInfo.router;
|
|
45
|
+
}
|
|
46
|
+
async loadManifestAsset() {
|
|
47
|
+
let { manifest } = this.ssrInfo;
|
|
48
|
+
if (this.loadedManifest) return this.loadedManifest;
|
|
49
|
+
try {
|
|
50
|
+
await import(
|
|
51
|
+
/* @vite-ignore */
|
|
52
|
+
/* webpackIgnore: true */
|
|
53
|
+
manifest.url
|
|
54
|
+
);
|
|
55
|
+
invariant(window.__reactRouterManifest, "Unable to load the client manifest");
|
|
56
|
+
invariant(window.__reactRouterManifest !== manifest, "The full route manifest asset did not provide a manifest.");
|
|
57
|
+
this.loadedManifest = window.__reactRouterManifest;
|
|
58
|
+
return this.loadedManifest;
|
|
59
|
+
} finally {
|
|
60
|
+
window.__reactRouterManifest = manifest;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
subscribe = (listener) => {
|
|
64
|
+
this.listeners.add(listener);
|
|
65
|
+
return () => {
|
|
66
|
+
this.listeners.delete(listener);
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
update(state, ready = this.ready) {
|
|
70
|
+
this.state = state;
|
|
71
|
+
this.ready = ready;
|
|
72
|
+
this.listeners.forEach((listener) => listener());
|
|
73
|
+
}
|
|
74
|
+
markReady = () => {
|
|
75
|
+
if (!this.ready) this.update(this.state, true);
|
|
76
|
+
};
|
|
77
|
+
register(registration) {
|
|
78
|
+
this.registrations.add(registration);
|
|
79
|
+
return () => {
|
|
80
|
+
this.registrations.delete(registration);
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
isComplete() {
|
|
84
|
+
return this.state === "complete";
|
|
85
|
+
}
|
|
86
|
+
loadAllRoutes = () => {
|
|
87
|
+
if (this.isComplete()) return Promise.resolve();
|
|
88
|
+
if (this.fullLoad) return this.fullLoad;
|
|
89
|
+
let { manifest, routeModules, context } = this.ssrInfo;
|
|
90
|
+
this.fullLoad = Promise.resolve().then(async () => {
|
|
91
|
+
try {
|
|
92
|
+
let fullManifest = await this.loadManifest();
|
|
93
|
+
invariant(fullManifest.version === manifest.version, "The full route manifest does not match the running client version.");
|
|
94
|
+
applyManifestPatches(fullManifest.routes, manifest, routeModules, context.ssr, context.isSpaMode, this.router.patchRoutes, context.unstable_apiOnly === true);
|
|
95
|
+
this.nextPaths.clear();
|
|
96
|
+
this.update("complete");
|
|
97
|
+
} catch (error) {
|
|
98
|
+
this.fullLoadErrorListeners.forEach((listener) => listener(error));
|
|
99
|
+
this.update("partial");
|
|
100
|
+
throw error;
|
|
101
|
+
} finally {
|
|
102
|
+
this.fullLoad = void 0;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
this.update("loading");
|
|
106
|
+
return this.fullLoad;
|
|
107
|
+
};
|
|
108
|
+
reportError(error) {
|
|
109
|
+
if (this.onError) {
|
|
110
|
+
let { location, matches } = this.router.state;
|
|
111
|
+
this.onError(error, {
|
|
112
|
+
location,
|
|
113
|
+
params: matches[0]?.params || {},
|
|
114
|
+
pattern: getRoutePattern(matches)
|
|
115
|
+
});
|
|
116
|
+
} else console.error(error);
|
|
117
|
+
}
|
|
118
|
+
async dispatchMismatch(source, reloadUrl) {
|
|
119
|
+
let { version } = this.ssrInfo.manifest;
|
|
120
|
+
let stopped = false;
|
|
121
|
+
let defaultBehavior;
|
|
122
|
+
let event = {
|
|
123
|
+
source,
|
|
124
|
+
reloadUrl,
|
|
125
|
+
version,
|
|
126
|
+
stopPropagation() {
|
|
127
|
+
stopped = true;
|
|
128
|
+
},
|
|
129
|
+
loadAllRoutes: this.loadAllRoutes,
|
|
130
|
+
defaultBehavior: () => {
|
|
131
|
+
defaultBehavior ??= Promise.resolve().then(async () => {
|
|
132
|
+
if (source !== "imperative" && !this.isComplete()) await handleClientVersionMismatch(true, version, reloadUrl);
|
|
133
|
+
});
|
|
134
|
+
return defaultBehavior;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
let registrations = [...this.registrations].reverse().filter((registration) => registration.current.onManifestMismatch);
|
|
138
|
+
if (registrations.length === 0) return event.defaultBehavior();
|
|
139
|
+
let loadErrors = /* @__PURE__ */ new Set();
|
|
140
|
+
let onLoadError = (error) => loadErrors.add(error);
|
|
141
|
+
this.fullLoadErrorListeners.add(onLoadError);
|
|
142
|
+
try {
|
|
143
|
+
for (let registration of registrations) {
|
|
144
|
+
if (!this.registrations.has(registration)) continue;
|
|
145
|
+
try {
|
|
146
|
+
await registration.current.onManifestMismatch?.(event);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (!loadErrors.has(error)) throw error;
|
|
149
|
+
this.reportError(error);
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
if (stopped) break;
|
|
153
|
+
}
|
|
154
|
+
} finally {
|
|
155
|
+
this.fullLoadErrorListeners.delete(onLoadError);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
requestUrl(paths) {
|
|
159
|
+
let { manifest, context } = this.ssrInfo;
|
|
160
|
+
let params = new URLSearchParams();
|
|
161
|
+
params.set("paths", getManifestPathsParam(paths));
|
|
162
|
+
params.set("version", manifest.version);
|
|
163
|
+
let url = new URL(getManifestPath(context.routeDiscovery.manifestPath, context.basename), context.unstable_apiServerOrigin ?? window.location.origin);
|
|
164
|
+
url.search = params.toString();
|
|
165
|
+
return url;
|
|
166
|
+
}
|
|
167
|
+
discoverRoutes = async (paths, options = {}) => {
|
|
168
|
+
if (options.signal?.aborted) return { type: "aborted" };
|
|
169
|
+
if (this.isComplete()) return { type: "success" };
|
|
170
|
+
let pathnames = paths.map((path) => {
|
|
171
|
+
invariant(path.startsWith("/") && !path.startsWith("//") && !path.includes("\\"), "discoverRoutes expects application-root paths beginning with a single '/'.");
|
|
172
|
+
return prependBasename({
|
|
173
|
+
pathname: new URL(path, window.location.origin).pathname,
|
|
174
|
+
basename: this.ssrInfo.context.basename || "/"
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
return this.discover(pathnames, "imperative", null, options.signal);
|
|
178
|
+
};
|
|
179
|
+
async discover(paths, source, reloadUrl, signal, patch = this.router.patchRoutes) {
|
|
180
|
+
let { manifest, routeModules, context } = this.ssrInfo;
|
|
181
|
+
if (signal?.aborted) return { type: "aborted" };
|
|
182
|
+
if (this.isComplete()) return { type: "success" };
|
|
183
|
+
let pending = getPathsWithAncestors(paths.filter((path) => !this.discoveredPaths.has(path)));
|
|
184
|
+
if (!pending.length) return { type: "success" };
|
|
185
|
+
let batches = [];
|
|
186
|
+
if (source !== "imperative") {
|
|
187
|
+
if (this.requestUrl(pending).href.length > 7680) {
|
|
188
|
+
this.nextPaths.clear();
|
|
189
|
+
return { type: "success" };
|
|
190
|
+
}
|
|
191
|
+
batches.push(pending);
|
|
192
|
+
} else {
|
|
193
|
+
let batch = [];
|
|
194
|
+
for (let path of pending) {
|
|
195
|
+
if (this.discoveredPaths.has(path)) continue;
|
|
196
|
+
if (this.requestUrl([path]).href.length > 7680) {
|
|
197
|
+
console.warn(`Unable to discover path exceeding the manifest request URL limit: ${path}`);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (this.requestUrl([...batch, path]).href.length > 7680) {
|
|
201
|
+
batches.push(batch);
|
|
202
|
+
batch = [];
|
|
203
|
+
}
|
|
204
|
+
batch.push(path);
|
|
205
|
+
}
|
|
206
|
+
if (batch.length) batches.push(batch);
|
|
207
|
+
}
|
|
208
|
+
for (let batch of batches) try {
|
|
209
|
+
if (signal?.aborted) return { type: "aborted" };
|
|
210
|
+
if (this.isComplete()) return { type: "success" };
|
|
211
|
+
let response;
|
|
212
|
+
try {
|
|
213
|
+
response = await this.fetchImplementation(new Request(this.requestUrl(batch), { signal }), { type: "manifest" });
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (signal?.aborted) return { type: "aborted" };
|
|
216
|
+
if (this.isComplete()) return { type: "success" };
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
219
|
+
if (signal?.aborted) return { type: "aborted" };
|
|
220
|
+
if (this.isComplete()) return { type: "success" };
|
|
221
|
+
if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
|
|
222
|
+
if (response.status === 204 && response.headers.has("X-Remix-Reload-Document")) {
|
|
223
|
+
if (this.mismatch || [...this.registrations].some((r) => r.current.onManifestMismatch)) {
|
|
224
|
+
if (!this.mismatch) this.mismatch = Promise.resolve().then(() => this.dispatchMismatch(source, reloadUrl)).finally(() => {
|
|
225
|
+
this.mismatch = void 0;
|
|
226
|
+
});
|
|
227
|
+
await waitForDiscovery(this.mismatch, signal);
|
|
228
|
+
} else await waitForDiscovery(this.dispatchMismatch(source, reloadUrl), signal);
|
|
229
|
+
if (signal?.aborted) return { type: "aborted" };
|
|
230
|
+
return { type: this.isComplete() ? "success" : "version-mismatch" };
|
|
231
|
+
}
|
|
232
|
+
await handleClientVersionMismatch(false, manifest.version, reloadUrl);
|
|
233
|
+
let patches;
|
|
234
|
+
try {
|
|
235
|
+
patches = await response.json();
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (signal?.aborted) return { type: "aborted" };
|
|
238
|
+
if (this.isComplete()) return { type: "success" };
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
if (signal?.aborted) return { type: "aborted" };
|
|
242
|
+
if (this.isComplete()) return { type: "success" };
|
|
243
|
+
applyManifestPatches(patches, manifest, routeModules, context.ssr, context.isSpaMode, patch, context.unstable_apiOnly === true);
|
|
244
|
+
batch.forEach((path) => {
|
|
245
|
+
if (this.discoveredPaths.size >= 1e3) this.discoveredPaths.delete(this.discoveredPaths.values().next().value);
|
|
246
|
+
this.discoveredPaths.add(path);
|
|
247
|
+
});
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (signal?.aborted) return { type: "aborted" };
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
return { type: "success" };
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
function waitForDiscovery(promise, signal) {
|
|
256
|
+
return new Promise((resolve, reject) => {
|
|
257
|
+
let abort = () => resolve();
|
|
258
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
259
|
+
if (signal?.aborted) abort();
|
|
260
|
+
promise.then(() => resolve(), reject).finally(() => signal?.removeEventListener("abort", abort));
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
const serverLoad = () => Promise.resolve();
|
|
264
|
+
const serverDiscover = async () => ({ type: "success" });
|
|
265
|
+
/**
|
|
266
|
+
* Control lazy route discovery in Framework Mode with `future.unstable_customRouteDiscovery`
|
|
267
|
+
* enabled. This allows you to imperatively discover routes as well as add custom behavior
|
|
268
|
+
* when a manifest version mismatch is detected. Calling `loadAllRoutes` will load the entire
|
|
269
|
+
* route manifest for the current (potentially stale) client version - this requires that prior
|
|
270
|
+
* client artifacts are still available on your CDN _and_ you have not made breaking changes to
|
|
271
|
+
* the API contracts of your loaders and actions.
|
|
272
|
+
*
|
|
273
|
+
* See the [Route Discovery guide](../../how-to/route-discovery) for more information and examples.
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* let { state, discoverRoutes, loadAllRoutes } = unstable_useRouteDiscovery({
|
|
277
|
+
* onManifestMismatch: async (event) => {
|
|
278
|
+
* // Eagerly load the complete route tree for this client without requiring a hard reload
|
|
279
|
+
* await event.loadAllRoutes();
|
|
280
|
+
* },
|
|
281
|
+
* });
|
|
282
|
+
*
|
|
283
|
+
* // Eagerly discover specific routes
|
|
284
|
+
* discoverRoutes(["/a", "/b"]);
|
|
285
|
+
*
|
|
286
|
+
* @name unstable_useRouteDiscovery
|
|
287
|
+
* @public
|
|
288
|
+
* @category Hooks
|
|
289
|
+
* @mode framework
|
|
290
|
+
* @param options An awaited manifest-mismatch handler.
|
|
291
|
+
* @returns The client version, full-manifest loading state, and discovery operations.
|
|
292
|
+
*/
|
|
293
|
+
function useRouteDiscovery(options = {}) {
|
|
294
|
+
let context = useFrameworkContext();
|
|
295
|
+
invariant(context.future.unstable_customRouteDiscovery, "unstable_useRouteDiscovery requires future.unstable_customRouteDiscovery to be enabled in react-router.config.ts.");
|
|
296
|
+
invariant(context.manifest.url, "unstable_useRouteDiscovery is not supported in RSC mode.");
|
|
297
|
+
let runtime = context.routeDiscoveryRuntime;
|
|
298
|
+
let registration = React$1.useRef({ current: options });
|
|
299
|
+
let hasHandlers = Boolean(options.onManifestMismatch);
|
|
300
|
+
React$1.useLayoutEffect(() => {
|
|
301
|
+
registration.current.current = options;
|
|
302
|
+
});
|
|
303
|
+
React$1.useLayoutEffect(() => {
|
|
304
|
+
if (hasHandlers) return runtime?.register(registration.current);
|
|
305
|
+
}, [runtime, hasHandlers]);
|
|
306
|
+
let state = context.routeDiscoveryState ?? (isFogOfWarEnabled(context.routeDiscovery, context.ssr) && !context.manifest.hmr ? "partial" : "complete");
|
|
307
|
+
return {
|
|
308
|
+
version: context.manifest.version,
|
|
309
|
+
state,
|
|
310
|
+
loadAllRoutes: runtime?.loadAllRoutes || serverLoad,
|
|
311
|
+
discoverRoutes: runtime?.discoverRoutes || serverDiscover
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function getCustomPatchRoutesOnNavigationFunction(getRouter, runtime) {
|
|
315
|
+
if (!runtime.enabled) return;
|
|
316
|
+
return async ({ path, patch, signal, fetcherKey }) => {
|
|
317
|
+
let { state } = getRouter();
|
|
318
|
+
if ((await runtime.discover([path], fetcherKey != null ? "fetcher" : "navigation", fetcherKey != null ? window.location.href : createPath(state.navigation.location || state.location), signal, patch)).type === "version-mismatch") throw new DiscoveryCancelledError();
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function useCustomRouteDiscovery(runtime, onError) {
|
|
322
|
+
React$1.useLayoutEffect(() => {
|
|
323
|
+
runtime.onError = onError;
|
|
324
|
+
}, [runtime, onError]);
|
|
325
|
+
let [state, setState] = React$1.useState(runtime.state);
|
|
326
|
+
React$1.useLayoutEffect(() => {
|
|
327
|
+
let updateState = () => {
|
|
328
|
+
let nextState = runtime.state;
|
|
329
|
+
React$1.startTransition(() => setState(nextState));
|
|
330
|
+
};
|
|
331
|
+
let unsubscribe = runtime.subscribe(updateState);
|
|
332
|
+
updateState();
|
|
333
|
+
return unsubscribe;
|
|
334
|
+
}, [runtime]);
|
|
335
|
+
React$1.useEffect(() => {
|
|
336
|
+
if (!runtime.enabled || window.navigator?.connection?.saveData === true) return;
|
|
337
|
+
let { discoveredPaths, nextPaths } = runtime;
|
|
338
|
+
function registerElement(el) {
|
|
339
|
+
let path = el.tagName === "FORM" ? el.getAttribute("action") : el.getAttribute("href");
|
|
340
|
+
if (!path) return;
|
|
341
|
+
let pathname = el.tagName === "A" ? el.pathname : new URL(path, window.location.origin).pathname;
|
|
342
|
+
if (!discoveredPaths.has(pathname)) nextPaths.add(pathname);
|
|
343
|
+
}
|
|
344
|
+
let active = false;
|
|
345
|
+
async function fetchPatches() {
|
|
346
|
+
if (!active || runtime.state === "complete") return;
|
|
347
|
+
document.querySelectorAll("a[data-discover], form[data-discover]").forEach(registerElement);
|
|
348
|
+
let lazyPaths = Array.from(nextPaths.keys()).filter((path) => {
|
|
349
|
+
if (discoveredPaths.has(path)) {
|
|
350
|
+
nextPaths.delete(path);
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
return true;
|
|
354
|
+
});
|
|
355
|
+
if (lazyPaths.length === 0) return;
|
|
356
|
+
try {
|
|
357
|
+
await runtime.discover(lazyPaths, "eager", null);
|
|
358
|
+
} catch (e) {
|
|
359
|
+
runtime.reportError(e);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
let debouncedFetchPatches = debounce(fetchPatches, 100);
|
|
363
|
+
let observer = new MutationObserver(() => debouncedFetchPatches());
|
|
364
|
+
function updateDiscovery() {
|
|
365
|
+
let { ready, state } = runtime;
|
|
366
|
+
if (!ready || state === "complete") {
|
|
367
|
+
active = false;
|
|
368
|
+
observer.disconnect();
|
|
369
|
+
} else if (!active) {
|
|
370
|
+
active = true;
|
|
371
|
+
observer.observe(document.documentElement, {
|
|
372
|
+
subtree: true,
|
|
373
|
+
childList: true,
|
|
374
|
+
attributes: true,
|
|
375
|
+
attributeFilter: [
|
|
376
|
+
"data-discover",
|
|
377
|
+
"href",
|
|
378
|
+
"action"
|
|
379
|
+
]
|
|
380
|
+
});
|
|
381
|
+
fetchPatches();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
let unsubscribe = runtime.subscribe(updateDiscovery);
|
|
385
|
+
updateDiscovery();
|
|
386
|
+
return () => {
|
|
387
|
+
active = false;
|
|
388
|
+
unsubscribe();
|
|
389
|
+
observer.disconnect();
|
|
390
|
+
};
|
|
391
|
+
}, [runtime]);
|
|
392
|
+
return state;
|
|
393
|
+
}
|
|
394
|
+
function applyManifestPatches(serverPatches, manifest, routeModules, ssr, isSpaMode, patchRoutes, isApiOnly) {
|
|
395
|
+
let knownRoutes = new Set(Object.keys(manifest.routes));
|
|
396
|
+
let patches = Object.values(serverPatches).reduce((acc, route) => {
|
|
397
|
+
if (route && !knownRoutes.has(route.id)) acc[route.id] = route;
|
|
398
|
+
return acc;
|
|
399
|
+
}, {});
|
|
400
|
+
let parentIds = /* @__PURE__ */ new Set();
|
|
401
|
+
Object.values(patches).forEach((patch) => {
|
|
402
|
+
if (patch && (!patch.parentId || !patches[patch.parentId])) parentIds.add(patch.parentId);
|
|
403
|
+
});
|
|
404
|
+
parentIds.forEach((parentId) => patchRoutes(parentId || null, createClientRoutes(patches, routeModules, null, ssr, isSpaMode, parentId, void 0, void 0, isApiOnly)));
|
|
405
|
+
Object.assign(manifest.routes, patches);
|
|
406
|
+
}
|
|
407
|
+
function debounce(callback, wait) {
|
|
408
|
+
let timeoutId;
|
|
409
|
+
return (...args) => {
|
|
410
|
+
window.clearTimeout(timeoutId);
|
|
411
|
+
timeoutId = window.setTimeout(() => callback(...args), wait);
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
//#endregion
|
|
415
|
+
export { RouteDiscoveryRuntime, getCustomPatchRoutesOnNavigationFunction, useCustomRouteDiscovery, useRouteDiscovery };
|