diff --git a/bun.lock b/bun.lock index c6bae598..58ffe7d8 100644 --- a/bun.lock +++ b/bun.lock @@ -97,7 +97,7 @@ "@react-navigation/native": "7.3.13", "@testing-library/react-native": "13.3.3", "@types/react": "~19.2.17", - "expo-router": "56.2.11", + "expo-router": "58.0.17", "react-native-builder-bob": "0.41.0", "react-test-renderer": "19.2.3", "typescript": "catalog:", @@ -105,6 +105,7 @@ "peerDependencies": { "@react-navigation/native": ">=7.3.0", "@types/react": "*", + "expo-router": ">=56.0.0", "react": ">=19.2.0", "react-native": "*", "react-native-gesture-handler": ">=2.16.1", @@ -114,6 +115,7 @@ "react-native-worklets": ">=0.8.0", }, "optionalPeers": [ + "expo-router", "react-native-teleport", ], }, @@ -532,6 +534,8 @@ "@expo/log-box": ["@expo/log-box@56.0.14", "", { "dependencies": { "@expo/dom-webview": "^56.0.6", "anser": "^1.4.9", "stacktrace-parser": "^0.1.10" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-3Eadcxz0J2NESG2iKe8DnAggsRCWqY0mBLhgQPNzClPVjS6o4NyD0F8kuOvWNngFwpJBC08HhY2o8P1mgSgeJg=="], + "@expo/log-box-utils": ["@expo/log-box-utils@58.0.2", "", {}, "sha512-My8Lwe9FU/yDwwzqwi5LN2rzzDcJqIAZKrD9f0XHZEKz/FQnWJYdt1rsV7+RwqMGeGq2XsxTCVT1ggTCOvumxw=="], + "@expo/metro": ["@expo/metro@56.0.0", "", { "dependencies": { "metro": "0.84.4", "metro-babel-transformer": "0.84.4", "metro-cache": "0.84.4", "metro-cache-key": "0.84.4", "metro-config": "0.84.4", "metro-core": "0.84.4", "metro-file-map": "0.84.4", "metro-minify-terser": "0.84.4", "metro-resolver": "0.84.4", "metro-runtime": "0.84.4", "metro-source-map": "0.84.4", "metro-symbolicate": "0.84.4", "metro-transform-plugins": "0.84.4", "metro-transform-worker": "0.84.4" } }, "sha512-5gIgQHtEpjjvsjKfVtIv23a98LLRV0/y07PDShEwYSytAMlE3FSF8RHXqtHc1sUJL6dn7hnuIBpIbrLXXuVi0A=="], "@expo/metro-config": ["@expo/metro-config@56.0.18", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "@babel/core": "^7.20.0", "@babel/generator": "^7.20.5", "@expo/config": "~56.0.13", "@expo/env": "~2.3.1", "@expo/json-file": "~10.2.0", "@expo/metro": "~56.0.0", "@expo/require-utils": "^56.1.6", "@expo/spawn-async": "^1.8.0", "@jridgewell/gen-mapping": "^0.3.13", "@jridgewell/remapping": "^2.3.5", "@jridgewell/sourcemap-codec": "^1.5.5", "browserslist": "^4.25.0", "chalk": "^4.1.0", "debug": "^4.3.2", "getenv": "^2.0.0", "glob": "^13.0.0", "hermes-parser": "^0.33.3", "jsc-safe-url": "^0.2.4", "lightningcss": "^1.30.1", "picomatch": "^4.0.4", "postcss": "^8.5.14", "resolve-from": "^5.0.0" }, "peerDependencies": { "expo": "*" }, "optionalPeers": ["expo"] }, "sha512-X7ephejQE7YjL4401ObdfpluL0enEmLK4ZjMLJrhtkFh0ksZI7jQMvUHxLXZzCHD0SlMXa/RdB9abnWsOj1E5A=="], @@ -3820,7 +3824,7 @@ "react-native-screen-transitions/@react-navigation/native": ["@react-navigation/native@7.3.13", "", { "dependencies": { "@react-navigation/core": "^7.21.10", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "standard-navigation": "^0.0.8", "use-latest-callback": "^0.2.4" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*" } }, "sha512-eiNUieNCJxCdHSVBUJWwoACspqlUGULHj36Z8CQRlymwPxOD8fXA23vXAqdWJ6VT3MmVh7akLLPvs9aKMArgxg=="], - "react-native-screen-transitions/expo-router": ["expo-router@56.2.11", "", { "dependencies": { "@expo/log-box": "^56.0.13", "@expo/metro-runtime": "^56.0.15", "@expo/schema-utils": "^56.0.0", "@expo/ui": "^56.0.18", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tabs": "^1.1.12", "@react-native-masked-view/masked-view": "^0.3.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "client-only": "^0.0.1", "color": "^4.2.3", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "expo-glass-effect": "^56.0.4", "expo-server": "^56.0.5", "expo-symbols": "^56.0.6", "fast-deep-equal": "^3.1.3", "invariant": "^2.2.4", "nanoid": "^3.3.8", "query-string": "^7.1.3", "react-fast-compare": "^3.2.2", "react-is": "^19.1.0", "react-native-drawer-layout": "^4.2.2", "react-native-screens": "^4.25.2", "server-only": "^0.0.1", "sf-symbols-typescript": "^2.1.0", "shallowequal": "^1.1.0", "standard-navigation": "^0.0.5", "vaul": "^1.1.2" }, "peerDependencies": { "@testing-library/react-native": ">= 13.2.0", "expo": "*", "expo-constants": "^56.0.18", "expo-linking": "^56.0.14", "react": "*", "react-dom": "*", "react-native": "*", "react-native-gesture-handler": "*", "react-native-reanimated": "*", "react-native-safe-area-context": ">= 5.4.0", "react-native-web": "*", "react-server-dom-webpack": "~19.0.4 || ~19.1.5 || ~19.2.4" }, "optionalPeers": ["@testing-library/react-native", "react-dom", "react-native-gesture-handler", "react-native-reanimated", "react-native-web", "react-server-dom-webpack"] }, "sha512-08DBTrKv3QanOc9u1JNxSEChW9c/qNFbQ0dO28OLvufWWfdSRkSdHmh365D2FgoZg1qaOzZPCDuL3tM6nGSfkQ=="], + "react-native-screen-transitions/expo-router": ["expo-router@58.0.17", "", { "dependencies": { "@expo/log-box": "^58.0.12", "@expo/metro-runtime": "^58.0.14", "@expo/schema-utils": "^58.0.1", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tabs": "^1.1.12", "@react-native-masked-view/masked-view": "^0.3.2", "@react-native/normalize-colors": "0.88.0-rc.4", "client-only": "^0.0.1", "expo-glass-effect": "^58.0.3", "expo-server": "^58.0.4", "nanoid": "^3.3.18", "react-fast-compare": "^3.2.2", "react-is": "^19.1.0", "react-native-drawer-layout": "^4.2.2", "react-native-screens": "^4.28.0 || ^5.0.0-alpha.3", "server-only": "^0.0.1", "sf-symbols-typescript": "^2.1.0", "standard-navigation": "^0.0.5" }, "peerDependencies": { "@expo/ui": "^58.0.15", "@testing-library/react-native": ">= 14.0.0", "expo": "*", "expo-constants": "^58.0.10", "expo-linking": "^58.0.11", "expo-symbols": "^58.0.4", "react": "*", "react-dom": "*", "react-native": "*", "react-native-gesture-handler": "*", "react-native-reanimated": "*", "react-native-safe-area-context": ">= 5.4.0", "react-native-web": "*", "react-native-worklets": "*", "react-server-dom-webpack": "~19.0.4 || ~19.1.5 || ~19.2.4 || ~19.3.0" }, "optionalPeers": ["@expo/ui", "@testing-library/react-native", "expo-symbols", "react-dom", "react-native-gesture-handler", "react-native-reanimated", "react-native-web", "react-native-worklets", "react-server-dom-webpack"] }, "sha512-KvkTndmwFpSFzyh8STq58pkFxjpOQZYf09V3EHcxadmHn/cX73Es0HCYyJiWjUJ4W1mmWmm6VXOf57qmdD/rjQ=="], "react-native-web/@react-native/normalize-colors": ["@react-native/normalize-colors@0.74.89", "", {}, "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg=="], @@ -4160,7 +4164,19 @@ "react-native-screen-transitions/@react-navigation/native/standard-navigation": ["standard-navigation@0.0.8", "", { "peerDependencies": { "react": "*" } }, "sha512-TyVbo7INUDWtsUWDFn8RR7kwR87U0S4xHfLfbbnyeC581TmmyqQ+eM+nPw8rQTSD8QitRVcYfPaSHr/QJiUy1g=="], - "react-native-screen-transitions/expo-router/react-native-screens": ["react-native-screens@4.27.0", "", { "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-IUukKYilhKhdJqGh59BmxrAjgIO84CaOkdowEY20+RmapOAFpqS//xOIcq2iqDQ4xVoxuwCHGWhY/Tbz6lTblA=="], + "react-native-screen-transitions/expo-router/@expo/log-box": ["@expo/log-box@58.0.12", "", { "dependencies": { "@expo/dom-webview": "^58.0.2", "@expo/log-box-utils": "^58.0.2", "anser": "^1.4.9", "stacktrace-parser": "^0.1.10" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" }, "optionalPeers": ["expo"] }, "sha512-IzDYoFY2iUsmSDvl1V7YUM7UkCPDyMVS4e4a38c2LnFc1A3WGMUiQh+vsHLYTPKjIelu4r7/u4V7Gxtfa0oJgA=="], + + "react-native-screen-transitions/expo-router/@expo/metro-runtime": ["@expo/metro-runtime@58.0.14", "", { "dependencies": { "@expo/log-box": "^58.0.12", "anser": "^1.4.9", "pretty-format": "^29.7.0", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-dom": "*", "react-native": "*" }, "optionalPeers": ["react-dom"] }, "sha512-IIvTo+RpN5jZVuYspQdECsxQ03VGWjZnGS6wtr9U7DERBpykUe+ZSHDZ1hsrvQ7HAucsVoKr9np0xAscGEuXtg=="], + + "react-native-screen-transitions/expo-router/@expo/schema-utils": ["@expo/schema-utils@58.0.1", "", {}, "sha512-r5ox9st0g1IYC37tKD0WYiKH6RXout9FqjSwVv0nIhRYYTXhChJfzcMnJ90zkBznKO1lZVCqdO302YVYsm3qQA=="], + + "react-native-screen-transitions/expo-router/@react-native/normalize-colors": ["@react-native/normalize-colors@0.88.0-rc.4", "", {}, "sha512-MDD2OXJwccKSB7ceVFw+ZFf1lomWrQODbwsKYwAhuf8mTiOArAea1c2fRJ9zpPp9IZmi3O3wSfGRBKqWcBCNDA=="], + + "react-native-screen-transitions/expo-router/expo-glass-effect": ["expo-glass-effect@58.0.3", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-NoFT5j2uZecNsiDAd47z37PWYTxNJnJMrVYiAU+SggiV9K91Sc0IuIcMsuOM4sUuPBQdSTs8/I9KXk5M0Z2O/w=="], + + "react-native-screen-transitions/expo-router/expo-server": ["expo-server@58.0.4", "", {}, "sha512-MWUNrC2Gs4+JS2KUeI87GH9NMWyBt0kjBYX7lbrL3zps8iEKu2ogVhtSXO2X/I46m9LQ/mTUrfrKKWLomUghTA=="], + + "react-native-screen-transitions/expo-router/react-native-screens": ["react-native-screens@4.29.0", "", { "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-zDZtvt0pU/eIWCLHwA48OMaC+jIfz7HJI0Kbdiywgh9xBY8rjV00zFlD6KIBxlmDFOzLrF0XvUwN+Xtnu/4V5Q=="], "react-native-screen-transitions/expo-router/standard-navigation": ["standard-navigation@0.0.5", "", {}, "sha512-YAmzwAiiQVocZxO/VGPFiQHcu5pKiz09QIGC0MK6aRMoa3E0QkoTQgcqJr7ZZ3OMiNhu4DkaGElFI5htjOIDbw=="], @@ -4250,6 +4266,8 @@ "react-native-builder-bob/yargs/cliui/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + "react-native-screen-transitions/expo-router/@expo/log-box/@expo/dom-webview": ["@expo/dom-webview@58.0.2", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-bLkREoP7qL3rEuzxP60xG+TjdPoGjbnZfxkvYo5imJxGWfKMOf7x962W06Pu+WWAr4gcDRk16iA+Clg8D0zjCg=="], + "read-pkg/normalize-package-data/hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "readdir-glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], diff --git a/packages/react-native-screen-transitions/package.json b/packages/react-native-screen-transitions/package.json index 2a5a7823..6d6ba59f 100644 --- a/packages/react-native-screen-transitions/package.json +++ b/packages/react-native-screen-transitions/package.json @@ -70,6 +70,7 @@ "peerDependencies": { "@react-navigation/native": ">=7.3.0", "@types/react": "*", + "expo-router": ">=56.0.0", "react": ">=19.2.0", "react-native": "*", "react-native-gesture-handler": ">=2.16.1", @@ -79,6 +80,9 @@ "react-native-worklets": ">=0.8.0" }, "peerDependenciesMeta": { + "expo-router": { + "optional": true + }, "react-native-teleport": { "optional": true } @@ -90,7 +94,7 @@ "@react-navigation/native": "7.3.13", "@testing-library/react-native": "13.3.3", "@types/react": "~19.2.17", - "expo-router": "56.2.11", + "expo-router": "58.0.17", "react-native-builder-bob": "0.41.0", "react-test-renderer": "19.2.3", "typescript": "catalog:" diff --git a/packages/react-native-screen-transitions/src/__tests__/create-blank-stack-props.test.ts b/packages/react-native-screen-transitions/src/__tests__/create-blank-stack-props.test.ts new file mode 100644 index 00000000..32dea56c --- /dev/null +++ b/packages/react-native-screen-transitions/src/__tests__/create-blank-stack-props.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, it } from "bun:test"; +import { createBlankStackProps } from "../navigators/expo-router/create-blank-stack-props"; + +const route = (key: string) => ({ key, name: key }); + +const navigation = { + getState: () => ({ key: "stack", index: 0, routes: [] }), + dispatch: () => {}, +}; + +describe("createBlankStackProps", () => { + it("passes the state through when no routes follow the focused route", () => { + const state = { key: "stack", index: 1, routes: [route("a"), route("b")] }; + + const props = createBlankStackProps({ state, navigation }); + + expect(props.navigationState).toBe(state); + expect(props.navigation).toBe(navigation); + }); + + it("drops preloaded routes that follow the focused route", () => { + const a = route("a"); + const b = route("b"); + const state = { + key: "stack", + index: 1, + routes: [a, b, { ...route("preloaded"), isPreloaded: true }], + }; + + const props = createBlankStackProps({ state, navigation }); + + expect(props.navigationState).toEqual({ + key: "stack", + index: 1, + routes: [a, b], + }); + }); +}); diff --git a/packages/react-native-screen-transitions/src/__tests__/get-stack-routes.test.ts b/packages/react-native-screen-transitions/src/__tests__/get-stack-routes.test.ts new file mode 100644 index 00000000..1bcea068 --- /dev/null +++ b/packages/react-native-screen-transitions/src/__tests__/get-stack-routes.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from "bun:test"; +import { getStackRoutes } from "../utils/navigation/get-stack-routes"; + +const route = (key: string) => ({ key, name: key }); + +describe("getStackRoutes", () => { + it("reads preloaded routes from `preloadedRoutes`", () => { + const routes = [route("a"), route("b")]; + const preloadedRoutes = [route("preloaded")]; + + const result = getStackRoutes({ index: 1, routes, preloadedRoutes }); + + expect(result.routes).toBe(routes); + expect(result.preloadedRoutes).toBe(preloadedRoutes); + }); + + it("returns no preloaded routes when the state has none", () => { + const routes = [route("a")]; + + expect(getStackRoutes({ index: 0, routes })).toEqual({ + routes, + preloadedRoutes: [], + }); + }); + + it("splits preloaded routes that follow the focused route", () => { + const a = route("a"); + const b = route("b"); + const preloaded = { ...route("preloaded"), isPreloaded: true }; + + const result = getStackRoutes({ index: 1, routes: [a, b, preloaded] }); + + expect(result.routes).toEqual([a, b]); + expect(result.preloadedRoutes).toEqual([preloaded]); + }); +}); diff --git a/packages/react-native-screen-transitions/src/adapters/with-screen-transitions/stack-layout.tsx b/packages/react-native-screen-transitions/src/adapters/with-screen-transitions/stack-layout.tsx index 32fde198..4888346b 100644 --- a/packages/react-native-screen-transitions/src/adapters/with-screen-transitions/stack-layout.tsx +++ b/packages/react-native-screen-transitions/src/adapters/with-screen-transitions/stack-layout.tsx @@ -1,10 +1,11 @@ -import type { NavigationState, Route } from "@react-navigation/native"; +import type { NavigationState } from "@react-navigation/native"; import { useMemo } from "react"; import { Overlay } from "../../components/overlay"; import { ScreenComposer } from "../../providers/screen/screen-composer"; import { BlankStackStoreProvider } from "../../providers/stack/blank-stack.provider"; import { StackCoreProvider } from "../../providers/stack/core.provider"; import type { BaseStackDescriptor, BaseStackRoute } from "../../types"; +import { getStackRoutes } from "../../utils/navigation/get-stack-routes"; import { isOverlayVisible } from "../../utils/overlay/visibility"; import { ScreenTransitionsAdapterProvider, @@ -24,10 +25,6 @@ import type { type DescriptorMap = Record; -type NavigationStateWithPreloads = NavigationState & { - preloadedRoutes?: Route[]; -}; - type TransitionStackState = { routes: BaseStackRoute[]; routeKeys: string[]; @@ -36,15 +33,6 @@ type TransitionStackState = { shouldShowFloatOverlay: boolean; }; -const EMPTY_PRELOADED_ROUTES: Route[] = []; - -function getPreloadedRoutes(state: NavigationState): Route[] { - return ( - (state as NavigationStateWithPreloads).preloadedRoutes ?? - EMPTY_PRELOADED_ROUTES - ); -} - function normalizeDescriptor( descriptor: BaseStackDescriptor, ): BaseStackDescriptor { @@ -63,8 +51,7 @@ function buildTransitionStackState({ state: NavigationState; descriptors: DescriptorMap; }): TransitionStackState { - const routes = state.routes as BaseStackRoute[]; - const preloadedRoutes = getPreloadedRoutes(state) as BaseStackRoute[]; + const { routes, preloadedRoutes } = getStackRoutes(state); const allRoutes = routes.concat(preloadedRoutes); const routeKeys = routes.map((route) => route.key); const scenes: ScreenTransitionsAdapterScene[] = []; diff --git a/packages/react-native-screen-transitions/src/navigators/expo-router/create-blank-stack-props.ts b/packages/react-native-screen-transitions/src/navigators/expo-router/create-blank-stack-props.ts new file mode 100644 index 00000000..1970a264 --- /dev/null +++ b/packages/react-native-screen-transitions/src/navigators/expo-router/create-blank-stack-props.ts @@ -0,0 +1,22 @@ +import type { + BaseStackNavigation, + BaseStackRoute, + BaseStackState, +} from "../../types/stack.types"; +import { getStackRoutes } from "../../utils/navigation/get-stack-routes"; +import type { BlankStackStandardNavigatorProps } from "../create-blank-stack-navigator"; + +export function createBlankStackProps({ + state, + navigation, +}: { + state: BaseStackState; + navigation: BaseStackNavigation; +}): BlankStackStandardNavigatorProps { + const { routes } = getStackRoutes(state); + + return { + navigationState: routes === state.routes ? state : { ...state, routes }, + navigation, + }; +} diff --git a/packages/react-native-screen-transitions/src/navigators/expo-router/index.tsx b/packages/react-native-screen-transitions/src/navigators/expo-router/index.tsx index d95b23d2..3806bde1 100644 --- a/packages/react-native-screen-transitions/src/navigators/expo-router/index.tsx +++ b/packages/react-native-screen-transitions/src/navigators/expo-router/index.tsx @@ -1,4 +1,5 @@ import { + type IntegrateWithRouterOptions, type StackNavigationState, StackRouter, type StackRouterOptions, @@ -10,21 +11,27 @@ import { type BlankStackStandardEventMap, type BlankStackStandardNavigatorProps, } from "../create-blank-stack-navigator"; +import { createBlankStackProps } from "./create-blank-stack-props"; type BlankStackExpoRouterParamList = Record; +type BlankStackExpoRouterState = + StackNavigationState; + +// Expo Router 57+ adds a sixth type argument for `createProps` that 56 rejects, +// so the options are checked with `satisfies` and cast to fit every version. export const BlankStack = unstable_integrateWithRouter< BlankStackNavigationOptions, - StackNavigationState, + BlankStackExpoRouterState, BlankStackStandardEventMap, BlankStackStandardNavigatorProps, StackRouterOptions >(BlankStackNavigator, StackRouter, { - createProps: ({ state, navigation }) => ({ - navigationState: state, - navigation, - }), -}); + createProps: createBlankStackProps, +} satisfies IntegrateWithRouterOptions< + BlankStackExpoRouterState, + BlankStackStandardNavigatorProps +> as never); export const BlankStackScreen = BlankStack.Screen; diff --git a/packages/react-native-screen-transitions/src/utils/navigation/get-stack-routes.ts b/packages/react-native-screen-transitions/src/utils/navigation/get-stack-routes.ts new file mode 100644 index 00000000..79e28fa3 --- /dev/null +++ b/packages/react-native-screen-transitions/src/utils/navigation/get-stack-routes.ts @@ -0,0 +1,26 @@ +type StackRoutesState = { + routes: Route[]; + index: number; + preloadedRoutes?: Route[]; +}; + +/** + * Splits a stack state into its active and preloaded routes. + * React Navigation keeps preloaded routes in `preloadedRoutes`, while + * Expo Router 58+ keeps them in `routes` after `index`. + */ +export function getStackRoutes(state: StackRoutesState) { + const activeCount = state.index + 1; + + if (state.routes.length <= activeCount) { + return { + routes: state.routes, + preloadedRoutes: state.preloadedRoutes ?? [], + }; + } + + return { + routes: state.routes.slice(0, activeCount), + preloadedRoutes: state.routes.slice(activeCount), + }; +}