@tanstack/react-router 0.0.1-beta.273 → 0.0.1-beta.275
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/build/cjs/Matches.js.map +1 -1
- package/build/cjs/RouterProvider.js +9 -6
- package/build/cjs/RouterProvider.js.map +1 -1
- package/build/cjs/fileRoute.js.map +1 -1
- package/build/cjs/index.js +4 -1
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/redirects.js.map +1 -1
- package/build/cjs/route.js.map +1 -1
- package/build/cjs/router.js +163 -105
- package/build/cjs/router.js.map +1 -1
- package/build/esm/index.js +170 -108
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +353 -353
- package/build/types/Matches.d.ts +4 -3
- package/build/types/RouterProvider.d.ts +1 -1
- package/build/types/fileRoute.d.ts +2 -2
- package/build/types/redirects.d.ts +1 -1
- package/build/types/route.d.ts +24 -24
- package/build/types/routeInfo.d.ts +1 -1
- package/build/types/router.d.ts +3 -1
- package/build/umd/index.development.js +172 -111
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +1 -1
- package/build/umd/index.production.js.map +1 -1
- package/package.json +2 -2
- package/src/Matches.tsx +4 -3
- package/src/RouterProvider.tsx +11 -5
- package/src/fileRoute.ts +3 -0
- package/src/redirects.ts +1 -1
- package/src/route.ts +31 -20
- package/src/routeInfo.ts +2 -0
- package/src/router.ts +211 -133
package/build/esm/index.js
CHANGED
|
@@ -472,9 +472,13 @@ function useLoaderData(opts) {
|
|
|
472
472
|
});
|
|
473
473
|
}
|
|
474
474
|
|
|
475
|
-
|
|
475
|
+
let routerContext = /*#__PURE__*/React.createContext(null);
|
|
476
476
|
if (typeof document !== 'undefined') {
|
|
477
|
-
window.__TSR_ROUTER_CONTEXT__
|
|
477
|
+
if (window.__TSR_ROUTER_CONTEXT__) {
|
|
478
|
+
routerContext = window.__TSR_ROUTER_CONTEXT__;
|
|
479
|
+
} else {
|
|
480
|
+
window.__TSR_ROUTER_CONTEXT__ = routerContext;
|
|
481
|
+
}
|
|
478
482
|
}
|
|
479
483
|
function RouterProvider({
|
|
480
484
|
router,
|
|
@@ -584,7 +588,7 @@ function Transitioner() {
|
|
|
584
588
|
return null;
|
|
585
589
|
}
|
|
586
590
|
function getRouteMatch(state, id) {
|
|
587
|
-
return [...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
|
|
591
|
+
return [...state.preloadMatches, ...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
|
|
588
592
|
}
|
|
589
593
|
function useRouterState(opts) {
|
|
590
594
|
const router = useRouter();
|
|
@@ -1653,11 +1657,18 @@ class Router {
|
|
|
1653
1657
|
return [parentSearch, searchError];
|
|
1654
1658
|
}
|
|
1655
1659
|
})();
|
|
1660
|
+
|
|
1661
|
+
// This is where we need to call route.options.loaderDeps() to get any additional
|
|
1662
|
+
// deps that the route's loader function might need to run. We need to do this
|
|
1663
|
+
// before we create the match so that we can pass the deps to the route's
|
|
1664
|
+
// potential key function which is used to uniquely identify the route match in state
|
|
1665
|
+
|
|
1666
|
+
const loaderDeps = route.options.loaderDeps?.({
|
|
1667
|
+
search: preMatchSearch
|
|
1668
|
+
}) ?? '';
|
|
1669
|
+
const loaderDepsHash = loaderDeps ? JSON.stringify(loaderDeps) : '';
|
|
1656
1670
|
const interpolatedPath = interpolatePath(route.fullPath, routeParams);
|
|
1657
|
-
const matchId = interpolatePath(route.id, routeParams, true) +
|
|
1658
|
-
search: preMatchSearch,
|
|
1659
|
-
location: this.state.location
|
|
1660
|
-
}) ?? '');
|
|
1671
|
+
const matchId = interpolatePath(route.id, routeParams, true) + loaderDepsHash;
|
|
1661
1672
|
|
|
1662
1673
|
// Waste not, want not. If we already have a match for this route,
|
|
1663
1674
|
// reuse it. This is important for layout routes, which might stick
|
|
@@ -1688,8 +1699,9 @@ class Router {
|
|
|
1688
1699
|
context: undefined,
|
|
1689
1700
|
abortController: new AbortController(),
|
|
1690
1701
|
shouldReloadDeps: undefined,
|
|
1691
|
-
|
|
1692
|
-
cause
|
|
1702
|
+
fetchCount: 0,
|
|
1703
|
+
cause,
|
|
1704
|
+
loaderDeps
|
|
1693
1705
|
};
|
|
1694
1706
|
|
|
1695
1707
|
// Regardless of whether we're reusing an existing match or creating
|
|
@@ -1897,10 +1909,13 @@ class Router {
|
|
|
1897
1909
|
}) => {
|
|
1898
1910
|
let latestPromise;
|
|
1899
1911
|
let firstBadMatchIndex;
|
|
1900
|
-
const
|
|
1912
|
+
const updateMatch = match => {
|
|
1913
|
+
const isPreload = this.state.preloadMatches.find(d => d.id === match.id);
|
|
1914
|
+
const isPending = this.state.pendingMatches?.find(d => d.id === match.id);
|
|
1915
|
+
const matchesKey = isPreload ? 'preloadMatches' : isPending ? 'pendingMatches' : 'matches';
|
|
1901
1916
|
this.__store.setState(s => ({
|
|
1902
1917
|
...s,
|
|
1903
|
-
|
|
1918
|
+
[matchesKey]: s[matchesKey]?.map(d => d.id === match.id ? match : d)
|
|
1904
1919
|
}));
|
|
1905
1920
|
};
|
|
1906
1921
|
|
|
@@ -1953,7 +1968,7 @@ class Router {
|
|
|
1953
1968
|
from: match.pathname
|
|
1954
1969
|
}),
|
|
1955
1970
|
buildLocation: this.buildLocation,
|
|
1956
|
-
cause: match.cause
|
|
1971
|
+
cause: preload ? 'preload' : match.cause
|
|
1957
1972
|
})) ?? {};
|
|
1958
1973
|
if (isRedirect(beforeLoadContext)) {
|
|
1959
1974
|
throw beforeLoadContext;
|
|
@@ -1983,7 +1998,7 @@ class Router {
|
|
|
1983
1998
|
const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
|
|
1984
1999
|
const matchPromises = [];
|
|
1985
2000
|
validResolvedMatches.forEach((match, index) => {
|
|
1986
|
-
matchPromises.push((async
|
|
2001
|
+
matchPromises.push(new Promise(async resolve => {
|
|
1987
2002
|
const parentMatchPromise = matchPromises[index - 1];
|
|
1988
2003
|
const route = this.looseRoutesById[match.routeId];
|
|
1989
2004
|
const handleErrorAndRedirect = err => {
|
|
@@ -1998,92 +2013,95 @@ class Router {
|
|
|
1998
2013
|
let loadPromise;
|
|
1999
2014
|
matches[index] = match = {
|
|
2000
2015
|
...match,
|
|
2001
|
-
fetchedAt: Date.now(),
|
|
2002
2016
|
showPending: false
|
|
2003
2017
|
};
|
|
2018
|
+
let didShowPending = false;
|
|
2004
2019
|
const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
};
|
|
2020
|
+
const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
|
|
2021
|
+
const shouldPending = !preload && pendingMs && (route.options.pendingComponent ?? this.options.defaultPendingComponent);
|
|
2022
|
+
const fetch = async () => {
|
|
2023
|
+
if (match.isFetching) {
|
|
2024
|
+
loadPromise = getRouteMatch(this.state, match.id)?.loadPromise;
|
|
2025
|
+
} else {
|
|
2026
|
+
const loaderContext = {
|
|
2027
|
+
params: match.params,
|
|
2028
|
+
deps: match.loaderDeps,
|
|
2029
|
+
preload: !!preload,
|
|
2030
|
+
parentMatchPromise,
|
|
2031
|
+
abortController: match.abortController,
|
|
2032
|
+
context: match.context,
|
|
2033
|
+
location: this.state.location,
|
|
2034
|
+
navigate: opts => this.navigate({
|
|
2035
|
+
...opts,
|
|
2036
|
+
from: match.pathname
|
|
2037
|
+
}),
|
|
2038
|
+
cause: preload ? 'preload' : match.cause
|
|
2039
|
+
};
|
|
2026
2040
|
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2041
|
+
// Default to reloading the route all the time
|
|
2042
|
+
let shouldLoad = true;
|
|
2043
|
+
const shouldReloadFn = route.options.shouldReload;
|
|
2044
|
+
let shouldReloadDeps = typeof shouldReloadFn === 'function' ? shouldReloadFn(loaderContext) : !!(shouldReloadFn ?? true);
|
|
2045
|
+
const compareDeps = () => {
|
|
2046
|
+
if (typeof shouldReloadDeps === 'object') {
|
|
2047
|
+
// compare the deps to see if they've changed
|
|
2048
|
+
shouldLoad = !deepEqual(shouldReloadDeps, match.shouldReloadDeps);
|
|
2049
|
+
} else {
|
|
2050
|
+
shouldLoad = !!shouldReloadDeps;
|
|
2051
|
+
}
|
|
2052
|
+
};
|
|
2053
|
+
|
|
2054
|
+
// If it's the first preload, or the route is entering, or we're
|
|
2055
|
+
// invalidating, we definitely need to load the route
|
|
2056
|
+
if (invalidate) ; else if (preload) {
|
|
2057
|
+
if (!match.fetchCount) ; else {
|
|
2058
|
+
compareDeps();
|
|
2059
|
+
}
|
|
2060
|
+
} else if (match.cause === 'enter') {
|
|
2061
|
+
if (!match.fetchCount) ; else {
|
|
2062
|
+
compareDeps();
|
|
2063
|
+
}
|
|
2037
2064
|
} else {
|
|
2038
|
-
|
|
2065
|
+
compareDeps();
|
|
2066
|
+
}
|
|
2067
|
+
if (typeof shouldReloadDeps === 'object') {
|
|
2068
|
+
matches[index] = match = {
|
|
2069
|
+
...match,
|
|
2070
|
+
shouldReloadDeps
|
|
2071
|
+
};
|
|
2039
2072
|
}
|
|
2040
|
-
}
|
|
2041
2073
|
|
|
2042
|
-
|
|
2043
|
-
|
|
2074
|
+
// If the user doesn't want the route to reload, just
|
|
2075
|
+
// resolve with the existing loader data
|
|
2044
2076
|
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
...match,
|
|
2051
|
-
isFetching: true
|
|
2052
|
-
};
|
|
2053
|
-
const componentsPromise = Promise.all(componentTypes.map(async type => {
|
|
2054
|
-
const component = route.options[type];
|
|
2055
|
-
if (component?.preload) {
|
|
2056
|
-
await component.preload();
|
|
2077
|
+
if (!shouldLoad) {
|
|
2078
|
+
loadPromise = Promise.resolve(match.loaderData);
|
|
2079
|
+
} else {
|
|
2080
|
+
if (match.fetchCount && match.status === 'success') {
|
|
2081
|
+
resolve();
|
|
2057
2082
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
loadPromise = Promise.all([componentsPromise, loaderPromise]).then(d => d[1]);
|
|
2061
|
-
}
|
|
2062
|
-
}
|
|
2063
|
-
matches[index] = match = {
|
|
2064
|
-
...match,
|
|
2065
|
-
loadPromise
|
|
2066
|
-
};
|
|
2067
|
-
if (!preload) {
|
|
2068
|
-
updatePendingMatch(match);
|
|
2069
|
-
}
|
|
2070
|
-
let didShowPending = false;
|
|
2071
|
-
const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
|
|
2072
|
-
await new Promise(async resolve => {
|
|
2073
|
-
// If the route has a pending component and a pendingMs option,
|
|
2074
|
-
// forcefully show the pending component
|
|
2075
|
-
if (pendingPromise) {
|
|
2076
|
-
pendingPromise.then(() => {
|
|
2077
|
-
if (latestPromise = checkLatest()) return;
|
|
2078
|
-
didShowPending = true;
|
|
2083
|
+
|
|
2084
|
+
// Otherwise, load the route
|
|
2079
2085
|
matches[index] = match = {
|
|
2080
2086
|
...match,
|
|
2081
|
-
|
|
2087
|
+
isFetching: true,
|
|
2088
|
+
fetchCount: match.fetchCount + 1
|
|
2082
2089
|
};
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2090
|
+
const componentsPromise = Promise.all(componentTypes.map(async type => {
|
|
2091
|
+
const component = route.options[type];
|
|
2092
|
+
if (component?.preload) {
|
|
2093
|
+
await component.preload();
|
|
2094
|
+
}
|
|
2095
|
+
}));
|
|
2096
|
+
const loaderPromise = route.options.loader?.(loaderContext);
|
|
2097
|
+
loadPromise = Promise.all([componentsPromise, loaderPromise]).then(d => d[1]);
|
|
2098
|
+
}
|
|
2086
2099
|
}
|
|
2100
|
+
matches[index] = match = {
|
|
2101
|
+
...match,
|
|
2102
|
+
loadPromise
|
|
2103
|
+
};
|
|
2104
|
+
updateMatch(match);
|
|
2087
2105
|
try {
|
|
2088
2106
|
const loaderData = await loadPromise;
|
|
2089
2107
|
if (latestPromise = checkLatest()) return await latestPromise;
|
|
@@ -2124,18 +2142,38 @@ class Router {
|
|
|
2124
2142
|
// we already moved the pendingMatches to the matches
|
|
2125
2143
|
// state, so we need to update that specific match
|
|
2126
2144
|
if (didShowPending && pendingMinMs && match.showPending) {
|
|
2127
|
-
|
|
2128
|
-
...s,
|
|
2129
|
-
matches: s.matches?.map(d => d.id === match.id ? match : d)
|
|
2130
|
-
}));
|
|
2145
|
+
updateMatch(match);
|
|
2131
2146
|
}
|
|
2132
2147
|
}
|
|
2133
|
-
|
|
2134
|
-
|
|
2148
|
+
updateMatch(match);
|
|
2149
|
+
};
|
|
2150
|
+
if (match.fetchCount && match.status === 'success') {
|
|
2151
|
+
// Background Fetching
|
|
2152
|
+
fetch();
|
|
2153
|
+
} else {
|
|
2154
|
+
// Critical Fetching
|
|
2155
|
+
|
|
2156
|
+
// If we need to potentially show the pending component,
|
|
2157
|
+
// start a timer to show it after the pendingMs
|
|
2158
|
+
if (shouldPending) {
|
|
2159
|
+
new Promise(r => setTimeout(r, pendingMs)).then(async () => {
|
|
2160
|
+
if (latestPromise = checkLatest()) return latestPromise;
|
|
2161
|
+
didShowPending = true;
|
|
2162
|
+
matches[index] = match = {
|
|
2163
|
+
...match,
|
|
2164
|
+
showPending: true
|
|
2165
|
+
};
|
|
2166
|
+
updateMatch(match);
|
|
2167
|
+
resolve();
|
|
2168
|
+
});
|
|
2135
2169
|
}
|
|
2136
|
-
|
|
2137
|
-
}
|
|
2138
|
-
|
|
2170
|
+
await fetch();
|
|
2171
|
+
}
|
|
2172
|
+
resolve();
|
|
2173
|
+
// No Fetching
|
|
2174
|
+
|
|
2175
|
+
resolve();
|
|
2176
|
+
}));
|
|
2139
2177
|
});
|
|
2140
2178
|
await Promise.all(matchPromises);
|
|
2141
2179
|
return matches;
|
|
@@ -2158,20 +2196,32 @@ class Router {
|
|
|
2158
2196
|
toLocation: next,
|
|
2159
2197
|
pathChanged: pathDidChange
|
|
2160
2198
|
});
|
|
2161
|
-
|
|
2162
|
-
// Match the routes
|
|
2163
|
-
let pendingMatches = this.matchRoutes(next.pathname, next.search, {
|
|
2164
|
-
debug: true
|
|
2165
|
-
});
|
|
2199
|
+
let pendingMatches;
|
|
2166
2200
|
const previousMatches = this.state.matches;
|
|
2201
|
+
this.__store.batch(() => {
|
|
2202
|
+
this.__store.setState(s => ({
|
|
2203
|
+
...s,
|
|
2204
|
+
preloadMatches: s.preloadMatches.filter(d => {
|
|
2205
|
+
return Date.now() - d.updatedAt < (this.options.defaultPreloadMaxAge ?? 3000);
|
|
2206
|
+
})
|
|
2207
|
+
}));
|
|
2167
2208
|
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2209
|
+
// Match the routes
|
|
2210
|
+
pendingMatches = this.matchRoutes(next.pathname, next.search, {
|
|
2211
|
+
debug: true
|
|
2212
|
+
});
|
|
2213
|
+
|
|
2214
|
+
// Ingest the new matches
|
|
2215
|
+
this.__store.setState(s => ({
|
|
2216
|
+
...s,
|
|
2217
|
+
isLoading: true,
|
|
2218
|
+
location: next,
|
|
2219
|
+
pendingMatches,
|
|
2220
|
+
preloadMatches: s.preloadMatches.filter(d => {
|
|
2221
|
+
return !pendingMatches.find(e => e.id === d.id);
|
|
2222
|
+
})
|
|
2223
|
+
}));
|
|
2224
|
+
});
|
|
2175
2225
|
try {
|
|
2176
2226
|
try {
|
|
2177
2227
|
// Load the matches
|
|
@@ -2229,6 +2279,17 @@ class Router {
|
|
|
2229
2279
|
let matches = this.matchRoutes(next.pathname, next.search, {
|
|
2230
2280
|
throwOnError: true
|
|
2231
2281
|
});
|
|
2282
|
+
const loadedMatchIds = Object.fromEntries([...this.state.matches, ...(this.state.pendingMatches ?? []), ...this.state.preloadMatches]?.map(d => [d.id, true]));
|
|
2283
|
+
this.__store.batch(() => {
|
|
2284
|
+
matches.forEach(match => {
|
|
2285
|
+
if (!loadedMatchIds[match.id]) {
|
|
2286
|
+
this.__store.setState(s => ({
|
|
2287
|
+
...s,
|
|
2288
|
+
preloadMatches: [...s.preloadMatches, match]
|
|
2289
|
+
}));
|
|
2290
|
+
}
|
|
2291
|
+
});
|
|
2292
|
+
});
|
|
2232
2293
|
matches = await this.loadMatches({
|
|
2233
2294
|
matches,
|
|
2234
2295
|
preload: true,
|
|
@@ -2291,7 +2352,7 @@ class Router {
|
|
|
2291
2352
|
dehydrate = () => {
|
|
2292
2353
|
return {
|
|
2293
2354
|
state: {
|
|
2294
|
-
dehydratedMatches: this.state.matches.map(d => pick(d, ['
|
|
2355
|
+
dehydratedMatches: this.state.matches.map(d => pick(d, ['id', 'status', 'updatedAt', 'loaderData']))
|
|
2295
2356
|
}
|
|
2296
2357
|
};
|
|
2297
2358
|
};
|
|
@@ -2354,6 +2415,7 @@ function getInitialRouterState(location) {
|
|
|
2354
2415
|
location,
|
|
2355
2416
|
matches: [],
|
|
2356
2417
|
pendingMatches: [],
|
|
2418
|
+
preloadMatches: [],
|
|
2357
2419
|
lastUpdated: Date.now()
|
|
2358
2420
|
};
|
|
2359
2421
|
}
|