@tanstack/react-router 1.64.1 → 1.64.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.64.1",
3
+ "version": "1.64.3",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -48,6 +48,7 @@ export type BuildLocationFn = <
48
48
  >(
49
49
  opts: ToOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & {
50
50
  leaveParams?: boolean
51
+ _includeValidateSearch?: boolean
51
52
  },
52
53
  ) => ParsedLocation
53
54
 
package/src/link.tsx CHANGED
@@ -649,32 +649,48 @@ export function useLinkProps<
649
649
 
650
650
  const isActive = useRouterState({
651
651
  select: (s) => {
652
- // Compare path/hash for matches
653
- const currentPathSplit = removeTrailingSlash(
654
- s.location.pathname,
655
- router.basepath,
656
- ).split('/')
657
- const nextPathSplit = removeTrailingSlash(
658
- next.pathname,
659
- router.basepath,
660
- ).split('/')
661
- const pathIsFuzzyEqual = nextPathSplit.every(
662
- (d, i) => d === currentPathSplit[i],
663
- )
664
- // Combine the matches based on user router.options
665
- const pathTest = activeOptions?.exact
666
- ? exactPathTest(s.location.pathname, next.pathname, router.basepath)
667
- : pathIsFuzzyEqual
668
- const hashTest = activeOptions?.includeHash
669
- ? s.location.hash === next.hash
670
- : true
671
- const searchTest =
672
- (activeOptions?.includeSearch ?? true)
673
- ? deepEqual(s.location.search, next.search, !activeOptions?.exact)
674
- : true
675
-
676
- // The final "active" test
677
- return pathTest && hashTest && searchTest
652
+ if (activeOptions?.exact) {
653
+ const testExact = exactPathTest(
654
+ s.location.pathname,
655
+ next.pathname,
656
+ router.basepath,
657
+ )
658
+ if (!testExact) {
659
+ return false
660
+ }
661
+ } else {
662
+ const currentPathSplit = removeTrailingSlash(
663
+ s.location.pathname,
664
+ router.basepath,
665
+ ).split('/')
666
+ const nextPathSplit = removeTrailingSlash(
667
+ next.pathname,
668
+ router.basepath,
669
+ ).split('/')
670
+
671
+ const pathIsFuzzyEqual = nextPathSplit.every(
672
+ (d, i) => d === currentPathSplit[i],
673
+ )
674
+ if (!pathIsFuzzyEqual) {
675
+ return false
676
+ }
677
+ }
678
+
679
+ if (activeOptions?.includeSearch ?? true) {
680
+ const searchTest = deepEqual(
681
+ s.location.search,
682
+ next.search,
683
+ !activeOptions?.exact,
684
+ )
685
+ if (!searchTest) {
686
+ return false
687
+ }
688
+ }
689
+
690
+ if (activeOptions?.includeHash) {
691
+ return s.location.hash === next.hash
692
+ }
693
+ return true
678
694
  },
679
695
  })
680
696
 
@@ -741,8 +757,9 @@ export function useLinkProps<
741
757
  })
742
758
 
743
759
  // All is well? Navigate!
744
- router.commitLocation({
745
- ...next,
760
+ // N.B. we don't call `router.commitLocation(next) here because we want to run `validateSearch` before committing
761
+ router.buildAndCommitLocation({
762
+ ...options,
746
763
  replace,
747
764
  resetScroll,
748
765
  startTransition,
@@ -975,7 +992,7 @@ export const Link: LinkComponent<'a'> = React.forwardRef<Element, any>(
975
992
  : rest.children
976
993
 
977
994
  if (typeof _asChild === 'undefined') {
978
- // the ReturnType of useLinkProps returns the correct type for a <a> element, not a general component that has a delete prop
995
+ // the ReturnType of useLinkProps returns the correct type for a <a> element, not a general component that has a disabled prop
979
996
  // @ts-expect-error
980
997
  delete linkProps.disabled
981
998
  }
package/src/router.ts CHANGED
@@ -1460,8 +1460,24 @@ export class Router<
1460
1460
  ? postSearchFilters.reduce((prev, next) => next(prev), destSearch)
1461
1461
  : destSearch
1462
1462
 
1463
- const search = replaceEqualDeep(fromSearch, postFilteredSearch)
1463
+ let search = postFilteredSearch
1464
1464
 
1465
+ if (opts._includeValidateSearch) {
1466
+ matchedRoutesResult?.matchedRoutes.forEach((route) => {
1467
+ try {
1468
+ if (route.options.validateSearch) {
1469
+ const validator =
1470
+ typeof route.options.validateSearch === 'object'
1471
+ ? route.options.validateSearch.parse
1472
+ : route.options.validateSearch
1473
+ search = { ...search, ...validator(search) }
1474
+ }
1475
+ } catch (e) {
1476
+ // ignore errors here because they are already handled in matchRoutes
1477
+ }
1478
+ })
1479
+ }
1480
+ search = replaceEqualDeep(fromSearch, search)
1465
1481
  const searchStr = this.options.stringifySearch(search)
1466
1482
 
1467
1483
  const hash =
@@ -1644,7 +1660,10 @@ export class Router<
1644
1660
  rest.hash = parsed.hash
1645
1661
  }
1646
1662
 
1647
- const location = this.buildLocation(rest as any)
1663
+ const location = this.buildLocation({
1664
+ ...(rest as any),
1665
+ _includeValidateSearch: true,
1666
+ })
1648
1667
  return this.commitLocation({
1649
1668
  ...location,
1650
1669
  viewTransition,