react-router 7.15.1 → 8.0.0-pre.0
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 +18 -2
- package/dist/development/dom-export.d.ts +6 -172
- package/dist/development/dom-export.js +12 -1007
- package/dist/development/index-react-server-client.d.ts +7 -4
- package/dist/development/index-react-server-client.js +8 -52
- package/dist/development/index-react-server.d.ts +1645 -1635
- package/dist/development/index-react-server.js +2880 -3642
- package/dist/development/index.d.ts +43 -1469
- package/dist/development/index.js +37 -2621
- package/dist/development/lib/actions.js +62 -0
- package/dist/development/lib/components.d.ts +1022 -0
- package/dist/development/lib/components.js +835 -0
- package/dist/development/lib/context.d.ts +83 -0
- package/dist/development/lib/context.js +41 -0
- package/dist/development/lib/dom/dom.d.ts +119 -0
- package/dist/development/lib/dom/dom.js +143 -0
- package/dist/development/lib/dom/lib.d.ts +2042 -0
- package/dist/development/lib/dom/lib.js +1259 -0
- package/dist/development/lib/dom/server.d.ts +138 -0
- package/dist/development/lib/dom/server.js +301 -0
- package/dist/development/lib/dom/ssr/components.d.ts +196 -0
- package/dist/development/lib/dom/ssr/components.js +579 -0
- package/dist/development/lib/dom/ssr/data.js +29 -0
- package/dist/development/lib/dom/ssr/entry.d.ts +59 -0
- package/dist/development/lib/dom/ssr/errorBoundaries.d.ts +27 -0
- package/dist/development/lib/dom/ssr/errorBoundaries.js +83 -0
- package/dist/development/lib/dom/ssr/errors.d.ts +7 -0
- package/dist/development/lib/dom/ssr/errors.js +36 -0
- package/dist/development/lib/dom/ssr/fallback.js +28 -0
- package/dist/development/lib/dom/ssr/fog-of-war.d.ts +12 -0
- package/dist/development/lib/dom/ssr/fog-of-war.js +170 -0
- package/dist/development/lib/dom/ssr/hydration.d.ts +32 -0
- package/dist/development/lib/dom/ssr/hydration.js +29 -0
- package/dist/development/lib/dom/ssr/invariant.js +16 -0
- package/dist/development/lib/dom/ssr/links.js +170 -0
- package/dist/development/lib/dom/ssr/markup.js +24 -0
- package/dist/development/lib/dom/ssr/routeModules.d.ts +206 -0
- package/dist/development/lib/dom/ssr/routeModules.js +31 -0
- package/dist/development/lib/dom/ssr/routes-test-stub.d.ts +62 -0
- package/dist/development/lib/dom/ssr/routes-test-stub.js +108 -0
- package/dist/development/lib/dom/ssr/routes.d.ts +33 -0
- package/dist/development/lib/dom/ssr/routes.js +303 -0
- package/dist/development/lib/dom/ssr/server.d.ts +45 -0
- package/dist/development/lib/dom/ssr/server.js +68 -0
- package/dist/development/lib/dom/ssr/single-fetch.d.ts +14 -0
- package/dist/development/lib/dom/ssr/single-fetch.js +346 -0
- package/dist/development/lib/dom-export/dom-router-provider.d.ts +9 -0
- package/dist/development/lib/dom-export/dom-router-provider.js +22 -0
- package/dist/development/lib/dom-export/hydrated-router.d.ts +125 -0
- package/dist/development/lib/dom-export/hydrated-router.js +153 -0
- package/dist/development/lib/errors.js +29 -0
- package/dist/development/lib/hooks.d.ts +947 -0
- package/dist/development/lib/hooks.js +1386 -0
- package/dist/development/lib/href.d.ts +20 -0
- package/dist/development/lib/href.js +50 -0
- package/dist/development/lib/router/history.d.ts +258 -0
- package/dist/development/lib/router/history.js +371 -0
- package/dist/development/lib/router/instrumentation.d.ts +86 -0
- package/dist/development/lib/router/instrumentation.js +213 -0
- package/dist/development/lib/router/links.d.ts +113 -0
- package/dist/development/lib/router/router.d.ts +663 -0
- package/dist/development/lib/router/router.js +2981 -0
- package/dist/development/lib/router/utils.d.ts +942 -0
- package/dist/development/lib/router/utils.js +791 -0
- package/dist/development/lib/rsc/browser.d.ts +137 -0
- package/dist/development/lib/rsc/browser.js +599 -0
- package/dist/development/lib/rsc/errorBoundaries.d.ts +11 -0
- package/dist/development/lib/rsc/errorBoundaries.js +90 -0
- package/dist/development/lib/rsc/html-stream/browser.d.ts +48 -0
- package/dist/development/lib/rsc/html-stream/browser.js +74 -0
- package/dist/development/lib/rsc/html-stream/server.js +78 -0
- package/dist/development/lib/rsc/route-modules.js +27 -0
- package/dist/development/lib/rsc/server.rsc.d.ts +219 -0
- package/dist/development/lib/rsc/server.ssr.d.ts +129 -0
- package/dist/development/lib/rsc/server.ssr.js +388 -0
- package/dist/development/lib/server-runtime/build.d.ts +66 -0
- package/dist/development/lib/server-runtime/cookies.d.ts +66 -0
- package/dist/development/lib/server-runtime/cookies.js +139 -0
- package/dist/development/lib/server-runtime/crypto.js +43 -0
- package/dist/development/lib/server-runtime/data.d.ts +13 -0
- package/dist/development/lib/server-runtime/data.js +25 -0
- package/dist/development/lib/server-runtime/dev.d.ts +9 -0
- package/dist/development/lib/server-runtime/dev.js +26 -0
- package/dist/development/lib/server-runtime/entry.js +20 -0
- package/dist/development/lib/server-runtime/errors.js +95 -0
- package/dist/development/lib/server-runtime/headers.js +73 -0
- package/dist/development/lib/server-runtime/invariant.js +19 -0
- package/dist/development/lib/server-runtime/mode.d.ts +12 -0
- package/dist/development/lib/server-runtime/mode.js +25 -0
- package/dist/development/lib/server-runtime/routeMatching.js +28 -0
- package/dist/development/lib/server-runtime/routes.d.ts +13 -0
- package/dist/development/lib/server-runtime/routes.js +74 -0
- package/dist/development/lib/server-runtime/server.d.ts +10 -0
- package/dist/development/lib/server-runtime/server.js +351 -0
- package/dist/development/lib/server-runtime/serverHandoff.js +17 -0
- package/dist/development/lib/server-runtime/sessions/cookieStorage.d.ts +25 -0
- package/dist/development/lib/server-runtime/sessions/cookieStorage.js +45 -0
- package/dist/development/lib/server-runtime/sessions/memoryStorage.d.ts +23 -0
- package/dist/development/lib/server-runtime/sessions/memoryStorage.js +52 -0
- package/dist/development/lib/server-runtime/sessions.d.ts +145 -0
- package/dist/development/lib/server-runtime/sessions.js +98 -0
- package/dist/development/lib/server-runtime/single-fetch.d.ts +7 -0
- package/dist/development/lib/server-runtime/single-fetch.js +215 -0
- package/dist/development/lib/server-runtime/urls.js +31 -0
- package/dist/development/lib/server-runtime/warnings.js +20 -0
- package/dist/development/lib/types/future.d.ts +9 -0
- package/dist/development/lib/types/internal.d.ts +26 -177
- package/dist/development/lib/types/internal.js +3 -2
- package/dist/{production/register-Bsscfj79.d.ts → development/lib/types/register.d.ts} +9 -15
- package/dist/development/lib/types/route-data.d.ts +113 -0
- package/dist/development/lib/types/route-module-annotations.d.ts +149 -0
- package/dist/development/lib/types/route-module.d.ts +19 -0
- package/dist/development/lib/types/serializes-to.d.ts +13 -0
- package/dist/development/lib/types/utils.d.ts +11 -0
- package/dist/development/vendor/turbo-stream-v2/flatten.js +159 -0
- package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +178 -0
- package/dist/development/vendor/turbo-stream-v2/unflatten.js +198 -0
- package/dist/development/vendor/turbo-stream-v2/utils.js +47 -0
- package/dist/production/dom-export.d.ts +6 -172
- package/dist/production/dom-export.js +12 -1007
- package/dist/production/index-react-server-client.d.ts +7 -4
- package/dist/production/index-react-server-client.js +8 -52
- package/dist/production/index-react-server.d.ts +1645 -1635
- package/dist/production/index-react-server.js +2871 -3642
- package/dist/production/index.d.ts +43 -1469
- package/dist/production/index.js +37 -2621
- package/dist/production/lib/actions.js +62 -0
- package/dist/production/lib/components.d.ts +1022 -0
- package/dist/production/lib/components.js +835 -0
- package/dist/production/lib/context.d.ts +83 -0
- package/dist/production/lib/context.js +41 -0
- package/dist/production/lib/dom/dom.d.ts +119 -0
- package/dist/production/lib/dom/dom.js +143 -0
- package/dist/production/lib/dom/lib.d.ts +2042 -0
- package/dist/production/lib/dom/lib.js +1259 -0
- package/dist/production/lib/dom/server.d.ts +138 -0
- package/dist/production/lib/dom/server.js +301 -0
- package/dist/production/lib/dom/ssr/components.d.ts +196 -0
- package/dist/production/lib/dom/ssr/components.js +579 -0
- package/dist/production/lib/dom/ssr/data.js +29 -0
- package/dist/production/lib/dom/ssr/entry.d.ts +59 -0
- package/dist/production/lib/dom/ssr/errorBoundaries.d.ts +27 -0
- package/dist/production/lib/dom/ssr/errorBoundaries.js +83 -0
- package/dist/production/lib/dom/ssr/errors.d.ts +7 -0
- package/dist/production/lib/dom/ssr/errors.js +36 -0
- package/dist/production/lib/dom/ssr/fallback.js +21 -0
- package/dist/production/lib/dom/ssr/fog-of-war.d.ts +12 -0
- package/dist/production/lib/dom/ssr/fog-of-war.js +170 -0
- package/dist/production/lib/dom/ssr/hydration.d.ts +32 -0
- package/dist/production/lib/dom/ssr/hydration.js +29 -0
- package/dist/production/lib/dom/ssr/invariant.js +16 -0
- package/dist/production/lib/dom/ssr/links.js +170 -0
- package/dist/production/lib/dom/ssr/markup.js +24 -0
- package/dist/production/lib/dom/ssr/routeModules.d.ts +206 -0
- package/dist/production/lib/dom/ssr/routeModules.js +31 -0
- package/dist/production/lib/dom/ssr/routes-test-stub.d.ts +62 -0
- package/dist/production/lib/dom/ssr/routes-test-stub.js +108 -0
- package/dist/production/lib/dom/ssr/routes.d.ts +33 -0
- package/dist/production/lib/dom/ssr/routes.js +303 -0
- package/dist/production/lib/dom/ssr/server.d.ts +45 -0
- package/dist/production/lib/dom/ssr/server.js +68 -0
- package/dist/production/lib/dom/ssr/single-fetch.d.ts +14 -0
- package/dist/production/lib/dom/ssr/single-fetch.js +346 -0
- package/dist/production/lib/dom-export/dom-router-provider.d.ts +9 -0
- package/dist/production/lib/dom-export/dom-router-provider.js +22 -0
- package/dist/production/lib/dom-export/hydrated-router.d.ts +125 -0
- package/dist/production/lib/dom-export/hydrated-router.js +153 -0
- package/dist/production/lib/errors.js +29 -0
- package/dist/production/lib/hooks.d.ts +947 -0
- package/dist/production/lib/hooks.js +1371 -0
- package/dist/production/lib/href.d.ts +20 -0
- package/dist/production/lib/href.js +50 -0
- package/dist/production/lib/router/history.d.ts +258 -0
- package/dist/production/lib/router/history.js +371 -0
- package/dist/production/lib/router/instrumentation.d.ts +86 -0
- package/dist/production/lib/router/instrumentation.js +213 -0
- package/dist/production/lib/router/links.d.ts +113 -0
- package/dist/production/lib/router/router.d.ts +663 -0
- package/dist/production/lib/router/router.js +2981 -0
- package/dist/production/lib/router/utils.d.ts +942 -0
- package/dist/production/lib/router/utils.js +782 -0
- package/dist/production/lib/rsc/browser.d.ts +137 -0
- package/dist/production/lib/rsc/browser.js +599 -0
- package/dist/production/lib/rsc/errorBoundaries.d.ts +11 -0
- package/dist/production/lib/rsc/errorBoundaries.js +90 -0
- package/dist/production/lib/rsc/html-stream/browser.d.ts +48 -0
- package/dist/production/lib/rsc/html-stream/browser.js +74 -0
- package/dist/production/lib/rsc/html-stream/server.js +78 -0
- package/dist/production/lib/rsc/route-modules.js +27 -0
- package/dist/production/lib/rsc/server.rsc.d.ts +219 -0
- package/dist/production/lib/rsc/server.ssr.d.ts +129 -0
- package/dist/production/lib/rsc/server.ssr.js +388 -0
- package/dist/production/lib/server-runtime/build.d.ts +66 -0
- package/dist/production/lib/server-runtime/cookies.d.ts +66 -0
- package/dist/production/lib/server-runtime/cookies.js +139 -0
- package/dist/production/lib/server-runtime/crypto.js +43 -0
- package/dist/production/lib/server-runtime/data.d.ts +13 -0
- package/dist/production/lib/server-runtime/data.js +25 -0
- package/dist/production/lib/server-runtime/dev.d.ts +9 -0
- package/dist/production/lib/server-runtime/dev.js +26 -0
- package/dist/production/lib/server-runtime/entry.js +20 -0
- package/dist/production/lib/server-runtime/errors.js +95 -0
- package/dist/production/lib/server-runtime/headers.js +73 -0
- package/dist/production/lib/server-runtime/invariant.js +19 -0
- package/dist/production/lib/server-runtime/mode.d.ts +12 -0
- package/dist/production/lib/server-runtime/mode.js +25 -0
- package/dist/production/lib/server-runtime/routeMatching.js +28 -0
- package/dist/production/lib/server-runtime/routes.d.ts +13 -0
- package/dist/production/lib/server-runtime/routes.js +74 -0
- package/dist/production/lib/server-runtime/server.d.ts +10 -0
- package/dist/production/lib/server-runtime/server.js +351 -0
- package/dist/production/lib/server-runtime/serverHandoff.js +17 -0
- package/dist/production/lib/server-runtime/sessions/cookieStorage.d.ts +25 -0
- package/dist/production/lib/server-runtime/sessions/cookieStorage.js +45 -0
- package/dist/production/lib/server-runtime/sessions/memoryStorage.d.ts +23 -0
- package/dist/production/lib/server-runtime/sessions/memoryStorage.js +52 -0
- package/dist/production/lib/server-runtime/sessions.d.ts +145 -0
- package/dist/production/lib/server-runtime/sessions.js +98 -0
- package/dist/production/lib/server-runtime/single-fetch.d.ts +7 -0
- package/dist/production/lib/server-runtime/single-fetch.js +215 -0
- package/dist/production/lib/server-runtime/urls.js +31 -0
- package/dist/production/lib/server-runtime/warnings.js +20 -0
- package/dist/production/lib/types/future.d.ts +9 -0
- package/dist/production/lib/types/internal.d.ts +26 -177
- package/dist/production/lib/types/internal.js +3 -2
- package/dist/{development/register-Bsscfj79.d.ts → production/lib/types/register.d.ts} +9 -15
- package/dist/production/lib/types/route-data.d.ts +113 -0
- package/dist/production/lib/types/route-module-annotations.d.ts +149 -0
- package/dist/production/lib/types/route-module.d.ts +19 -0
- package/dist/production/lib/types/serializes-to.d.ts +13 -0
- package/dist/production/lib/types/utils.d.ts +11 -0
- package/dist/production/vendor/turbo-stream-v2/flatten.js +159 -0
- package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +178 -0
- package/dist/production/vendor/turbo-stream-v2/unflatten.js +198 -0
- package/dist/production/vendor/turbo-stream-v2/utils.js +47 -0
- package/docs/explanation/backend-for-frontend.md +50 -0
- package/docs/explanation/code-splitting.md +77 -0
- package/docs/explanation/concurrency.md +135 -0
- package/docs/explanation/form-vs-fetcher.md +292 -0
- package/docs/explanation/hot-module-replacement.md +137 -0
- package/docs/explanation/hydration.md +14 -0
- package/docs/explanation/index-query-param.md +86 -0
- package/docs/explanation/index.md +4 -0
- package/docs/explanation/lazy-route-discovery.md +78 -0
- package/docs/explanation/location.md +6 -0
- package/docs/explanation/progressive-enhancement.md +150 -0
- package/docs/explanation/race-conditions.md +88 -0
- package/docs/explanation/react-transitions.md +160 -0
- package/docs/explanation/route-matching.md +7 -0
- package/docs/explanation/server-client-execution.md +4 -0
- package/docs/explanation/sessions-and-cookies.md +465 -0
- package/docs/explanation/special-files.md +16 -0
- package/docs/explanation/state-management.md +524 -0
- package/docs/explanation/styling.md +87 -0
- package/docs/explanation/type-safety.md +82 -0
- package/docs/how-to/accessibility.md +44 -0
- package/docs/how-to/client-data.md +199 -0
- package/docs/how-to/data-strategy.md +317 -0
- package/docs/how-to/error-boundary.md +231 -0
- package/docs/how-to/error-reporting.md +134 -0
- package/docs/how-to/fetchers.md +307 -0
- package/docs/how-to/file-route-conventions.md +410 -0
- package/docs/how-to/file-uploads.md +217 -0
- package/docs/how-to/form-validation.md +120 -0
- package/docs/how-to/headers.md +164 -0
- package/docs/how-to/index.md +4 -0
- package/docs/how-to/instrumentation.md +556 -0
- package/docs/how-to/meta.md +40 -0
- package/docs/how-to/middleware.md +728 -0
- package/docs/how-to/navigation-blocking.md +233 -0
- package/docs/how-to/optimize-revalidation.md +12 -0
- package/docs/how-to/pre-rendering.md +225 -0
- package/docs/how-to/presets.md +103 -0
- package/docs/how-to/react-server-components.md +899 -0
- package/docs/how-to/resource-routes.md +126 -0
- package/docs/how-to/route-module-type-safety.md +100 -0
- package/docs/how-to/search-params.md +4 -0
- package/docs/how-to/security.md +30 -0
- package/docs/how-to/server-bundles.md +66 -0
- package/docs/how-to/spa.md +120 -0
- package/docs/how-to/status.md +63 -0
- package/docs/how-to/suspense.md +132 -0
- package/docs/how-to/using-handle.md +117 -0
- package/docs/how-to/view-transitions.md +237 -0
- package/docs/how-to/webhook.md +50 -0
- package/docs/index.md +39 -0
- package/docs/start/data/actions.md +138 -0
- package/docs/start/data/custom.md +198 -0
- package/docs/start/data/data-loading.md +44 -0
- package/docs/start/data/index.md +4 -0
- package/docs/start/data/installation.md +52 -0
- package/docs/start/data/navigating.md +12 -0
- package/docs/start/data/pending-ui.md +12 -0
- package/docs/start/data/route-object.md +248 -0
- package/docs/start/data/routing.md +281 -0
- package/docs/start/data/testing.md +8 -0
- package/docs/start/declarative/index.md +4 -0
- package/docs/start/declarative/installation.md +43 -0
- package/docs/start/declarative/navigating.md +133 -0
- package/docs/start/declarative/routing.md +237 -0
- package/docs/start/declarative/url-values.md +65 -0
- package/docs/start/framework/actions.md +175 -0
- package/docs/start/framework/data-loading.md +201 -0
- package/docs/start/framework/deploying.md +96 -0
- package/docs/start/framework/index.md +4 -0
- package/docs/start/framework/installation.md +42 -0
- package/docs/start/framework/navigating.md +182 -0
- package/docs/start/framework/pending-ui.md +142 -0
- package/docs/start/framework/rendering.md +59 -0
- package/docs/start/framework/route-module.md +527 -0
- package/docs/start/framework/routing.md +362 -0
- package/docs/start/framework/testing.md +133 -0
- package/docs/start/index.md +4 -0
- package/docs/start/modes.md +201 -0
- package/docs/upgrading/component-routes.md +363 -0
- package/docs/upgrading/future.md +31 -0
- package/docs/upgrading/index.md +4 -0
- package/docs/upgrading/remix.md +403 -0
- package/docs/upgrading/router-provider.md +442 -0
- package/docs/upgrading/v6.md +379 -0
- package/package.json +44 -87
- package/dist/development/browser-3AnU12UI.d.mts +0 -318
- package/dist/development/browser-BOdXz9dK.d.ts +0 -318
- package/dist/development/chunk-4N6VE7H7.mjs +0 -11528
- package/dist/development/chunk-4YRVXM2U.js +0 -188
- package/dist/development/chunk-66UKHEGQ.js +0 -1362
- package/dist/development/chunk-D6LUOGOQ.js +0 -10229
- package/dist/development/chunk-RJYABSBD.mjs +0 -2585
- package/dist/development/context-ByvtofY2.d.mts +0 -1771
- package/dist/development/data-BVUf681J.d.mts +0 -1732
- package/dist/development/data-BqZ2x964.d.ts +0 -1732
- package/dist/development/dom-export.d.mts +0 -172
- package/dist/development/dom-export.mjs +0 -1008
- package/dist/development/index-react-server-client-BS5F89FR.d.ts +0 -3655
- package/dist/development/index-react-server-client-DY04-103.d.mts +0 -2600
- package/dist/development/index-react-server-client.d.mts +0 -4
- package/dist/development/index-react-server-client.mjs +0 -59
- package/dist/development/index-react-server.d.mts +0 -2703
- package/dist/development/index-react-server.mjs +0 -3780
- package/dist/development/index.d.mts +0 -1472
- package/dist/development/index.mjs +0 -277
- package/dist/development/instrumentation-cRWWLfsU.d.ts +0 -715
- package/dist/development/lib/types/internal.d.mts +0 -184
- package/dist/development/lib/types/internal.mjs +0 -10
- package/dist/development/register-Df8okEea.d.mts +0 -30
- package/dist/production/browser-3AnU12UI.d.mts +0 -318
- package/dist/production/browser-BOdXz9dK.d.ts +0 -318
- package/dist/production/chunk-6S4627ZB.mjs +0 -2585
- package/dist/production/chunk-HUBUW7R3.js +0 -10229
- package/dist/production/chunk-JAKZPQZC.mjs +0 -11528
- package/dist/production/chunk-PNZCCTKT.js +0 -1362
- package/dist/production/chunk-Y6IFXO7V.js +0 -188
- package/dist/production/context-ByvtofY2.d.mts +0 -1771
- package/dist/production/data-BVUf681J.d.mts +0 -1732
- package/dist/production/data-BqZ2x964.d.ts +0 -1732
- package/dist/production/dom-export.d.mts +0 -172
- package/dist/production/dom-export.mjs +0 -1008
- package/dist/production/index-react-server-client-BS5F89FR.d.ts +0 -3655
- package/dist/production/index-react-server-client-DY04-103.d.mts +0 -2600
- package/dist/production/index-react-server-client.d.mts +0 -4
- package/dist/production/index-react-server-client.mjs +0 -59
- package/dist/production/index-react-server.d.mts +0 -2703
- package/dist/production/index-react-server.mjs +0 -3780
- package/dist/production/index.d.mts +0 -1472
- package/dist/production/index.mjs +0 -277
- package/dist/production/instrumentation-cRWWLfsU.d.ts +0 -715
- package/dist/production/lib/types/internal.d.mts +0 -184
- package/dist/production/lib/types/internal.mjs +0 -10
- package/dist/production/register-Df8okEea.d.mts +0 -30
|
@@ -0,0 +1,2981 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* react-router v8.0.0-pre.0
|
|
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 { createBrowserURLImpl, createLocation, createPath, invariant, parsePath, warning } from "./history.js";
|
|
12
|
+
import { ErrorResponseImpl, RouterContextProvider, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, flattenAndRankRoutes, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, matchRoutesImpl, prependBasename, removeDoubleSlashes, resolveTo, stripBasename } from "./utils.js";
|
|
13
|
+
import { getRouteInstrumentationUpdates, instrumentClientSideRouter } from "./instrumentation.js";
|
|
14
|
+
//#region lib/router/router.ts
|
|
15
|
+
const validMutationMethodsArr = [
|
|
16
|
+
"POST",
|
|
17
|
+
"PUT",
|
|
18
|
+
"PATCH",
|
|
19
|
+
"DELETE"
|
|
20
|
+
];
|
|
21
|
+
const validMutationMethods = new Set(validMutationMethodsArr);
|
|
22
|
+
const validRequestMethodsArr = ["GET", ...validMutationMethodsArr];
|
|
23
|
+
const validRequestMethods = new Set(validRequestMethodsArr);
|
|
24
|
+
const redirectStatusCodes = new Set([
|
|
25
|
+
301,
|
|
26
|
+
302,
|
|
27
|
+
303,
|
|
28
|
+
307,
|
|
29
|
+
308
|
|
30
|
+
]);
|
|
31
|
+
const redirectPreserveMethodStatusCodes = new Set([307, 308]);
|
|
32
|
+
const IDLE_NAVIGATION = {
|
|
33
|
+
state: "idle",
|
|
34
|
+
location: void 0,
|
|
35
|
+
matches: void 0,
|
|
36
|
+
historyAction: void 0,
|
|
37
|
+
formMethod: void 0,
|
|
38
|
+
formAction: void 0,
|
|
39
|
+
formEncType: void 0,
|
|
40
|
+
formData: void 0,
|
|
41
|
+
json: void 0,
|
|
42
|
+
text: void 0
|
|
43
|
+
};
|
|
44
|
+
const IDLE_FETCHER = {
|
|
45
|
+
state: "idle",
|
|
46
|
+
data: void 0,
|
|
47
|
+
formMethod: void 0,
|
|
48
|
+
formAction: void 0,
|
|
49
|
+
formEncType: void 0,
|
|
50
|
+
formData: void 0,
|
|
51
|
+
json: void 0,
|
|
52
|
+
text: void 0
|
|
53
|
+
};
|
|
54
|
+
const IDLE_BLOCKER = {
|
|
55
|
+
state: "unblocked",
|
|
56
|
+
proceed: void 0,
|
|
57
|
+
reset: void 0,
|
|
58
|
+
location: void 0
|
|
59
|
+
};
|
|
60
|
+
const TRANSITIONS_STORAGE_KEY = "remix-router-transitions";
|
|
61
|
+
const ResetLoaderDataSymbol = Symbol("ResetLoaderData");
|
|
62
|
+
/**
|
|
63
|
+
* Encapsulates the stable and in-flight route trees together with their
|
|
64
|
+
* pre-computed branch caches so the structures always stay in sync.
|
|
65
|
+
*/
|
|
66
|
+
var DataRoutes = class {
|
|
67
|
+
#routes;
|
|
68
|
+
#branches;
|
|
69
|
+
#hmrRoutes;
|
|
70
|
+
#hmrBranches;
|
|
71
|
+
constructor(routes) {
|
|
72
|
+
this.#routes = routes;
|
|
73
|
+
this.#branches = flattenAndRankRoutes(routes);
|
|
74
|
+
}
|
|
75
|
+
/** The stable route tree */
|
|
76
|
+
get stableRoutes() {
|
|
77
|
+
return this.#routes;
|
|
78
|
+
}
|
|
79
|
+
/** The in-flight route tree if one is active, otherwise the stable tree */
|
|
80
|
+
get activeRoutes() {
|
|
81
|
+
return this.#hmrRoutes ?? this.#routes;
|
|
82
|
+
}
|
|
83
|
+
/** Pre-computed branches */
|
|
84
|
+
get branches() {
|
|
85
|
+
return this.#hmrBranches ?? this.#branches;
|
|
86
|
+
}
|
|
87
|
+
get hasHMRRoutes() {
|
|
88
|
+
return this.#hmrRoutes != null;
|
|
89
|
+
}
|
|
90
|
+
/** Replace the stable route tree and recompute its branches */
|
|
91
|
+
setRoutes(routes) {
|
|
92
|
+
this.#routes = routes;
|
|
93
|
+
this.#branches = flattenAndRankRoutes(routes);
|
|
94
|
+
}
|
|
95
|
+
/** Set a new in-flight route tree and recompute its branches */
|
|
96
|
+
setHmrRoutes(routes) {
|
|
97
|
+
this.#hmrRoutes = routes;
|
|
98
|
+
this.#hmrBranches = flattenAndRankRoutes(routes);
|
|
99
|
+
}
|
|
100
|
+
/** Commit in-flight routes/branches to the stable slot and clear in-flight */
|
|
101
|
+
commitHmrRoutes() {
|
|
102
|
+
if (this.#hmrRoutes) {
|
|
103
|
+
this.#routes = this.#hmrRoutes;
|
|
104
|
+
this.#branches = this.#hmrBranches;
|
|
105
|
+
this.#hmrRoutes = void 0;
|
|
106
|
+
this.#hmrBranches = void 0;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Create a router and listen to history POP navigations
|
|
112
|
+
*/
|
|
113
|
+
function createRouter(init) {
|
|
114
|
+
const routerWindow = init.window ? init.window : typeof window !== "undefined" ? window : void 0;
|
|
115
|
+
const isBrowser = typeof routerWindow !== "undefined" && typeof routerWindow.document !== "undefined" && typeof routerWindow.document.createElement !== "undefined";
|
|
116
|
+
invariant(init.routes.length > 0, "You must provide a non-empty routes array to createRouter");
|
|
117
|
+
let hydrationRouteProperties = init.hydrationRouteProperties || [];
|
|
118
|
+
let _mapRouteProperties = init.mapRouteProperties;
|
|
119
|
+
let mapRouteProperties = _mapRouteProperties ? _mapRouteProperties : () => ({});
|
|
120
|
+
if (init.instrumentations) {
|
|
121
|
+
let instrumentations = init.instrumentations;
|
|
122
|
+
mapRouteProperties = (route) => {
|
|
123
|
+
return {
|
|
124
|
+
..._mapRouteProperties?.(route),
|
|
125
|
+
...getRouteInstrumentationUpdates(instrumentations.map((i) => i.route).filter(Boolean), route)
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
let manifest = {};
|
|
130
|
+
let dataRoutes = new DataRoutes(convertRoutesToDataRoutes(init.routes, mapRouteProperties, void 0, manifest));
|
|
131
|
+
let basename = init.basename || "/";
|
|
132
|
+
if (!basename.startsWith("/")) basename = `/${basename}`;
|
|
133
|
+
let dataStrategyImpl = init.dataStrategy || defaultDataStrategyWithMiddleware;
|
|
134
|
+
let future = { ...init.future };
|
|
135
|
+
let unlistenHistory = null;
|
|
136
|
+
let subscribers = /* @__PURE__ */ new Set();
|
|
137
|
+
let bufferedInitialStateUpdate = null;
|
|
138
|
+
let savedScrollPositions = null;
|
|
139
|
+
let getScrollRestorationKey = null;
|
|
140
|
+
let getScrollPosition = null;
|
|
141
|
+
let initialScrollRestored = init.hydrationData != null;
|
|
142
|
+
let initialMatches = matchRoutesImpl(dataRoutes.activeRoutes, init.history.location, basename, false, dataRoutes.branches);
|
|
143
|
+
let initialMatchesIsFOW = false;
|
|
144
|
+
let initialErrors = null;
|
|
145
|
+
let initialized;
|
|
146
|
+
let renderFallback;
|
|
147
|
+
if (initialMatches == null && !init.patchRoutesOnNavigation) {
|
|
148
|
+
let error = getInternalRouterError(404, { pathname: init.history.location.pathname });
|
|
149
|
+
let { matches, route } = getShortCircuitMatches(dataRoutes.activeRoutes);
|
|
150
|
+
initialized = true;
|
|
151
|
+
renderFallback = !initialized;
|
|
152
|
+
initialMatches = matches;
|
|
153
|
+
initialErrors = { [route.id]: error };
|
|
154
|
+
} else {
|
|
155
|
+
if (initialMatches && !init.hydrationData) {
|
|
156
|
+
if (checkFogOfWar(initialMatches, dataRoutes.activeRoutes, init.history.location.pathname).active) initialMatches = null;
|
|
157
|
+
}
|
|
158
|
+
if (!initialMatches) {
|
|
159
|
+
initialized = false;
|
|
160
|
+
renderFallback = !initialized;
|
|
161
|
+
initialMatches = [];
|
|
162
|
+
let fogOfWar = checkFogOfWar(null, dataRoutes.activeRoutes, init.history.location.pathname);
|
|
163
|
+
if (fogOfWar.active && fogOfWar.matches) {
|
|
164
|
+
initialMatchesIsFOW = true;
|
|
165
|
+
initialMatches = fogOfWar.matches;
|
|
166
|
+
}
|
|
167
|
+
} else if (initialMatches.some((m) => m.route.lazy)) {
|
|
168
|
+
initialized = false;
|
|
169
|
+
renderFallback = !initialized;
|
|
170
|
+
} else if (!initialMatches.some((m) => routeHasLoaderOrMiddleware(m.route))) {
|
|
171
|
+
initialized = true;
|
|
172
|
+
renderFallback = !initialized;
|
|
173
|
+
} else {
|
|
174
|
+
let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
|
|
175
|
+
let errors = init.hydrationData ? init.hydrationData.errors : null;
|
|
176
|
+
let relevantMatches = initialMatches;
|
|
177
|
+
if (errors) {
|
|
178
|
+
let idx = initialMatches.findIndex((m) => errors[m.route.id] !== void 0);
|
|
179
|
+
relevantMatches = relevantMatches.slice(0, idx + 1);
|
|
180
|
+
}
|
|
181
|
+
renderFallback = false;
|
|
182
|
+
initialized = true;
|
|
183
|
+
relevantMatches.forEach((m) => {
|
|
184
|
+
let status = getRouteHydrationStatus(m.route, loaderData, errors);
|
|
185
|
+
renderFallback = renderFallback || status.renderFallback;
|
|
186
|
+
initialized = initialized && !status.shouldLoad;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
let router;
|
|
191
|
+
let state = {
|
|
192
|
+
historyAction: init.history.action,
|
|
193
|
+
location: init.history.location,
|
|
194
|
+
matches: initialMatches,
|
|
195
|
+
initialized,
|
|
196
|
+
renderFallback,
|
|
197
|
+
navigation: IDLE_NAVIGATION,
|
|
198
|
+
restoreScrollPosition: init.hydrationData != null ? false : null,
|
|
199
|
+
preventScrollReset: false,
|
|
200
|
+
revalidation: "idle",
|
|
201
|
+
loaderData: init.hydrationData && init.hydrationData.loaderData || {},
|
|
202
|
+
actionData: init.hydrationData && init.hydrationData.actionData || null,
|
|
203
|
+
errors: init.hydrationData && init.hydrationData.errors || initialErrors,
|
|
204
|
+
fetchers: /* @__PURE__ */ new Map(),
|
|
205
|
+
blockers: /* @__PURE__ */ new Map()
|
|
206
|
+
};
|
|
207
|
+
let pendingAction = "POP";
|
|
208
|
+
let pendingPopstateNavigationDfd = null;
|
|
209
|
+
let pendingPreventScrollReset = false;
|
|
210
|
+
let pendingNavigationController;
|
|
211
|
+
let pendingViewTransitionEnabled = false;
|
|
212
|
+
let appliedViewTransitions = /* @__PURE__ */ new Map();
|
|
213
|
+
let removePageHideEventListener = null;
|
|
214
|
+
let isUninterruptedRevalidation = false;
|
|
215
|
+
let isRevalidationRequired = false;
|
|
216
|
+
let cancelledFetcherLoads = /* @__PURE__ */ new Set();
|
|
217
|
+
let fetchControllers = /* @__PURE__ */ new Map();
|
|
218
|
+
let incrementingLoadId = 0;
|
|
219
|
+
let pendingNavigationLoadId = -1;
|
|
220
|
+
let fetchReloadIds = /* @__PURE__ */ new Map();
|
|
221
|
+
let fetchRedirectIds = /* @__PURE__ */ new Set();
|
|
222
|
+
let fetchLoadMatches = /* @__PURE__ */ new Map();
|
|
223
|
+
let activeFetchers = /* @__PURE__ */ new Map();
|
|
224
|
+
let fetchersQueuedForDeletion = /* @__PURE__ */ new Set();
|
|
225
|
+
let blockerFunctions = /* @__PURE__ */ new Map();
|
|
226
|
+
let unblockBlockerHistoryUpdate = void 0;
|
|
227
|
+
let pendingRevalidationDfd = null;
|
|
228
|
+
function initialize() {
|
|
229
|
+
unlistenHistory = init.history.listen(({ action: historyAction, location, delta }) => {
|
|
230
|
+
if (unblockBlockerHistoryUpdate) {
|
|
231
|
+
unblockBlockerHistoryUpdate();
|
|
232
|
+
unblockBlockerHistoryUpdate = void 0;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
warning(blockerFunctions.size === 0 || delta != null, "You are trying to use a blocker on a POP navigation to a location that was not created by @remix-run/router. This will fail silently in production. This can happen if you are navigating outside the router via `window.history.pushState`/`window.location.hash` instead of using router navigation APIs. This can also happen if you are using createHashRouter and the user manually changes the URL.");
|
|
236
|
+
let blockerKey = shouldBlockNavigation({
|
|
237
|
+
currentLocation: state.location,
|
|
238
|
+
nextLocation: location,
|
|
239
|
+
historyAction
|
|
240
|
+
});
|
|
241
|
+
if (blockerKey && delta != null) {
|
|
242
|
+
let nextHistoryUpdatePromise = new Promise((resolve) => {
|
|
243
|
+
unblockBlockerHistoryUpdate = resolve;
|
|
244
|
+
});
|
|
245
|
+
init.history.go(delta * -1);
|
|
246
|
+
updateBlocker(blockerKey, {
|
|
247
|
+
state: "blocked",
|
|
248
|
+
location,
|
|
249
|
+
proceed() {
|
|
250
|
+
updateBlocker(blockerKey, {
|
|
251
|
+
state: "proceeding",
|
|
252
|
+
proceed: void 0,
|
|
253
|
+
reset: void 0,
|
|
254
|
+
location
|
|
255
|
+
});
|
|
256
|
+
nextHistoryUpdatePromise.then(() => init.history.go(delta));
|
|
257
|
+
},
|
|
258
|
+
reset() {
|
|
259
|
+
let blockers = new Map(state.blockers);
|
|
260
|
+
blockers.set(blockerKey, IDLE_BLOCKER);
|
|
261
|
+
updateState({ blockers });
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
pendingPopstateNavigationDfd?.resolve();
|
|
265
|
+
pendingPopstateNavigationDfd = null;
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
return startNavigation(historyAction, location);
|
|
269
|
+
});
|
|
270
|
+
if (isBrowser) {
|
|
271
|
+
restoreAppliedTransitions(routerWindow, appliedViewTransitions);
|
|
272
|
+
let _saveAppliedTransitions = () => persistAppliedTransitions(routerWindow, appliedViewTransitions);
|
|
273
|
+
routerWindow.addEventListener("pagehide", _saveAppliedTransitions);
|
|
274
|
+
removePageHideEventListener = () => routerWindow.removeEventListener("pagehide", _saveAppliedTransitions);
|
|
275
|
+
}
|
|
276
|
+
if (!state.initialized) startNavigation("POP", state.location, { initialHydration: true });
|
|
277
|
+
return router;
|
|
278
|
+
}
|
|
279
|
+
function dispose() {
|
|
280
|
+
if (unlistenHistory) unlistenHistory();
|
|
281
|
+
if (removePageHideEventListener) removePageHideEventListener();
|
|
282
|
+
subscribers.clear();
|
|
283
|
+
pendingNavigationController && pendingNavigationController.abort();
|
|
284
|
+
state.fetchers.forEach((_, key) => deleteFetcher(state.fetchers, key));
|
|
285
|
+
state.blockers.forEach((_, key) => deleteBlocker(key));
|
|
286
|
+
}
|
|
287
|
+
function subscribe(fn) {
|
|
288
|
+
subscribers.add(fn);
|
|
289
|
+
if (bufferedInitialStateUpdate) {
|
|
290
|
+
let { newErrors } = bufferedInitialStateUpdate;
|
|
291
|
+
bufferedInitialStateUpdate = null;
|
|
292
|
+
fn(state, {
|
|
293
|
+
deletedFetchers: [],
|
|
294
|
+
newErrors,
|
|
295
|
+
viewTransitionOpts: void 0,
|
|
296
|
+
flushSync: false
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
return () => subscribers.delete(fn);
|
|
300
|
+
}
|
|
301
|
+
function updateState(newState, opts = {}) {
|
|
302
|
+
if (newState.matches) newState.matches = newState.matches.map((m) => {
|
|
303
|
+
let route = manifest[m.route.id];
|
|
304
|
+
let matchRoute = m.route;
|
|
305
|
+
if (matchRoute.element !== route.element || matchRoute.errorElement !== route.errorElement || matchRoute.hydrateFallbackElement !== route.hydrateFallbackElement) return {
|
|
306
|
+
...m,
|
|
307
|
+
route
|
|
308
|
+
};
|
|
309
|
+
return m;
|
|
310
|
+
});
|
|
311
|
+
state = {
|
|
312
|
+
...state,
|
|
313
|
+
...newState
|
|
314
|
+
};
|
|
315
|
+
let unmountedFetchers = [];
|
|
316
|
+
let mountedFetchers = [];
|
|
317
|
+
state.fetchers.forEach((fetcher, key) => {
|
|
318
|
+
if (fetcher.state === "idle") if (fetchersQueuedForDeletion.has(key)) unmountedFetchers.push(key);
|
|
319
|
+
else mountedFetchers.push(key);
|
|
320
|
+
});
|
|
321
|
+
fetchersQueuedForDeletion.forEach((key) => {
|
|
322
|
+
if (!state.fetchers.has(key) && !fetchControllers.has(key)) unmountedFetchers.push(key);
|
|
323
|
+
});
|
|
324
|
+
if (subscribers.size === 0) bufferedInitialStateUpdate = { newErrors: newState.errors ?? null };
|
|
325
|
+
[...subscribers].forEach((subscriber) => subscriber(state, {
|
|
326
|
+
deletedFetchers: unmountedFetchers,
|
|
327
|
+
newErrors: newState.errors ?? null,
|
|
328
|
+
viewTransitionOpts: opts.viewTransitionOpts,
|
|
329
|
+
flushSync: opts.flushSync === true
|
|
330
|
+
}));
|
|
331
|
+
unmountedFetchers.forEach((key) => deleteFetcher(state.fetchers, key));
|
|
332
|
+
mountedFetchers.forEach((key) => state.fetchers.delete(key));
|
|
333
|
+
}
|
|
334
|
+
function completeNavigation(location, newState, { flushSync } = {}) {
|
|
335
|
+
let isActionReload = state.actionData != null && state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && state.navigation.state === "loading" && location.state?._isRedirect !== true;
|
|
336
|
+
let actionData;
|
|
337
|
+
if (newState.actionData) if (Object.keys(newState.actionData).length > 0) actionData = newState.actionData;
|
|
338
|
+
else actionData = null;
|
|
339
|
+
else if (isActionReload) actionData = state.actionData;
|
|
340
|
+
else actionData = null;
|
|
341
|
+
let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData;
|
|
342
|
+
let blockers = state.blockers;
|
|
343
|
+
if (blockers.size > 0) {
|
|
344
|
+
blockers = new Map(blockers);
|
|
345
|
+
blockers.forEach((_, k) => blockers.set(k, IDLE_BLOCKER));
|
|
346
|
+
}
|
|
347
|
+
let restoreScrollPosition = isUninterruptedRevalidation ? false : getSavedScrollPosition(location, newState.matches || state.matches);
|
|
348
|
+
let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && location.state?._isRedirect !== true;
|
|
349
|
+
dataRoutes.commitHmrRoutes();
|
|
350
|
+
if (isUninterruptedRevalidation) {} else if (pendingAction === "POP") {} else if (pendingAction === "PUSH") init.history.push(location, location.state);
|
|
351
|
+
else if (pendingAction === "REPLACE") init.history.replace(location, location.state);
|
|
352
|
+
let viewTransitionOpts;
|
|
353
|
+
if (pendingAction === "POP") {
|
|
354
|
+
let priorPaths = appliedViewTransitions.get(state.location.pathname);
|
|
355
|
+
if (priorPaths && priorPaths.has(location.pathname)) viewTransitionOpts = {
|
|
356
|
+
currentLocation: state.location,
|
|
357
|
+
nextLocation: location
|
|
358
|
+
};
|
|
359
|
+
else if (appliedViewTransitions.has(location.pathname)) viewTransitionOpts = {
|
|
360
|
+
currentLocation: location,
|
|
361
|
+
nextLocation: state.location
|
|
362
|
+
};
|
|
363
|
+
} else if (pendingViewTransitionEnabled) {
|
|
364
|
+
let toPaths = appliedViewTransitions.get(state.location.pathname);
|
|
365
|
+
if (toPaths) toPaths.add(location.pathname);
|
|
366
|
+
else {
|
|
367
|
+
toPaths = new Set([location.pathname]);
|
|
368
|
+
appliedViewTransitions.set(state.location.pathname, toPaths);
|
|
369
|
+
}
|
|
370
|
+
viewTransitionOpts = {
|
|
371
|
+
currentLocation: state.location,
|
|
372
|
+
nextLocation: location
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
updateState({
|
|
376
|
+
...newState,
|
|
377
|
+
actionData,
|
|
378
|
+
loaderData,
|
|
379
|
+
historyAction: pendingAction,
|
|
380
|
+
location,
|
|
381
|
+
initialized: true,
|
|
382
|
+
renderFallback: false,
|
|
383
|
+
navigation: IDLE_NAVIGATION,
|
|
384
|
+
revalidation: "idle",
|
|
385
|
+
restoreScrollPosition,
|
|
386
|
+
preventScrollReset,
|
|
387
|
+
blockers
|
|
388
|
+
}, {
|
|
389
|
+
viewTransitionOpts,
|
|
390
|
+
flushSync: flushSync === true
|
|
391
|
+
});
|
|
392
|
+
pendingAction = "POP";
|
|
393
|
+
pendingPreventScrollReset = false;
|
|
394
|
+
pendingViewTransitionEnabled = false;
|
|
395
|
+
isUninterruptedRevalidation = false;
|
|
396
|
+
isRevalidationRequired = false;
|
|
397
|
+
pendingPopstateNavigationDfd?.resolve();
|
|
398
|
+
pendingPopstateNavigationDfd = null;
|
|
399
|
+
pendingRevalidationDfd?.resolve();
|
|
400
|
+
pendingRevalidationDfd = null;
|
|
401
|
+
}
|
|
402
|
+
async function navigate(to, opts) {
|
|
403
|
+
pendingPopstateNavigationDfd?.resolve();
|
|
404
|
+
pendingPopstateNavigationDfd = null;
|
|
405
|
+
if (typeof to === "number") {
|
|
406
|
+
if (!pendingPopstateNavigationDfd) pendingPopstateNavigationDfd = createDeferred();
|
|
407
|
+
let promise = pendingPopstateNavigationDfd.promise;
|
|
408
|
+
init.history.go(to);
|
|
409
|
+
return promise;
|
|
410
|
+
}
|
|
411
|
+
let { path, submission, error } = normalizeNavigateOptions(false, normalizeTo(state.location, state.matches, basename, to, opts?.fromRouteId, opts?.relative), opts);
|
|
412
|
+
let maskPath;
|
|
413
|
+
if (opts?.mask) maskPath = {
|
|
414
|
+
pathname: "",
|
|
415
|
+
search: "",
|
|
416
|
+
hash: "",
|
|
417
|
+
...typeof opts.mask === "string" ? parsePath(opts.mask) : {
|
|
418
|
+
...state.location.mask,
|
|
419
|
+
...opts.mask
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
let currentLocation = state.location;
|
|
423
|
+
let nextLocation = createLocation(currentLocation, path, opts && opts.state, void 0, maskPath);
|
|
424
|
+
nextLocation = {
|
|
425
|
+
...nextLocation,
|
|
426
|
+
...init.history.encodeLocation(nextLocation)
|
|
427
|
+
};
|
|
428
|
+
let userReplace = opts && opts.replace != null ? opts.replace : void 0;
|
|
429
|
+
let historyAction = "PUSH";
|
|
430
|
+
if (userReplace === true) historyAction = "REPLACE";
|
|
431
|
+
else if (userReplace === false) {} else if (submission != null && isMutationMethod(submission.formMethod) && submission.formAction === state.location.pathname + state.location.search) historyAction = "REPLACE";
|
|
432
|
+
let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : void 0;
|
|
433
|
+
let flushSync = (opts && opts.flushSync) === true;
|
|
434
|
+
let blockerKey = shouldBlockNavigation({
|
|
435
|
+
currentLocation,
|
|
436
|
+
nextLocation,
|
|
437
|
+
historyAction
|
|
438
|
+
});
|
|
439
|
+
if (blockerKey) {
|
|
440
|
+
updateBlocker(blockerKey, {
|
|
441
|
+
state: "blocked",
|
|
442
|
+
location: nextLocation,
|
|
443
|
+
proceed() {
|
|
444
|
+
updateBlocker(blockerKey, {
|
|
445
|
+
state: "proceeding",
|
|
446
|
+
proceed: void 0,
|
|
447
|
+
reset: void 0,
|
|
448
|
+
location: nextLocation
|
|
449
|
+
});
|
|
450
|
+
navigate(to, opts);
|
|
451
|
+
},
|
|
452
|
+
reset() {
|
|
453
|
+
let blockers = new Map(state.blockers);
|
|
454
|
+
blockers.set(blockerKey, IDLE_BLOCKER);
|
|
455
|
+
updateState({ blockers });
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
await startNavigation(historyAction, nextLocation, {
|
|
461
|
+
submission,
|
|
462
|
+
pendingError: error,
|
|
463
|
+
preventScrollReset,
|
|
464
|
+
replace: opts && opts.replace,
|
|
465
|
+
enableViewTransition: opts && opts.viewTransition,
|
|
466
|
+
flushSync,
|
|
467
|
+
callSiteDefaultShouldRevalidate: opts && opts.defaultShouldRevalidate
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
function revalidate() {
|
|
471
|
+
if (!pendingRevalidationDfd) pendingRevalidationDfd = createDeferred();
|
|
472
|
+
interruptActiveLoads();
|
|
473
|
+
updateState({ revalidation: "loading" });
|
|
474
|
+
let promise = pendingRevalidationDfd.promise;
|
|
475
|
+
if (state.navigation.state === "submitting") return promise;
|
|
476
|
+
if (state.navigation.state === "idle") {
|
|
477
|
+
startNavigation(state.historyAction, state.location, { startUninterruptedRevalidation: true });
|
|
478
|
+
return promise;
|
|
479
|
+
}
|
|
480
|
+
startNavigation(pendingAction || state.historyAction, state.navigation.location, {
|
|
481
|
+
overrideNavigation: state.navigation,
|
|
482
|
+
enableViewTransition: pendingViewTransitionEnabled === true
|
|
483
|
+
});
|
|
484
|
+
return promise;
|
|
485
|
+
}
|
|
486
|
+
async function startNavigation(historyAction, location, opts) {
|
|
487
|
+
pendingNavigationController && pendingNavigationController.abort();
|
|
488
|
+
pendingNavigationController = null;
|
|
489
|
+
pendingAction = historyAction;
|
|
490
|
+
isUninterruptedRevalidation = (opts && opts.startUninterruptedRevalidation) === true;
|
|
491
|
+
saveScrollPosition(state.location, state.matches);
|
|
492
|
+
pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
493
|
+
pendingViewTransitionEnabled = (opts && opts.enableViewTransition) === true;
|
|
494
|
+
let routesToUse = dataRoutes.activeRoutes;
|
|
495
|
+
let matches = opts?.initialHydration && state.matches && state.matches.length > 0 && !initialMatchesIsFOW ? state.matches : matchRoutesImpl(routesToUse, location, basename, false, dataRoutes.branches);
|
|
496
|
+
let flushSync = (opts && opts.flushSync) === true;
|
|
497
|
+
if (matches && state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
|
|
498
|
+
completeNavigation(location, { matches }, { flushSync });
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
let fogOfWar = checkFogOfWar(matches, routesToUse, location.pathname);
|
|
502
|
+
if (fogOfWar.active && fogOfWar.matches) matches = fogOfWar.matches;
|
|
503
|
+
if (!matches) {
|
|
504
|
+
let { error, notFoundMatches, route } = handleNavigational404(location.pathname);
|
|
505
|
+
completeNavigation(location, {
|
|
506
|
+
matches: notFoundMatches,
|
|
507
|
+
loaderData: {},
|
|
508
|
+
errors: { [route.id]: error }
|
|
509
|
+
}, { flushSync });
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
let loadingNavigation = opts && opts.overrideNavigation ? {
|
|
513
|
+
...opts.overrideNavigation,
|
|
514
|
+
matches,
|
|
515
|
+
historyAction
|
|
516
|
+
} : void 0;
|
|
517
|
+
pendingNavigationController = new AbortController();
|
|
518
|
+
let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission);
|
|
519
|
+
let scopedContext = init.getContext ? await init.getContext() : new RouterContextProvider();
|
|
520
|
+
let pendingActionResult;
|
|
521
|
+
if (opts && opts.pendingError) pendingActionResult = [findNearestBoundary(matches).route.id, {
|
|
522
|
+
type: "error",
|
|
523
|
+
error: opts.pendingError
|
|
524
|
+
}];
|
|
525
|
+
else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) {
|
|
526
|
+
let actionResult = await handleAction(request, location, opts.submission, matches, historyAction, scopedContext, fogOfWar.active, opts && opts.initialHydration === true, {
|
|
527
|
+
replace: opts.replace,
|
|
528
|
+
flushSync
|
|
529
|
+
});
|
|
530
|
+
if (actionResult.shortCircuited) return;
|
|
531
|
+
if (actionResult.pendingActionResult) {
|
|
532
|
+
let [routeId, result] = actionResult.pendingActionResult;
|
|
533
|
+
if (isErrorResult(result) && isRouteErrorResponse(result.error) && result.error.status === 404) {
|
|
534
|
+
pendingNavigationController = null;
|
|
535
|
+
completeNavigation(location, {
|
|
536
|
+
matches: actionResult.matches,
|
|
537
|
+
loaderData: {},
|
|
538
|
+
errors: { [routeId]: result.error }
|
|
539
|
+
});
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
matches = actionResult.matches || matches;
|
|
544
|
+
pendingActionResult = actionResult.pendingActionResult;
|
|
545
|
+
loadingNavigation = getLoadingNavigation(location, matches, historyAction, opts.submission);
|
|
546
|
+
flushSync = false;
|
|
547
|
+
fogOfWar.active = false;
|
|
548
|
+
request = createClientSideRequest(init.history, request.url, request.signal);
|
|
549
|
+
}
|
|
550
|
+
let { shortCircuited, matches: updatedMatches, loaderData, errors, workingFetchers } = await handleLoaders(request, location, matches, historyAction, scopedContext, fogOfWar.active, loadingNavigation, opts && opts.submission, opts && opts.fetcherSubmission, opts && opts.replace, opts && opts.initialHydration === true, flushSync, pendingActionResult, opts && opts.callSiteDefaultShouldRevalidate);
|
|
551
|
+
if (shortCircuited) return;
|
|
552
|
+
pendingNavigationController = null;
|
|
553
|
+
completeNavigation(location, {
|
|
554
|
+
matches: updatedMatches || matches,
|
|
555
|
+
...getActionDataForCommit(pendingActionResult),
|
|
556
|
+
loaderData,
|
|
557
|
+
errors,
|
|
558
|
+
...workingFetchers ? { fetchers: workingFetchers } : {}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
async function handleAction(request, location, submission, matches, historyAction, scopedContext, isFogOfWar, initialHydration, opts = {}) {
|
|
562
|
+
interruptActiveLoads();
|
|
563
|
+
updateState({ navigation: getSubmittingNavigation(location, matches, historyAction, submission) }, { flushSync: opts.flushSync === true });
|
|
564
|
+
if (isFogOfWar) {
|
|
565
|
+
let discoverResult = await discoverRoutes(matches, location.pathname, request.signal);
|
|
566
|
+
if (discoverResult.type === "aborted") return { shortCircuited: true };
|
|
567
|
+
else if (discoverResult.type === "error") {
|
|
568
|
+
if (discoverResult.partialMatches.length === 0) {
|
|
569
|
+
let { matches, route } = getShortCircuitMatches(dataRoutes.activeRoutes);
|
|
570
|
+
return {
|
|
571
|
+
matches,
|
|
572
|
+
pendingActionResult: [route.id, {
|
|
573
|
+
type: "error",
|
|
574
|
+
error: discoverResult.error
|
|
575
|
+
}]
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
|
|
579
|
+
return {
|
|
580
|
+
matches: discoverResult.partialMatches,
|
|
581
|
+
pendingActionResult: [boundaryId, {
|
|
582
|
+
type: "error",
|
|
583
|
+
error: discoverResult.error
|
|
584
|
+
}]
|
|
585
|
+
};
|
|
586
|
+
} else if (!discoverResult.matches) {
|
|
587
|
+
let { notFoundMatches, error, route } = handleNavigational404(location.pathname);
|
|
588
|
+
return {
|
|
589
|
+
matches: notFoundMatches,
|
|
590
|
+
pendingActionResult: [route.id, {
|
|
591
|
+
type: "error",
|
|
592
|
+
error
|
|
593
|
+
}]
|
|
594
|
+
};
|
|
595
|
+
} else matches = discoverResult.matches;
|
|
596
|
+
}
|
|
597
|
+
let result;
|
|
598
|
+
let actionMatch = getTargetMatch(matches, location);
|
|
599
|
+
if (!actionMatch.route.action && !actionMatch.route.lazy) result = {
|
|
600
|
+
type: "error",
|
|
601
|
+
error: getInternalRouterError(405, {
|
|
602
|
+
method: request.method,
|
|
603
|
+
pathname: location.pathname,
|
|
604
|
+
routeId: actionMatch.route.id
|
|
605
|
+
})
|
|
606
|
+
};
|
|
607
|
+
else {
|
|
608
|
+
let results = await callDataStrategy(request, location, getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, actionMatch, initialHydration ? [] : hydrationRouteProperties, scopedContext), scopedContext, null);
|
|
609
|
+
result = results[actionMatch.route.id];
|
|
610
|
+
if (!result) {
|
|
611
|
+
for (let match of matches) if (results[match.route.id]) {
|
|
612
|
+
result = results[match.route.id];
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
if (request.signal.aborted) return { shortCircuited: true };
|
|
617
|
+
}
|
|
618
|
+
if (isRedirectResult(result)) {
|
|
619
|
+
let replace;
|
|
620
|
+
if (opts && opts.replace != null) replace = opts.replace;
|
|
621
|
+
else replace = normalizeRedirectLocation(result.response.headers.get("Location"), new URL(request.url), basename, init.history) === state.location.pathname + state.location.search;
|
|
622
|
+
await startRedirectNavigation(request, result, true, {
|
|
623
|
+
submission,
|
|
624
|
+
replace
|
|
625
|
+
});
|
|
626
|
+
return { shortCircuited: true };
|
|
627
|
+
}
|
|
628
|
+
if (isErrorResult(result)) {
|
|
629
|
+
let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);
|
|
630
|
+
if ((opts && opts.replace) !== true) pendingAction = "PUSH";
|
|
631
|
+
return {
|
|
632
|
+
matches,
|
|
633
|
+
pendingActionResult: [
|
|
634
|
+
boundaryMatch.route.id,
|
|
635
|
+
result,
|
|
636
|
+
actionMatch.route.id
|
|
637
|
+
]
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
return {
|
|
641
|
+
matches,
|
|
642
|
+
pendingActionResult: [actionMatch.route.id, result]
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
async function handleLoaders(request, location, matches, historyAction, scopedContext, isFogOfWar, overrideNavigation, submission, fetcherSubmission, replace, initialHydration, flushSync, pendingActionResult, callSiteDefaultShouldRevalidate) {
|
|
646
|
+
let loadingNavigation = overrideNavigation || getLoadingNavigation(location, matches, historyAction, submission);
|
|
647
|
+
let activeSubmission = submission || fetcherSubmission || getSubmissionFromNavigation(loadingNavigation);
|
|
648
|
+
let shouldUpdateNavigationState = !isUninterruptedRevalidation && !initialHydration;
|
|
649
|
+
if (isFogOfWar) {
|
|
650
|
+
if (shouldUpdateNavigationState) {
|
|
651
|
+
let actionData = getUpdatedActionData(pendingActionResult);
|
|
652
|
+
updateState({
|
|
653
|
+
navigation: loadingNavigation,
|
|
654
|
+
...actionData !== void 0 ? { actionData } : {}
|
|
655
|
+
}, { flushSync });
|
|
656
|
+
}
|
|
657
|
+
let discoverResult = await discoverRoutes(matches, location.pathname, request.signal);
|
|
658
|
+
if (discoverResult.type === "aborted") return { shortCircuited: true };
|
|
659
|
+
else if (discoverResult.type === "error") {
|
|
660
|
+
if (discoverResult.partialMatches.length === 0) {
|
|
661
|
+
let { matches, route } = getShortCircuitMatches(dataRoutes.activeRoutes);
|
|
662
|
+
return {
|
|
663
|
+
matches,
|
|
664
|
+
loaderData: {},
|
|
665
|
+
errors: { [route.id]: discoverResult.error }
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
|
|
669
|
+
return {
|
|
670
|
+
matches: discoverResult.partialMatches,
|
|
671
|
+
loaderData: {},
|
|
672
|
+
errors: { [boundaryId]: discoverResult.error }
|
|
673
|
+
};
|
|
674
|
+
} else if (!discoverResult.matches) {
|
|
675
|
+
let { error, notFoundMatches, route } = handleNavigational404(location.pathname);
|
|
676
|
+
return {
|
|
677
|
+
matches: notFoundMatches,
|
|
678
|
+
loaderData: {},
|
|
679
|
+
errors: { [route.id]: error }
|
|
680
|
+
};
|
|
681
|
+
} else matches = discoverResult.matches;
|
|
682
|
+
}
|
|
683
|
+
let routesToUse = dataRoutes.activeRoutes;
|
|
684
|
+
let { dsMatches, revalidatingFetchers } = getMatchesToLoad(request, scopedContext, mapRouteProperties, manifest, init.history, state, matches, activeSubmission, location, initialHydration ? [] : hydrationRouteProperties, initialHydration === true, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, init.patchRoutesOnNavigation != null, dataRoutes.branches, pendingActionResult, callSiteDefaultShouldRevalidate);
|
|
685
|
+
pendingNavigationLoadId = ++incrementingLoadId;
|
|
686
|
+
if (!init.dataStrategy && !dsMatches.some((m) => m.shouldLoad) && !dsMatches.some((m) => m.route.middleware && m.route.middleware.length > 0) && revalidatingFetchers.length === 0) {
|
|
687
|
+
let workingFetchers = new Map(state.fetchers);
|
|
688
|
+
let didUpdateFetcherRedirects = markFetchRedirectsDone(workingFetchers);
|
|
689
|
+
completeNavigation(location, {
|
|
690
|
+
matches,
|
|
691
|
+
loaderData: {},
|
|
692
|
+
errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? { [pendingActionResult[0]]: pendingActionResult[1].error } : null,
|
|
693
|
+
...getActionDataForCommit(pendingActionResult),
|
|
694
|
+
...didUpdateFetcherRedirects ? { fetchers: workingFetchers } : {}
|
|
695
|
+
}, { flushSync });
|
|
696
|
+
return { shortCircuited: true };
|
|
697
|
+
}
|
|
698
|
+
if (shouldUpdateNavigationState) {
|
|
699
|
+
let updates = {};
|
|
700
|
+
if (!isFogOfWar) {
|
|
701
|
+
updates.navigation = loadingNavigation;
|
|
702
|
+
let actionData = getUpdatedActionData(pendingActionResult);
|
|
703
|
+
if (actionData !== void 0) updates.actionData = actionData;
|
|
704
|
+
}
|
|
705
|
+
if (revalidatingFetchers.length > 0) updates.fetchers = getUpdatedRevalidatingFetchers(revalidatingFetchers);
|
|
706
|
+
updateState(updates, { flushSync });
|
|
707
|
+
}
|
|
708
|
+
revalidatingFetchers.forEach((rf) => {
|
|
709
|
+
abortFetcher(rf.key);
|
|
710
|
+
if (rf.controller) fetchControllers.set(rf.key, rf.controller);
|
|
711
|
+
});
|
|
712
|
+
let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((f) => abortFetcher(f.key));
|
|
713
|
+
if (pendingNavigationController) pendingNavigationController.signal.addEventListener("abort", abortPendingFetchRevalidations);
|
|
714
|
+
let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, request, location, scopedContext);
|
|
715
|
+
if (request.signal.aborted) return { shortCircuited: true };
|
|
716
|
+
if (pendingNavigationController) pendingNavigationController.signal.removeEventListener("abort", abortPendingFetchRevalidations);
|
|
717
|
+
revalidatingFetchers.forEach((rf) => fetchControllers.delete(rf.key));
|
|
718
|
+
let redirect = findRedirect(loaderResults);
|
|
719
|
+
if (redirect) {
|
|
720
|
+
await startRedirectNavigation(request, redirect.result, true, { replace });
|
|
721
|
+
return { shortCircuited: true };
|
|
722
|
+
}
|
|
723
|
+
redirect = findRedirect(fetcherResults);
|
|
724
|
+
if (redirect) {
|
|
725
|
+
fetchRedirectIds.add(redirect.key);
|
|
726
|
+
await startRedirectNavigation(request, redirect.result, true, { replace });
|
|
727
|
+
return { shortCircuited: true };
|
|
728
|
+
}
|
|
729
|
+
let workingFetchers = new Map(state.fetchers);
|
|
730
|
+
let { loaderData, errors } = processLoaderData(state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults, workingFetchers);
|
|
731
|
+
if (initialHydration && state.errors) errors = {
|
|
732
|
+
...state.errors,
|
|
733
|
+
...errors
|
|
734
|
+
};
|
|
735
|
+
let didUpdateFetcherRedirects = markFetchRedirectsDone(workingFetchers);
|
|
736
|
+
let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId, workingFetchers);
|
|
737
|
+
let shouldUpdateFetchers = didUpdateFetcherRedirects || didAbortFetchLoads || revalidatingFetchers.length > 0;
|
|
738
|
+
return {
|
|
739
|
+
matches,
|
|
740
|
+
loaderData,
|
|
741
|
+
errors,
|
|
742
|
+
...shouldUpdateFetchers ? { workingFetchers } : {}
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
function getUpdatedActionData(pendingActionResult) {
|
|
746
|
+
if (pendingActionResult && !isErrorResult(pendingActionResult[1])) return { [pendingActionResult[0]]: pendingActionResult[1].data };
|
|
747
|
+
else if (state.actionData) if (Object.keys(state.actionData).length === 0) return null;
|
|
748
|
+
else return state.actionData;
|
|
749
|
+
}
|
|
750
|
+
function getUpdatedRevalidatingFetchers(revalidatingFetchers) {
|
|
751
|
+
let workingFetchers = new Map(state.fetchers);
|
|
752
|
+
revalidatingFetchers.forEach((rf) => {
|
|
753
|
+
let fetcher = workingFetchers.get(rf.key);
|
|
754
|
+
let revalidatingFetcher = getLoadingFetcher(void 0, fetcher ? fetcher.data : void 0);
|
|
755
|
+
workingFetchers.set(rf.key, revalidatingFetcher);
|
|
756
|
+
});
|
|
757
|
+
return workingFetchers;
|
|
758
|
+
}
|
|
759
|
+
async function fetch(key, routeId, href, opts) {
|
|
760
|
+
abortFetcher(key);
|
|
761
|
+
let flushSync = (opts && opts.flushSync) === true;
|
|
762
|
+
let routesToUse = dataRoutes.activeRoutes;
|
|
763
|
+
let normalizedPath = normalizeTo(state.location, state.matches, basename, href, routeId, opts?.relative);
|
|
764
|
+
let matches = matchRoutesImpl(routesToUse, normalizedPath, basename, false, dataRoutes.branches);
|
|
765
|
+
let fogOfWar = checkFogOfWar(matches, routesToUse, normalizedPath);
|
|
766
|
+
if (fogOfWar.active && fogOfWar.matches) matches = fogOfWar.matches;
|
|
767
|
+
if (!matches) {
|
|
768
|
+
setFetcherError(key, routeId, getInternalRouterError(404, { pathname: normalizedPath }), { flushSync });
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
let { path, submission, error } = normalizeNavigateOptions(true, normalizedPath, opts);
|
|
772
|
+
if (error) {
|
|
773
|
+
setFetcherError(key, routeId, error, { flushSync });
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
let scopedContext = init.getContext ? await init.getContext() : new RouterContextProvider();
|
|
777
|
+
let preventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
778
|
+
if (submission && isMutationMethod(submission.formMethod)) {
|
|
779
|
+
await handleFetcherAction(key, routeId, path, matches, scopedContext, fogOfWar.active, flushSync, preventScrollReset, submission, opts && opts.defaultShouldRevalidate);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
fetchLoadMatches.set(key, {
|
|
783
|
+
routeId,
|
|
784
|
+
path
|
|
785
|
+
});
|
|
786
|
+
await handleFetcherLoader(key, routeId, path, matches, scopedContext, fogOfWar.active, flushSync, preventScrollReset, submission);
|
|
787
|
+
}
|
|
788
|
+
async function handleFetcherAction(key, routeId, path, requestMatches, scopedContext, isFogOfWar, flushSync, preventScrollReset, submission, callSiteDefaultShouldRevalidate) {
|
|
789
|
+
interruptActiveLoads();
|
|
790
|
+
fetchLoadMatches.delete(key);
|
|
791
|
+
updateFetcherState(key, getSubmittingFetcher(submission, state.fetchers.get(key)), { flushSync });
|
|
792
|
+
let abortController = new AbortController();
|
|
793
|
+
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
|
|
794
|
+
if (isFogOfWar) {
|
|
795
|
+
let discoverResult = await discoverRoutes(requestMatches, new URL(fetchRequest.url).pathname, fetchRequest.signal, key);
|
|
796
|
+
if (discoverResult.type === "aborted") return;
|
|
797
|
+
else if (discoverResult.type === "error") {
|
|
798
|
+
setFetcherError(key, routeId, discoverResult.error, { flushSync });
|
|
799
|
+
return;
|
|
800
|
+
} else if (!discoverResult.matches) {
|
|
801
|
+
setFetcherError(key, routeId, getInternalRouterError(404, { pathname: path }), { flushSync });
|
|
802
|
+
return;
|
|
803
|
+
} else requestMatches = discoverResult.matches;
|
|
804
|
+
}
|
|
805
|
+
let match = getTargetMatch(requestMatches, path);
|
|
806
|
+
if (!match.route.action && !match.route.lazy) {
|
|
807
|
+
setFetcherError(key, routeId, getInternalRouterError(405, {
|
|
808
|
+
method: submission.formMethod,
|
|
809
|
+
pathname: path,
|
|
810
|
+
routeId
|
|
811
|
+
}), { flushSync });
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
fetchControllers.set(key, abortController);
|
|
815
|
+
let originatingLoadId = incrementingLoadId;
|
|
816
|
+
let fetchMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, path, requestMatches, match, hydrationRouteProperties, scopedContext);
|
|
817
|
+
let actionResults = await callDataStrategy(fetchRequest, path, fetchMatches, scopedContext, key);
|
|
818
|
+
let actionResult = actionResults[match.route.id];
|
|
819
|
+
if (!actionResult) {
|
|
820
|
+
for (let match of fetchMatches) if (actionResults[match.route.id]) {
|
|
821
|
+
actionResult = actionResults[match.route.id];
|
|
822
|
+
break;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
if (fetchRequest.signal.aborted) {
|
|
826
|
+
if (fetchControllers.get(key) === abortController) fetchControllers.delete(key);
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
if (fetchersQueuedForDeletion.has(key)) {
|
|
830
|
+
if (isRedirectResult(actionResult) || isErrorResult(actionResult)) {
|
|
831
|
+
updateFetcherState(key, getDoneFetcher(void 0));
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
} else {
|
|
835
|
+
if (isRedirectResult(actionResult)) {
|
|
836
|
+
fetchControllers.delete(key);
|
|
837
|
+
if (pendingNavigationLoadId > originatingLoadId) {
|
|
838
|
+
updateFetcherState(key, getDoneFetcher(void 0));
|
|
839
|
+
return;
|
|
840
|
+
} else {
|
|
841
|
+
fetchRedirectIds.add(key);
|
|
842
|
+
updateFetcherState(key, getLoadingFetcher(submission));
|
|
843
|
+
return startRedirectNavigation(fetchRequest, actionResult, false, {
|
|
844
|
+
fetcherSubmission: submission,
|
|
845
|
+
preventScrollReset
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
if (isErrorResult(actionResult)) {
|
|
850
|
+
setFetcherError(key, routeId, actionResult.error);
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
let nextLocation = state.navigation.location || state.location;
|
|
855
|
+
let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal);
|
|
856
|
+
let routesToUse = dataRoutes.activeRoutes;
|
|
857
|
+
let matches = state.navigation.state !== "idle" ? matchRoutesImpl(routesToUse, state.navigation.location, basename, false, dataRoutes.branches) : state.matches;
|
|
858
|
+
invariant(matches, "Didn't find any matches after fetcher action");
|
|
859
|
+
let loadId = ++incrementingLoadId;
|
|
860
|
+
fetchReloadIds.set(key, loadId);
|
|
861
|
+
let { dsMatches, revalidatingFetchers } = getMatchesToLoad(revalidationRequest, scopedContext, mapRouteProperties, manifest, init.history, state, matches, submission, nextLocation, hydrationRouteProperties, false, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, init.patchRoutesOnNavigation != null, dataRoutes.branches, [match.route.id, actionResult], callSiteDefaultShouldRevalidate);
|
|
862
|
+
let loadFetcher = getLoadingFetcher(submission, actionResult.data);
|
|
863
|
+
let workingFetchers = new Map(state.fetchers);
|
|
864
|
+
workingFetchers.set(key, loadFetcher);
|
|
865
|
+
revalidatingFetchers.filter((rf) => rf.key !== key).forEach((rf) => {
|
|
866
|
+
let staleKey = rf.key;
|
|
867
|
+
let existingFetcher = workingFetchers.get(staleKey);
|
|
868
|
+
let revalidatingFetcher = getLoadingFetcher(void 0, existingFetcher ? existingFetcher.data : void 0);
|
|
869
|
+
workingFetchers.set(staleKey, revalidatingFetcher);
|
|
870
|
+
abortFetcher(staleKey);
|
|
871
|
+
if (rf.controller) fetchControllers.set(staleKey, rf.controller);
|
|
872
|
+
});
|
|
873
|
+
updateState({ fetchers: workingFetchers });
|
|
874
|
+
let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((rf) => abortFetcher(rf.key));
|
|
875
|
+
abortController.signal.addEventListener("abort", abortPendingFetchRevalidations);
|
|
876
|
+
let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, revalidationRequest, nextLocation, scopedContext);
|
|
877
|
+
if (abortController.signal.aborted) return;
|
|
878
|
+
abortController.signal.removeEventListener("abort", abortPendingFetchRevalidations);
|
|
879
|
+
fetchReloadIds.delete(key);
|
|
880
|
+
fetchControllers.delete(key);
|
|
881
|
+
revalidatingFetchers.forEach((r) => fetchControllers.delete(r.key));
|
|
882
|
+
let fetcherIsMounted = state.fetchers.has(key);
|
|
883
|
+
let getRedirectStateWithDoneFetcher = (s) => {
|
|
884
|
+
if (!fetcherIsMounted) return s;
|
|
885
|
+
let workingFetchers = new Map(s.fetchers);
|
|
886
|
+
workingFetchers.set(key, getDoneFetcher(actionResult.data));
|
|
887
|
+
return {
|
|
888
|
+
...s,
|
|
889
|
+
fetchers: workingFetchers
|
|
890
|
+
};
|
|
891
|
+
};
|
|
892
|
+
let redirect = findRedirect(loaderResults);
|
|
893
|
+
if (redirect) {
|
|
894
|
+
state = getRedirectStateWithDoneFetcher(state);
|
|
895
|
+
return startRedirectNavigation(revalidationRequest, redirect.result, false, { preventScrollReset });
|
|
896
|
+
}
|
|
897
|
+
redirect = findRedirect(fetcherResults);
|
|
898
|
+
if (redirect) {
|
|
899
|
+
fetchRedirectIds.add(redirect.key);
|
|
900
|
+
state = getRedirectStateWithDoneFetcher(state);
|
|
901
|
+
return startRedirectNavigation(revalidationRequest, redirect.result, false, { preventScrollReset });
|
|
902
|
+
}
|
|
903
|
+
let finalFetchers = new Map(state.fetchers);
|
|
904
|
+
if (fetcherIsMounted) finalFetchers.set(key, getDoneFetcher(actionResult.data));
|
|
905
|
+
let { loaderData, errors } = processLoaderData(state, matches, loaderResults, void 0, revalidatingFetchers, fetcherResults, finalFetchers);
|
|
906
|
+
abortStaleFetchLoads(loadId, finalFetchers);
|
|
907
|
+
if (state.navigation.state === "loading" && loadId > pendingNavigationLoadId) {
|
|
908
|
+
invariant(pendingAction, "Expected pending action");
|
|
909
|
+
pendingNavigationController && pendingNavigationController.abort();
|
|
910
|
+
completeNavigation(state.navigation.location, {
|
|
911
|
+
matches,
|
|
912
|
+
loaderData,
|
|
913
|
+
errors,
|
|
914
|
+
fetchers: finalFetchers
|
|
915
|
+
});
|
|
916
|
+
} else {
|
|
917
|
+
updateState({
|
|
918
|
+
errors,
|
|
919
|
+
loaderData: mergeLoaderData(state.loaderData, loaderData, matches, errors),
|
|
920
|
+
fetchers: finalFetchers
|
|
921
|
+
});
|
|
922
|
+
isRevalidationRequired = false;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
async function handleFetcherLoader(key, routeId, path, matches, scopedContext, isFogOfWar, flushSync, preventScrollReset, submission) {
|
|
926
|
+
let existingFetcher = state.fetchers.get(key);
|
|
927
|
+
updateFetcherState(key, getLoadingFetcher(submission, existingFetcher ? existingFetcher.data : void 0), { flushSync });
|
|
928
|
+
let abortController = new AbortController();
|
|
929
|
+
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
|
|
930
|
+
if (isFogOfWar) {
|
|
931
|
+
let discoverResult = await discoverRoutes(matches, new URL(fetchRequest.url).pathname, fetchRequest.signal, key);
|
|
932
|
+
if (discoverResult.type === "aborted") return;
|
|
933
|
+
else if (discoverResult.type === "error") {
|
|
934
|
+
setFetcherError(key, routeId, discoverResult.error, { flushSync });
|
|
935
|
+
return;
|
|
936
|
+
} else if (!discoverResult.matches) {
|
|
937
|
+
setFetcherError(key, routeId, getInternalRouterError(404, { pathname: path }), { flushSync });
|
|
938
|
+
return;
|
|
939
|
+
} else matches = discoverResult.matches;
|
|
940
|
+
}
|
|
941
|
+
let match = getTargetMatch(matches, path);
|
|
942
|
+
fetchControllers.set(key, abortController);
|
|
943
|
+
let originatingLoadId = incrementingLoadId;
|
|
944
|
+
let results = await callDataStrategy(fetchRequest, path, getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, path, matches, match, hydrationRouteProperties, scopedContext), scopedContext, key);
|
|
945
|
+
let result = results[match.route.id];
|
|
946
|
+
if (!result) {
|
|
947
|
+
for (let match of matches) if (results[match.route.id]) {
|
|
948
|
+
result = results[match.route.id];
|
|
949
|
+
break;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
if (fetchControllers.get(key) === abortController) fetchControllers.delete(key);
|
|
953
|
+
if (fetchRequest.signal.aborted) return;
|
|
954
|
+
if (fetchersQueuedForDeletion.has(key)) {
|
|
955
|
+
updateFetcherState(key, getDoneFetcher(void 0));
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
if (isRedirectResult(result)) if (pendingNavigationLoadId > originatingLoadId) {
|
|
959
|
+
updateFetcherState(key, getDoneFetcher(void 0));
|
|
960
|
+
return;
|
|
961
|
+
} else {
|
|
962
|
+
fetchRedirectIds.add(key);
|
|
963
|
+
await startRedirectNavigation(fetchRequest, result, false, { preventScrollReset });
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
if (isErrorResult(result)) {
|
|
967
|
+
setFetcherError(key, routeId, result.error);
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
updateFetcherState(key, getDoneFetcher(result.data));
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Utility function to handle redirects returned from an action or loader.
|
|
974
|
+
* Normally, a redirect "replaces" the navigation that triggered it. So, for
|
|
975
|
+
* example:
|
|
976
|
+
*
|
|
977
|
+
* - user is on /a
|
|
978
|
+
* - user clicks a link to /b
|
|
979
|
+
* - loader for /b redirects to /c
|
|
980
|
+
*
|
|
981
|
+
* In a non-JS app the browser would track the in-flight navigation to /b and
|
|
982
|
+
* then replace it with /c when it encountered the redirect response. In
|
|
983
|
+
* the end it would only ever update the URL bar with /c.
|
|
984
|
+
*
|
|
985
|
+
* In client-side routing using pushState/replaceState, we aim to emulate
|
|
986
|
+
* this behavior and we also do not update history until the end of the
|
|
987
|
+
* navigation (including processed redirects). This means that we never
|
|
988
|
+
* actually touch history until we've processed redirects, so we just use
|
|
989
|
+
* the history action from the original navigation (PUSH or REPLACE).
|
|
990
|
+
*/
|
|
991
|
+
async function startRedirectNavigation(request, redirect, isNavigation, { submission, fetcherSubmission, preventScrollReset, replace } = {}) {
|
|
992
|
+
if (!isNavigation) {
|
|
993
|
+
pendingPopstateNavigationDfd?.resolve();
|
|
994
|
+
pendingPopstateNavigationDfd = null;
|
|
995
|
+
}
|
|
996
|
+
if (redirect.response.headers.has("X-Remix-Revalidate")) isRevalidationRequired = true;
|
|
997
|
+
let location = redirect.response.headers.get("Location");
|
|
998
|
+
invariant(location, "Expected a Location header on the redirect Response");
|
|
999
|
+
location = normalizeRedirectLocation(location, new URL(request.url), basename, init.history);
|
|
1000
|
+
let redirectLocation = createLocation(state.location, location, { _isRedirect: true });
|
|
1001
|
+
if (isBrowser) {
|
|
1002
|
+
let isDocumentReload = false;
|
|
1003
|
+
if (redirect.response.headers.has("X-Remix-Reload-Document")) isDocumentReload = true;
|
|
1004
|
+
else if (isAbsoluteUrl(location)) {
|
|
1005
|
+
const url = createBrowserURLImpl(routerWindow, location, true);
|
|
1006
|
+
isDocumentReload = url.origin !== routerWindow.location.origin || stripBasename(url.pathname, basename) == null;
|
|
1007
|
+
}
|
|
1008
|
+
if (isDocumentReload) {
|
|
1009
|
+
if (replace) routerWindow.location.replace(location);
|
|
1010
|
+
else routerWindow.location.assign(location);
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
pendingNavigationController = null;
|
|
1015
|
+
let redirectNavigationType = replace === true || redirect.response.headers.has("X-Remix-Replace") ? "REPLACE" : "PUSH";
|
|
1016
|
+
let { formMethod, formAction, formEncType } = state.navigation;
|
|
1017
|
+
if (!submission && !fetcherSubmission && formMethod && formAction && formEncType) submission = getSubmissionFromNavigation(state.navigation);
|
|
1018
|
+
let activeSubmission = submission || fetcherSubmission;
|
|
1019
|
+
if (redirectPreserveMethodStatusCodes.has(redirect.response.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) await startNavigation(redirectNavigationType, redirectLocation, {
|
|
1020
|
+
submission: {
|
|
1021
|
+
...activeSubmission,
|
|
1022
|
+
formAction: location
|
|
1023
|
+
},
|
|
1024
|
+
preventScrollReset: preventScrollReset || pendingPreventScrollReset,
|
|
1025
|
+
enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0
|
|
1026
|
+
});
|
|
1027
|
+
else await startNavigation(redirectNavigationType, redirectLocation, {
|
|
1028
|
+
overrideNavigation: getLoadingNavigation(redirectLocation, [], redirectNavigationType, submission),
|
|
1029
|
+
fetcherSubmission,
|
|
1030
|
+
preventScrollReset: preventScrollReset || pendingPreventScrollReset,
|
|
1031
|
+
enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
async function callDataStrategy(request, path, matches, scopedContext, fetcherKey) {
|
|
1035
|
+
let results;
|
|
1036
|
+
let dataResults = {};
|
|
1037
|
+
try {
|
|
1038
|
+
results = await callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, scopedContext, false);
|
|
1039
|
+
} catch (e) {
|
|
1040
|
+
matches.filter((m) => m.shouldLoad).forEach((m) => {
|
|
1041
|
+
dataResults[m.route.id] = {
|
|
1042
|
+
type: "error",
|
|
1043
|
+
error: e
|
|
1044
|
+
};
|
|
1045
|
+
});
|
|
1046
|
+
return dataResults;
|
|
1047
|
+
}
|
|
1048
|
+
if (request.signal.aborted) return dataResults;
|
|
1049
|
+
if (!isMutationMethod(request.method)) for (let match of matches) {
|
|
1050
|
+
if (results[match.route.id]?.type === "error") break;
|
|
1051
|
+
if (!results.hasOwnProperty(match.route.id) && !state.loaderData.hasOwnProperty(match.route.id) && (!state.errors || !state.errors.hasOwnProperty(match.route.id)) && match.shouldCallHandler()) results[match.route.id] = {
|
|
1052
|
+
type: "error",
|
|
1053
|
+
result: /* @__PURE__ */ new Error(`No result returned from dataStrategy for route ${match.route.id}`)
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
for (let [routeId, result] of Object.entries(results)) if (isRedirectDataStrategyResult(result)) {
|
|
1057
|
+
let response = result.result;
|
|
1058
|
+
dataResults[routeId] = {
|
|
1059
|
+
type: "redirect",
|
|
1060
|
+
response: normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename)
|
|
1061
|
+
};
|
|
1062
|
+
} else dataResults[routeId] = await convertDataStrategyResultToDataResult(result);
|
|
1063
|
+
return dataResults;
|
|
1064
|
+
}
|
|
1065
|
+
async function callLoadersAndMaybeResolveData(matches, fetchersToLoad, request, location, scopedContext) {
|
|
1066
|
+
let loaderResultsPromise = callDataStrategy(request, location, matches, scopedContext, null);
|
|
1067
|
+
let fetcherResultsPromise = Promise.all(fetchersToLoad.map(async (f) => {
|
|
1068
|
+
if (f.matches && f.match && f.request && f.controller) {
|
|
1069
|
+
let result = (await callDataStrategy(f.request, f.path, f.matches, scopedContext, f.key))[f.match.route.id];
|
|
1070
|
+
return { [f.key]: result };
|
|
1071
|
+
} else return Promise.resolve({ [f.key]: {
|
|
1072
|
+
type: "error",
|
|
1073
|
+
error: getInternalRouterError(404, { pathname: f.path })
|
|
1074
|
+
} });
|
|
1075
|
+
}));
|
|
1076
|
+
return {
|
|
1077
|
+
loaderResults: await loaderResultsPromise,
|
|
1078
|
+
fetcherResults: (await fetcherResultsPromise).reduce((acc, r) => Object.assign(acc, r), {})
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
function interruptActiveLoads() {
|
|
1082
|
+
isRevalidationRequired = true;
|
|
1083
|
+
fetchLoadMatches.forEach((_, key) => {
|
|
1084
|
+
if (fetchControllers.has(key)) cancelledFetcherLoads.add(key);
|
|
1085
|
+
abortFetcher(key);
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
function updateFetcherState(key, fetcher, opts = {}) {
|
|
1089
|
+
let workingFetchers = new Map(state.fetchers);
|
|
1090
|
+
workingFetchers.set(key, fetcher);
|
|
1091
|
+
updateState({ fetchers: workingFetchers }, { flushSync: (opts && opts.flushSync) === true });
|
|
1092
|
+
}
|
|
1093
|
+
function setFetcherError(key, routeId, error, opts = {}) {
|
|
1094
|
+
let boundaryMatch = findNearestBoundary(state.matches, routeId);
|
|
1095
|
+
let workingFetchers = new Map(state.fetchers);
|
|
1096
|
+
deleteFetcher(workingFetchers, key);
|
|
1097
|
+
updateState({
|
|
1098
|
+
errors: { [boundaryMatch.route.id]: error },
|
|
1099
|
+
fetchers: workingFetchers
|
|
1100
|
+
}, { flushSync: (opts && opts.flushSync) === true });
|
|
1101
|
+
}
|
|
1102
|
+
function getFetcher(key) {
|
|
1103
|
+
activeFetchers.set(key, (activeFetchers.get(key) || 0) + 1);
|
|
1104
|
+
if (fetchersQueuedForDeletion.has(key)) fetchersQueuedForDeletion.delete(key);
|
|
1105
|
+
return state.fetchers.get(key) || IDLE_FETCHER;
|
|
1106
|
+
}
|
|
1107
|
+
function resetFetcher(key, opts) {
|
|
1108
|
+
abortFetcher(key, opts?.reason);
|
|
1109
|
+
updateFetcherState(key, getDoneFetcher(null));
|
|
1110
|
+
}
|
|
1111
|
+
function deleteFetcher(fetchers, key) {
|
|
1112
|
+
let fetcher = state.fetchers.get(key);
|
|
1113
|
+
if (fetchControllers.has(key) && !(fetcher && fetcher.state === "loading" && fetchReloadIds.has(key))) abortFetcher(key);
|
|
1114
|
+
fetchLoadMatches.delete(key);
|
|
1115
|
+
fetchReloadIds.delete(key);
|
|
1116
|
+
fetchRedirectIds.delete(key);
|
|
1117
|
+
fetchersQueuedForDeletion.delete(key);
|
|
1118
|
+
cancelledFetcherLoads.delete(key);
|
|
1119
|
+
fetchers.delete(key);
|
|
1120
|
+
}
|
|
1121
|
+
function queueFetcherForDeletion(key) {
|
|
1122
|
+
let count = (activeFetchers.get(key) || 0) - 1;
|
|
1123
|
+
if (count <= 0) {
|
|
1124
|
+
activeFetchers.delete(key);
|
|
1125
|
+
fetchersQueuedForDeletion.add(key);
|
|
1126
|
+
} else activeFetchers.set(key, count);
|
|
1127
|
+
updateState({ fetchers: new Map(state.fetchers) });
|
|
1128
|
+
}
|
|
1129
|
+
function abortFetcher(key, reason) {
|
|
1130
|
+
let controller = fetchControllers.get(key);
|
|
1131
|
+
if (controller) {
|
|
1132
|
+
controller.abort(reason);
|
|
1133
|
+
fetchControllers.delete(key);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
function markFetchersDone(keys, fetchers) {
|
|
1137
|
+
for (let key of keys) {
|
|
1138
|
+
let fetcher = fetchers.get(key);
|
|
1139
|
+
invariant(fetcher, `Expected fetcher: ${key}`);
|
|
1140
|
+
let doneFetcher = getDoneFetcher(fetcher.data);
|
|
1141
|
+
fetchers.set(key, doneFetcher);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
function markFetchRedirectsDone(fetchers) {
|
|
1145
|
+
let doneKeys = [];
|
|
1146
|
+
let didUpdateFetchers = false;
|
|
1147
|
+
for (let key of fetchRedirectIds) {
|
|
1148
|
+
let fetcher = fetchers.get(key);
|
|
1149
|
+
invariant(fetcher, `Expected fetcher: ${key}`);
|
|
1150
|
+
if (fetcher.state === "loading") {
|
|
1151
|
+
fetchRedirectIds.delete(key);
|
|
1152
|
+
doneKeys.push(key);
|
|
1153
|
+
didUpdateFetchers = true;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
markFetchersDone(doneKeys, fetchers);
|
|
1157
|
+
return didUpdateFetchers;
|
|
1158
|
+
}
|
|
1159
|
+
function abortStaleFetchLoads(landedId, fetchers) {
|
|
1160
|
+
let yeetedKeys = [];
|
|
1161
|
+
for (let [key, id] of fetchReloadIds) if (id < landedId) {
|
|
1162
|
+
let fetcher = fetchers.get(key);
|
|
1163
|
+
invariant(fetcher, `Expected fetcher: ${key}`);
|
|
1164
|
+
if (fetcher.state === "loading") {
|
|
1165
|
+
abortFetcher(key);
|
|
1166
|
+
fetchReloadIds.delete(key);
|
|
1167
|
+
yeetedKeys.push(key);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
markFetchersDone(yeetedKeys, fetchers);
|
|
1171
|
+
return yeetedKeys.length > 0;
|
|
1172
|
+
}
|
|
1173
|
+
function getBlocker(key, fn) {
|
|
1174
|
+
let blocker = state.blockers.get(key) || IDLE_BLOCKER;
|
|
1175
|
+
if (blockerFunctions.get(key) !== fn) blockerFunctions.set(key, fn);
|
|
1176
|
+
return blocker;
|
|
1177
|
+
}
|
|
1178
|
+
function deleteBlocker(key) {
|
|
1179
|
+
state.blockers.delete(key);
|
|
1180
|
+
blockerFunctions.delete(key);
|
|
1181
|
+
}
|
|
1182
|
+
function updateBlocker(key, newBlocker) {
|
|
1183
|
+
let blocker = state.blockers.get(key) || IDLE_BLOCKER;
|
|
1184
|
+
invariant(blocker.state === "unblocked" && newBlocker.state === "blocked" || blocker.state === "blocked" && newBlocker.state === "blocked" || blocker.state === "blocked" && newBlocker.state === "proceeding" || blocker.state === "blocked" && newBlocker.state === "unblocked" || blocker.state === "proceeding" && newBlocker.state === "unblocked", `Invalid blocker state transition: ${blocker.state} -> ${newBlocker.state}`);
|
|
1185
|
+
let blockers = new Map(state.blockers);
|
|
1186
|
+
blockers.set(key, newBlocker);
|
|
1187
|
+
updateState({ blockers });
|
|
1188
|
+
}
|
|
1189
|
+
function shouldBlockNavigation({ currentLocation, nextLocation, historyAction }) {
|
|
1190
|
+
if (blockerFunctions.size === 0) return;
|
|
1191
|
+
if (blockerFunctions.size > 1) warning(false, "A router only supports one blocker at a time");
|
|
1192
|
+
let entries = Array.from(blockerFunctions.entries());
|
|
1193
|
+
let [blockerKey, blockerFunction] = entries[entries.length - 1];
|
|
1194
|
+
let blocker = state.blockers.get(blockerKey);
|
|
1195
|
+
if (blocker && blocker.state === "proceeding") return;
|
|
1196
|
+
if (blockerFunction({
|
|
1197
|
+
currentLocation,
|
|
1198
|
+
nextLocation,
|
|
1199
|
+
historyAction
|
|
1200
|
+
})) return blockerKey;
|
|
1201
|
+
}
|
|
1202
|
+
function handleNavigational404(pathname) {
|
|
1203
|
+
let error = getInternalRouterError(404, { pathname });
|
|
1204
|
+
let routesToUse = dataRoutes.activeRoutes;
|
|
1205
|
+
let { matches, route } = getShortCircuitMatches(routesToUse);
|
|
1206
|
+
return {
|
|
1207
|
+
notFoundMatches: matches,
|
|
1208
|
+
route,
|
|
1209
|
+
error
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
function enableScrollRestoration(positions, getPosition, getKey) {
|
|
1213
|
+
savedScrollPositions = positions;
|
|
1214
|
+
getScrollPosition = getPosition;
|
|
1215
|
+
getScrollRestorationKey = getKey || null;
|
|
1216
|
+
if (!initialScrollRestored && state.navigation === IDLE_NAVIGATION) {
|
|
1217
|
+
initialScrollRestored = true;
|
|
1218
|
+
let y = getSavedScrollPosition(state.location, state.matches);
|
|
1219
|
+
if (y != null) updateState({ restoreScrollPosition: y });
|
|
1220
|
+
}
|
|
1221
|
+
return () => {
|
|
1222
|
+
savedScrollPositions = null;
|
|
1223
|
+
getScrollPosition = null;
|
|
1224
|
+
getScrollRestorationKey = null;
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
function getScrollKey(location, matches) {
|
|
1228
|
+
if (getScrollRestorationKey) return getScrollRestorationKey(location, matches.map((m) => convertRouteMatchToUiMatch(m, state.loaderData))) || location.key;
|
|
1229
|
+
return location.key;
|
|
1230
|
+
}
|
|
1231
|
+
function saveScrollPosition(location, matches) {
|
|
1232
|
+
if (savedScrollPositions && getScrollPosition) {
|
|
1233
|
+
let key = getScrollKey(location, matches);
|
|
1234
|
+
savedScrollPositions[key] = getScrollPosition();
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
function getSavedScrollPosition(location, matches) {
|
|
1238
|
+
if (savedScrollPositions) {
|
|
1239
|
+
let key = getScrollKey(location, matches);
|
|
1240
|
+
let y = savedScrollPositions[key];
|
|
1241
|
+
if (typeof y === "number") return y;
|
|
1242
|
+
}
|
|
1243
|
+
return null;
|
|
1244
|
+
}
|
|
1245
|
+
function checkFogOfWar(matches, routesToUse, pathname) {
|
|
1246
|
+
if (init.patchRoutesOnNavigation) {
|
|
1247
|
+
let activeBranches = dataRoutes.branches;
|
|
1248
|
+
if (!matches) return {
|
|
1249
|
+
active: true,
|
|
1250
|
+
matches: matchRoutesImpl(routesToUse, pathname, basename, true, activeBranches) || []
|
|
1251
|
+
};
|
|
1252
|
+
else if (Object.keys(matches[0].params).length > 0) return {
|
|
1253
|
+
active: true,
|
|
1254
|
+
matches: matchRoutesImpl(routesToUse, pathname, basename, true, activeBranches)
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
return {
|
|
1258
|
+
active: false,
|
|
1259
|
+
matches: null
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
async function discoverRoutes(matches, pathname, signal, fetcherKey) {
|
|
1263
|
+
if (!init.patchRoutesOnNavigation) return {
|
|
1264
|
+
type: "success",
|
|
1265
|
+
matches
|
|
1266
|
+
};
|
|
1267
|
+
let partialMatches = matches;
|
|
1268
|
+
while (true) {
|
|
1269
|
+
let localManifest = manifest;
|
|
1270
|
+
try {
|
|
1271
|
+
await init.patchRoutesOnNavigation({
|
|
1272
|
+
signal,
|
|
1273
|
+
path: pathname,
|
|
1274
|
+
matches: partialMatches,
|
|
1275
|
+
fetcherKey,
|
|
1276
|
+
patch: (routeId, children) => {
|
|
1277
|
+
if (signal.aborted) return;
|
|
1278
|
+
patchRoutesImpl(routeId, children, dataRoutes, localManifest, mapRouteProperties, false);
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1281
|
+
} catch (e) {
|
|
1282
|
+
return {
|
|
1283
|
+
type: "error",
|
|
1284
|
+
error: e,
|
|
1285
|
+
partialMatches
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
if (signal.aborted) return { type: "aborted" };
|
|
1289
|
+
let activeBranches = dataRoutes.branches;
|
|
1290
|
+
let newMatches = matchRoutesImpl(dataRoutes.activeRoutes, pathname, basename, false, activeBranches);
|
|
1291
|
+
let newPartialMatches = null;
|
|
1292
|
+
if (newMatches) if (Object.keys(newMatches[0].params).length === 0) return {
|
|
1293
|
+
type: "success",
|
|
1294
|
+
matches: newMatches
|
|
1295
|
+
};
|
|
1296
|
+
else {
|
|
1297
|
+
newPartialMatches = matchRoutesImpl(dataRoutes.activeRoutes, pathname, basename, true, activeBranches);
|
|
1298
|
+
if (!(newPartialMatches && partialMatches.length < newPartialMatches.length && compareMatches(partialMatches, newPartialMatches.slice(0, partialMatches.length)))) return {
|
|
1299
|
+
type: "success",
|
|
1300
|
+
matches: newMatches
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
if (!newPartialMatches) newPartialMatches = matchRoutesImpl(dataRoutes.activeRoutes, pathname, basename, true, activeBranches);
|
|
1304
|
+
if (!newPartialMatches || compareMatches(partialMatches, newPartialMatches)) return {
|
|
1305
|
+
type: "success",
|
|
1306
|
+
matches: null
|
|
1307
|
+
};
|
|
1308
|
+
partialMatches = newPartialMatches;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
function compareMatches(a, b) {
|
|
1312
|
+
return a.length === b.length && a.every((m, i) => m.route.id === b[i].route.id);
|
|
1313
|
+
}
|
|
1314
|
+
function _internalSetRoutes(newRoutes) {
|
|
1315
|
+
manifest = {};
|
|
1316
|
+
dataRoutes.setHmrRoutes(convertRoutesToDataRoutes(newRoutes, mapRouteProperties, void 0, manifest));
|
|
1317
|
+
}
|
|
1318
|
+
function patchRoutes(routeId, children, unstable_allowElementMutations = false) {
|
|
1319
|
+
patchRoutesImpl(routeId, children, dataRoutes, manifest, mapRouteProperties, unstable_allowElementMutations);
|
|
1320
|
+
if (!dataRoutes.hasHMRRoutes) updateState({});
|
|
1321
|
+
}
|
|
1322
|
+
router = {
|
|
1323
|
+
get basename() {
|
|
1324
|
+
return basename;
|
|
1325
|
+
},
|
|
1326
|
+
get future() {
|
|
1327
|
+
return future;
|
|
1328
|
+
},
|
|
1329
|
+
get state() {
|
|
1330
|
+
return state;
|
|
1331
|
+
},
|
|
1332
|
+
get routes() {
|
|
1333
|
+
return dataRoutes.stableRoutes;
|
|
1334
|
+
},
|
|
1335
|
+
get branches() {
|
|
1336
|
+
return dataRoutes.branches;
|
|
1337
|
+
},
|
|
1338
|
+
get manifest() {
|
|
1339
|
+
return manifest;
|
|
1340
|
+
},
|
|
1341
|
+
get window() {
|
|
1342
|
+
return routerWindow;
|
|
1343
|
+
},
|
|
1344
|
+
initialize,
|
|
1345
|
+
subscribe,
|
|
1346
|
+
enableScrollRestoration,
|
|
1347
|
+
navigate,
|
|
1348
|
+
fetch,
|
|
1349
|
+
revalidate,
|
|
1350
|
+
createHref: (to) => init.history.createHref(to),
|
|
1351
|
+
encodeLocation: (to) => init.history.encodeLocation(to),
|
|
1352
|
+
getFetcher,
|
|
1353
|
+
resetFetcher,
|
|
1354
|
+
deleteFetcher: queueFetcherForDeletion,
|
|
1355
|
+
dispose,
|
|
1356
|
+
getBlocker,
|
|
1357
|
+
deleteBlocker,
|
|
1358
|
+
patchRoutes,
|
|
1359
|
+
_internalFetchControllers: fetchControllers,
|
|
1360
|
+
_internalSetRoutes,
|
|
1361
|
+
_internalSetStateDoNotUseOrYouWillBreakYourApp(newState) {
|
|
1362
|
+
updateState(newState);
|
|
1363
|
+
}
|
|
1364
|
+
};
|
|
1365
|
+
if (init.instrumentations) router = instrumentClientSideRouter(router, init.instrumentations.map((i) => i.router).filter(Boolean));
|
|
1366
|
+
return router;
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Create a static handler to perform server-side data loading
|
|
1370
|
+
*
|
|
1371
|
+
* @example
|
|
1372
|
+
* export async function handleRequest(request: Request) {
|
|
1373
|
+
* let { query, dataRoutes } = createStaticHandler(routes);
|
|
1374
|
+
* let context = await query(request);
|
|
1375
|
+
*
|
|
1376
|
+
* if (context instanceof Response) {
|
|
1377
|
+
* return context;
|
|
1378
|
+
* }
|
|
1379
|
+
*
|
|
1380
|
+
* let router = createStaticRouter(dataRoutes, context);
|
|
1381
|
+
* return new Response(
|
|
1382
|
+
* ReactDOMServer.renderToString(<StaticRouterProvider ... />),
|
|
1383
|
+
* { headers: { "Content-Type": "text/html" } }
|
|
1384
|
+
* );
|
|
1385
|
+
* }
|
|
1386
|
+
*
|
|
1387
|
+
* @public
|
|
1388
|
+
* @category Data Routers
|
|
1389
|
+
* @mode data
|
|
1390
|
+
* @param routes The {@link RouteObject | route objects} to create a static
|
|
1391
|
+
* handler for
|
|
1392
|
+
* @param opts Options
|
|
1393
|
+
* @param opts.basename The base URL for the static handler (default: `/`)
|
|
1394
|
+
* @param opts.future Future flags for the static handler
|
|
1395
|
+
* @returns A static handler that can be used to query data for the provided
|
|
1396
|
+
* routes
|
|
1397
|
+
*/
|
|
1398
|
+
function createStaticHandler(routes, opts) {
|
|
1399
|
+
invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
|
|
1400
|
+
let manifest = {};
|
|
1401
|
+
let basename = (opts ? opts.basename : null) || "/";
|
|
1402
|
+
let _mapRouteProperties = opts?.mapRouteProperties;
|
|
1403
|
+
let mapRouteProperties = _mapRouteProperties ? _mapRouteProperties : () => ({});
|
|
1404
|
+
({ ...opts?.future });
|
|
1405
|
+
if (opts?.instrumentations) {
|
|
1406
|
+
let instrumentations = opts.instrumentations;
|
|
1407
|
+
mapRouteProperties = (route) => {
|
|
1408
|
+
return {
|
|
1409
|
+
..._mapRouteProperties?.(route),
|
|
1410
|
+
...getRouteInstrumentationUpdates(instrumentations.map((i) => i.route).filter(Boolean), route)
|
|
1411
|
+
};
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
let dataRoutes = convertRoutesToDataRoutes(routes, mapRouteProperties, void 0, manifest);
|
|
1415
|
+
let routeBranches = flattenAndRankRoutes(dataRoutes);
|
|
1416
|
+
/**
|
|
1417
|
+
* The query() method is intended for document requests, in which we want to
|
|
1418
|
+
* call an optional action and potentially multiple loaders for all nested
|
|
1419
|
+
* routes. It returns a StaticHandlerContext object, which is very similar
|
|
1420
|
+
* to the router state (location, loaderData, actionData, errors, etc.) and
|
|
1421
|
+
* also adds SSR-specific information such as the statusCode and headers
|
|
1422
|
+
* from action/loaders Responses.
|
|
1423
|
+
*
|
|
1424
|
+
* It _should_ never throw and should report all errors through the
|
|
1425
|
+
* returned handlerContext.errors object, properly associating errors to
|
|
1426
|
+
* their error boundary. Additionally, it tracks _deepestRenderedBoundaryId
|
|
1427
|
+
* which can be used to emulate React error boundaries during SSR by performing
|
|
1428
|
+
* a second pass only down to the boundaryId.
|
|
1429
|
+
*
|
|
1430
|
+
* The one exception where we do not return a StaticHandlerContext is when a
|
|
1431
|
+
* redirect response is returned or thrown from any action/loader. We
|
|
1432
|
+
* propagate that out and return the raw Response so the HTTP server can
|
|
1433
|
+
* return it directly.
|
|
1434
|
+
*
|
|
1435
|
+
* - `opts.requestContext` is an optional server context that will be passed
|
|
1436
|
+
* to actions/loaders in the `context` parameter
|
|
1437
|
+
* - `opts.skipLoaderErrorBubbling` is an optional parameter that will prevent
|
|
1438
|
+
* the bubbling of errors which allows single-fetch-type implementations
|
|
1439
|
+
* where the client will handle the bubbling and we may need to return data
|
|
1440
|
+
* for the handling route
|
|
1441
|
+
*/
|
|
1442
|
+
async function query(request, { requestContext, filterMatchesToLoad, skipLoaderErrorBubbling, skipRevalidation, dataStrategy, generateMiddlewareResponse, normalizePath } = {}) {
|
|
1443
|
+
let normalizePathImpl = normalizePath || defaultNormalizePath;
|
|
1444
|
+
let method = request.method;
|
|
1445
|
+
let location = createLocation("", normalizePathImpl(request), null, "default");
|
|
1446
|
+
let matches = matchRoutesImpl(dataRoutes, location, basename, false, routeBranches);
|
|
1447
|
+
requestContext = requestContext != null ? requestContext : new RouterContextProvider();
|
|
1448
|
+
if (!isValidMethod(method) && method !== "HEAD") {
|
|
1449
|
+
let error = getInternalRouterError(405, { method });
|
|
1450
|
+
let { matches: methodNotAllowedMatches, route } = getShortCircuitMatches(dataRoutes);
|
|
1451
|
+
let staticContext = {
|
|
1452
|
+
basename,
|
|
1453
|
+
location,
|
|
1454
|
+
matches: methodNotAllowedMatches,
|
|
1455
|
+
loaderData: {},
|
|
1456
|
+
actionData: null,
|
|
1457
|
+
errors: { [route.id]: error },
|
|
1458
|
+
statusCode: error.status,
|
|
1459
|
+
loaderHeaders: {},
|
|
1460
|
+
actionHeaders: {}
|
|
1461
|
+
};
|
|
1462
|
+
return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
|
|
1463
|
+
} else if (!matches) {
|
|
1464
|
+
let error = getInternalRouterError(404, { pathname: location.pathname });
|
|
1465
|
+
let { matches: notFoundMatches, route } = getShortCircuitMatches(dataRoutes);
|
|
1466
|
+
let staticContext = {
|
|
1467
|
+
basename,
|
|
1468
|
+
location,
|
|
1469
|
+
matches: notFoundMatches,
|
|
1470
|
+
loaderData: {},
|
|
1471
|
+
actionData: null,
|
|
1472
|
+
errors: { [route.id]: error },
|
|
1473
|
+
statusCode: error.status,
|
|
1474
|
+
loaderHeaders: {},
|
|
1475
|
+
actionHeaders: {}
|
|
1476
|
+
};
|
|
1477
|
+
return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
|
|
1478
|
+
}
|
|
1479
|
+
if (generateMiddlewareResponse) {
|
|
1480
|
+
invariant(requestContext instanceof RouterContextProvider, "When using middleware in `staticHandler.query()`, any provided `requestContext` must be an instance of `RouterContextProvider`");
|
|
1481
|
+
try {
|
|
1482
|
+
await loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties);
|
|
1483
|
+
let renderedStaticContext;
|
|
1484
|
+
let response = await runServerMiddlewarePipeline({
|
|
1485
|
+
request,
|
|
1486
|
+
url: createDataFunctionUrl(request, location),
|
|
1487
|
+
pattern: getRoutePattern(matches),
|
|
1488
|
+
matches,
|
|
1489
|
+
params: matches[0].params,
|
|
1490
|
+
context: requestContext
|
|
1491
|
+
}, async () => {
|
|
1492
|
+
return await generateMiddlewareResponse(async (revalidationRequest, opts = {}) => {
|
|
1493
|
+
let result = await queryImpl(revalidationRequest, location, matches, requestContext, dataStrategy || null, skipLoaderErrorBubbling === true, null, "filterMatchesToLoad" in opts ? opts.filterMatchesToLoad ?? null : filterMatchesToLoad ?? null, skipRevalidation === true);
|
|
1494
|
+
if (isResponse(result)) return result;
|
|
1495
|
+
renderedStaticContext = {
|
|
1496
|
+
location,
|
|
1497
|
+
basename,
|
|
1498
|
+
...result
|
|
1499
|
+
};
|
|
1500
|
+
return renderedStaticContext;
|
|
1501
|
+
});
|
|
1502
|
+
}, async (error, routeId) => {
|
|
1503
|
+
if (isRedirectResponse(error)) return error;
|
|
1504
|
+
if (isResponse(error)) try {
|
|
1505
|
+
error = new ErrorResponseImpl(error.status, error.statusText, await parseResponseBody(error));
|
|
1506
|
+
} catch (e) {
|
|
1507
|
+
error = e;
|
|
1508
|
+
}
|
|
1509
|
+
if (isDataWithResponseInit(error)) error = dataWithResponseInitToErrorResponse(error);
|
|
1510
|
+
if (renderedStaticContext) {
|
|
1511
|
+
if (routeId in renderedStaticContext.loaderData) renderedStaticContext.loaderData[routeId] = void 0;
|
|
1512
|
+
let staticContext = getStaticContextFromError(dataRoutes, renderedStaticContext, error, skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, routeId).route.id);
|
|
1513
|
+
return generateMiddlewareResponse(() => Promise.resolve(staticContext));
|
|
1514
|
+
} else {
|
|
1515
|
+
let staticContext = {
|
|
1516
|
+
matches,
|
|
1517
|
+
location,
|
|
1518
|
+
basename,
|
|
1519
|
+
loaderData: {},
|
|
1520
|
+
actionData: null,
|
|
1521
|
+
errors: { [skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, matches.find((m) => m.route.id === routeId || m.route.loader)?.route.id || routeId).route.id]: error },
|
|
1522
|
+
statusCode: isRouteErrorResponse(error) ? error.status : 500,
|
|
1523
|
+
actionHeaders: {},
|
|
1524
|
+
loaderHeaders: {}
|
|
1525
|
+
};
|
|
1526
|
+
return generateMiddlewareResponse(() => Promise.resolve(staticContext));
|
|
1527
|
+
}
|
|
1528
|
+
});
|
|
1529
|
+
invariant(isResponse(response), "Expected a response in query()");
|
|
1530
|
+
return response;
|
|
1531
|
+
} catch (e) {
|
|
1532
|
+
if (isResponse(e)) return e;
|
|
1533
|
+
throw e;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
let result = await queryImpl(request, location, matches, requestContext, dataStrategy || null, skipLoaderErrorBubbling === true, null, filterMatchesToLoad || null, skipRevalidation === true);
|
|
1537
|
+
if (isResponse(result)) return result;
|
|
1538
|
+
return {
|
|
1539
|
+
location,
|
|
1540
|
+
basename,
|
|
1541
|
+
...result
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* The queryRoute() method is intended for targeted route requests, either
|
|
1546
|
+
* for fetch ?_data requests or resource route requests. In this case, we
|
|
1547
|
+
* are only ever calling a single action or loader, and we are returning the
|
|
1548
|
+
* returned value directly. In most cases, this will be a Response returned
|
|
1549
|
+
* from the action/loader, but it may be a primitive or other value as well -
|
|
1550
|
+
* and in such cases the calling context should handle that accordingly.
|
|
1551
|
+
*
|
|
1552
|
+
* We do respect the throw/return differentiation, so if an action/loader
|
|
1553
|
+
* throws, then this method will throw the value. This is important so we
|
|
1554
|
+
* can do proper boundary identification in Remix where a thrown Response
|
|
1555
|
+
* must go to the Catch Boundary but a returned Response is happy-path.
|
|
1556
|
+
*
|
|
1557
|
+
* One thing to note is that any Router-initiated Errors that make sense
|
|
1558
|
+
* to associate with a status code will be thrown as an ErrorResponse
|
|
1559
|
+
* instance which include the raw Error, such that the calling context can
|
|
1560
|
+
* serialize the error as they see fit while including the proper response
|
|
1561
|
+
* code. Examples here are 404 and 405 errors that occur prior to reaching
|
|
1562
|
+
* any user-defined loaders.
|
|
1563
|
+
*
|
|
1564
|
+
* - `opts.routeId` allows you to specify the specific route handler to call.
|
|
1565
|
+
* If not provided the handler will determine the proper route by matching
|
|
1566
|
+
* against `request.url`
|
|
1567
|
+
* - `opts.requestContext` is an optional server context that will be passed
|
|
1568
|
+
* to actions/loaders in the `context` parameter
|
|
1569
|
+
*/
|
|
1570
|
+
async function queryRoute(request, { routeId, requestContext, dataStrategy, generateMiddlewareResponse, normalizePath } = {}) {
|
|
1571
|
+
let normalizePathImpl = normalizePath || defaultNormalizePath;
|
|
1572
|
+
let method = request.method;
|
|
1573
|
+
let location = createLocation("", normalizePathImpl(request), null, "default");
|
|
1574
|
+
let matches = matchRoutesImpl(dataRoutes, location, basename, false, routeBranches);
|
|
1575
|
+
requestContext = requestContext != null ? requestContext : new RouterContextProvider();
|
|
1576
|
+
if (!isValidMethod(method) && method !== "HEAD" && method !== "OPTIONS") throw getInternalRouterError(405, { method });
|
|
1577
|
+
else if (!matches) throw getInternalRouterError(404, { pathname: location.pathname });
|
|
1578
|
+
let match = routeId ? matches.find((m) => m.route.id === routeId) : getTargetMatch(matches, location);
|
|
1579
|
+
if (routeId && !match) throw getInternalRouterError(403, {
|
|
1580
|
+
pathname: location.pathname,
|
|
1581
|
+
routeId
|
|
1582
|
+
});
|
|
1583
|
+
else if (!match) throw getInternalRouterError(404, { pathname: location.pathname });
|
|
1584
|
+
if (generateMiddlewareResponse) {
|
|
1585
|
+
invariant(requestContext instanceof RouterContextProvider, "When using middleware in `staticHandler.queryRoute()`, any provided `requestContext` must be an instance of `RouterContextProvider`");
|
|
1586
|
+
await loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties);
|
|
1587
|
+
return await runServerMiddlewarePipeline({
|
|
1588
|
+
request,
|
|
1589
|
+
url: createDataFunctionUrl(request, location),
|
|
1590
|
+
pattern: getRoutePattern(matches),
|
|
1591
|
+
matches,
|
|
1592
|
+
params: matches[0].params,
|
|
1593
|
+
context: requestContext
|
|
1594
|
+
}, async () => {
|
|
1595
|
+
return await generateMiddlewareResponse(async (innerRequest) => {
|
|
1596
|
+
let processed = handleQueryResult(await queryImpl(innerRequest, location, matches, requestContext, dataStrategy || null, false, match, null, false));
|
|
1597
|
+
return isResponse(processed) ? processed : typeof processed === "string" ? new Response(processed) : Response.json(processed);
|
|
1598
|
+
});
|
|
1599
|
+
}, (error) => {
|
|
1600
|
+
if (isDataWithResponseInit(error)) return Promise.resolve(dataWithResponseInitToResponse(error));
|
|
1601
|
+
if (isResponse(error)) return Promise.resolve(error);
|
|
1602
|
+
throw error;
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
return handleQueryResult(await queryImpl(request, location, matches, requestContext, dataStrategy || null, false, match, null, false));
|
|
1606
|
+
function handleQueryResult(result) {
|
|
1607
|
+
if (isResponse(result)) return result;
|
|
1608
|
+
let error = result.errors ? Object.values(result.errors)[0] : void 0;
|
|
1609
|
+
if (error !== void 0) throw error;
|
|
1610
|
+
if (result.actionData) return Object.values(result.actionData)[0];
|
|
1611
|
+
if (result.loaderData) return Object.values(result.loaderData)[0];
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
async function queryImpl(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, skipRevalidation) {
|
|
1615
|
+
invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
|
|
1616
|
+
try {
|
|
1617
|
+
if (isMutationMethod(request.method)) return await submit(request, location, matches, routeMatch || getTargetMatch(matches, location), requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch != null, filterMatchesToLoad, skipRevalidation);
|
|
1618
|
+
let result = await loadRouteData(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad);
|
|
1619
|
+
return isResponse(result) ? result : {
|
|
1620
|
+
...result,
|
|
1621
|
+
actionData: null,
|
|
1622
|
+
actionHeaders: {}
|
|
1623
|
+
};
|
|
1624
|
+
} catch (e) {
|
|
1625
|
+
if (isDataStrategyResult(e) && isResponse(e.result)) {
|
|
1626
|
+
if (e.type === "error") throw e.result;
|
|
1627
|
+
return e.result;
|
|
1628
|
+
}
|
|
1629
|
+
if (isRedirectResponse(e)) return e;
|
|
1630
|
+
throw e;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
async function submit(request, location, matches, actionMatch, requestContext, dataStrategy, skipLoaderErrorBubbling, isRouteRequest, filterMatchesToLoad, skipRevalidation) {
|
|
1634
|
+
let result;
|
|
1635
|
+
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
1636
|
+
let error = getInternalRouterError(405, {
|
|
1637
|
+
method: request.method,
|
|
1638
|
+
pathname: new URL(request.url).pathname,
|
|
1639
|
+
routeId: actionMatch.route.id
|
|
1640
|
+
});
|
|
1641
|
+
if (isRouteRequest) throw error;
|
|
1642
|
+
result = {
|
|
1643
|
+
type: "error",
|
|
1644
|
+
error
|
|
1645
|
+
};
|
|
1646
|
+
} else {
|
|
1647
|
+
result = (await callDataStrategy(request, location, getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, actionMatch, [], requestContext), isRouteRequest, requestContext, dataStrategy))[actionMatch.route.id];
|
|
1648
|
+
if (request.signal.aborted) throwStaticHandlerAbortedError(request, isRouteRequest);
|
|
1649
|
+
}
|
|
1650
|
+
if (isRedirectResult(result)) throw new Response(null, {
|
|
1651
|
+
status: result.response.status,
|
|
1652
|
+
headers: { Location: result.response.headers.get("Location") }
|
|
1653
|
+
});
|
|
1654
|
+
if (isRouteRequest) {
|
|
1655
|
+
if (isErrorResult(result)) throw result.error;
|
|
1656
|
+
return {
|
|
1657
|
+
matches: [actionMatch],
|
|
1658
|
+
loaderData: {},
|
|
1659
|
+
actionData: { [actionMatch.route.id]: result.data },
|
|
1660
|
+
errors: null,
|
|
1661
|
+
statusCode: 200,
|
|
1662
|
+
loaderHeaders: {},
|
|
1663
|
+
actionHeaders: {}
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
if (skipRevalidation) if (isErrorResult(result)) {
|
|
1667
|
+
let boundaryMatch = skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id);
|
|
1668
|
+
return {
|
|
1669
|
+
statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
|
|
1670
|
+
actionData: null,
|
|
1671
|
+
actionHeaders: { ...result.headers ? { [actionMatch.route.id]: result.headers } : {} },
|
|
1672
|
+
matches,
|
|
1673
|
+
loaderData: {},
|
|
1674
|
+
errors: { [boundaryMatch.route.id]: result.error },
|
|
1675
|
+
loaderHeaders: {}
|
|
1676
|
+
};
|
|
1677
|
+
} else return {
|
|
1678
|
+
actionData: { [actionMatch.route.id]: result.data },
|
|
1679
|
+
actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {},
|
|
1680
|
+
matches,
|
|
1681
|
+
loaderData: {},
|
|
1682
|
+
errors: null,
|
|
1683
|
+
statusCode: result.statusCode || 200,
|
|
1684
|
+
loaderHeaders: {}
|
|
1685
|
+
};
|
|
1686
|
+
let loaderRequest = new Request(request.url, {
|
|
1687
|
+
headers: request.headers,
|
|
1688
|
+
redirect: request.redirect,
|
|
1689
|
+
signal: request.signal
|
|
1690
|
+
});
|
|
1691
|
+
if (isErrorResult(result)) return {
|
|
1692
|
+
...await loadRouteData(loaderRequest, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, null, filterMatchesToLoad, [(skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id)).route.id, result]),
|
|
1693
|
+
statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
|
|
1694
|
+
actionData: null,
|
|
1695
|
+
actionHeaders: { ...result.headers ? { [actionMatch.route.id]: result.headers } : {} }
|
|
1696
|
+
};
|
|
1697
|
+
return {
|
|
1698
|
+
...await loadRouteData(loaderRequest, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, null, filterMatchesToLoad),
|
|
1699
|
+
actionData: { [actionMatch.route.id]: result.data },
|
|
1700
|
+
...result.statusCode ? { statusCode: result.statusCode } : {},
|
|
1701
|
+
actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {}
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
async function loadRouteData(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, pendingActionResult) {
|
|
1705
|
+
let isRouteRequest = routeMatch != null;
|
|
1706
|
+
if (isRouteRequest && !routeMatch?.route.loader && !routeMatch?.route.lazy) throw getInternalRouterError(400, {
|
|
1707
|
+
method: request.method,
|
|
1708
|
+
pathname: new URL(request.url).pathname,
|
|
1709
|
+
routeId: routeMatch?.route.id
|
|
1710
|
+
});
|
|
1711
|
+
let dsMatches;
|
|
1712
|
+
if (routeMatch) dsMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, routeMatch, [], requestContext);
|
|
1713
|
+
else {
|
|
1714
|
+
let maxIdx = pendingActionResult && isErrorResult(pendingActionResult[1]) ? matches.findIndex((m) => m.route.id === pendingActionResult[0]) - 1 : void 0;
|
|
1715
|
+
let pattern = getRoutePattern(matches);
|
|
1716
|
+
dsMatches = matches.map((match, index) => {
|
|
1717
|
+
if (maxIdx != null && index > maxIdx) return getDataStrategyMatch(mapRouteProperties, manifest, request, location, pattern, match, [], requestContext, false);
|
|
1718
|
+
return getDataStrategyMatch(mapRouteProperties, manifest, request, location, pattern, match, [], requestContext, (match.route.loader || match.route.lazy) != null && (!filterMatchesToLoad || filterMatchesToLoad(match)));
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
if (!dataStrategy && !dsMatches.some((m) => m.shouldLoad)) return {
|
|
1722
|
+
matches,
|
|
1723
|
+
loaderData: {},
|
|
1724
|
+
errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? { [pendingActionResult[0]]: pendingActionResult[1].error } : null,
|
|
1725
|
+
statusCode: 200,
|
|
1726
|
+
loaderHeaders: {}
|
|
1727
|
+
};
|
|
1728
|
+
let results = await callDataStrategy(request, location, dsMatches, isRouteRequest, requestContext, dataStrategy);
|
|
1729
|
+
if (request.signal.aborted) throwStaticHandlerAbortedError(request, isRouteRequest);
|
|
1730
|
+
return {
|
|
1731
|
+
...processRouteLoaderData(matches, results, pendingActionResult, true, skipLoaderErrorBubbling),
|
|
1732
|
+
matches
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
async function callDataStrategy(request, location, matches, isRouteRequest, requestContext, dataStrategy) {
|
|
1736
|
+
let results = await callDataStrategyImpl(dataStrategy || defaultDataStrategy, request, location, matches, null, requestContext, true);
|
|
1737
|
+
let dataResults = {};
|
|
1738
|
+
await Promise.all(matches.map(async (match) => {
|
|
1739
|
+
if (!(match.route.id in results)) return;
|
|
1740
|
+
let result = results[match.route.id];
|
|
1741
|
+
if (isRedirectDataStrategyResult(result)) {
|
|
1742
|
+
let response = result.result;
|
|
1743
|
+
throw normalizeRelativeRoutingRedirectResponse(response, request, match.route.id, matches, basename);
|
|
1744
|
+
}
|
|
1745
|
+
if (isRouteRequest) {
|
|
1746
|
+
if (isResponse(result.result)) throw result;
|
|
1747
|
+
else if (isDataWithResponseInit(result.result)) throw dataWithResponseInitToResponse(result.result);
|
|
1748
|
+
}
|
|
1749
|
+
dataResults[match.route.id] = await convertDataStrategyResultToDataResult(result);
|
|
1750
|
+
}));
|
|
1751
|
+
return dataResults;
|
|
1752
|
+
}
|
|
1753
|
+
return {
|
|
1754
|
+
dataRoutes,
|
|
1755
|
+
_internalRouteBranches: routeBranches,
|
|
1756
|
+
query,
|
|
1757
|
+
queryRoute
|
|
1758
|
+
};
|
|
1759
|
+
}
|
|
1760
|
+
/**
|
|
1761
|
+
* Given an existing StaticHandlerContext and an error thrown at render time,
|
|
1762
|
+
* provide an updated StaticHandlerContext suitable for a second SSR render
|
|
1763
|
+
*
|
|
1764
|
+
* @category Utils
|
|
1765
|
+
*/
|
|
1766
|
+
function getStaticContextFromError(routes, handlerContext, error, boundaryId) {
|
|
1767
|
+
let errorBoundaryId = boundaryId || handlerContext._deepestRenderedBoundaryId || routes[0].id;
|
|
1768
|
+
return {
|
|
1769
|
+
...handlerContext,
|
|
1770
|
+
statusCode: isRouteErrorResponse(error) ? error.status : 500,
|
|
1771
|
+
errors: { [errorBoundaryId]: error }
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
function throwStaticHandlerAbortedError(request, isRouteRequest) {
|
|
1775
|
+
if (request.signal.reason !== void 0) throw request.signal.reason;
|
|
1776
|
+
throw new Error(`${isRouteRequest ? "queryRoute" : "query"}() call aborted without an \`AbortSignal.reason\`: ${request.method} ${request.url}`);
|
|
1777
|
+
}
|
|
1778
|
+
function isSubmissionNavigation(opts) {
|
|
1779
|
+
return opts != null && ("formData" in opts && opts.formData != null || "body" in opts && opts.body !== void 0);
|
|
1780
|
+
}
|
|
1781
|
+
function defaultNormalizePath(request) {
|
|
1782
|
+
let url = new URL(request.url);
|
|
1783
|
+
return {
|
|
1784
|
+
pathname: url.pathname,
|
|
1785
|
+
search: url.search,
|
|
1786
|
+
hash: url.hash
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
function normalizeTo(location, matches, basename, to, fromRouteId, relative) {
|
|
1790
|
+
let contextualMatches;
|
|
1791
|
+
let activeRouteMatch;
|
|
1792
|
+
if (fromRouteId) {
|
|
1793
|
+
contextualMatches = [];
|
|
1794
|
+
for (let match of matches) {
|
|
1795
|
+
contextualMatches.push(match);
|
|
1796
|
+
if (match.route.id === fromRouteId) {
|
|
1797
|
+
activeRouteMatch = match;
|
|
1798
|
+
break;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
} else {
|
|
1802
|
+
contextualMatches = matches;
|
|
1803
|
+
activeRouteMatch = matches[matches.length - 1];
|
|
1804
|
+
}
|
|
1805
|
+
let path = resolveTo(to ? to : ".", getResolveToMatches(contextualMatches), stripBasename(location.pathname, basename) || location.pathname, relative === "path");
|
|
1806
|
+
if (to == null) {
|
|
1807
|
+
path.search = location.search;
|
|
1808
|
+
path.hash = location.hash;
|
|
1809
|
+
}
|
|
1810
|
+
if ((to == null || to === "" || to === ".") && activeRouteMatch) {
|
|
1811
|
+
let nakedIndex = hasNakedIndexQuery(path.search);
|
|
1812
|
+
if (activeRouteMatch.route.index && !nakedIndex) path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
|
|
1813
|
+
else if (!activeRouteMatch.route.index && nakedIndex) {
|
|
1814
|
+
let params = new URLSearchParams(path.search);
|
|
1815
|
+
let indexValues = params.getAll("index");
|
|
1816
|
+
params.delete("index");
|
|
1817
|
+
indexValues.filter((v) => v).forEach((v) => params.append("index", v));
|
|
1818
|
+
let qs = params.toString();
|
|
1819
|
+
path.search = qs ? `?${qs}` : "";
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
if (basename !== "/") path.pathname = prependBasename({
|
|
1823
|
+
basename,
|
|
1824
|
+
pathname: path.pathname
|
|
1825
|
+
});
|
|
1826
|
+
return createPath(path);
|
|
1827
|
+
}
|
|
1828
|
+
function normalizeNavigateOptions(isFetcher, path, opts) {
|
|
1829
|
+
if (!opts || !isSubmissionNavigation(opts)) return { path };
|
|
1830
|
+
if (opts.formMethod && !isValidMethod(opts.formMethod)) return {
|
|
1831
|
+
path,
|
|
1832
|
+
error: getInternalRouterError(405, { method: opts.formMethod })
|
|
1833
|
+
};
|
|
1834
|
+
let getInvalidBodyError = () => ({
|
|
1835
|
+
path,
|
|
1836
|
+
error: getInternalRouterError(400, { type: "invalid-body" })
|
|
1837
|
+
});
|
|
1838
|
+
let formMethod = (opts.formMethod || "get").toUpperCase();
|
|
1839
|
+
let formAction = stripHashFromPath(path);
|
|
1840
|
+
if (opts.body !== void 0) {
|
|
1841
|
+
if (opts.formEncType === "text/plain") {
|
|
1842
|
+
if (!isMutationMethod(formMethod)) return getInvalidBodyError();
|
|
1843
|
+
let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ? Array.from(opts.body.entries()).reduce((acc, [name, value]) => `${acc}${name}=${value}\n`, "") : String(opts.body);
|
|
1844
|
+
return {
|
|
1845
|
+
path,
|
|
1846
|
+
submission: {
|
|
1847
|
+
formMethod,
|
|
1848
|
+
formAction,
|
|
1849
|
+
formEncType: opts.formEncType,
|
|
1850
|
+
formData: void 0,
|
|
1851
|
+
json: void 0,
|
|
1852
|
+
text
|
|
1853
|
+
}
|
|
1854
|
+
};
|
|
1855
|
+
} else if (opts.formEncType === "application/json") {
|
|
1856
|
+
if (!isMutationMethod(formMethod)) return getInvalidBodyError();
|
|
1857
|
+
try {
|
|
1858
|
+
let json = typeof opts.body === "string" ? JSON.parse(opts.body) : opts.body;
|
|
1859
|
+
return {
|
|
1860
|
+
path,
|
|
1861
|
+
submission: {
|
|
1862
|
+
formMethod,
|
|
1863
|
+
formAction,
|
|
1864
|
+
formEncType: opts.formEncType,
|
|
1865
|
+
formData: void 0,
|
|
1866
|
+
json,
|
|
1867
|
+
text: void 0
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1870
|
+
} catch (e) {
|
|
1871
|
+
return getInvalidBodyError();
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
invariant(typeof FormData === "function", "FormData is not available in this environment");
|
|
1876
|
+
let searchParams;
|
|
1877
|
+
let formData;
|
|
1878
|
+
if (opts.formData) {
|
|
1879
|
+
searchParams = convertFormDataToSearchParams(opts.formData);
|
|
1880
|
+
formData = opts.formData;
|
|
1881
|
+
} else if (opts.body instanceof FormData) {
|
|
1882
|
+
searchParams = convertFormDataToSearchParams(opts.body);
|
|
1883
|
+
formData = opts.body;
|
|
1884
|
+
} else if (opts.body instanceof URLSearchParams) {
|
|
1885
|
+
searchParams = opts.body;
|
|
1886
|
+
formData = convertSearchParamsToFormData(searchParams);
|
|
1887
|
+
} else if (opts.body == null) {
|
|
1888
|
+
searchParams = new URLSearchParams();
|
|
1889
|
+
formData = new FormData();
|
|
1890
|
+
} else try {
|
|
1891
|
+
searchParams = new URLSearchParams(opts.body);
|
|
1892
|
+
formData = convertSearchParamsToFormData(searchParams);
|
|
1893
|
+
} catch (e) {
|
|
1894
|
+
return getInvalidBodyError();
|
|
1895
|
+
}
|
|
1896
|
+
let submission = {
|
|
1897
|
+
formMethod,
|
|
1898
|
+
formAction,
|
|
1899
|
+
formEncType: opts && opts.formEncType || "application/x-www-form-urlencoded",
|
|
1900
|
+
formData,
|
|
1901
|
+
json: void 0,
|
|
1902
|
+
text: void 0
|
|
1903
|
+
};
|
|
1904
|
+
if (isMutationMethod(submission.formMethod)) return {
|
|
1905
|
+
path,
|
|
1906
|
+
submission
|
|
1907
|
+
};
|
|
1908
|
+
let parsedPath = parsePath(path);
|
|
1909
|
+
if (isFetcher && parsedPath.search && hasNakedIndexQuery(parsedPath.search)) searchParams.append("index", "");
|
|
1910
|
+
parsedPath.search = `?${searchParams}`;
|
|
1911
|
+
return {
|
|
1912
|
+
path: createPath(parsedPath),
|
|
1913
|
+
submission
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
function getMatchesToLoad(request, scopedContext, mapRouteProperties, manifest, history, state, matches, submission, location, lazyRoutePropertiesToSkip, initialHydration, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, hasPatchRoutesOnNavigation, branches, pendingActionResult, callSiteDefaultShouldRevalidate) {
|
|
1917
|
+
let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : void 0;
|
|
1918
|
+
let currentUrl = history.createURL(state.location);
|
|
1919
|
+
let nextUrl = history.createURL(location);
|
|
1920
|
+
let maxIdx;
|
|
1921
|
+
if (initialHydration && state.errors) {
|
|
1922
|
+
let boundaryId = Object.keys(state.errors)[0];
|
|
1923
|
+
maxIdx = matches.findIndex((m) => m.route.id === boundaryId);
|
|
1924
|
+
} else if (pendingActionResult && isErrorResult(pendingActionResult[1])) {
|
|
1925
|
+
let boundaryId = pendingActionResult[0];
|
|
1926
|
+
maxIdx = matches.findIndex((m) => m.route.id === boundaryId) - 1;
|
|
1927
|
+
}
|
|
1928
|
+
let actionStatus = pendingActionResult ? pendingActionResult[1].statusCode : void 0;
|
|
1929
|
+
let shouldSkipRevalidation = actionStatus && actionStatus >= 400;
|
|
1930
|
+
let baseShouldRevalidateArgs = {
|
|
1931
|
+
currentUrl,
|
|
1932
|
+
currentParams: state.matches[0]?.params || {},
|
|
1933
|
+
nextUrl,
|
|
1934
|
+
nextParams: matches[0].params,
|
|
1935
|
+
...submission,
|
|
1936
|
+
actionResult,
|
|
1937
|
+
actionStatus
|
|
1938
|
+
};
|
|
1939
|
+
let pattern = getRoutePattern(matches);
|
|
1940
|
+
let dsMatches = matches.map((match, index) => {
|
|
1941
|
+
let { route } = match;
|
|
1942
|
+
let forceShouldLoad = null;
|
|
1943
|
+
if (maxIdx != null && index > maxIdx) forceShouldLoad = false;
|
|
1944
|
+
else if (route.lazy) forceShouldLoad = true;
|
|
1945
|
+
else if (!routeHasLoaderOrMiddleware(route)) forceShouldLoad = false;
|
|
1946
|
+
else if (initialHydration) {
|
|
1947
|
+
let { shouldLoad } = getRouteHydrationStatus(route, state.loaderData, state.errors);
|
|
1948
|
+
forceShouldLoad = shouldLoad;
|
|
1949
|
+
} else if (isNewLoader(state.loaderData, state.matches[index], match)) forceShouldLoad = true;
|
|
1950
|
+
if (forceShouldLoad !== null) return getDataStrategyMatch(mapRouteProperties, manifest, request, location, pattern, match, lazyRoutePropertiesToSkip, scopedContext, forceShouldLoad);
|
|
1951
|
+
let defaultShouldRevalidate = false;
|
|
1952
|
+
if (typeof callSiteDefaultShouldRevalidate === "boolean") defaultShouldRevalidate = callSiteDefaultShouldRevalidate;
|
|
1953
|
+
else if (shouldSkipRevalidation) defaultShouldRevalidate = false;
|
|
1954
|
+
else if (isRevalidationRequired) defaultShouldRevalidate = true;
|
|
1955
|
+
else if (currentUrl.pathname + currentUrl.search === nextUrl.pathname + nextUrl.search) defaultShouldRevalidate = true;
|
|
1956
|
+
else if (currentUrl.search !== nextUrl.search) defaultShouldRevalidate = true;
|
|
1957
|
+
else if (isNewRouteInstance(state.matches[index], match)) defaultShouldRevalidate = true;
|
|
1958
|
+
let shouldRevalidateArgs = {
|
|
1959
|
+
...baseShouldRevalidateArgs,
|
|
1960
|
+
defaultShouldRevalidate
|
|
1961
|
+
};
|
|
1962
|
+
return getDataStrategyMatch(mapRouteProperties, manifest, request, location, pattern, match, lazyRoutePropertiesToSkip, scopedContext, shouldRevalidateLoader(match, shouldRevalidateArgs), shouldRevalidateArgs, callSiteDefaultShouldRevalidate);
|
|
1963
|
+
});
|
|
1964
|
+
let revalidatingFetchers = [];
|
|
1965
|
+
fetchLoadMatches.forEach((f, key) => {
|
|
1966
|
+
if (initialHydration || !matches.some((m) => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) return;
|
|
1967
|
+
let fetcher = state.fetchers.get(key);
|
|
1968
|
+
let isMidInitialLoad = fetcher && fetcher.state !== "idle" && fetcher.data === void 0;
|
|
1969
|
+
let fetcherMatches = matchRoutesImpl(routesToUse, f.path, basename ?? "/", false, branches);
|
|
1970
|
+
if (!fetcherMatches) {
|
|
1971
|
+
if (hasPatchRoutesOnNavigation && isMidInitialLoad) return;
|
|
1972
|
+
revalidatingFetchers.push({
|
|
1973
|
+
key,
|
|
1974
|
+
routeId: f.routeId,
|
|
1975
|
+
path: f.path,
|
|
1976
|
+
matches: null,
|
|
1977
|
+
match: null,
|
|
1978
|
+
request: null,
|
|
1979
|
+
controller: null
|
|
1980
|
+
});
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
if (fetchRedirectIds.has(key)) return;
|
|
1984
|
+
let fetcherMatch = getTargetMatch(fetcherMatches, f.path);
|
|
1985
|
+
let fetchController = new AbortController();
|
|
1986
|
+
let fetchRequest = createClientSideRequest(history, f.path, fetchController.signal);
|
|
1987
|
+
let fetcherDsMatches = null;
|
|
1988
|
+
if (cancelledFetcherLoads.has(key)) {
|
|
1989
|
+
cancelledFetcherLoads.delete(key);
|
|
1990
|
+
fetcherDsMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, f.path, fetcherMatches, fetcherMatch, lazyRoutePropertiesToSkip, scopedContext);
|
|
1991
|
+
} else if (isMidInitialLoad) {
|
|
1992
|
+
if (isRevalidationRequired) fetcherDsMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, f.path, fetcherMatches, fetcherMatch, lazyRoutePropertiesToSkip, scopedContext);
|
|
1993
|
+
} else {
|
|
1994
|
+
let defaultShouldRevalidate;
|
|
1995
|
+
if (typeof callSiteDefaultShouldRevalidate === "boolean") defaultShouldRevalidate = callSiteDefaultShouldRevalidate;
|
|
1996
|
+
else if (shouldSkipRevalidation) defaultShouldRevalidate = false;
|
|
1997
|
+
else defaultShouldRevalidate = isRevalidationRequired;
|
|
1998
|
+
let shouldRevalidateArgs = {
|
|
1999
|
+
...baseShouldRevalidateArgs,
|
|
2000
|
+
defaultShouldRevalidate
|
|
2001
|
+
};
|
|
2002
|
+
if (shouldRevalidateLoader(fetcherMatch, shouldRevalidateArgs)) fetcherDsMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, f.path, fetcherMatches, fetcherMatch, lazyRoutePropertiesToSkip, scopedContext, shouldRevalidateArgs);
|
|
2003
|
+
}
|
|
2004
|
+
if (fetcherDsMatches) revalidatingFetchers.push({
|
|
2005
|
+
key,
|
|
2006
|
+
routeId: f.routeId,
|
|
2007
|
+
path: f.path,
|
|
2008
|
+
matches: fetcherDsMatches,
|
|
2009
|
+
match: fetcherMatch,
|
|
2010
|
+
request: fetchRequest,
|
|
2011
|
+
controller: fetchController
|
|
2012
|
+
});
|
|
2013
|
+
});
|
|
2014
|
+
return {
|
|
2015
|
+
dsMatches,
|
|
2016
|
+
revalidatingFetchers
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
function routeHasLoaderOrMiddleware(route) {
|
|
2020
|
+
return route.loader != null || route.middleware != null && route.middleware.length > 0;
|
|
2021
|
+
}
|
|
2022
|
+
function getRouteHydrationStatus(route, loaderData, errors) {
|
|
2023
|
+
if (route.lazy) return {
|
|
2024
|
+
shouldLoad: true,
|
|
2025
|
+
renderFallback: true
|
|
2026
|
+
};
|
|
2027
|
+
if (!routeHasLoaderOrMiddleware(route)) return {
|
|
2028
|
+
shouldLoad: false,
|
|
2029
|
+
renderFallback: false
|
|
2030
|
+
};
|
|
2031
|
+
let hasData = loaderData != null && route.id in loaderData;
|
|
2032
|
+
let hasError = errors != null && errors[route.id] !== void 0;
|
|
2033
|
+
if (!hasData && hasError) return {
|
|
2034
|
+
shouldLoad: false,
|
|
2035
|
+
renderFallback: false
|
|
2036
|
+
};
|
|
2037
|
+
if (typeof route.loader === "function" && route.loader.hydrate === true) return {
|
|
2038
|
+
shouldLoad: true,
|
|
2039
|
+
renderFallback: !hasData
|
|
2040
|
+
};
|
|
2041
|
+
let shouldLoad = !hasData && !hasError;
|
|
2042
|
+
return {
|
|
2043
|
+
shouldLoad,
|
|
2044
|
+
renderFallback: shouldLoad
|
|
2045
|
+
};
|
|
2046
|
+
}
|
|
2047
|
+
function isNewLoader(currentLoaderData, currentMatch, match) {
|
|
2048
|
+
let isNew = !currentMatch || match.route.id !== currentMatch.route.id;
|
|
2049
|
+
let isMissingData = !currentLoaderData.hasOwnProperty(match.route.id);
|
|
2050
|
+
return isNew || isMissingData;
|
|
2051
|
+
}
|
|
2052
|
+
function isNewRouteInstance(currentMatch, match) {
|
|
2053
|
+
let currentPath = currentMatch.route.path;
|
|
2054
|
+
return currentMatch.pathname !== match.pathname || currentPath != null && currentPath.endsWith("*") && currentMatch.params["*"] !== match.params["*"];
|
|
2055
|
+
}
|
|
2056
|
+
function shouldRevalidateLoader(loaderMatch, arg) {
|
|
2057
|
+
if (loaderMatch.route.shouldRevalidate) {
|
|
2058
|
+
let routeChoice = loaderMatch.route.shouldRevalidate(arg);
|
|
2059
|
+
if (typeof routeChoice === "boolean") return routeChoice;
|
|
2060
|
+
}
|
|
2061
|
+
return arg.defaultShouldRevalidate;
|
|
2062
|
+
}
|
|
2063
|
+
function patchRoutesImpl(routeId, children, dataRoutes, manifest, mapRouteProperties, allowElementMutations) {
|
|
2064
|
+
let childrenToPatch;
|
|
2065
|
+
if (routeId) {
|
|
2066
|
+
let route = manifest[routeId];
|
|
2067
|
+
invariant(route, `No route found to patch children into: routeId = ${routeId}`);
|
|
2068
|
+
if (!route.children) route.children = [];
|
|
2069
|
+
childrenToPatch = route.children;
|
|
2070
|
+
} else childrenToPatch = dataRoutes.activeRoutes;
|
|
2071
|
+
let uniqueChildren = [];
|
|
2072
|
+
let existingChildren = [];
|
|
2073
|
+
children.forEach((newRoute) => {
|
|
2074
|
+
let existingRoute = childrenToPatch.find((existingRoute) => isSameRoute(newRoute, existingRoute));
|
|
2075
|
+
if (existingRoute) existingChildren.push({
|
|
2076
|
+
existingRoute,
|
|
2077
|
+
newRoute
|
|
2078
|
+
});
|
|
2079
|
+
else uniqueChildren.push(newRoute);
|
|
2080
|
+
});
|
|
2081
|
+
if (uniqueChildren.length > 0) {
|
|
2082
|
+
let newRoutes = convertRoutesToDataRoutes(uniqueChildren, mapRouteProperties, [
|
|
2083
|
+
routeId || "_",
|
|
2084
|
+
"patch",
|
|
2085
|
+
String(childrenToPatch?.length || "0")
|
|
2086
|
+
], manifest);
|
|
2087
|
+
childrenToPatch.push(...newRoutes);
|
|
2088
|
+
}
|
|
2089
|
+
if (allowElementMutations && existingChildren.length > 0) for (let i = 0; i < existingChildren.length; i++) {
|
|
2090
|
+
let { existingRoute, newRoute } = existingChildren[i];
|
|
2091
|
+
let existingRouteTyped = existingRoute;
|
|
2092
|
+
let [newRouteTyped] = convertRoutesToDataRoutes([newRoute], mapRouteProperties, [], {}, true);
|
|
2093
|
+
Object.assign(existingRouteTyped, {
|
|
2094
|
+
element: newRouteTyped.element ? newRouteTyped.element : existingRouteTyped.element,
|
|
2095
|
+
errorElement: newRouteTyped.errorElement ? newRouteTyped.errorElement : existingRouteTyped.errorElement,
|
|
2096
|
+
hydrateFallbackElement: newRouteTyped.hydrateFallbackElement ? newRouteTyped.hydrateFallbackElement : existingRouteTyped.hydrateFallbackElement
|
|
2097
|
+
});
|
|
2098
|
+
}
|
|
2099
|
+
if (!dataRoutes.hasHMRRoutes) dataRoutes.setRoutes([...dataRoutes.activeRoutes]);
|
|
2100
|
+
}
|
|
2101
|
+
function isSameRoute(newRoute, existingRoute) {
|
|
2102
|
+
if ("id" in newRoute && "id" in existingRoute && newRoute.id === existingRoute.id) return true;
|
|
2103
|
+
if (!(newRoute.index === existingRoute.index && newRoute.path === existingRoute.path && newRoute.caseSensitive === existingRoute.caseSensitive)) return false;
|
|
2104
|
+
if ((!newRoute.children || newRoute.children.length === 0) && (!existingRoute.children || existingRoute.children.length === 0)) return true;
|
|
2105
|
+
return newRoute.children?.every((aChild, i) => existingRoute.children?.some((bChild) => isSameRoute(aChild, bChild))) ?? false;
|
|
2106
|
+
}
|
|
2107
|
+
const lazyRoutePropertyCache = /* @__PURE__ */ new WeakMap();
|
|
2108
|
+
const loadLazyRouteProperty = ({ key, route, manifest, mapRouteProperties }) => {
|
|
2109
|
+
let routeToUpdate = manifest[route.id];
|
|
2110
|
+
invariant(routeToUpdate, "No route found in manifest");
|
|
2111
|
+
if (!routeToUpdate.lazy || typeof routeToUpdate.lazy !== "object") return;
|
|
2112
|
+
let lazyFn = routeToUpdate.lazy[key];
|
|
2113
|
+
if (!lazyFn) return;
|
|
2114
|
+
let cache = lazyRoutePropertyCache.get(routeToUpdate);
|
|
2115
|
+
if (!cache) {
|
|
2116
|
+
cache = {};
|
|
2117
|
+
lazyRoutePropertyCache.set(routeToUpdate, cache);
|
|
2118
|
+
}
|
|
2119
|
+
let cachedPromise = cache[key];
|
|
2120
|
+
if (cachedPromise) return cachedPromise;
|
|
2121
|
+
let propertyPromise = (async () => {
|
|
2122
|
+
let isUnsupported = isUnsupportedLazyRouteObjectKey(key);
|
|
2123
|
+
let isStaticallyDefined = routeToUpdate[key] !== void 0;
|
|
2124
|
+
if (isUnsupported) {
|
|
2125
|
+
warning(!isUnsupported, "Route property " + key + " is not a supported lazy route property. This property will be ignored.");
|
|
2126
|
+
cache[key] = Promise.resolve();
|
|
2127
|
+
} else if (isStaticallyDefined) warning(false, `Route "${routeToUpdate.id}" has a static property "${key}" defined. The lazy property will be ignored.`);
|
|
2128
|
+
else {
|
|
2129
|
+
let value = await lazyFn();
|
|
2130
|
+
if (value != null) {
|
|
2131
|
+
Object.assign(routeToUpdate, { [key]: value });
|
|
2132
|
+
Object.assign(routeToUpdate, mapRouteProperties(routeToUpdate));
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
if (typeof routeToUpdate.lazy === "object") {
|
|
2136
|
+
routeToUpdate.lazy[key] = void 0;
|
|
2137
|
+
if (Object.values(routeToUpdate.lazy).every((value) => value === void 0)) routeToUpdate.lazy = void 0;
|
|
2138
|
+
}
|
|
2139
|
+
})();
|
|
2140
|
+
cache[key] = propertyPromise;
|
|
2141
|
+
return propertyPromise;
|
|
2142
|
+
};
|
|
2143
|
+
const lazyRouteFunctionCache = /* @__PURE__ */ new WeakMap();
|
|
2144
|
+
/**
|
|
2145
|
+
* Execute route.lazy functions to lazily load route modules (loader, action,
|
|
2146
|
+
* shouldRevalidate) and update the routeManifest in place which shares objects
|
|
2147
|
+
* with dataRoutes so those get updated as well.
|
|
2148
|
+
*/
|
|
2149
|
+
function loadLazyRoute(route, type, manifest, mapRouteProperties, lazyRoutePropertiesToSkip) {
|
|
2150
|
+
let routeToUpdate = manifest[route.id];
|
|
2151
|
+
invariant(routeToUpdate, "No route found in manifest");
|
|
2152
|
+
if (!route.lazy) return {
|
|
2153
|
+
lazyRoutePromise: void 0,
|
|
2154
|
+
lazyHandlerPromise: void 0
|
|
2155
|
+
};
|
|
2156
|
+
if (typeof route.lazy === "function") {
|
|
2157
|
+
let cachedPromise = lazyRouteFunctionCache.get(routeToUpdate);
|
|
2158
|
+
if (cachedPromise) return {
|
|
2159
|
+
lazyRoutePromise: cachedPromise,
|
|
2160
|
+
lazyHandlerPromise: cachedPromise
|
|
2161
|
+
};
|
|
2162
|
+
let lazyRoutePromise = (async () => {
|
|
2163
|
+
invariant(typeof route.lazy === "function", "No lazy route function found");
|
|
2164
|
+
let lazyRoute = await route.lazy();
|
|
2165
|
+
let routeUpdates = {};
|
|
2166
|
+
for (let lazyRouteProperty in lazyRoute) {
|
|
2167
|
+
let lazyValue = lazyRoute[lazyRouteProperty];
|
|
2168
|
+
if (lazyValue === void 0) continue;
|
|
2169
|
+
let isUnsupported = isUnsupportedLazyRouteFunctionKey(lazyRouteProperty);
|
|
2170
|
+
let isStaticallyDefined = routeToUpdate[lazyRouteProperty] !== void 0;
|
|
2171
|
+
if (isUnsupported) warning(!isUnsupported, "Route property " + lazyRouteProperty + " is not a supported property to be returned from a lazy route function. This property will be ignored.");
|
|
2172
|
+
else if (isStaticallyDefined) warning(!isStaticallyDefined, `Route "${routeToUpdate.id}" has a static property "${lazyRouteProperty}" defined but its lazy function is also returning a value for this property. The lazy route property "${lazyRouteProperty}" will be ignored.`);
|
|
2173
|
+
else routeUpdates[lazyRouteProperty] = lazyValue;
|
|
2174
|
+
}
|
|
2175
|
+
Object.assign(routeToUpdate, routeUpdates);
|
|
2176
|
+
Object.assign(routeToUpdate, {
|
|
2177
|
+
...mapRouteProperties(routeToUpdate),
|
|
2178
|
+
lazy: void 0
|
|
2179
|
+
});
|
|
2180
|
+
})();
|
|
2181
|
+
lazyRouteFunctionCache.set(routeToUpdate, lazyRoutePromise);
|
|
2182
|
+
lazyRoutePromise.catch(() => {});
|
|
2183
|
+
return {
|
|
2184
|
+
lazyRoutePromise,
|
|
2185
|
+
lazyHandlerPromise: lazyRoutePromise
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
let lazyKeys = Object.keys(route.lazy);
|
|
2189
|
+
let lazyPropertyPromises = [];
|
|
2190
|
+
let lazyHandlerPromise = void 0;
|
|
2191
|
+
for (let key of lazyKeys) {
|
|
2192
|
+
if (lazyRoutePropertiesToSkip && lazyRoutePropertiesToSkip.includes(key)) continue;
|
|
2193
|
+
let promise = loadLazyRouteProperty({
|
|
2194
|
+
key,
|
|
2195
|
+
route,
|
|
2196
|
+
manifest,
|
|
2197
|
+
mapRouteProperties
|
|
2198
|
+
});
|
|
2199
|
+
if (promise) {
|
|
2200
|
+
lazyPropertyPromises.push(promise);
|
|
2201
|
+
if (key === type) lazyHandlerPromise = promise;
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
let lazyRoutePromise = lazyPropertyPromises.length > 0 ? Promise.all(lazyPropertyPromises).then(() => {}) : void 0;
|
|
2205
|
+
lazyRoutePromise?.catch(() => {});
|
|
2206
|
+
lazyHandlerPromise?.catch(() => {});
|
|
2207
|
+
return {
|
|
2208
|
+
lazyRoutePromise,
|
|
2209
|
+
lazyHandlerPromise
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
function isNonNullable(value) {
|
|
2213
|
+
return value !== void 0;
|
|
2214
|
+
}
|
|
2215
|
+
function loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties) {
|
|
2216
|
+
let promises = matches.map(({ route }) => {
|
|
2217
|
+
if (typeof route.lazy !== "object" || !route.lazy.middleware) return;
|
|
2218
|
+
return loadLazyRouteProperty({
|
|
2219
|
+
key: "middleware",
|
|
2220
|
+
route,
|
|
2221
|
+
manifest,
|
|
2222
|
+
mapRouteProperties
|
|
2223
|
+
});
|
|
2224
|
+
}).filter(isNonNullable);
|
|
2225
|
+
return promises.length > 0 ? Promise.all(promises) : void 0;
|
|
2226
|
+
}
|
|
2227
|
+
async function defaultDataStrategy(args) {
|
|
2228
|
+
let matchesToLoad = args.matches.filter((m) => m.shouldLoad);
|
|
2229
|
+
let keyedResults = {};
|
|
2230
|
+
(await Promise.all(matchesToLoad.map((m) => m.resolve()))).forEach((result, i) => {
|
|
2231
|
+
keyedResults[matchesToLoad[i].route.id] = result;
|
|
2232
|
+
});
|
|
2233
|
+
return keyedResults;
|
|
2234
|
+
}
|
|
2235
|
+
async function defaultDataStrategyWithMiddleware(args) {
|
|
2236
|
+
if (!args.matches.some((m) => m.route.middleware)) return defaultDataStrategy(args);
|
|
2237
|
+
return runClientMiddlewarePipeline(args, () => defaultDataStrategy(args));
|
|
2238
|
+
}
|
|
2239
|
+
function runServerMiddlewarePipeline(args, handler, errorHandler) {
|
|
2240
|
+
return runMiddlewarePipeline(args, handler, processResult, isResponse, errorHandler);
|
|
2241
|
+
function processResult(result) {
|
|
2242
|
+
return isDataWithResponseInit(result) ? dataWithResponseInitToResponse(result) : result;
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
function runClientMiddlewarePipeline(args, handler) {
|
|
2246
|
+
return runMiddlewarePipeline(args, handler, (r) => {
|
|
2247
|
+
if (isRedirectResponse(r)) throw r;
|
|
2248
|
+
return r;
|
|
2249
|
+
}, isDataStrategyResults, errorHandler);
|
|
2250
|
+
async function errorHandler(error, routeId, nextResult) {
|
|
2251
|
+
if (nextResult) return Object.assign(nextResult.value, { [routeId]: {
|
|
2252
|
+
type: "error",
|
|
2253
|
+
result: error
|
|
2254
|
+
} });
|
|
2255
|
+
else {
|
|
2256
|
+
let { matches } = args;
|
|
2257
|
+
let maxBoundaryIdx = Math.min(Math.max(matches.findIndex((m) => m.route.id === routeId), 0), Math.max(matches.findIndex((m) => m.shouldCallHandler()), 0));
|
|
2258
|
+
let deepestRouteId = matches[maxBoundaryIdx].route.id;
|
|
2259
|
+
for (let match of matches.slice(0, maxBoundaryIdx + 1)) try {
|
|
2260
|
+
await match._lazyPromises?.route;
|
|
2261
|
+
} catch {
|
|
2262
|
+
deepestRouteId = match.route.id;
|
|
2263
|
+
break;
|
|
2264
|
+
}
|
|
2265
|
+
return { [findNearestBoundary(matches, deepestRouteId).route.id]: {
|
|
2266
|
+
type: "error",
|
|
2267
|
+
result: error
|
|
2268
|
+
} };
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
async function runMiddlewarePipeline(args, handler, processResult, isResult, errorHandler) {
|
|
2273
|
+
let { matches, ...dataFnArgs } = args;
|
|
2274
|
+
return await callRouteMiddleware(dataFnArgs, matches.flatMap((m) => m.route.middleware ? m.route.middleware.map((fn) => [m.route.id, fn]) : []), handler, processResult, isResult, errorHandler);
|
|
2275
|
+
}
|
|
2276
|
+
async function callRouteMiddleware(args, middlewares, handler, processResult, isResult, errorHandler, idx = 0) {
|
|
2277
|
+
let { request } = args;
|
|
2278
|
+
if (request.signal.aborted) throw request.signal.reason ?? /* @__PURE__ */ new Error(`Request aborted: ${request.method} ${request.url}`);
|
|
2279
|
+
let tuple = middlewares[idx];
|
|
2280
|
+
if (!tuple) return await handler();
|
|
2281
|
+
let [routeId, middleware] = tuple;
|
|
2282
|
+
let nextResult;
|
|
2283
|
+
let next = async () => {
|
|
2284
|
+
if (nextResult) throw new Error("You may only call `next()` once per middleware");
|
|
2285
|
+
try {
|
|
2286
|
+
nextResult = { value: await callRouteMiddleware(args, middlewares, handler, processResult, isResult, errorHandler, idx + 1) };
|
|
2287
|
+
return nextResult.value;
|
|
2288
|
+
} catch (error) {
|
|
2289
|
+
nextResult = { value: await errorHandler(error, routeId, nextResult) };
|
|
2290
|
+
return nextResult.value;
|
|
2291
|
+
}
|
|
2292
|
+
};
|
|
2293
|
+
try {
|
|
2294
|
+
let value = await middleware(args, next);
|
|
2295
|
+
let result = value != null ? processResult(value) : void 0;
|
|
2296
|
+
if (isResult(result)) return result;
|
|
2297
|
+
else if (nextResult) return result ?? nextResult.value;
|
|
2298
|
+
else {
|
|
2299
|
+
nextResult = { value: await next() };
|
|
2300
|
+
return nextResult.value;
|
|
2301
|
+
}
|
|
2302
|
+
} catch (error) {
|
|
2303
|
+
return await errorHandler(error, routeId, nextResult);
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
function getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip) {
|
|
2307
|
+
let lazyMiddlewarePromise = loadLazyRouteProperty({
|
|
2308
|
+
key: "middleware",
|
|
2309
|
+
route: match.route,
|
|
2310
|
+
manifest,
|
|
2311
|
+
mapRouteProperties
|
|
2312
|
+
});
|
|
2313
|
+
let lazyRoutePromises = loadLazyRoute(match.route, isMutationMethod(request.method) ? "action" : "loader", manifest, mapRouteProperties, lazyRoutePropertiesToSkip);
|
|
2314
|
+
return {
|
|
2315
|
+
middleware: lazyMiddlewarePromise,
|
|
2316
|
+
route: lazyRoutePromises.lazyRoutePromise,
|
|
2317
|
+
handler: lazyRoutePromises.lazyHandlerPromise
|
|
2318
|
+
};
|
|
2319
|
+
}
|
|
2320
|
+
function getDataStrategyMatch(mapRouteProperties, manifest, request, path, pattern, match, lazyRoutePropertiesToSkip, scopedContext, shouldLoad, shouldRevalidateArgs = null, callSiteDefaultShouldRevalidate) {
|
|
2321
|
+
let isUsingNewApi = false;
|
|
2322
|
+
let _lazyPromises = getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip);
|
|
2323
|
+
return {
|
|
2324
|
+
...match,
|
|
2325
|
+
_lazyPromises,
|
|
2326
|
+
shouldLoad,
|
|
2327
|
+
shouldRevalidateArgs,
|
|
2328
|
+
shouldCallHandler(defaultShouldRevalidate) {
|
|
2329
|
+
isUsingNewApi = true;
|
|
2330
|
+
if (!shouldRevalidateArgs) return shouldLoad;
|
|
2331
|
+
if (typeof callSiteDefaultShouldRevalidate === "boolean") return shouldRevalidateLoader(match, {
|
|
2332
|
+
...shouldRevalidateArgs,
|
|
2333
|
+
defaultShouldRevalidate: callSiteDefaultShouldRevalidate
|
|
2334
|
+
});
|
|
2335
|
+
if (typeof defaultShouldRevalidate === "boolean") return shouldRevalidateLoader(match, {
|
|
2336
|
+
...shouldRevalidateArgs,
|
|
2337
|
+
defaultShouldRevalidate
|
|
2338
|
+
});
|
|
2339
|
+
return shouldRevalidateLoader(match, shouldRevalidateArgs);
|
|
2340
|
+
},
|
|
2341
|
+
resolve(handlerOverride) {
|
|
2342
|
+
let { lazy, loader, middleware } = match.route;
|
|
2343
|
+
let callHandler = isUsingNewApi || shouldLoad || handlerOverride && !isMutationMethod(request.method) && (lazy || loader);
|
|
2344
|
+
let isMiddlewareOnlyRoute = middleware && middleware.length > 0 && !loader && !lazy;
|
|
2345
|
+
if (callHandler && (isMutationMethod(request.method) || !isMiddlewareOnlyRoute)) return callLoaderOrAction({
|
|
2346
|
+
request,
|
|
2347
|
+
path,
|
|
2348
|
+
pattern,
|
|
2349
|
+
match,
|
|
2350
|
+
lazyHandlerPromise: _lazyPromises?.handler,
|
|
2351
|
+
lazyRoutePromise: _lazyPromises?.route,
|
|
2352
|
+
handlerOverride,
|
|
2353
|
+
scopedContext
|
|
2354
|
+
});
|
|
2355
|
+
return Promise.resolve({
|
|
2356
|
+
type: "data",
|
|
2357
|
+
result: void 0
|
|
2358
|
+
});
|
|
2359
|
+
}
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
2362
|
+
function getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, path, matches, targetMatch, lazyRoutePropertiesToSkip, scopedContext, shouldRevalidateArgs = null) {
|
|
2363
|
+
return matches.map((match) => {
|
|
2364
|
+
if (match.route.id !== targetMatch.route.id) return {
|
|
2365
|
+
...match,
|
|
2366
|
+
shouldLoad: false,
|
|
2367
|
+
shouldRevalidateArgs,
|
|
2368
|
+
shouldCallHandler: () => false,
|
|
2369
|
+
_lazyPromises: getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip),
|
|
2370
|
+
resolve: () => Promise.resolve({
|
|
2371
|
+
type: "data",
|
|
2372
|
+
result: void 0
|
|
2373
|
+
})
|
|
2374
|
+
};
|
|
2375
|
+
return getDataStrategyMatch(mapRouteProperties, manifest, request, path, getRoutePattern(matches), match, lazyRoutePropertiesToSkip, scopedContext, true, shouldRevalidateArgs);
|
|
2376
|
+
});
|
|
2377
|
+
}
|
|
2378
|
+
async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, scopedContext, isStaticHandler) {
|
|
2379
|
+
if (matches.some((m) => m._lazyPromises?.middleware)) await Promise.all(matches.map((m) => m._lazyPromises?.middleware));
|
|
2380
|
+
let dataStrategyArgs = {
|
|
2381
|
+
request,
|
|
2382
|
+
url: createDataFunctionUrl(request, path),
|
|
2383
|
+
pattern: getRoutePattern(matches),
|
|
2384
|
+
params: matches[0].params,
|
|
2385
|
+
context: scopedContext,
|
|
2386
|
+
matches
|
|
2387
|
+
};
|
|
2388
|
+
let runClientMiddleware = isStaticHandler ? () => {
|
|
2389
|
+
throw new Error("You cannot call `runClientMiddleware()` from a static handler `dataStrategy`. Middleware is run outside of `dataStrategy` during SSR in order to bubble up the Response. You can enable middleware via the `respond` API in `query`/`queryRoute`");
|
|
2390
|
+
} : (cb) => {
|
|
2391
|
+
let typedDataStrategyArgs = dataStrategyArgs;
|
|
2392
|
+
return runClientMiddlewarePipeline(typedDataStrategyArgs, () => {
|
|
2393
|
+
return cb({
|
|
2394
|
+
...typedDataStrategyArgs,
|
|
2395
|
+
fetcherKey,
|
|
2396
|
+
runClientMiddleware: () => {
|
|
2397
|
+
throw new Error("Cannot call `runClientMiddleware()` from within an `runClientMiddleware` handler");
|
|
2398
|
+
}
|
|
2399
|
+
});
|
|
2400
|
+
});
|
|
2401
|
+
};
|
|
2402
|
+
let results = await dataStrategyImpl({
|
|
2403
|
+
...dataStrategyArgs,
|
|
2404
|
+
fetcherKey,
|
|
2405
|
+
runClientMiddleware
|
|
2406
|
+
});
|
|
2407
|
+
try {
|
|
2408
|
+
await Promise.all(matches.flatMap((m) => [m._lazyPromises?.handler, m._lazyPromises?.route]));
|
|
2409
|
+
} catch (e) {}
|
|
2410
|
+
return results;
|
|
2411
|
+
}
|
|
2412
|
+
async function callLoaderOrAction({ request, path, pattern, match, lazyHandlerPromise, lazyRoutePromise, handlerOverride, scopedContext }) {
|
|
2413
|
+
let result;
|
|
2414
|
+
let onReject;
|
|
2415
|
+
let isAction = isMutationMethod(request.method);
|
|
2416
|
+
let type = isAction ? "action" : "loader";
|
|
2417
|
+
let runHandler = (handler) => {
|
|
2418
|
+
let reject;
|
|
2419
|
+
let abortPromise = new Promise((_, r) => reject = r);
|
|
2420
|
+
onReject = () => reject();
|
|
2421
|
+
request.signal.addEventListener("abort", onReject);
|
|
2422
|
+
let actualHandler = (ctx) => {
|
|
2423
|
+
if (typeof handler !== "function") return Promise.reject(/* @__PURE__ */ new Error(`You cannot call the handler for a route which defines a boolean "${type}" [routeId: ${match.route.id}]`));
|
|
2424
|
+
return handler({
|
|
2425
|
+
request,
|
|
2426
|
+
url: createDataFunctionUrl(request, path),
|
|
2427
|
+
pattern,
|
|
2428
|
+
params: match.params,
|
|
2429
|
+
context: scopedContext
|
|
2430
|
+
}, ...ctx !== void 0 ? [ctx] : []);
|
|
2431
|
+
};
|
|
2432
|
+
let handlerPromise = (async () => {
|
|
2433
|
+
try {
|
|
2434
|
+
return {
|
|
2435
|
+
type: "data",
|
|
2436
|
+
result: await (handlerOverride ? handlerOverride((ctx) => actualHandler(ctx)) : actualHandler())
|
|
2437
|
+
};
|
|
2438
|
+
} catch (e) {
|
|
2439
|
+
return {
|
|
2440
|
+
type: "error",
|
|
2441
|
+
result: e
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
})();
|
|
2445
|
+
return Promise.race([handlerPromise, abortPromise]);
|
|
2446
|
+
};
|
|
2447
|
+
try {
|
|
2448
|
+
let handler = isAction ? match.route.action : match.route.loader;
|
|
2449
|
+
if (lazyHandlerPromise || lazyRoutePromise) if (handler) {
|
|
2450
|
+
let handlerError;
|
|
2451
|
+
let [value] = await Promise.all([
|
|
2452
|
+
runHandler(handler).catch((e) => {
|
|
2453
|
+
handlerError = e;
|
|
2454
|
+
}),
|
|
2455
|
+
lazyHandlerPromise,
|
|
2456
|
+
lazyRoutePromise
|
|
2457
|
+
]);
|
|
2458
|
+
if (handlerError !== void 0) throw handlerError;
|
|
2459
|
+
result = value;
|
|
2460
|
+
} else {
|
|
2461
|
+
await lazyHandlerPromise;
|
|
2462
|
+
let handler = isAction ? match.route.action : match.route.loader;
|
|
2463
|
+
if (handler) [result] = await Promise.all([runHandler(handler), lazyRoutePromise]);
|
|
2464
|
+
else if (type === "action") {
|
|
2465
|
+
let url = new URL(request.url);
|
|
2466
|
+
let pathname = url.pathname + url.search;
|
|
2467
|
+
throw getInternalRouterError(405, {
|
|
2468
|
+
method: request.method,
|
|
2469
|
+
pathname,
|
|
2470
|
+
routeId: match.route.id
|
|
2471
|
+
});
|
|
2472
|
+
} else return {
|
|
2473
|
+
type: "data",
|
|
2474
|
+
result: void 0
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
2477
|
+
else if (!handler) {
|
|
2478
|
+
let url = new URL(request.url);
|
|
2479
|
+
throw getInternalRouterError(404, { pathname: url.pathname + url.search });
|
|
2480
|
+
} else result = await runHandler(handler);
|
|
2481
|
+
} catch (e) {
|
|
2482
|
+
return {
|
|
2483
|
+
type: "error",
|
|
2484
|
+
result: e
|
|
2485
|
+
};
|
|
2486
|
+
} finally {
|
|
2487
|
+
if (onReject) request.signal.removeEventListener("abort", onReject);
|
|
2488
|
+
}
|
|
2489
|
+
return result;
|
|
2490
|
+
}
|
|
2491
|
+
async function parseResponseBody(response) {
|
|
2492
|
+
let contentType = response.headers.get("Content-Type");
|
|
2493
|
+
if (contentType && /\bapplication\/json\b/.test(contentType)) return response.body == null ? null : response.json();
|
|
2494
|
+
return response.text();
|
|
2495
|
+
}
|
|
2496
|
+
async function convertDataStrategyResultToDataResult(dataStrategyResult) {
|
|
2497
|
+
let { result, type } = dataStrategyResult;
|
|
2498
|
+
if (isResponse(result)) {
|
|
2499
|
+
let data;
|
|
2500
|
+
try {
|
|
2501
|
+
data = await parseResponseBody(result);
|
|
2502
|
+
} catch (e) {
|
|
2503
|
+
return {
|
|
2504
|
+
type: "error",
|
|
2505
|
+
error: e
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
if (type === "error") return {
|
|
2509
|
+
type: "error",
|
|
2510
|
+
error: new ErrorResponseImpl(result.status, result.statusText, data),
|
|
2511
|
+
statusCode: result.status,
|
|
2512
|
+
headers: result.headers
|
|
2513
|
+
};
|
|
2514
|
+
return {
|
|
2515
|
+
type: "data",
|
|
2516
|
+
data,
|
|
2517
|
+
statusCode: result.status,
|
|
2518
|
+
headers: result.headers
|
|
2519
|
+
};
|
|
2520
|
+
}
|
|
2521
|
+
if (type === "error") {
|
|
2522
|
+
if (isDataWithResponseInit(result)) {
|
|
2523
|
+
if (result.data instanceof Error) return {
|
|
2524
|
+
type: "error",
|
|
2525
|
+
error: result.data,
|
|
2526
|
+
statusCode: result.init?.status,
|
|
2527
|
+
headers: result.init?.headers ? new Headers(result.init.headers) : void 0
|
|
2528
|
+
};
|
|
2529
|
+
return {
|
|
2530
|
+
type: "error",
|
|
2531
|
+
error: dataWithResponseInitToErrorResponse(result),
|
|
2532
|
+
statusCode: isRouteErrorResponse(result) ? result.status : void 0,
|
|
2533
|
+
headers: result.init?.headers ? new Headers(result.init.headers) : void 0
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
return {
|
|
2537
|
+
type: "error",
|
|
2538
|
+
error: result,
|
|
2539
|
+
statusCode: isRouteErrorResponse(result) ? result.status : void 0
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
if (isDataWithResponseInit(result)) return {
|
|
2543
|
+
type: "data",
|
|
2544
|
+
data: result.data,
|
|
2545
|
+
statusCode: result.init?.status,
|
|
2546
|
+
headers: result.init?.headers ? new Headers(result.init.headers) : void 0
|
|
2547
|
+
};
|
|
2548
|
+
return {
|
|
2549
|
+
type: "data",
|
|
2550
|
+
data: result
|
|
2551
|
+
};
|
|
2552
|
+
}
|
|
2553
|
+
function normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename) {
|
|
2554
|
+
let location = response.headers.get("Location");
|
|
2555
|
+
invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header");
|
|
2556
|
+
if (!isAbsoluteUrl(location)) {
|
|
2557
|
+
let trimmedMatches = matches.slice(0, matches.findIndex((m) => m.route.id === routeId) + 1);
|
|
2558
|
+
location = normalizeTo(new URL(request.url), trimmedMatches, basename, location);
|
|
2559
|
+
response.headers.set("Location", location);
|
|
2560
|
+
}
|
|
2561
|
+
return response;
|
|
2562
|
+
}
|
|
2563
|
+
const invalidProtocols = [
|
|
2564
|
+
"about:",
|
|
2565
|
+
"blob:",
|
|
2566
|
+
"chrome:",
|
|
2567
|
+
"chrome-untrusted:",
|
|
2568
|
+
"content:",
|
|
2569
|
+
"data:",
|
|
2570
|
+
"devtools:",
|
|
2571
|
+
"file:",
|
|
2572
|
+
"filesystem:",
|
|
2573
|
+
"javascript:"
|
|
2574
|
+
];
|
|
2575
|
+
function normalizeRedirectLocation(location, currentUrl, basename, historyInstance) {
|
|
2576
|
+
if (isAbsoluteUrl(location)) {
|
|
2577
|
+
let normalizedLocation = location;
|
|
2578
|
+
let url = normalizedLocation.startsWith("//") ? new URL(currentUrl.protocol + normalizedLocation) : new URL(normalizedLocation);
|
|
2579
|
+
if (invalidProtocols.includes(url.protocol)) throw new Error("Invalid redirect location");
|
|
2580
|
+
let isSameBasename = stripBasename(url.pathname, basename) != null;
|
|
2581
|
+
if (url.origin === currentUrl.origin && isSameBasename) return removeDoubleSlashes(url.pathname) + url.search + url.hash;
|
|
2582
|
+
}
|
|
2583
|
+
try {
|
|
2584
|
+
let url = historyInstance.createURL(location);
|
|
2585
|
+
if (invalidProtocols.includes(url.protocol)) throw new Error("Invalid redirect location");
|
|
2586
|
+
} catch (e) {}
|
|
2587
|
+
return location;
|
|
2588
|
+
}
|
|
2589
|
+
function createClientSideRequest(history, location, signal, submission) {
|
|
2590
|
+
let url = history.createURL(stripHashFromPath(location)).toString();
|
|
2591
|
+
let init = { signal };
|
|
2592
|
+
if (submission && isMutationMethod(submission.formMethod)) {
|
|
2593
|
+
let { formMethod, formEncType } = submission;
|
|
2594
|
+
init.method = formMethod.toUpperCase();
|
|
2595
|
+
if (formEncType === "application/json") {
|
|
2596
|
+
init.headers = new Headers({ "Content-Type": formEncType });
|
|
2597
|
+
init.body = JSON.stringify(submission.json);
|
|
2598
|
+
} else if (formEncType === "text/plain") init.body = submission.text;
|
|
2599
|
+
else if (formEncType === "application/x-www-form-urlencoded" && submission.formData) init.body = convertFormDataToSearchParams(submission.formData);
|
|
2600
|
+
else init.body = submission.formData;
|
|
2601
|
+
}
|
|
2602
|
+
return new Request(url, init);
|
|
2603
|
+
}
|
|
2604
|
+
function createDataFunctionUrl(request, path) {
|
|
2605
|
+
let url = new URL(request.url);
|
|
2606
|
+
let parsed = typeof path === "string" ? parsePath(path) : path;
|
|
2607
|
+
url.pathname = parsed.pathname || "/";
|
|
2608
|
+
if (parsed.search) {
|
|
2609
|
+
let searchParams = new URLSearchParams(parsed.search);
|
|
2610
|
+
let indexValues = searchParams.getAll("index");
|
|
2611
|
+
searchParams.delete("index");
|
|
2612
|
+
for (let value of indexValues.filter(Boolean)) searchParams.append("index", value);
|
|
2613
|
+
let search = searchParams.toString();
|
|
2614
|
+
url.search = search ? `?${search}` : "";
|
|
2615
|
+
} else url.search = "";
|
|
2616
|
+
url.hash = parsed.hash || "";
|
|
2617
|
+
return url;
|
|
2618
|
+
}
|
|
2619
|
+
function convertFormDataToSearchParams(formData) {
|
|
2620
|
+
let searchParams = new URLSearchParams();
|
|
2621
|
+
for (let [key, value] of formData.entries()) searchParams.append(key, typeof value === "string" ? value : value.name);
|
|
2622
|
+
return searchParams;
|
|
2623
|
+
}
|
|
2624
|
+
function convertSearchParamsToFormData(searchParams) {
|
|
2625
|
+
let formData = new FormData();
|
|
2626
|
+
for (let [key, value] of searchParams.entries()) formData.append(key, value);
|
|
2627
|
+
return formData;
|
|
2628
|
+
}
|
|
2629
|
+
function processRouteLoaderData(matches, results, pendingActionResult, isStaticHandler = false, skipLoaderErrorBubbling = false) {
|
|
2630
|
+
let loaderData = {};
|
|
2631
|
+
let errors = null;
|
|
2632
|
+
let statusCode;
|
|
2633
|
+
let foundError = false;
|
|
2634
|
+
let loaderHeaders = {};
|
|
2635
|
+
let pendingError = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : void 0;
|
|
2636
|
+
matches.forEach((match) => {
|
|
2637
|
+
if (!(match.route.id in results)) return;
|
|
2638
|
+
let id = match.route.id;
|
|
2639
|
+
let result = results[id];
|
|
2640
|
+
invariant(!isRedirectResult(result), "Cannot handle redirect results in processLoaderData");
|
|
2641
|
+
if (isErrorResult(result)) {
|
|
2642
|
+
let error = result.error;
|
|
2643
|
+
if (pendingError !== void 0) {
|
|
2644
|
+
error = pendingError;
|
|
2645
|
+
pendingError = void 0;
|
|
2646
|
+
}
|
|
2647
|
+
errors = errors || {};
|
|
2648
|
+
if (skipLoaderErrorBubbling) errors[id] = error;
|
|
2649
|
+
else {
|
|
2650
|
+
let boundaryMatch = findNearestBoundary(matches, id);
|
|
2651
|
+
if (errors[boundaryMatch.route.id] == null) errors[boundaryMatch.route.id] = error;
|
|
2652
|
+
}
|
|
2653
|
+
if (!isStaticHandler) loaderData[id] = ResetLoaderDataSymbol;
|
|
2654
|
+
if (!foundError) {
|
|
2655
|
+
foundError = true;
|
|
2656
|
+
statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
|
|
2657
|
+
}
|
|
2658
|
+
if (result.headers) loaderHeaders[id] = result.headers;
|
|
2659
|
+
} else {
|
|
2660
|
+
loaderData[id] = result.data;
|
|
2661
|
+
if (result.statusCode && result.statusCode !== 200 && !foundError) statusCode = result.statusCode;
|
|
2662
|
+
if (result.headers) loaderHeaders[id] = result.headers;
|
|
2663
|
+
}
|
|
2664
|
+
});
|
|
2665
|
+
if (pendingError !== void 0 && pendingActionResult) {
|
|
2666
|
+
errors = { [pendingActionResult[0]]: pendingError };
|
|
2667
|
+
if (pendingActionResult[2]) loaderData[pendingActionResult[2]] = void 0;
|
|
2668
|
+
}
|
|
2669
|
+
return {
|
|
2670
|
+
loaderData,
|
|
2671
|
+
errors,
|
|
2672
|
+
statusCode: statusCode || 200,
|
|
2673
|
+
loaderHeaders
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
function processLoaderData(state, matches, results, pendingActionResult, revalidatingFetchers, fetcherResults, workingFetchers) {
|
|
2677
|
+
let { loaderData, errors } = processRouteLoaderData(matches, results, pendingActionResult);
|
|
2678
|
+
revalidatingFetchers.filter((f) => !f.matches || f.matches.some((m) => m.shouldLoad)).forEach((rf) => {
|
|
2679
|
+
let { key, match, controller } = rf;
|
|
2680
|
+
if (controller && controller.signal.aborted) return;
|
|
2681
|
+
let result = fetcherResults[key];
|
|
2682
|
+
invariant(result, "Did not find corresponding fetcher result");
|
|
2683
|
+
if (isErrorResult(result)) {
|
|
2684
|
+
let boundaryMatch = findNearestBoundary(state.matches, match?.route.id);
|
|
2685
|
+
if (!(errors && errors[boundaryMatch.route.id])) errors = {
|
|
2686
|
+
...errors,
|
|
2687
|
+
[boundaryMatch.route.id]: result.error
|
|
2688
|
+
};
|
|
2689
|
+
workingFetchers.delete(key);
|
|
2690
|
+
} else if (isRedirectResult(result)) invariant(false, "Unhandled fetcher revalidation redirect");
|
|
2691
|
+
else {
|
|
2692
|
+
let doneFetcher = getDoneFetcher(result.data);
|
|
2693
|
+
workingFetchers.set(key, doneFetcher);
|
|
2694
|
+
}
|
|
2695
|
+
});
|
|
2696
|
+
return {
|
|
2697
|
+
loaderData,
|
|
2698
|
+
errors
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
function mergeLoaderData(loaderData, newLoaderData, matches, errors) {
|
|
2702
|
+
let mergedLoaderData = Object.entries(newLoaderData).filter(([, v]) => v !== ResetLoaderDataSymbol).reduce((merged, [k, v]) => {
|
|
2703
|
+
merged[k] = v;
|
|
2704
|
+
return merged;
|
|
2705
|
+
}, {});
|
|
2706
|
+
for (let match of matches) {
|
|
2707
|
+
let id = match.route.id;
|
|
2708
|
+
if (!newLoaderData.hasOwnProperty(id) && loaderData.hasOwnProperty(id) && match.route.loader) mergedLoaderData[id] = loaderData[id];
|
|
2709
|
+
if (errors && errors.hasOwnProperty(id)) break;
|
|
2710
|
+
}
|
|
2711
|
+
return mergedLoaderData;
|
|
2712
|
+
}
|
|
2713
|
+
function getActionDataForCommit(pendingActionResult) {
|
|
2714
|
+
if (!pendingActionResult) return {};
|
|
2715
|
+
return isErrorResult(pendingActionResult[1]) ? { actionData: {} } : { actionData: { [pendingActionResult[0]]: pendingActionResult[1].data } };
|
|
2716
|
+
}
|
|
2717
|
+
function findNearestBoundary(matches, routeId) {
|
|
2718
|
+
return (routeId ? matches.slice(0, matches.findIndex((m) => m.route.id === routeId) + 1) : [...matches]).reverse().find((m) => m.route.ErrorBoundary != null || m.route.errorElement != null) || matches[0];
|
|
2719
|
+
}
|
|
2720
|
+
function getShortCircuitMatches(routes) {
|
|
2721
|
+
let route = routes.length === 1 ? routes[0] : routes.find((r) => r.index || !r.path || r.path === "/") || { id: `__shim-error-route__` };
|
|
2722
|
+
return {
|
|
2723
|
+
matches: [{
|
|
2724
|
+
params: {},
|
|
2725
|
+
pathname: "",
|
|
2726
|
+
pathnameBase: "",
|
|
2727
|
+
route
|
|
2728
|
+
}],
|
|
2729
|
+
route
|
|
2730
|
+
};
|
|
2731
|
+
}
|
|
2732
|
+
function getInternalRouterError(status, { pathname, routeId, method, type, message } = {}) {
|
|
2733
|
+
let statusText = "Unknown Server Error";
|
|
2734
|
+
let errorMessage = "Unknown @remix-run/router error";
|
|
2735
|
+
if (status === 400) {
|
|
2736
|
+
statusText = "Bad Request";
|
|
2737
|
+
if (method && pathname && routeId) errorMessage = `You made a ${method} request to "${pathname}" but did not provide a \`loader\` for route "${routeId}", so there is no way to handle the request.`;
|
|
2738
|
+
else if (type === "invalid-body") errorMessage = "Unable to encode submission body";
|
|
2739
|
+
} else if (status === 403) {
|
|
2740
|
+
statusText = "Forbidden";
|
|
2741
|
+
errorMessage = `Route "${routeId}" does not match URL "${pathname}"`;
|
|
2742
|
+
} else if (status === 404) {
|
|
2743
|
+
statusText = "Not Found";
|
|
2744
|
+
errorMessage = `No route matches URL "${pathname}"`;
|
|
2745
|
+
} else if (status === 405) {
|
|
2746
|
+
statusText = "Method Not Allowed";
|
|
2747
|
+
if (method && pathname && routeId) errorMessage = `You made a ${method.toUpperCase()} request to "${pathname}" but did not provide an \`action\` for route "${routeId}", so there is no way to handle the request.`;
|
|
2748
|
+
else if (method) errorMessage = `Invalid request method "${method.toUpperCase()}"`;
|
|
2749
|
+
}
|
|
2750
|
+
return new ErrorResponseImpl(status || 500, statusText, new Error(errorMessage), true);
|
|
2751
|
+
}
|
|
2752
|
+
function findRedirect(results) {
|
|
2753
|
+
let entries = Object.entries(results);
|
|
2754
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
2755
|
+
let [key, result] = entries[i];
|
|
2756
|
+
if (isRedirectResult(result)) return {
|
|
2757
|
+
key,
|
|
2758
|
+
result
|
|
2759
|
+
};
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
function stripHashFromPath(path) {
|
|
2763
|
+
return createPath({
|
|
2764
|
+
...typeof path === "string" ? parsePath(path) : path,
|
|
2765
|
+
hash: ""
|
|
2766
|
+
});
|
|
2767
|
+
}
|
|
2768
|
+
function isHashChangeOnly(a, b) {
|
|
2769
|
+
if (a.pathname !== b.pathname || a.search !== b.search) return false;
|
|
2770
|
+
if (a.hash === "") return b.hash !== "";
|
|
2771
|
+
else if (a.hash === b.hash) return true;
|
|
2772
|
+
else if (b.hash !== "") return true;
|
|
2773
|
+
return false;
|
|
2774
|
+
}
|
|
2775
|
+
function dataWithResponseInitToResponse(data) {
|
|
2776
|
+
return Response.json(data.data, data.init ?? void 0);
|
|
2777
|
+
}
|
|
2778
|
+
function dataWithResponseInitToErrorResponse(data) {
|
|
2779
|
+
return new ErrorResponseImpl(data.init?.status ?? 500, data.init?.statusText ?? "Internal Server Error", data.data);
|
|
2780
|
+
}
|
|
2781
|
+
function isDataStrategyResults(result) {
|
|
2782
|
+
return result != null && typeof result === "object" && Object.entries(result).every(([key, value]) => typeof key === "string" && isDataStrategyResult(value));
|
|
2783
|
+
}
|
|
2784
|
+
function isDataStrategyResult(result) {
|
|
2785
|
+
return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === "data" || result.type === "error");
|
|
2786
|
+
}
|
|
2787
|
+
function isRedirectDataStrategyResult(result) {
|
|
2788
|
+
return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
|
|
2789
|
+
}
|
|
2790
|
+
function isErrorResult(result) {
|
|
2791
|
+
return result.type === "error";
|
|
2792
|
+
}
|
|
2793
|
+
function isRedirectResult(result) {
|
|
2794
|
+
return (result && result.type) === "redirect";
|
|
2795
|
+
}
|
|
2796
|
+
function isDataWithResponseInit(value) {
|
|
2797
|
+
return typeof value === "object" && value != null && "type" in value && "data" in value && "init" in value && value.type === "DataWithResponseInit";
|
|
2798
|
+
}
|
|
2799
|
+
function isResponse(value) {
|
|
2800
|
+
return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
|
|
2801
|
+
}
|
|
2802
|
+
function isRedirectStatusCode(statusCode) {
|
|
2803
|
+
return redirectStatusCodes.has(statusCode);
|
|
2804
|
+
}
|
|
2805
|
+
function isRedirectResponse(result) {
|
|
2806
|
+
return isResponse(result) && isRedirectStatusCode(result.status) && result.headers.has("Location");
|
|
2807
|
+
}
|
|
2808
|
+
function isValidMethod(method) {
|
|
2809
|
+
return validRequestMethods.has(method.toUpperCase());
|
|
2810
|
+
}
|
|
2811
|
+
function isMutationMethod(method) {
|
|
2812
|
+
return validMutationMethods.has(method.toUpperCase());
|
|
2813
|
+
}
|
|
2814
|
+
function hasNakedIndexQuery(search) {
|
|
2815
|
+
return new URLSearchParams(search).getAll("index").some((v) => v === "");
|
|
2816
|
+
}
|
|
2817
|
+
function getTargetMatch(matches, location) {
|
|
2818
|
+
let search = typeof location === "string" ? parsePath(location).search : location.search;
|
|
2819
|
+
if (matches[matches.length - 1].route.index && hasNakedIndexQuery(search || "")) return matches[matches.length - 1];
|
|
2820
|
+
let pathMatches = getPathContributingMatches(matches);
|
|
2821
|
+
return pathMatches[pathMatches.length - 1];
|
|
2822
|
+
}
|
|
2823
|
+
function getSubmissionFromNavigation(navigation) {
|
|
2824
|
+
let { formMethod, formAction, formEncType, text, formData, json } = navigation;
|
|
2825
|
+
if (!formMethod || !formAction || !formEncType) return;
|
|
2826
|
+
if (text != null) return {
|
|
2827
|
+
formMethod,
|
|
2828
|
+
formAction,
|
|
2829
|
+
formEncType,
|
|
2830
|
+
formData: void 0,
|
|
2831
|
+
json: void 0,
|
|
2832
|
+
text
|
|
2833
|
+
};
|
|
2834
|
+
else if (formData != null) return {
|
|
2835
|
+
formMethod,
|
|
2836
|
+
formAction,
|
|
2837
|
+
formEncType,
|
|
2838
|
+
formData,
|
|
2839
|
+
json: void 0,
|
|
2840
|
+
text: void 0
|
|
2841
|
+
};
|
|
2842
|
+
else if (json !== void 0) return {
|
|
2843
|
+
formMethod,
|
|
2844
|
+
formAction,
|
|
2845
|
+
formEncType,
|
|
2846
|
+
formData: void 0,
|
|
2847
|
+
json,
|
|
2848
|
+
text: void 0
|
|
2849
|
+
};
|
|
2850
|
+
}
|
|
2851
|
+
function getLoadingNavigation(location, matches, historyAction, submission) {
|
|
2852
|
+
if (submission) return {
|
|
2853
|
+
state: "loading",
|
|
2854
|
+
location,
|
|
2855
|
+
matches,
|
|
2856
|
+
historyAction,
|
|
2857
|
+
formMethod: submission.formMethod,
|
|
2858
|
+
formAction: submission.formAction,
|
|
2859
|
+
formEncType: submission.formEncType,
|
|
2860
|
+
formData: submission.formData,
|
|
2861
|
+
json: submission.json,
|
|
2862
|
+
text: submission.text
|
|
2863
|
+
};
|
|
2864
|
+
else return {
|
|
2865
|
+
state: "loading",
|
|
2866
|
+
location,
|
|
2867
|
+
matches,
|
|
2868
|
+
historyAction,
|
|
2869
|
+
formMethod: void 0,
|
|
2870
|
+
formAction: void 0,
|
|
2871
|
+
formEncType: void 0,
|
|
2872
|
+
formData: void 0,
|
|
2873
|
+
json: void 0,
|
|
2874
|
+
text: void 0
|
|
2875
|
+
};
|
|
2876
|
+
}
|
|
2877
|
+
function getSubmittingNavigation(location, matches, historyAction, submission) {
|
|
2878
|
+
return {
|
|
2879
|
+
state: "submitting",
|
|
2880
|
+
location,
|
|
2881
|
+
matches,
|
|
2882
|
+
historyAction,
|
|
2883
|
+
formMethod: submission.formMethod,
|
|
2884
|
+
formAction: submission.formAction,
|
|
2885
|
+
formEncType: submission.formEncType,
|
|
2886
|
+
formData: submission.formData,
|
|
2887
|
+
json: submission.json,
|
|
2888
|
+
text: submission.text
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2891
|
+
function getLoadingFetcher(submission, data) {
|
|
2892
|
+
if (submission) return {
|
|
2893
|
+
state: "loading",
|
|
2894
|
+
formMethod: submission.formMethod,
|
|
2895
|
+
formAction: submission.formAction,
|
|
2896
|
+
formEncType: submission.formEncType,
|
|
2897
|
+
formData: submission.formData,
|
|
2898
|
+
json: submission.json,
|
|
2899
|
+
text: submission.text,
|
|
2900
|
+
data
|
|
2901
|
+
};
|
|
2902
|
+
else return {
|
|
2903
|
+
state: "loading",
|
|
2904
|
+
formMethod: void 0,
|
|
2905
|
+
formAction: void 0,
|
|
2906
|
+
formEncType: void 0,
|
|
2907
|
+
formData: void 0,
|
|
2908
|
+
json: void 0,
|
|
2909
|
+
text: void 0,
|
|
2910
|
+
data
|
|
2911
|
+
};
|
|
2912
|
+
}
|
|
2913
|
+
function getSubmittingFetcher(submission, existingFetcher) {
|
|
2914
|
+
return {
|
|
2915
|
+
state: "submitting",
|
|
2916
|
+
formMethod: submission.formMethod,
|
|
2917
|
+
formAction: submission.formAction,
|
|
2918
|
+
formEncType: submission.formEncType,
|
|
2919
|
+
formData: submission.formData,
|
|
2920
|
+
json: submission.json,
|
|
2921
|
+
text: submission.text,
|
|
2922
|
+
data: existingFetcher ? existingFetcher.data : void 0
|
|
2923
|
+
};
|
|
2924
|
+
}
|
|
2925
|
+
function getDoneFetcher(data) {
|
|
2926
|
+
return {
|
|
2927
|
+
state: "idle",
|
|
2928
|
+
formMethod: void 0,
|
|
2929
|
+
formAction: void 0,
|
|
2930
|
+
formEncType: void 0,
|
|
2931
|
+
formData: void 0,
|
|
2932
|
+
json: void 0,
|
|
2933
|
+
text: void 0,
|
|
2934
|
+
data
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2937
|
+
function restoreAppliedTransitions(_window, transitions) {
|
|
2938
|
+
try {
|
|
2939
|
+
let sessionPositions = _window.sessionStorage.getItem(TRANSITIONS_STORAGE_KEY);
|
|
2940
|
+
if (sessionPositions) {
|
|
2941
|
+
let json = JSON.parse(sessionPositions);
|
|
2942
|
+
for (let [k, v] of Object.entries(json || {})) if (v && Array.isArray(v)) transitions.set(k, new Set(v || []));
|
|
2943
|
+
}
|
|
2944
|
+
} catch (e) {}
|
|
2945
|
+
}
|
|
2946
|
+
function persistAppliedTransitions(_window, transitions) {
|
|
2947
|
+
if (transitions.size > 0) {
|
|
2948
|
+
let json = {};
|
|
2949
|
+
for (let [k, v] of transitions) json[k] = [...v];
|
|
2950
|
+
try {
|
|
2951
|
+
_window.sessionStorage.setItem(TRANSITIONS_STORAGE_KEY, JSON.stringify(json));
|
|
2952
|
+
} catch (error) {
|
|
2953
|
+
warning(false, `Failed to save applied view transitions in sessionStorage (${error}).`);
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
function createDeferred() {
|
|
2958
|
+
let resolve;
|
|
2959
|
+
let reject;
|
|
2960
|
+
let promise = new Promise((res, rej) => {
|
|
2961
|
+
resolve = async (val) => {
|
|
2962
|
+
res(val);
|
|
2963
|
+
try {
|
|
2964
|
+
await promise;
|
|
2965
|
+
} catch (e) {}
|
|
2966
|
+
};
|
|
2967
|
+
reject = async (error) => {
|
|
2968
|
+
rej(error);
|
|
2969
|
+
try {
|
|
2970
|
+
await promise;
|
|
2971
|
+
} catch (e) {}
|
|
2972
|
+
};
|
|
2973
|
+
});
|
|
2974
|
+
return {
|
|
2975
|
+
promise,
|
|
2976
|
+
resolve,
|
|
2977
|
+
reject
|
|
2978
|
+
};
|
|
2979
|
+
}
|
|
2980
|
+
//#endregion
|
|
2981
|
+
export { IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, createRouter, createStaticHandler, getStaticContextFromError, invalidProtocols, isDataWithResponseInit, isMutationMethod, isRedirectResponse, isRedirectStatusCode, isResponse };
|