@remix-run/react
Advanced tools
Comparing version 0.0.0-nightly-6f23bc960-20240821 to 0.0.0-nightly-6f8288990-20241030
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -24,2 +24,3 @@ import type { HydrationState, Router } from "@remix-run/router"; | ||
var __remixRevalidation: number | undefined; | ||
var __remixHdrActive: boolean; | ||
var __remixClearCriticalCss: (() => void) | undefined; | ||
@@ -26,0 +27,0 @@ var $RefreshRuntime$: { |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -77,3 +77,3 @@ * Copyright (c) Remix Software Inc. | ||
// snapshot is decoded into window.__remixContext.state | ||
if (window.__remixContext.future.unstable_singleFetch) { | ||
if (window.__remixContext.future.v3_singleFetch) { | ||
// Note: `stateDecodingPromise` is not coupled to `router` - we'll reach this | ||
@@ -155,8 +155,8 @@ // code potentially many times waiting for our state to arrive, but we'll | ||
// Single fetch enables this underlying behavior | ||
v7_skipActionErrorRevalidation: window.__remixContext.future.unstable_singleFetch === true | ||
v7_skipActionErrorRevalidation: window.__remixContext.future.v3_singleFetch === true | ||
}, | ||
hydrationData, | ||
mapRouteProperties: reactRouter.UNSAFE_mapRouteProperties, | ||
unstable_dataStrategy: window.__remixContext.future.unstable_singleFetch ? singleFetch.getSingleFetchDataStrategy(window.__remixManifest, window.__remixRouteModules) : undefined, | ||
unstable_patchRoutesOnNavigation: fogOfWar.getPatchRoutesOnNavigationFunction(window.__remixManifest, window.__remixRouteModules, window.__remixContext.future, window.__remixContext.isSpaMode, window.__remixContext.basename) | ||
dataStrategy: window.__remixContext.future.v3_singleFetch ? singleFetch.getSingleFetchDataStrategy(window.__remixManifest, window.__remixRouteModules, () => router) : undefined, | ||
patchRoutesOnNavigation: fogOfWar.getPatchRoutesOnNavigationFunction(window.__remixManifest, window.__remixRouteModules, window.__remixContext.future, window.__remixContext.isSpaMode, window.__remixContext.basename) | ||
}); | ||
@@ -236,3 +236,3 @@ | ||
} | ||
}))), window.__remixContext.future.unstable_singleFetch ? /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null) : null) | ||
}))), window.__remixContext.future.v3_singleFetch ? /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null) : null) | ||
); | ||
@@ -239,0 +239,0 @@ } |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -328,7 +328,46 @@ * Copyright (c) Remix Software Inc. | ||
let { | ||
loaderData, | ||
matches | ||
} = useDataRouterStateContext(); | ||
let newMatchesForData = React__namespace.useMemo(() => links.getNewMatchesForLinks(page, nextMatches, matches, manifest, location, "data"), [page, nextMatches, matches, manifest, location]); | ||
let newMatchesForAssets = React__namespace.useMemo(() => links.getNewMatchesForLinks(page, nextMatches, matches, manifest, location, "assets"), [page, nextMatches, matches, manifest, location]); | ||
let dataHrefs = React__namespace.useMemo(() => links.getDataLinkHrefs(page, newMatchesForData, manifest), [newMatchesForData, page, manifest]); | ||
let newMatchesForData = React__namespace.useMemo(() => links.getNewMatchesForLinks(page, nextMatches, matches, manifest, location, future, "data"), [page, nextMatches, matches, manifest, location, future]); | ||
let dataHrefs = React__namespace.useMemo(() => { | ||
if (!future.v3_singleFetch) { | ||
return links.getDataLinkHrefs(page, newMatchesForData, manifest); | ||
} | ||
if (page === location.pathname + location.search + location.hash) { | ||
// Because we opt-into revalidation, don't compute this for the current page | ||
// since it would always trigger a prefetch of the existing loaders | ||
return []; | ||
} | ||
// Single-fetch is harder :) | ||
// This parallels the logic in the single fetch data strategy | ||
let routesParams = new Set(); | ||
let foundOptOutRoute = false; | ||
nextMatches.forEach(m => { | ||
var _routeModules$m$route; | ||
if (!manifest.routes[m.route.id].hasLoader) { | ||
return; | ||
} | ||
if (!newMatchesForData.some(m2 => m2.route.id === m.route.id) && m.route.id in loaderData && (_routeModules$m$route = routeModules[m.route.id]) !== null && _routeModules$m$route !== void 0 && _routeModules$m$route.shouldRevalidate) { | ||
foundOptOutRoute = true; | ||
} else if (manifest.routes[m.route.id].hasClientLoader) { | ||
foundOptOutRoute = true; | ||
} else { | ||
routesParams.add(m.route.id); | ||
} | ||
}); | ||
if (routesParams.size === 0) { | ||
return []; | ||
} | ||
let url = singleFetch.singleFetchUrl(page); | ||
// When one or more routes have opted out, we add a _routes param to | ||
// limit the loaders to those that have a server loader and did not | ||
// opt out | ||
if (foundOptOutRoute && routesParams.size > 0) { | ||
url.searchParams.set("_routes", nextMatches.filter(m => routesParams.has(m.route.id)).map(m => m.route.id).join(",")); | ||
} | ||
return [url.pathname + url.search]; | ||
}, [future.v3_singleFetch, loaderData, location, manifest, newMatchesForData, nextMatches, page, routeModules]); | ||
let newMatchesForAssets = React__namespace.useMemo(() => links.getNewMatchesForLinks(page, nextMatches, matches, manifest, location, future, "assets"), [page, nextMatches, matches, manifest, location, future]); | ||
let moduleHrefs = React__namespace.useMemo(() => links.getModuleLinkHrefs(newMatchesForAssets, manifest), [newMatchesForAssets, manifest]); | ||
@@ -339,25 +378,9 @@ | ||
let keyedPrefetchLinks = useKeyedPrefetchLinks(newMatchesForAssets); | ||
let linksToRender = null; | ||
if (!future.unstable_singleFetch) { | ||
// Non-single-fetch prefetching | ||
linksToRender = dataHrefs.map(href => /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({ | ||
key: href, | ||
rel: "prefetch", | ||
as: "fetch", | ||
href: href | ||
}, linkProps))); | ||
} else if (newMatchesForData.length > 0) { | ||
// Single-fetch with routes that require data | ||
let url = singleFetch.addRevalidationParam(manifest, routeModules, nextMatches.map(m => m.route), newMatchesForData.map(m => m.route), singleFetch.singleFetchUrl(page)); | ||
if (url.searchParams.get("_routes") !== "") { | ||
linksToRender = /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({ | ||
key: url.pathname + url.search, | ||
rel: "prefetch", | ||
as: "fetch", | ||
href: url.pathname + url.search | ||
}, linkProps)); | ||
} | ||
} else ; | ||
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, linksToRender, moduleHrefs.map(href => /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({ | ||
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, dataHrefs.map(href => /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({ | ||
key: href, | ||
rel: "prefetch", | ||
as: "fetch", | ||
href: href | ||
}, linkProps))), moduleHrefs.map(href => /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({ | ||
key: href, | ||
rel: "modulepreload", | ||
@@ -583,3 +606,3 @@ href: href | ||
var _manifest$hmr; | ||
let streamScript = future.unstable_singleFetch ? | ||
let streamScript = future.v3_singleFetch ? | ||
// prettier-ignore | ||
@@ -590,3 +613,3 @@ "window.__remixContext.stream = new ReadableStream({" + "start(controller){" + "window.__remixContext.streamController = controller;" + "}" + "}).pipeThrough(new TextEncoderStream());" : ""; | ||
// When single fetch is enabled, deferred is handled by turbo-stream | ||
let activeDeferreds = future.unstable_singleFetch ? undefined : staticContext === null || staticContext === void 0 ? void 0 : staticContext.activeDeferreds; | ||
let activeDeferreds = future.v3_singleFetch ? undefined : staticContext === null || staticContext === void 0 ? void 0 : staticContext.activeDeferreds; | ||
@@ -593,0 +616,0 @@ // This sets up the __remixContext with utility functions used by the |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -35,4 +35,4 @@ import type { StaticHandlerContext } from "@remix-run/router"; | ||
v3_relativeSplatPath: boolean; | ||
unstable_lazyRouteDiscovery: boolean; | ||
unstable_singleFetch: boolean; | ||
v3_lazyRouteDiscovery: boolean; | ||
v3_singleFetch: boolean; | ||
} | ||
@@ -39,0 +39,0 @@ export interface AssetsManifest { |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -117,3 +117,3 @@ * Copyright (c) Remix Software Inc. | ||
// snapshot is decoded into window.__remixContext.state | ||
if (window.__remixContext.future.unstable_singleFetch) { | ||
if (window.__remixContext.future.v3_singleFetch) { | ||
// Note: `stateDecodingPromise` is not coupled to `router` - we'll reach this | ||
@@ -195,8 +195,8 @@ // code potentially many times waiting for our state to arrive, but we'll | ||
// Single fetch enables this underlying behavior | ||
v7_skipActionErrorRevalidation: window.__remixContext.future.unstable_singleFetch === true | ||
v7_skipActionErrorRevalidation: window.__remixContext.future.v3_singleFetch === true | ||
}, | ||
hydrationData, | ||
mapRouteProperties: UNSAFE_mapRouteProperties, | ||
unstable_dataStrategy: window.__remixContext.future.unstable_singleFetch ? getSingleFetchDataStrategy(window.__remixManifest, window.__remixRouteModules) : undefined, | ||
unstable_patchRoutesOnNavigation: getPatchRoutesOnNavigationFunction(window.__remixManifest, window.__remixRouteModules, window.__remixContext.future, window.__remixContext.isSpaMode, window.__remixContext.basename) | ||
dataStrategy: window.__remixContext.future.v3_singleFetch ? getSingleFetchDataStrategy(window.__remixManifest, window.__remixRouteModules, () => router) : undefined, | ||
patchRoutesOnNavigation: getPatchRoutesOnNavigationFunction(window.__remixManifest, window.__remixRouteModules, window.__remixContext.future, window.__remixContext.isSpaMode, window.__remixContext.basename) | ||
}); | ||
@@ -276,3 +276,3 @@ | ||
} | ||
}))), window.__remixContext.future.unstable_singleFetch ? /*#__PURE__*/React.createElement(React.Fragment, null) : null) | ||
}))), window.__remixContext.future.v3_singleFetch ? /*#__PURE__*/React.createElement(React.Fragment, null) : null) | ||
); | ||
@@ -279,0 +279,0 @@ } |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -17,3 +17,3 @@ * Copyright (c) Remix Software Inc. | ||
import { escapeHtml, createHtml } from './markup.js'; | ||
import { addRevalidationParam, singleFetchUrl } from './single-fetch.js'; | ||
import { singleFetchUrl } from './single-fetch.js'; | ||
import { isFogOfWarEnabled, getPartialManifest } from './fog-of-war.js'; | ||
@@ -305,7 +305,46 @@ | ||
let { | ||
loaderData, | ||
matches | ||
} = useDataRouterStateContext(); | ||
let newMatchesForData = React.useMemo(() => getNewMatchesForLinks(page, nextMatches, matches, manifest, location, "data"), [page, nextMatches, matches, manifest, location]); | ||
let newMatchesForAssets = React.useMemo(() => getNewMatchesForLinks(page, nextMatches, matches, manifest, location, "assets"), [page, nextMatches, matches, manifest, location]); | ||
let dataHrefs = React.useMemo(() => getDataLinkHrefs(page, newMatchesForData, manifest), [newMatchesForData, page, manifest]); | ||
let newMatchesForData = React.useMemo(() => getNewMatchesForLinks(page, nextMatches, matches, manifest, location, future, "data"), [page, nextMatches, matches, manifest, location, future]); | ||
let dataHrefs = React.useMemo(() => { | ||
if (!future.v3_singleFetch) { | ||
return getDataLinkHrefs(page, newMatchesForData, manifest); | ||
} | ||
if (page === location.pathname + location.search + location.hash) { | ||
// Because we opt-into revalidation, don't compute this for the current page | ||
// since it would always trigger a prefetch of the existing loaders | ||
return []; | ||
} | ||
// Single-fetch is harder :) | ||
// This parallels the logic in the single fetch data strategy | ||
let routesParams = new Set(); | ||
let foundOptOutRoute = false; | ||
nextMatches.forEach(m => { | ||
var _routeModules$m$route; | ||
if (!manifest.routes[m.route.id].hasLoader) { | ||
return; | ||
} | ||
if (!newMatchesForData.some(m2 => m2.route.id === m.route.id) && m.route.id in loaderData && (_routeModules$m$route = routeModules[m.route.id]) !== null && _routeModules$m$route !== void 0 && _routeModules$m$route.shouldRevalidate) { | ||
foundOptOutRoute = true; | ||
} else if (manifest.routes[m.route.id].hasClientLoader) { | ||
foundOptOutRoute = true; | ||
} else { | ||
routesParams.add(m.route.id); | ||
} | ||
}); | ||
if (routesParams.size === 0) { | ||
return []; | ||
} | ||
let url = singleFetchUrl(page); | ||
// When one or more routes have opted out, we add a _routes param to | ||
// limit the loaders to those that have a server loader and did not | ||
// opt out | ||
if (foundOptOutRoute && routesParams.size > 0) { | ||
url.searchParams.set("_routes", nextMatches.filter(m => routesParams.has(m.route.id)).map(m => m.route.id).join(",")); | ||
} | ||
return [url.pathname + url.search]; | ||
}, [future.v3_singleFetch, loaderData, location, manifest, newMatchesForData, nextMatches, page, routeModules]); | ||
let newMatchesForAssets = React.useMemo(() => getNewMatchesForLinks(page, nextMatches, matches, manifest, location, future, "assets"), [page, nextMatches, matches, manifest, location, future]); | ||
let moduleHrefs = React.useMemo(() => getModuleLinkHrefs(newMatchesForAssets, manifest), [newMatchesForAssets, manifest]); | ||
@@ -316,25 +355,9 @@ | ||
let keyedPrefetchLinks = useKeyedPrefetchLinks(newMatchesForAssets); | ||
let linksToRender = null; | ||
if (!future.unstable_singleFetch) { | ||
// Non-single-fetch prefetching | ||
linksToRender = dataHrefs.map(href => /*#__PURE__*/React.createElement("link", _extends({ | ||
key: href, | ||
rel: "prefetch", | ||
as: "fetch", | ||
href: href | ||
}, linkProps))); | ||
} else if (newMatchesForData.length > 0) { | ||
// Single-fetch with routes that require data | ||
let url = addRevalidationParam(manifest, routeModules, nextMatches.map(m => m.route), newMatchesForData.map(m => m.route), singleFetchUrl(page)); | ||
if (url.searchParams.get("_routes") !== "") { | ||
linksToRender = /*#__PURE__*/React.createElement("link", _extends({ | ||
key: url.pathname + url.search, | ||
rel: "prefetch", | ||
as: "fetch", | ||
href: url.pathname + url.search | ||
}, linkProps)); | ||
} | ||
} else ; | ||
return /*#__PURE__*/React.createElement(React.Fragment, null, linksToRender, moduleHrefs.map(href => /*#__PURE__*/React.createElement("link", _extends({ | ||
return /*#__PURE__*/React.createElement(React.Fragment, null, dataHrefs.map(href => /*#__PURE__*/React.createElement("link", _extends({ | ||
key: href, | ||
rel: "prefetch", | ||
as: "fetch", | ||
href: href | ||
}, linkProps))), moduleHrefs.map(href => /*#__PURE__*/React.createElement("link", _extends({ | ||
key: href, | ||
rel: "modulepreload", | ||
@@ -560,3 +583,3 @@ href: href | ||
var _manifest$hmr; | ||
let streamScript = future.unstable_singleFetch ? | ||
let streamScript = future.v3_singleFetch ? | ||
// prettier-ignore | ||
@@ -567,3 +590,3 @@ "window.__remixContext.stream = new ReadableStream({" + "start(controller){" + "window.__remixContext.streamController = controller;" + "}" + "}).pipeThrough(new TextEncoderStream());" : ""; | ||
// When single fetch is enabled, deferred is handled by turbo-stream | ||
let activeDeferreds = future.unstable_singleFetch ? undefined : staticContext === null || staticContext === void 0 ? void 0 : staticContext.activeDeferreds; | ||
let activeDeferreds = future.v3_singleFetch ? undefined : staticContext === null || staticContext === void 0 ? void 0 : staticContext.activeDeferreds; | ||
@@ -570,0 +593,0 @@ // This sets up the __remixContext with utility functions used by the |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -27,4 +27,5 @@ * Copyright (c) Remix Software Inc. | ||
"💿 Hey developer 👋. You can provide a way better UX than this " + | ||
"when your app is running \`clientLoader\` functions on hydration. " + | ||
"Check out https://remix.run/route/hydrate-fallback for more information." | ||
"when your app is loading JS modules and/or running \`clientLoader\` " + | ||
"functions. Check out https://remix.run/route/hydrate-fallback " + | ||
"for more information." | ||
); | ||
@@ -31,0 +32,0 @@ ` |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -27,3 +27,3 @@ * Copyright (c) Remix Software Inc. | ||
function isFogOfWarEnabled(future, isSpaMode) { | ||
return future.unstable_lazyRouteDiscovery === true && !isSpaMode; | ||
return future.v3_lazyRouteDiscovery === true && !isSpaMode; | ||
} | ||
@@ -30,0 +30,0 @@ function getPartialManifest(manifest, router) { |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -11,4 +11,4 @@ * Copyright (c) Remix Software Inc. | ||
*/ | ||
export { Navigate, NavigationType, Outlet, Route, Routes, createPath, createRoutesFromChildren, createRoutesFromElements, createSearchParams, generatePath, isRouteErrorResponse, matchPath, matchRoutes, parsePath, renderMatches, resolvePath, unstable_usePrompt, unstable_useViewTransitionState, useAsyncError, useAsyncValue, useBeforeUnload, useBlocker, useFetchers, useFormAction, useHref, useInRouterContext, useLinkClickHandler, useLocation, useMatch, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRoutes, useSearchParams, useSubmit } from 'react-router-dom'; | ||
export { defer, json, redirect, redirectDocument, replace, unstable_data } from '@remix-run/server-runtime'; | ||
export { Navigate, NavigationType, Outlet, Route, Routes, createPath, createRoutesFromChildren, createRoutesFromElements, createSearchParams, generatePath, isRouteErrorResponse, matchPath, matchRoutes, parsePath, renderMatches, resolvePath, unstable_usePrompt, useAsyncError, useAsyncValue, useBeforeUnload, useBlocker, useFetchers, useFormAction, useHref, useInRouterContext, useLinkClickHandler, useLocation, useMatch, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRoutes, useSearchParams, useSubmit, useViewTransitionState } from 'react-router-dom'; | ||
export { data, defer, json, redirect, redirectDocument, replace } from '@remix-run/server-runtime'; | ||
export { RemixBrowser } from './browser.js'; | ||
@@ -18,2 +18,1 @@ export { Await, Form, Link, Links, LiveReload, Meta, NavLink, PrefetchPageLinks, Scripts, RemixContext as UNSAFE_RemixContext, useActionData, useFetcher, useLoaderData, useMatches, useRouteLoaderData } from './components.js'; | ||
export { RemixServer } from './server.js'; | ||
export { defineClientAction as unstable_defineClientAction, defineClientLoader as unstable_defineClientLoader } from './single-fetch.js'; |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -120,3 +120,3 @@ * Copyright (c) Remix Software Inc. | ||
// This is ridiculously identical to transition.ts `filterMatchesToLoad` | ||
function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, location, mode) { | ||
function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, location, future, mode) { | ||
let path = parsePathPatch(page); | ||
@@ -140,3 +140,3 @@ let isNew = (match, index) => { | ||
// version doesn't care about submissions | ||
let newMatches = mode === "data" && location.search !== path.search ? | ||
let newMatches = mode === "data" && (future.v3_singleFetch || location.search !== path.search) ? | ||
// this is really similar to stuff in transition.ts, maybe somebody smarter | ||
@@ -152,2 +152,7 @@ // than me (or in less of a hurry) can share some of it. You're the best. | ||
} | ||
// For reused routes on GET navigations, by default: | ||
// - Single fetch always revalidates | ||
// - Multi fetch revalidates if search params changed | ||
let defaultShouldRevalidate = future.v3_singleFetch || location.search !== path.search; | ||
if (match.route.shouldRevalidate) { | ||
@@ -160,3 +165,3 @@ var _currentMatches$; | ||
nextParams: match.params, | ||
defaultShouldRevalidate: true | ||
defaultShouldRevalidate | ||
}); | ||
@@ -167,3 +172,3 @@ if (typeof routeChoice === "boolean") { | ||
} | ||
return true; | ||
return defaultShouldRevalidate; | ||
}) : nextMatches.filter((match, index) => { | ||
@@ -170,0 +175,0 @@ let manifestRoute = manifest.routes[match.route.id]; |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -174,3 +174,3 @@ * Copyright (c) Remix Software Inc. | ||
handle: routeModule.handle, | ||
shouldRevalidate: needsRevalidation ? wrapShouldRevalidateForHdr(route.id, routeModule.shouldRevalidate, needsRevalidation) : routeModule.shouldRevalidate | ||
shouldRevalidate: getShouldRevalidateFunction(future, routeModule, route.id, needsRevalidation) | ||
}); | ||
@@ -200,6 +200,9 @@ let initialData = initialState === null || initialState === void 0 ? void 0 : (_initialState$loaderD = initialState.loaderData) === null || _initialState$loaderD === void 0 ? void 0 : _initialState$loaderD[route.id]; | ||
if (isHydrationRequest) { | ||
if (initialData !== undefined) { | ||
return initialData; | ||
} | ||
if (initialError !== undefined) { | ||
throw initialError; | ||
} | ||
return initialData; | ||
return null; | ||
} | ||
@@ -293,5 +296,2 @@ | ||
} | ||
if (needsRevalidation) { | ||
lazyRoute.shouldRevalidate = wrapShouldRevalidateForHdr(route.id, mod.shouldRevalidate, needsRevalidation); | ||
} | ||
return { | ||
@@ -305,3 +305,3 @@ ...(lazyRoute.loader ? { | ||
hasErrorBoundary: lazyRoute.hasErrorBoundary, | ||
shouldRevalidate: lazyRoute.shouldRevalidate, | ||
shouldRevalidate: getShouldRevalidateFunction(future, lazyRoute, route.id, needsRevalidation), | ||
handle: lazyRoute.handle, | ||
@@ -320,3 +320,20 @@ // No need to wrap these in layout since the root route is never | ||
} | ||
function getShouldRevalidateFunction(future, route, routeId, needsRevalidation) { | ||
// During HDR we force revalidation for updated routes | ||
if (needsRevalidation) { | ||
return wrapShouldRevalidateForHdr(routeId, route.shouldRevalidate, needsRevalidation); | ||
} | ||
// Single fetch revalidates by default, so override the RR default value which | ||
// matches the multi-fetch behavior with `true` | ||
if (future.v3_singleFetch && route.shouldRevalidate) { | ||
let fn = route.shouldRevalidate; | ||
return opts => fn({ | ||
...opts, | ||
defaultShouldRevalidate: true | ||
}); | ||
} | ||
return route.shouldRevalidate; | ||
} | ||
// When an HMR / HDR update happens we opt out of all user-defined | ||
@@ -323,0 +340,0 @@ // revalidation logic and force a revalidation on the first call |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -84,3 +84,3 @@ * Copyright (c) Remix Software Inc. | ||
hydrate: false | ||
}))), context.future.unstable_singleFetch && context.serverHandoffStream ? /*#__PURE__*/React.createElement(React.Suspense, null, /*#__PURE__*/React.createElement(StreamTransfer, { | ||
}))), context.future.v3_singleFetch && context.serverHandoffStream ? /*#__PURE__*/React.createElement(React.Suspense, null, /*#__PURE__*/React.createElement(StreamTransfer, { | ||
context: context, | ||
@@ -87,0 +87,0 @@ identifier: 0, |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -12,3 +12,3 @@ * Copyright (c) Remix Software Inc. | ||
import * as React from 'react'; | ||
import { UNSAFE_ErrorResponseImpl, isRouteErrorResponse, unstable_data, redirect } from '@remix-run/router'; | ||
import { isRouteErrorResponse, data, UNSAFE_ErrorResponseImpl, redirect } from '@remix-run/router'; | ||
import { UNSAFE_SingleFetchRedirectSymbol } from '@remix-run/server-runtime'; | ||
@@ -20,9 +20,2 @@ import { decode } from 'turbo-stream'; | ||
// clientLoader | ||
let defineClientLoader = clientLoader => clientLoader; | ||
// clientAction | ||
let defineClientAction = clientAction => clientAction; | ||
// StreamTransfer recursively renders down chunks of the `serverHandoffStream` | ||
@@ -94,40 +87,56 @@ // into the client-side `streamController` | ||
} | ||
function getSingleFetchDataStrategy(manifest, routeModules) { | ||
function getSingleFetchDataStrategy(manifest, routeModules, getRouter) { | ||
return async ({ | ||
request, | ||
matches | ||
}) => request.method !== "GET" ? singleFetchActionStrategy(request, matches) : singleFetchLoaderStrategy(manifest, routeModules, request, matches); | ||
matches, | ||
fetcherKey | ||
}) => { | ||
// Actions are simple and behave the same for navigations and fetchers | ||
if (request.method !== "GET") { | ||
return singleFetchActionStrategy(request, matches); | ||
} | ||
// Fetcher loads are singular calls to one loader | ||
if (fetcherKey) { | ||
return singleFetchLoaderFetcherStrategy(request, matches); | ||
} | ||
// Navigational loads are more complex... | ||
return singleFetchLoaderNavigationStrategy(manifest, routeModules, getRouter(), request, matches); | ||
}; | ||
} | ||
// Actions are simple since they're singular calls to the server | ||
function singleFetchActionStrategy(request, matches) { | ||
return Promise.all(matches.map(async m => { | ||
let actionStatus; | ||
let result = await m.resolve(async handler => { | ||
let result = await handler(async () => { | ||
let url = singleFetchUrl(request.url); | ||
let init = await createRequestInit(request); | ||
let { | ||
data, | ||
status | ||
} = await fetchAndDecode(url, init); | ||
actionStatus = status; | ||
return unwrapSingleFetchResult(data, m.route.id); | ||
}); | ||
return { | ||
type: "data", | ||
result | ||
}; | ||
// Actions are simple since they're singular calls to the server for both | ||
// navigations and fetchers) | ||
async function singleFetchActionStrategy(request, matches) { | ||
let actionMatch = matches.find(m => m.shouldLoad); | ||
invariant(actionMatch, "No action match found"); | ||
let actionStatus = undefined; | ||
let result = await actionMatch.resolve(async handler => { | ||
let result = await handler(async () => { | ||
let url = singleFetchUrl(request.url); | ||
let init = await createRequestInit(request); | ||
let { | ||
data, | ||
status | ||
} = await fetchAndDecode(url, init); | ||
actionStatus = status; | ||
return unwrapSingleFetchResult(data, actionMatch.route.id); | ||
}); | ||
if (isResponse(result.result) || isRouteErrorResponse(result.result)) { | ||
return result; | ||
} | ||
return result; | ||
}); | ||
if (isResponse(result.result) || isRouteErrorResponse(result.result)) { | ||
return { | ||
[actionMatch.route.id]: result | ||
}; | ||
} | ||
// For non-responses, proxy along the statusCode via unstable_data() | ||
// (most notably for skipping action error revalidation) | ||
return { | ||
// For non-responses, proxy along the statusCode via data() | ||
// (most notably for skipping action error revalidation) | ||
return { | ||
[actionMatch.route.id]: { | ||
type: result.type, | ||
result: unstable_data(result.result, actionStatus) | ||
}; | ||
})); | ||
result: data(result.result, actionStatus) | ||
} | ||
}; | ||
} | ||
@@ -137,37 +146,144 @@ | ||
// create a singular promise for all server-loader routes to latch onto. | ||
function singleFetchLoaderStrategy(manifest, routeModules, request, matches) { | ||
let singleFetchPromise; | ||
return Promise.all(matches.map(async m => m.resolve(async handler => { | ||
let result; | ||
let url = stripIndexParam(singleFetchUrl(request.url)); | ||
let init = await createRequestInit(request); | ||
async function singleFetchLoaderNavigationStrategy(manifest, routeModules, router, request, matches) { | ||
// Track which routes need a server load - in case we need to tack on a | ||
// `_routes` param | ||
let routesParams = new Set(); | ||
// When a route has a client loader, it calls it's singular server loader | ||
// We only add `_routes` when one or more routes opts out of a load via | ||
// `shouldRevalidate` or `clientLoader` | ||
let foundOptOutRoute = false; | ||
// Deferreds for each route so we can be sure they've all loaded via | ||
// `match.resolve()`, and a singular promise that can tell us all routes | ||
// have been resolved | ||
let routeDfds = matches.map(() => createDeferred()); | ||
let routesLoadedPromise = Promise.all(routeDfds.map(d => d.promise)); | ||
// Deferred that we'll use for the call to the server that each match can | ||
// await and parse out it's specific result | ||
let singleFetchDfd = createDeferred(); | ||
// Base URL and RequestInit for calls to the server | ||
let url = stripIndexParam(singleFetchUrl(request.url)); | ||
let init = await createRequestInit(request); | ||
// We'll build up this results object as we loop through matches | ||
let results = {}; | ||
let resolvePromise = Promise.all(matches.map(async (m, i) => m.resolve(async handler => { | ||
routeDfds[i].resolve(); | ||
if (!m.shouldLoad) { | ||
var _routeModules$m$route; | ||
// If we're not yet initialized and this is the initial load, respect | ||
// `shouldLoad` because we're only dealing with `clientLoader.hydrate` | ||
// routes which will fall into the `clientLoader` section below. | ||
if (!router.state.initialized) { | ||
return; | ||
} | ||
// Otherwise, we opt out if we currently have data, a `loader`, and a | ||
// `shouldRevalidate` function. This implies that the user opted out | ||
// via `shouldRevalidate` | ||
if (m.route.id in router.state.loaderData && manifest.routes[m.route.id].hasLoader && (_routeModules$m$route = routeModules[m.route.id]) !== null && _routeModules$m$route !== void 0 && _routeModules$m$route.shouldRevalidate) { | ||
foundOptOutRoute = true; | ||
return; | ||
} | ||
} | ||
// When a route has a client loader, it opts out of the singular call and | ||
// calls it's server loader via `serverLoader()` using a `?_routes` param | ||
if (manifest.routes[m.route.id].hasClientLoader) { | ||
result = await handler(async () => { | ||
url.searchParams.set("_routes", m.route.id); | ||
let { | ||
data | ||
} = await fetchAndDecode(url, init); | ||
if (manifest.routes[m.route.id].hasLoader) { | ||
foundOptOutRoute = true; | ||
} | ||
try { | ||
let result = await fetchSingleLoader(handler, url, init, m.route.id); | ||
results[m.route.id] = { | ||
type: "data", | ||
result | ||
}; | ||
} catch (e) { | ||
results[m.route.id] = { | ||
type: "error", | ||
result: e | ||
}; | ||
} | ||
return; | ||
} | ||
// Load this route on the server if it has a loader | ||
if (manifest.routes[m.route.id].hasLoader) { | ||
routesParams.add(m.route.id); | ||
} | ||
// Lump this match in with the others on a singular promise | ||
try { | ||
let result = await handler(async () => { | ||
let data = await singleFetchDfd.promise; | ||
return unwrapSingleFetchResults(data, m.route.id); | ||
}); | ||
} else { | ||
result = await handler(async () => { | ||
// Otherwise we let multiple routes hook onto the same promise | ||
if (!singleFetchPromise) { | ||
url = addRevalidationParam(manifest, routeModules, matches.map(m => m.route), matches.filter(m => m.shouldLoad).map(m => m.route), url); | ||
singleFetchPromise = fetchAndDecode(url, init).then(({ | ||
data | ||
}) => data); | ||
} | ||
let results = await singleFetchPromise; | ||
return unwrapSingleFetchResults(results, m.route.id); | ||
}); | ||
results[m.route.id] = { | ||
type: "data", | ||
result | ||
}; | ||
} catch (e) { | ||
results[m.route.id] = { | ||
type: "error", | ||
result: e | ||
}; | ||
} | ||
return { | ||
type: "data", | ||
result | ||
}; | ||
}))); | ||
// Wait for all routes to resolve above before we make the HTTP call | ||
await routesLoadedPromise; | ||
// We can skip the server call: | ||
// - On initial hydration - only clientLoaders can pass through via `clientLoader.hydrate` | ||
// - If there are no routes to fetch from the server | ||
// | ||
// One exception - if we are performing an HDR revalidation we have to call | ||
// the server in case a new loader has shown up that the manifest doesn't yet | ||
// know about | ||
if ((!router.state.initialized || routesParams.size === 0) && !window.__remixHdrActive) { | ||
singleFetchDfd.resolve({}); | ||
} else { | ||
try { | ||
// When one or more routes have opted out, we add a _routes param to | ||
// limit the loaders to those that have a server loader and did not | ||
// opt out | ||
if (foundOptOutRoute && routesParams.size > 0) { | ||
url.searchParams.set("_routes", matches.filter(m => routesParams.has(m.route.id)).map(m => m.route.id).join(",")); | ||
} | ||
let data = await fetchAndDecode(url, init); | ||
singleFetchDfd.resolve(data.data); | ||
} catch (e) { | ||
singleFetchDfd.reject(e); | ||
} | ||
} | ||
await resolvePromise; | ||
return results; | ||
} | ||
// Fetcher loader calls are much simpler than navigational loader calls | ||
async function singleFetchLoaderFetcherStrategy(request, matches) { | ||
let fetcherMatch = matches.find(m => m.shouldLoad); | ||
invariant(fetcherMatch, "No fetcher match found"); | ||
let result = await fetcherMatch.resolve(async handler => { | ||
let url = stripIndexParam(singleFetchUrl(request.url)); | ||
let init = await createRequestInit(request); | ||
return fetchSingleLoader(handler, url, init, fetcherMatch.route.id); | ||
}); | ||
return { | ||
[fetcherMatch.route.id]: result | ||
}; | ||
} | ||
function fetchSingleLoader(handler, url, init, routeId) { | ||
return handler(async () => { | ||
let singleLoaderUrl = new URL(url); | ||
singleLoaderUrl.searchParams.set("_routes", routeId); | ||
let { | ||
data | ||
} = await fetchAndDecode(singleLoaderUrl, init); | ||
return unwrapSingleFetchResults(data, routeId); | ||
}); | ||
} | ||
function stripIndexParam(url) { | ||
@@ -187,42 +303,2 @@ let indexValues = url.searchParams.getAll("index"); | ||
} | ||
// Determine which routes we want to load so we can add a `?_routes` search param | ||
// for fine-grained revalidation if necessary. There's some nuance to this decision: | ||
// | ||
// - The presence of `shouldRevalidate` and `clientLoader` functions are the only | ||
// way to trigger fine-grained single fetch loader calls. without either of | ||
// these on the route matches we just always ask for the full `.data` request. | ||
// - If any routes have a `shouldRevalidate` or `clientLoader` then we do a | ||
// comparison of the routes we matched and the routes we're aiming to load | ||
// - If they don't match up, then we add the `_routes` param or fine-grained | ||
// loading | ||
// - This is used by the single fetch implementation above and by the | ||
// `<PrefetchPageLinksImpl>` component so we can prefetch routes using the | ||
// same logic | ||
function addRevalidationParam(manifest, routeModules, matchedRoutes, loadRoutes, url) { | ||
let genRouteIds = arr => arr.filter(id => manifest.routes[id].hasLoader).join(","); | ||
// Look at the `routeModules` for `shouldRevalidate` here instead of the manifest | ||
// since HDR adds a wrapper for `shouldRevalidate` even if the route didn't have one | ||
// initially. | ||
// TODO: We probably can get rid of that wrapper once we're strictly on on | ||
// single-fetch in v3 and just leverage a needsRevalidation data structure here | ||
// to determine what to fetch | ||
let needsParam = matchedRoutes.some(r => { | ||
var _routeModules$r$id, _manifest$routes$r$id; | ||
return ((_routeModules$r$id = routeModules[r.id]) === null || _routeModules$r$id === void 0 ? void 0 : _routeModules$r$id.shouldRevalidate) || ((_manifest$routes$r$id = manifest.routes[r.id]) === null || _manifest$routes$r$id === void 0 ? void 0 : _manifest$routes$r$id.hasClientLoader); | ||
}); | ||
if (!needsParam) { | ||
return url; | ||
} | ||
let matchedIds = genRouteIds(matchedRoutes.map(r => r.id)); | ||
let loadIds = genRouteIds(loadRoutes.filter(r => { | ||
var _manifest$routes$r$id2; | ||
return !((_manifest$routes$r$id2 = manifest.routes[r.id]) !== null && _manifest$routes$r$id2 !== void 0 && _manifest$routes$r$id2.hasClientLoader); | ||
}).map(r => r.id)); | ||
if (matchedIds !== loadIds) { | ||
url.searchParams.set("_routes", loadIds); | ||
} | ||
return url; | ||
} | ||
function singleFetchUrl(reqUrl) { | ||
@@ -291,2 +367,13 @@ let url = typeof reqUrl === "string" ? new URL(reqUrl, window.location.origin) : reqUrl; | ||
} | ||
}, (type, value) => { | ||
if (type === "SingleFetchFallback") { | ||
return { | ||
value: undefined | ||
}; | ||
} | ||
if (type === "SingleFetchClassInstance") { | ||
return { | ||
value | ||
}; | ||
} | ||
}] | ||
@@ -326,3 +413,28 @@ }); | ||
} | ||
function createDeferred() { | ||
let resolve; | ||
let reject; | ||
let promise = new Promise((res, rej) => { | ||
resolve = async val => { | ||
res(val); | ||
try { | ||
await promise; | ||
} catch (e) {} | ||
}; | ||
reject = async error => { | ||
rej(error); | ||
try { | ||
await promise; | ||
} catch (e) {} | ||
}; | ||
}); | ||
return { | ||
promise, | ||
//@ts-ignore | ||
resolve, | ||
//@ts-ignore | ||
reject | ||
}; | ||
} | ||
export { StreamTransfer, addRevalidationParam, decodeViaTurboStream, defineClientAction, defineClientLoader, getSingleFetchDataStrategy, singleFetchUrl }; | ||
export { StreamTransfer, decodeViaTurboStream, getSingleFetchDataStrategy, singleFetchUrl }; |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -51,4 +51,5 @@ * Copyright (c) Remix Software Inc. | ||
"💿 Hey developer 👋. You can provide a way better UX than this " + | ||
"when your app is running \`clientLoader\` functions on hydration. " + | ||
"Check out https://remix.run/route/hydrate-fallback for more information." | ||
"when your app is loading JS modules and/or running \`clientLoader\` " + | ||
"functions. Check out https://remix.run/route/hydrate-fallback " + | ||
"for more information." | ||
); | ||
@@ -55,0 +56,0 @@ ` |
import type { Router } from "@remix-run/router"; | ||
import type { unstable_PatchRoutesOnNavigationFunction } from "react-router"; | ||
import type { PatchRoutesOnNavigationFunction } from "react-router"; | ||
import type { AssetsManifest, FutureConfig } from "./entry"; | ||
@@ -26,4 +26,4 @@ import type { RouteModules } from "./routeModules"; | ||
}; | ||
export declare function getPatchRoutesOnNavigationFunction(manifest: AssetsManifest, routeModules: RouteModules, future: FutureConfig, isSpaMode: boolean, basename: string | undefined): unstable_PatchRoutesOnNavigationFunction | undefined; | ||
export declare function getPatchRoutesOnNavigationFunction(manifest: AssetsManifest, routeModules: RouteModules, future: FutureConfig, isSpaMode: boolean, basename: string | undefined): PatchRoutesOnNavigationFunction | undefined; | ||
export declare function useFogOFWarDiscovery(router: Router, manifest: AssetsManifest, routeModules: RouteModules, future: FutureConfig, isSpaMode: boolean): void; | ||
export declare function fetchAndApplyManifestPatches(paths: string[], manifest: AssetsManifest, routeModules: RouteModules, future: FutureConfig, isSpaMode: boolean, basename: string | undefined, patchRoutes: Router["patchRoutes"]): Promise<void>; |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -51,3 +51,3 @@ * Copyright (c) Remix Software Inc. | ||
function isFogOfWarEnabled(future, isSpaMode) { | ||
return future.unstable_lazyRouteDiscovery === true && !isSpaMode; | ||
return future.v3_lazyRouteDiscovery === true && !isSpaMode; | ||
} | ||
@@ -54,0 +54,0 @@ function getPartialManifest(manifest, router$1) { |
export type { ErrorResponse, Fetcher, FetcherWithComponents, FormEncType, FormMethod, Location, NavigateFunction, Navigation, Params, Path, ShouldRevalidateFunction, ShouldRevalidateFunctionArgs, SubmitFunction, SubmitOptions, Blocker, BlockerFunction, } from "react-router-dom"; | ||
export { createPath, createRoutesFromChildren, createRoutesFromElements, createSearchParams, generatePath, matchPath, matchRoutes, parsePath, renderMatches, resolvePath, Navigate, NavigationType, Outlet, Route, Routes, useAsyncError, useAsyncValue, isRouteErrorResponse, useBeforeUnload, useFetchers, useFormAction, useHref, useInRouterContext, useLinkClickHandler, useLocation, useMatch, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRoutes, useSearchParams, useSubmit, useBlocker, unstable_usePrompt, unstable_useViewTransitionState, } from "react-router-dom"; | ||
export { defer, json, redirect, redirectDocument, replace, unstable_data, } from "@remix-run/server-runtime"; | ||
export { createPath, createRoutesFromChildren, createRoutesFromElements, createSearchParams, generatePath, matchPath, matchRoutes, parsePath, renderMatches, resolvePath, Navigate, NavigationType, Outlet, Route, Routes, useAsyncError, useAsyncValue, isRouteErrorResponse, useBeforeUnload, useFetchers, useFormAction, useHref, useInRouterContext, useLinkClickHandler, useLocation, useMatch, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRoutes, useSearchParams, useSubmit, useBlocker, useViewTransitionState, unstable_usePrompt, } from "react-router-dom"; | ||
export { defer, json, redirect, redirectDocument, replace, data, } from "@remix-run/server-runtime"; | ||
export type { RemixBrowserProps } from "./browser"; | ||
@@ -13,5 +13,3 @@ export { RemixBrowser } from "./browser"; | ||
export { RemixServer } from "./server"; | ||
export type { ClientAction as unstable_ClientAction, ClientLoader as unstable_ClientLoader, } from "./single-fetch"; | ||
export { defineClientAction as unstable_defineClientAction, defineClientLoader as unstable_defineClientLoader, } from "./single-fetch"; | ||
export type { FutureConfig as UNSAFE_FutureConfig, AssetsManifest as UNSAFE_AssetsManifest, RemixContextObject as UNSAFE_RemixContextObject, } from "./entry"; | ||
export type { EntryRoute as UNSAFE_EntryRoute, RouteManifest as UNSAFE_RouteManifest, } from "./routes"; |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -21,3 +21,2 @@ * Copyright (c) Remix Software Inc. | ||
var server = require('./server.js'); | ||
var singleFetch = require('./single-fetch.js'); | ||
@@ -94,6 +93,2 @@ | ||
}); | ||
Object.defineProperty(exports, 'unstable_useViewTransitionState', { | ||
enumerable: true, | ||
get: function () { return reactRouterDom.unstable_useViewTransitionState; } | ||
}); | ||
Object.defineProperty(exports, 'useAsyncError', { | ||
@@ -191,2 +186,10 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, 'useViewTransitionState', { | ||
enumerable: true, | ||
get: function () { return reactRouterDom.useViewTransitionState; } | ||
}); | ||
Object.defineProperty(exports, 'data', { | ||
enumerable: true, | ||
get: function () { return serverRuntime.data; } | ||
}); | ||
Object.defineProperty(exports, 'defer', { | ||
@@ -212,6 +215,2 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, 'unstable_data', { | ||
enumerable: true, | ||
get: function () { return serverRuntime.unstable_data; } | ||
}); | ||
exports.RemixBrowser = browser.RemixBrowser; | ||
@@ -235,3 +234,1 @@ exports.Await = components.Await; | ||
exports.RemixServer = server.RemixServer; | ||
exports.unstable_defineClientAction = singleFetch.defineClientAction; | ||
exports.unstable_defineClientLoader = singleFetch.defineClientLoader; |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
import type { AgnosticDataRouteMatch } from "@remix-run/router"; | ||
import type { Location } from "react-router-dom"; | ||
import type { AssetsManifest } from "./entry"; | ||
import type { AssetsManifest, FutureConfig } from "./entry"; | ||
import type { RouteModules, RouteModule } from "./routeModules"; | ||
@@ -121,3 +121,3 @@ import type { EntryRoute } from "./routes"; | ||
export declare function getKeyedPrefetchLinks(matches: AgnosticDataRouteMatch[], manifest: AssetsManifest, routeModules: RouteModules): Promise<KeyedHtmlLinkDescriptor[]>; | ||
export declare function getNewMatchesForLinks(page: string, nextMatches: AgnosticDataRouteMatch[], currentMatches: AgnosticDataRouteMatch[], manifest: AssetsManifest, location: Location, mode: "data" | "assets"): AgnosticDataRouteMatch[]; | ||
export declare function getNewMatchesForLinks(page: string, nextMatches: AgnosticDataRouteMatch[], currentMatches: AgnosticDataRouteMatch[], manifest: AssetsManifest, location: Location, future: FutureConfig, mode: "data" | "assets"): AgnosticDataRouteMatch[]; | ||
export declare function getDataLinkHrefs(page: string, matches: AgnosticDataRouteMatch[], manifest: AssetsManifest): string[]; | ||
@@ -124,0 +124,0 @@ export declare function getModuleLinkHrefs(matches: AgnosticDataRouteMatch[], manifestPatch: AssetsManifest): string[]; |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -124,3 +124,3 @@ * Copyright (c) Remix Software Inc. | ||
// This is ridiculously identical to transition.ts `filterMatchesToLoad` | ||
function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, location, mode) { | ||
function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, location, future, mode) { | ||
let path = parsePathPatch(page); | ||
@@ -144,3 +144,3 @@ let isNew = (match, index) => { | ||
// version doesn't care about submissions | ||
let newMatches = mode === "data" && location.search !== path.search ? | ||
let newMatches = mode === "data" && (future.v3_singleFetch || location.search !== path.search) ? | ||
// this is really similar to stuff in transition.ts, maybe somebody smarter | ||
@@ -156,2 +156,7 @@ // than me (or in less of a hurry) can share some of it. You're the best. | ||
} | ||
// For reused routes on GET navigations, by default: | ||
// - Single fetch always revalidates | ||
// - Multi fetch revalidates if search params changed | ||
let defaultShouldRevalidate = future.v3_singleFetch || location.search !== path.search; | ||
if (match.route.shouldRevalidate) { | ||
@@ -164,3 +169,3 @@ var _currentMatches$; | ||
nextParams: match.params, | ||
defaultShouldRevalidate: true | ||
defaultShouldRevalidate | ||
}); | ||
@@ -171,3 +176,3 @@ if (typeof routeChoice === "boolean") { | ||
} | ||
return true; | ||
return defaultShouldRevalidate; | ||
}) : nextMatches.filter((match, index) => { | ||
@@ -174,0 +179,0 @@ let manifestRoute = manifest.routes[match.route.id]; |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -198,3 +198,3 @@ * Copyright (c) Remix Software Inc. | ||
handle: routeModule.handle, | ||
shouldRevalidate: needsRevalidation ? wrapShouldRevalidateForHdr(route.id, routeModule.shouldRevalidate, needsRevalidation) : routeModule.shouldRevalidate | ||
shouldRevalidate: getShouldRevalidateFunction(future, routeModule, route.id, needsRevalidation) | ||
}); | ||
@@ -224,6 +224,9 @@ let initialData = initialState === null || initialState === void 0 ? void 0 : (_initialState$loaderD = initialState.loaderData) === null || _initialState$loaderD === void 0 ? void 0 : _initialState$loaderD[route.id]; | ||
if (isHydrationRequest) { | ||
if (initialData !== undefined) { | ||
return initialData; | ||
} | ||
if (initialError !== undefined) { | ||
throw initialError; | ||
} | ||
return initialData; | ||
return null; | ||
} | ||
@@ -317,5 +320,2 @@ | ||
} | ||
if (needsRevalidation) { | ||
lazyRoute.shouldRevalidate = wrapShouldRevalidateForHdr(route.id, mod.shouldRevalidate, needsRevalidation); | ||
} | ||
return { | ||
@@ -329,3 +329,3 @@ ...(lazyRoute.loader ? { | ||
hasErrorBoundary: lazyRoute.hasErrorBoundary, | ||
shouldRevalidate: lazyRoute.shouldRevalidate, | ||
shouldRevalidate: getShouldRevalidateFunction(future, lazyRoute, route.id, needsRevalidation), | ||
handle: lazyRoute.handle, | ||
@@ -344,3 +344,20 @@ // No need to wrap these in layout since the root route is never | ||
} | ||
function getShouldRevalidateFunction(future, route, routeId, needsRevalidation) { | ||
// During HDR we force revalidation for updated routes | ||
if (needsRevalidation) { | ||
return wrapShouldRevalidateForHdr(routeId, route.shouldRevalidate, needsRevalidation); | ||
} | ||
// Single fetch revalidates by default, so override the RR default value which | ||
// matches the multi-fetch behavior with `true` | ||
if (future.v3_singleFetch && route.shouldRevalidate) { | ||
let fn = route.shouldRevalidate; | ||
return opts => fn({ | ||
...opts, | ||
defaultShouldRevalidate: true | ||
}); | ||
} | ||
return route.shouldRevalidate; | ||
} | ||
// When an HMR / HDR update happens we opt out of all user-defined | ||
@@ -347,0 +364,0 @@ // revalidation logic and force a revalidation on the first call |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -108,3 +108,3 @@ * Copyright (c) Remix Software Inc. | ||
hydrate: false | ||
}))), context.future.unstable_singleFetch && context.serverHandoffStream ? /*#__PURE__*/React__namespace.createElement(React__namespace.Suspense, null, /*#__PURE__*/React__namespace.createElement(singleFetch.StreamTransfer, { | ||
}))), context.future.v3_singleFetch && context.serverHandoffStream ? /*#__PURE__*/React__namespace.createElement(React__namespace.Suspense, null, /*#__PURE__*/React__namespace.createElement(singleFetch.StreamTransfer, { | ||
context: context, | ||
@@ -111,0 +111,0 @@ identifier: 0, |
import * as React from "react"; | ||
import type { ActionFunctionArgs as RRActionArgs, LoaderFunctionArgs as RRLoaderArgs, unstable_DataStrategyFunction as DataStrategyFunction } from "@remix-run/router"; | ||
import type { unstable_Action, unstable_Loader, unstable_Serialize } from "@remix-run/server-runtime"; | ||
import type { DataRouteObject } from "react-router-dom"; | ||
import type { DataStrategyFunction, Router as RemixRouter } from "@remix-run/router"; | ||
import type { AssetsManifest, EntryContext } from "./entry"; | ||
import type { RouteModules } from "./routeModules"; | ||
type ClientLoaderArgs = RRLoaderArgs<undefined> & { | ||
serverLoader: <T extends unstable_Loader>() => Promise<unstable_Serialize<T>>; | ||
}; | ||
export type ClientLoader = (args: ClientLoaderArgs) => unknown; | ||
export declare let defineClientLoader: <T extends ClientLoader>(clientLoader: T) => T & { | ||
hydrate?: boolean | undefined; | ||
}; | ||
type ClientActionArgs = RRActionArgs<undefined> & { | ||
serverAction: <T extends unstable_Action>() => Promise<unstable_Serialize<T>>; | ||
}; | ||
export type ClientAction = (args: ClientActionArgs) => unknown; | ||
export declare let defineClientAction: <T extends ClientAction>(clientAction: T) => T; | ||
import { type RouteModules } from "./routeModules"; | ||
interface StreamTransferProps { | ||
@@ -27,4 +13,3 @@ context: EntryContext; | ||
export declare function StreamTransfer({ context, identifier, reader, textDecoder, nonce, }: StreamTransferProps): React.JSX.Element | null; | ||
export declare function getSingleFetchDataStrategy(manifest: AssetsManifest, routeModules: RouteModules): DataStrategyFunction; | ||
export declare function addRevalidationParam(manifest: AssetsManifest, routeModules: RouteModules, matchedRoutes: DataRouteObject[], loadRoutes: DataRouteObject[], url: URL): URL; | ||
export declare function getSingleFetchDataStrategy(manifest: AssetsManifest, routeModules: RouteModules, getRouter: () => RemixRouter): DataStrategyFunction; | ||
export declare function singleFetchUrl(reqUrl: URL | string): URL; | ||
@@ -31,0 +16,0 @@ export declare function decodeViaTurboStream(body: ReadableStream<Uint8Array>, global: Window | typeof globalThis): Promise<{ |
/** | ||
* @remix-run/react v0.0.0-nightly-6f23bc960-20240821 | ||
* @remix-run/react v0.0.0-nightly-6f8288990-20241030 | ||
* | ||
@@ -43,9 +43,2 @@ * Copyright (c) Remix Software Inc. | ||
// clientLoader | ||
let defineClientLoader = clientLoader => clientLoader; | ||
// clientAction | ||
let defineClientAction = clientAction => clientAction; | ||
// StreamTransfer recursively renders down chunks of the `serverHandoffStream` | ||
@@ -117,40 +110,56 @@ // into the client-side `streamController` | ||
} | ||
function getSingleFetchDataStrategy(manifest, routeModules) { | ||
function getSingleFetchDataStrategy(manifest, routeModules, getRouter) { | ||
return async ({ | ||
request, | ||
matches | ||
}) => request.method !== "GET" ? singleFetchActionStrategy(request, matches) : singleFetchLoaderStrategy(manifest, routeModules, request, matches); | ||
matches, | ||
fetcherKey | ||
}) => { | ||
// Actions are simple and behave the same for navigations and fetchers | ||
if (request.method !== "GET") { | ||
return singleFetchActionStrategy(request, matches); | ||
} | ||
// Fetcher loads are singular calls to one loader | ||
if (fetcherKey) { | ||
return singleFetchLoaderFetcherStrategy(request, matches); | ||
} | ||
// Navigational loads are more complex... | ||
return singleFetchLoaderNavigationStrategy(manifest, routeModules, getRouter(), request, matches); | ||
}; | ||
} | ||
// Actions are simple since they're singular calls to the server | ||
function singleFetchActionStrategy(request, matches) { | ||
return Promise.all(matches.map(async m => { | ||
let actionStatus; | ||
let result = await m.resolve(async handler => { | ||
let result = await handler(async () => { | ||
let url = singleFetchUrl(request.url); | ||
let init = await data.createRequestInit(request); | ||
let { | ||
data: data$1, | ||
status | ||
} = await fetchAndDecode(url, init); | ||
actionStatus = status; | ||
return unwrapSingleFetchResult(data$1, m.route.id); | ||
}); | ||
return { | ||
type: "data", | ||
result | ||
}; | ||
// Actions are simple since they're singular calls to the server for both | ||
// navigations and fetchers) | ||
async function singleFetchActionStrategy(request, matches) { | ||
let actionMatch = matches.find(m => m.shouldLoad); | ||
invariant(actionMatch, "No action match found"); | ||
let actionStatus = undefined; | ||
let result = await actionMatch.resolve(async handler => { | ||
let result = await handler(async () => { | ||
let url = singleFetchUrl(request.url); | ||
let init = await data.createRequestInit(request); | ||
let { | ||
data: data$1, | ||
status | ||
} = await fetchAndDecode(url, init); | ||
actionStatus = status; | ||
return unwrapSingleFetchResult(data$1, actionMatch.route.id); | ||
}); | ||
if (data.isResponse(result.result) || router.isRouteErrorResponse(result.result)) { | ||
return result; | ||
} | ||
return result; | ||
}); | ||
if (data.isResponse(result.result) || router.isRouteErrorResponse(result.result)) { | ||
return { | ||
[actionMatch.route.id]: result | ||
}; | ||
} | ||
// For non-responses, proxy along the statusCode via unstable_data() | ||
// (most notably for skipping action error revalidation) | ||
return { | ||
// For non-responses, proxy along the statusCode via data() | ||
// (most notably for skipping action error revalidation) | ||
return { | ||
[actionMatch.route.id]: { | ||
type: result.type, | ||
result: router.unstable_data(result.result, actionStatus) | ||
}; | ||
})); | ||
result: router.data(result.result, actionStatus) | ||
} | ||
}; | ||
} | ||
@@ -160,37 +169,144 @@ | ||
// create a singular promise for all server-loader routes to latch onto. | ||
function singleFetchLoaderStrategy(manifest, routeModules, request, matches) { | ||
let singleFetchPromise; | ||
return Promise.all(matches.map(async m => m.resolve(async handler => { | ||
let result; | ||
let url = stripIndexParam(singleFetchUrl(request.url)); | ||
let init = await data.createRequestInit(request); | ||
async function singleFetchLoaderNavigationStrategy(manifest, routeModules, router, request, matches) { | ||
// Track which routes need a server load - in case we need to tack on a | ||
// `_routes` param | ||
let routesParams = new Set(); | ||
// When a route has a client loader, it calls it's singular server loader | ||
// We only add `_routes` when one or more routes opts out of a load via | ||
// `shouldRevalidate` or `clientLoader` | ||
let foundOptOutRoute = false; | ||
// Deferreds for each route so we can be sure they've all loaded via | ||
// `match.resolve()`, and a singular promise that can tell us all routes | ||
// have been resolved | ||
let routeDfds = matches.map(() => createDeferred()); | ||
let routesLoadedPromise = Promise.all(routeDfds.map(d => d.promise)); | ||
// Deferred that we'll use for the call to the server that each match can | ||
// await and parse out it's specific result | ||
let singleFetchDfd = createDeferred(); | ||
// Base URL and RequestInit for calls to the server | ||
let url = stripIndexParam(singleFetchUrl(request.url)); | ||
let init = await data.createRequestInit(request); | ||
// We'll build up this results object as we loop through matches | ||
let results = {}; | ||
let resolvePromise = Promise.all(matches.map(async (m, i) => m.resolve(async handler => { | ||
routeDfds[i].resolve(); | ||
if (!m.shouldLoad) { | ||
var _routeModules$m$route; | ||
// If we're not yet initialized and this is the initial load, respect | ||
// `shouldLoad` because we're only dealing with `clientLoader.hydrate` | ||
// routes which will fall into the `clientLoader` section below. | ||
if (!router.state.initialized) { | ||
return; | ||
} | ||
// Otherwise, we opt out if we currently have data, a `loader`, and a | ||
// `shouldRevalidate` function. This implies that the user opted out | ||
// via `shouldRevalidate` | ||
if (m.route.id in router.state.loaderData && manifest.routes[m.route.id].hasLoader && (_routeModules$m$route = routeModules[m.route.id]) !== null && _routeModules$m$route !== void 0 && _routeModules$m$route.shouldRevalidate) { | ||
foundOptOutRoute = true; | ||
return; | ||
} | ||
} | ||
// When a route has a client loader, it opts out of the singular call and | ||
// calls it's server loader via `serverLoader()` using a `?_routes` param | ||
if (manifest.routes[m.route.id].hasClientLoader) { | ||
result = await handler(async () => { | ||
url.searchParams.set("_routes", m.route.id); | ||
let { | ||
data | ||
} = await fetchAndDecode(url, init); | ||
if (manifest.routes[m.route.id].hasLoader) { | ||
foundOptOutRoute = true; | ||
} | ||
try { | ||
let result = await fetchSingleLoader(handler, url, init, m.route.id); | ||
results[m.route.id] = { | ||
type: "data", | ||
result | ||
}; | ||
} catch (e) { | ||
results[m.route.id] = { | ||
type: "error", | ||
result: e | ||
}; | ||
} | ||
return; | ||
} | ||
// Load this route on the server if it has a loader | ||
if (manifest.routes[m.route.id].hasLoader) { | ||
routesParams.add(m.route.id); | ||
} | ||
// Lump this match in with the others on a singular promise | ||
try { | ||
let result = await handler(async () => { | ||
let data = await singleFetchDfd.promise; | ||
return unwrapSingleFetchResults(data, m.route.id); | ||
}); | ||
} else { | ||
result = await handler(async () => { | ||
// Otherwise we let multiple routes hook onto the same promise | ||
if (!singleFetchPromise) { | ||
url = addRevalidationParam(manifest, routeModules, matches.map(m => m.route), matches.filter(m => m.shouldLoad).map(m => m.route), url); | ||
singleFetchPromise = fetchAndDecode(url, init).then(({ | ||
data | ||
}) => data); | ||
} | ||
let results = await singleFetchPromise; | ||
return unwrapSingleFetchResults(results, m.route.id); | ||
}); | ||
results[m.route.id] = { | ||
type: "data", | ||
result | ||
}; | ||
} catch (e) { | ||
results[m.route.id] = { | ||
type: "error", | ||
result: e | ||
}; | ||
} | ||
return { | ||
type: "data", | ||
result | ||
}; | ||
}))); | ||
// Wait for all routes to resolve above before we make the HTTP call | ||
await routesLoadedPromise; | ||
// We can skip the server call: | ||
// - On initial hydration - only clientLoaders can pass through via `clientLoader.hydrate` | ||
// - If there are no routes to fetch from the server | ||
// | ||
// One exception - if we are performing an HDR revalidation we have to call | ||
// the server in case a new loader has shown up that the manifest doesn't yet | ||
// know about | ||
if ((!router.state.initialized || routesParams.size === 0) && !window.__remixHdrActive) { | ||
singleFetchDfd.resolve({}); | ||
} else { | ||
try { | ||
// When one or more routes have opted out, we add a _routes param to | ||
// limit the loaders to those that have a server loader and did not | ||
// opt out | ||
if (foundOptOutRoute && routesParams.size > 0) { | ||
url.searchParams.set("_routes", matches.filter(m => routesParams.has(m.route.id)).map(m => m.route.id).join(",")); | ||
} | ||
let data = await fetchAndDecode(url, init); | ||
singleFetchDfd.resolve(data.data); | ||
} catch (e) { | ||
singleFetchDfd.reject(e); | ||
} | ||
} | ||
await resolvePromise; | ||
return results; | ||
} | ||
// Fetcher loader calls are much simpler than navigational loader calls | ||
async function singleFetchLoaderFetcherStrategy(request, matches) { | ||
let fetcherMatch = matches.find(m => m.shouldLoad); | ||
invariant(fetcherMatch, "No fetcher match found"); | ||
let result = await fetcherMatch.resolve(async handler => { | ||
let url = stripIndexParam(singleFetchUrl(request.url)); | ||
let init = await data.createRequestInit(request); | ||
return fetchSingleLoader(handler, url, init, fetcherMatch.route.id); | ||
}); | ||
return { | ||
[fetcherMatch.route.id]: result | ||
}; | ||
} | ||
function fetchSingleLoader(handler, url, init, routeId) { | ||
return handler(async () => { | ||
let singleLoaderUrl = new URL(url); | ||
singleLoaderUrl.searchParams.set("_routes", routeId); | ||
let { | ||
data | ||
} = await fetchAndDecode(singleLoaderUrl, init); | ||
return unwrapSingleFetchResults(data, routeId); | ||
}); | ||
} | ||
function stripIndexParam(url) { | ||
@@ -210,42 +326,2 @@ let indexValues = url.searchParams.getAll("index"); | ||
} | ||
// Determine which routes we want to load so we can add a `?_routes` search param | ||
// for fine-grained revalidation if necessary. There's some nuance to this decision: | ||
// | ||
// - The presence of `shouldRevalidate` and `clientLoader` functions are the only | ||
// way to trigger fine-grained single fetch loader calls. without either of | ||
// these on the route matches we just always ask for the full `.data` request. | ||
// - If any routes have a `shouldRevalidate` or `clientLoader` then we do a | ||
// comparison of the routes we matched and the routes we're aiming to load | ||
// - If they don't match up, then we add the `_routes` param or fine-grained | ||
// loading | ||
// - This is used by the single fetch implementation above and by the | ||
// `<PrefetchPageLinksImpl>` component so we can prefetch routes using the | ||
// same logic | ||
function addRevalidationParam(manifest, routeModules, matchedRoutes, loadRoutes, url) { | ||
let genRouteIds = arr => arr.filter(id => manifest.routes[id].hasLoader).join(","); | ||
// Look at the `routeModules` for `shouldRevalidate` here instead of the manifest | ||
// since HDR adds a wrapper for `shouldRevalidate` even if the route didn't have one | ||
// initially. | ||
// TODO: We probably can get rid of that wrapper once we're strictly on on | ||
// single-fetch in v3 and just leverage a needsRevalidation data structure here | ||
// to determine what to fetch | ||
let needsParam = matchedRoutes.some(r => { | ||
var _routeModules$r$id, _manifest$routes$r$id; | ||
return ((_routeModules$r$id = routeModules[r.id]) === null || _routeModules$r$id === void 0 ? void 0 : _routeModules$r$id.shouldRevalidate) || ((_manifest$routes$r$id = manifest.routes[r.id]) === null || _manifest$routes$r$id === void 0 ? void 0 : _manifest$routes$r$id.hasClientLoader); | ||
}); | ||
if (!needsParam) { | ||
return url; | ||
} | ||
let matchedIds = genRouteIds(matchedRoutes.map(r => r.id)); | ||
let loadIds = genRouteIds(loadRoutes.filter(r => { | ||
var _manifest$routes$r$id2; | ||
return !((_manifest$routes$r$id2 = manifest.routes[r.id]) !== null && _manifest$routes$r$id2 !== void 0 && _manifest$routes$r$id2.hasClientLoader); | ||
}).map(r => r.id)); | ||
if (matchedIds !== loadIds) { | ||
url.searchParams.set("_routes", loadIds); | ||
} | ||
return url; | ||
} | ||
function singleFetchUrl(reqUrl) { | ||
@@ -314,2 +390,13 @@ let url = typeof reqUrl === "string" ? new URL(reqUrl, window.location.origin) : reqUrl; | ||
} | ||
}, (type, value) => { | ||
if (type === "SingleFetchFallback") { | ||
return { | ||
value: undefined | ||
}; | ||
} | ||
if (type === "SingleFetchClassInstance") { | ||
return { | ||
value | ||
}; | ||
} | ||
}] | ||
@@ -349,9 +436,31 @@ }); | ||
} | ||
function createDeferred() { | ||
let resolve; | ||
let reject; | ||
let promise = new Promise((res, rej) => { | ||
resolve = async val => { | ||
res(val); | ||
try { | ||
await promise; | ||
} catch (e) {} | ||
}; | ||
reject = async error => { | ||
rej(error); | ||
try { | ||
await promise; | ||
} catch (e) {} | ||
}; | ||
}); | ||
return { | ||
promise, | ||
//@ts-ignore | ||
resolve, | ||
//@ts-ignore | ||
reject | ||
}; | ||
} | ||
exports.StreamTransfer = StreamTransfer; | ||
exports.addRevalidationParam = addRevalidationParam; | ||
exports.decodeViaTurboStream = decodeViaTurboStream; | ||
exports.defineClientAction = defineClientAction; | ||
exports.defineClientLoader = defineClientLoader; | ||
exports.getSingleFetchDataStrategy = getSingleFetchDataStrategy; | ||
exports.singleFetchUrl = singleFetchUrl; |
{ | ||
"name": "@remix-run/react", | ||
"version": "0.0.0-nightly-6f23bc960-20240821", | ||
"version": "0.0.0-nightly-6f8288990-20241030", | ||
"description": "React DOM bindings for Remix", | ||
@@ -19,11 +19,11 @@ "bugs": { | ||
"dependencies": { | ||
"@remix-run/router": "1.19.1", | ||
"@remix-run/server-runtime": "0.0.0-nightly-6f23bc960-20240821", | ||
"react-router": "6.26.1", | ||
"react-router-dom": "6.26.1", | ||
"turbo-stream": "2.3.0" | ||
"@remix-run/router": "1.21.0-pre.0", | ||
"@remix-run/server-runtime": "0.0.0-nightly-6f8288990-20241030", | ||
"react-router": "6.28.0-pre.0", | ||
"react-router-dom": "6.28.0-pre.0", | ||
"turbo-stream": "2.4.0" | ||
}, | ||
"devDependencies": { | ||
"@remix-run/node": "0.0.0-nightly-6f23bc960-20240821", | ||
"@remix-run/react": "0.0.0-nightly-6f23bc960-20240821", | ||
"@remix-run/node": "0.0.0-nightly-6f8288990-20241030", | ||
"@remix-run/react": "0.0.0-nightly-6f8288990-20241030", | ||
"@testing-library/jest-dom": "^5.17.0", | ||
@@ -30,0 +30,0 @@ "@testing-library/react": "^13.3.0", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
303234
7601
55
+ Added@remix-run/router@1.21.0-pre.0(transitive)
+ Added@remix-run/server-runtime@0.0.0-nightly-6f8288990-20241030(transitive)
+ Addedreact-router@6.28.0-pre.0(transitive)
+ Addedreact-router-dom@6.28.0-pre.0(transitive)
+ Addedturbo-stream@2.4.0(transitive)
- Removed@remix-run/router@1.19.1(transitive)
- Removed@remix-run/server-runtime@0.0.0-nightly-6f23bc960-20240821(transitive)
- Removedreact-router@6.26.1(transitive)
- Removedreact-router-dom@6.26.1(transitive)
- Removedturbo-stream@2.3.0(transitive)
Updated@remix-run/server-runtime@0.0.0-nightly-6f8288990-20241030
Updatedreact-router@6.28.0-pre.0
Updatedturbo-stream@2.4.0