@react-navigation/core 8.0.0-alpha.4 → 8.0.0-alpha.41
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/README.md +17 -14
- package/lib/module/BaseNavigationContainer.js +173 -90
- package/lib/module/BaseNavigationContainer.js.map +1 -1
- package/lib/module/ConsumedParamsContext.js +5 -0
- package/lib/module/ConsumedParamsContext.js.map +1 -0
- package/lib/module/DataLoading.js +129 -0
- package/lib/module/DataLoading.js.map +1 -0
- package/lib/module/NavigationBuilderContext.js +7 -10
- package/lib/module/NavigationBuilderContext.js.map +1 -1
- package/lib/module/NavigationIndependentTree.js +54 -9
- package/lib/module/NavigationIndependentTree.js.map +1 -1
- package/lib/module/NavigationProvider.js +37 -12
- package/lib/module/NavigationProvider.js.map +1 -1
- package/lib/module/NavigationRootContext.js +8 -0
- package/lib/module/NavigationRootContext.js.map +1 -0
- package/lib/module/NavigationStateContext.js +3 -0
- package/lib/module/NavigationStateContext.js.map +1 -1
- package/lib/module/PreventRemoveContext.js.map +1 -1
- package/lib/module/PreventRemoveProvider.js +40 -31
- package/lib/module/PreventRemoveProvider.js.map +1 -1
- package/lib/module/SceneView.js +29 -57
- package/lib/module/SceneView.js.map +1 -1
- package/lib/module/StaticNavigation.js +219 -67
- package/lib/module/StaticNavigation.js.map +1 -1
- package/lib/module/StaticTreeContext.js +5 -0
- package/lib/module/StaticTreeContext.js.map +1 -0
- package/lib/module/createNavigationContainerRef.js +7 -9
- package/lib/module/createNavigationContainerRef.js.map +1 -1
- package/lib/module/createNavigatorFactory.js +27 -5
- package/lib/module/createNavigatorFactory.js.map +1 -1
- package/lib/module/findFocusedRoute.js +7 -4
- package/lib/module/findFocusedRoute.js.map +1 -1
- package/lib/module/getActionFromState.js +45 -20
- package/lib/module/getActionFromState.js.map +1 -1
- package/lib/module/getPathFromState.js +204 -94
- package/lib/module/getPathFromState.js.map +1 -1
- package/lib/module/getPatternParts.js +61 -14
- package/lib/module/getPatternParts.js.map +1 -1
- package/lib/module/getStateFromPath.js +608 -272
- package/lib/module/getStateFromPath.js.map +1 -1
- package/lib/module/getStateFromRouteParams.js +23 -0
- package/lib/module/getStateFromRouteParams.js.map +1 -0
- package/lib/module/index.js +7 -6
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal.js +2 -0
- package/lib/module/internal.js.map +1 -0
- package/lib/module/isRecordEqual.js +1 -1
- package/lib/module/isRecordEqual.js.map +1 -1
- package/lib/module/queryString.js +71 -0
- package/lib/module/queryString.js.map +1 -0
- package/lib/module/theming/useTheme.js +1 -1
- package/lib/module/theming/useTheme.js.map +1 -1
- package/lib/module/types.js +35 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/useDescriptors.js +65 -25
- package/lib/module/useDescriptors.js.map +1 -1
- package/lib/module/useEventEmitter.js +48 -33
- package/lib/module/useEventEmitter.js.map +1 -1
- package/lib/module/useFocusEffect.js +4 -2
- package/lib/module/useFocusEffect.js.map +1 -1
- package/lib/module/useFocusEvents.js +15 -5
- package/lib/module/useFocusEvents.js.map +1 -1
- package/lib/module/useFocusedListenersChildrenAdapter.js +3 -3
- package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
- package/lib/module/useIsFocused.js +8 -12
- package/lib/module/useIsFocused.js.map +1 -1
- package/lib/module/useKeyedChildListeners.js +5 -2
- package/lib/module/useKeyedChildListeners.js.map +1 -1
- package/lib/module/useLazyValue.js.map +1 -1
- package/lib/module/useNavigation.js +4 -17
- package/lib/module/useNavigation.js.map +1 -1
- package/lib/module/useNavigationBuilder.js +244 -147
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useNavigationCache.js +92 -89
- package/lib/module/useNavigationCache.js.map +1 -1
- package/lib/module/useNavigationHelpers.js +22 -29
- package/lib/module/useNavigationHelpers.js.map +1 -1
- package/lib/module/useNavigationIndependentTree.js +1 -1
- package/lib/module/useNavigationIndependentTree.js.map +1 -1
- package/lib/module/useNavigationState.js +158 -46
- package/lib/module/useNavigationState.js.map +1 -1
- package/lib/module/useOnAction.js +58 -11
- package/lib/module/useOnAction.js.map +1 -1
- package/lib/module/useOnGetNavigation.js +26 -0
- package/lib/module/useOnGetNavigation.js.map +1 -0
- package/lib/module/useOnGetState.js +7 -4
- package/lib/module/useOnGetState.js.map +1 -1
- package/lib/module/useOnPreventRemove.js +40 -31
- package/lib/module/useOnPreventRemove.js.map +1 -1
- package/lib/module/useOnRouteFocus.js +2 -2
- package/lib/module/useOnRouteFocus.js.map +1 -1
- package/lib/module/useOptionsGetters.js +35 -27
- package/lib/module/useOptionsGetters.js.map +1 -1
- package/lib/module/usePreventRemove.js +11 -5
- package/lib/module/usePreventRemove.js.map +1 -1
- package/lib/module/usePreventRemoveContext.js +2 -2
- package/lib/module/usePreventRemoveContext.js.map +1 -1
- package/lib/module/useRegisterNavigator.js +3 -4
- package/lib/module/useRegisterNavigator.js.map +1 -1
- package/lib/module/useRoute.js +2 -0
- package/lib/module/useRoute.js.map +1 -1
- package/lib/module/useRouteCache.js +51 -35
- package/lib/module/useRouteCache.js.map +1 -1
- package/lib/module/useScheduleUpdate.js +12 -10
- package/lib/module/useScheduleUpdate.js.map +1 -1
- package/lib/module/useStateForPath.js +1 -1
- package/lib/module/useStateForPath.js.map +1 -1
- package/lib/module/useSyncState.js +37 -15
- package/lib/module/useSyncState.js.map +1 -1
- package/lib/module/validatePathConfig.js +50 -36
- package/lib/module/validatePathConfig.js.map +1 -1
- package/lib/typescript/src/BaseNavigationContainer.d.ts +6 -3
- package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
- package/lib/typescript/src/ConsumedParamsContext.d.ts +3 -0
- package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -0
- package/lib/typescript/src/DataLoading.d.ts +21 -0
- package/lib/typescript/src/DataLoading.d.ts.map +1 -0
- package/lib/typescript/src/EnsureSingleNavigator.d.ts +1 -1
- package/lib/typescript/src/EnsureSingleNavigator.d.ts.map +1 -1
- package/lib/typescript/src/Group.d.ts +1 -1
- package/lib/typescript/src/Group.d.ts.map +1 -1
- package/lib/typescript/src/NavigationBuilderContext.d.ts +22 -14
- package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationContainerRefContext.d.ts +1 -1
- package/lib/typescript/src/NavigationContainerRefContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts +4 -4
- package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationHelpersContext.d.ts +1 -1
- package/lib/typescript/src/NavigationHelpersContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationIndependentTree.d.ts +1 -1
- package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
- package/lib/typescript/src/NavigationProvider.d.ts +21 -18
- package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
- package/lib/typescript/src/NavigationRootContext.d.ts +8 -0
- package/lib/typescript/src/NavigationRootContext.d.ts.map +1 -0
- package/lib/typescript/src/NavigationStateContext.d.ts +3 -3
- package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
- package/lib/typescript/src/PreventRemoveContext.d.ts +1 -0
- package/lib/typescript/src/PreventRemoveContext.d.ts.map +1 -1
- package/lib/typescript/src/PreventRemoveProvider.d.ts +1 -1
- package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
- package/lib/typescript/src/SceneView.d.ts +5 -2
- package/lib/typescript/src/SceneView.d.ts.map +1 -1
- package/lib/typescript/src/Screen.d.ts +1 -1
- package/lib/typescript/src/Screen.d.ts.map +1 -1
- package/lib/typescript/src/StaticContainer.d.ts +1 -1
- package/lib/typescript/src/StaticContainer.d.ts.map +1 -1
- package/lib/typescript/src/StaticNavigation.d.ts +189 -111
- package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
- package/lib/typescript/src/StaticTreeContext.d.ts +4 -0
- package/lib/typescript/src/StaticTreeContext.d.ts.map +1 -0
- package/lib/typescript/src/createNavigationContainerRef.d.ts +1 -1
- package/lib/typescript/src/createNavigationContainerRef.d.ts.map +1 -1
- package/lib/typescript/src/createNavigatorFactory.d.ts +23 -5
- package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
- package/lib/typescript/src/findFocusedRoute.d.ts +3 -8
- package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
- package/lib/typescript/src/getActionFromState.d.ts +4 -4
- package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
- package/lib/typescript/src/getPathFromState.d.ts +3 -3
- package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
- package/lib/typescript/src/getPatternParts.d.ts +5 -2
- package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
- package/lib/typescript/src/getStateFromPath.d.ts +4 -7
- package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
- package/lib/typescript/src/getStateFromRouteParams.d.ts +3 -0
- package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +36 -33
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/internal.d.ts +3 -0
- package/lib/typescript/src/internal.d.ts.map +1 -0
- package/lib/typescript/src/isRecordEqual.d.ts.map +1 -1
- package/lib/typescript/src/queryString.d.ts +5 -0
- package/lib/typescript/src/queryString.d.ts.map +1 -0
- package/lib/typescript/src/theming/ThemeContext.d.ts +1 -1
- package/lib/typescript/src/theming/ThemeContext.d.ts.map +1 -1
- package/lib/typescript/src/theming/ThemeProvider.d.ts +2 -2
- package/lib/typescript/src/theming/ThemeProvider.d.ts.map +1 -1
- package/lib/typescript/src/theming/useTheme.d.ts +1 -1
- package/lib/typescript/src/theming/useTheme.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +265 -167
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useChildListeners.d.ts +3 -3
- package/lib/typescript/src/useChildListeners.d.ts.map +1 -1
- package/lib/typescript/src/useDescriptors.d.ts +13 -11
- package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
- package/lib/typescript/src/useEventEmitter.d.ts +2 -2
- package/lib/typescript/src/useEventEmitter.d.ts.map +1 -1
- package/lib/typescript/src/useFocusEffect.d.ts.map +1 -1
- package/lib/typescript/src/useFocusEvents.d.ts +2 -2
- package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
- package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts +2 -2
- package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts.map +1 -1
- package/lib/typescript/src/useIsFocused.d.ts +4 -0
- package/lib/typescript/src/useIsFocused.d.ts.map +1 -1
- package/lib/typescript/src/useKeyedChildListeners.d.ts +4 -3
- package/lib/typescript/src/useKeyedChildListeners.d.ts.map +1 -1
- package/lib/typescript/src/useLazyValue.d.ts.map +1 -1
- package/lib/typescript/src/useNavigation.d.ts +4 -2
- package/lib/typescript/src/useNavigation.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationBuilder.d.ts +22 -63
- package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationCache.d.ts +6 -5
- package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationContainerRef.d.ts +1 -1
- package/lib/typescript/src/useNavigationContainerRef.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationHelpers.d.ts +18 -58
- package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationState.d.ts +19 -3
- package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
- package/lib/typescript/src/useOnAction.d.ts +9 -9
- package/lib/typescript/src/useOnAction.d.ts.map +1 -1
- package/lib/typescript/src/useOnGetNavigation.d.ts +12 -0
- package/lib/typescript/src/useOnGetNavigation.d.ts.map +1 -0
- package/lib/typescript/src/useOnGetState.d.ts +1 -1
- package/lib/typescript/src/useOnGetState.d.ts.map +1 -1
- package/lib/typescript/src/useOnPreventRemove.d.ts +5 -9
- package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
- package/lib/typescript/src/useOnRouteFocus.d.ts +6 -6
- package/lib/typescript/src/useOnRouteFocus.d.ts.map +1 -1
- package/lib/typescript/src/useOptionsGetters.d.ts +4 -4
- package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
- package/lib/typescript/src/usePreventRemove.d.ts.map +1 -1
- package/lib/typescript/src/usePreventRemoveContext.d.ts +2 -1
- package/lib/typescript/src/usePreventRemoveContext.d.ts.map +1 -1
- package/lib/typescript/src/useRegisterNavigator.d.ts.map +1 -1
- package/lib/typescript/src/useRoute.d.ts +7 -3
- package/lib/typescript/src/useRoute.d.ts.map +1 -1
- package/lib/typescript/src/useRouteCache.d.ts +7 -12
- package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
- package/lib/typescript/src/useScheduleUpdate.d.ts +5 -4
- package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
- package/lib/typescript/src/useStateForPath.d.ts +1 -1
- package/lib/typescript/src/useStateForPath.d.ts.map +1 -1
- package/lib/typescript/src/useSyncState.d.ts +1 -0
- package/lib/typescript/src/useSyncState.d.ts.map +1 -1
- package/lib/typescript/src/utilities.d.ts +79 -39
- package/lib/typescript/src/utilities.d.ts.map +1 -1
- package/lib/typescript/src/validatePathConfig.d.ts.map +1 -1
- package/package.json +26 -22
- package/src/BaseNavigationContainer.tsx +459 -332
- package/src/ConsumedParamsContext.tsx +5 -0
- package/src/DataLoading.tsx +206 -0
- package/src/NavigationBuilderContext.tsx +44 -25
- package/src/NavigationFocusedRouteStateContext.tsx +4 -4
- package/src/NavigationIndependentTree.tsx +60 -12
- package/src/NavigationProvider.tsx +59 -21
- package/src/NavigationRootContext.tsx +11 -0
- package/src/NavigationStateContext.tsx +6 -6
- package/src/PreventRemoveContext.tsx +1 -0
- package/src/PreventRemoveProvider.tsx +55 -56
- package/src/SceneView.tsx +31 -57
- package/src/StaticNavigation.tsx +715 -328
- package/src/StaticTreeContext.tsx +7 -0
- package/src/createNavigationContainerRef.tsx +12 -11
- package/src/createNavigatorFactory.tsx +92 -8
- package/src/findFocusedRoute.tsx +16 -16
- package/src/getActionFromState.tsx +87 -51
- package/src/getPathFromState.tsx +310 -134
- package/src/getPatternParts.tsx +98 -16
- package/src/getStateFromPath.tsx +971 -344
- package/src/getStateFromRouteParams.tsx +59 -0
- package/src/index.tsx +57 -6
- package/src/internal.tsx +2 -0
- package/src/isRecordEqual.tsx +3 -1
- package/src/queryString.ts +109 -0
- package/src/theming/useTheme.tsx +1 -1
- package/src/types.tsx +799 -377
- package/src/useDescriptors.tsx +151 -43
- package/src/useEventEmitter.tsx +53 -39
- package/src/useFocusEffect.tsx +5 -2
- package/src/useFocusEvents.tsx +19 -8
- package/src/useFocusedListenersChildrenAdapter.tsx +3 -3
- package/src/useIsFocused.tsx +18 -21
- package/src/useKeyedChildListeners.tsx +5 -2
- package/src/useLazyValue.tsx +2 -2
- package/src/useNavigation.tsx +9 -26
- package/src/useNavigationBuilder.tsx +370 -201
- package/src/useNavigationCache.tsx +148 -123
- package/src/useNavigationHelpers.tsx +27 -37
- package/src/useNavigationIndependentTree.tsx +1 -1
- package/src/useNavigationState.tsx +207 -69
- package/src/useOnAction.tsx +92 -18
- package/src/useOnGetNavigation.tsx +30 -0
- package/src/useOnGetState.tsx +8 -4
- package/src/useOnPreventRemove.tsx +78 -43
- package/src/useOnRouteFocus.tsx +11 -15
- package/src/useOptionsGetters.tsx +44 -30
- package/src/usePreventRemove.tsx +13 -5
- package/src/usePreventRemoveContext.tsx +2 -2
- package/src/useRegisterNavigator.tsx +4 -4
- package/src/useRoute.tsx +20 -6
- package/src/useRouteCache.tsx +57 -35
- package/src/useScheduleUpdate.tsx +13 -11
- package/src/useStateForPath.tsx +1 -1
- package/src/useSyncState.tsx +39 -15
- package/src/utilities.tsx +151 -35
- package/src/validatePathConfig.tsx +86 -57
- package/lib/module/CurrentRenderContext.js +0 -10
- package/lib/module/CurrentRenderContext.js.map +0 -1
- package/lib/module/NavigationMetaContext.js +0 -15
- package/lib/module/NavigationMetaContext.js.map +0 -1
- package/lib/module/checkSerializable.js +0 -46
- package/lib/module/checkSerializable.js.map +0 -1
- package/lib/module/useComponent.js +0 -34
- package/lib/module/useComponent.js.map +0 -1
- package/lib/module/useCurrentRender.js +0 -19
- package/lib/module/useCurrentRender.js.map +0 -1
- package/lib/typescript/src/CurrentRenderContext.d.ts +0 -9
- package/lib/typescript/src/CurrentRenderContext.d.ts.map +0 -1
- package/lib/typescript/src/NavigationMetaContext.d.ts +0 -12
- package/lib/typescript/src/NavigationMetaContext.d.ts.map +0 -1
- package/lib/typescript/src/checkSerializable.d.ts +0 -10
- package/lib/typescript/src/checkSerializable.d.ts.map +0 -1
- package/lib/typescript/src/useComponent.d.ts +0 -7
- package/lib/typescript/src/useComponent.d.ts.map +0 -1
- package/lib/typescript/src/useCurrentRender.d.ts +0 -14
- package/lib/typescript/src/useCurrentRender.d.ts.map +0 -1
- package/src/CurrentRenderContext.tsx +0 -9
- package/src/NavigationMetaContext.tsx +0 -14
- package/src/checkSerializable.tsx +0 -74
- package/src/useComponent.tsx +0 -37
- package/src/useCurrentRender.tsx +0 -31
|
@@ -1,12 +1,67 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import escape from 'escape-string-regexp';
|
|
4
|
-
import * as queryString from 'query-string';
|
|
5
3
|
import { arrayStartsWith } from "./arrayStartsWith.js";
|
|
6
|
-
import {
|
|
7
|
-
import { getPatternParts } from "./getPatternParts.js";
|
|
4
|
+
import { combinePatternParts, getPatternParts } from "./getPatternParts.js";
|
|
8
5
|
import { isArrayEqual } from "./isArrayEqual.js";
|
|
6
|
+
import * as queryString from "./queryString.js";
|
|
9
7
|
import { validatePathConfig } from "./validatePathConfig.js";
|
|
8
|
+
const INVALID_SCHEMA_RESULT_ERROR = 'Invalid validation result from schema. It should be an object with either "value" or "issues" property and cannot be asynchronous.';
|
|
9
|
+
const INVALID_PARSER_ERROR = 'Invalid parser. Expected a function or a Standard Schema V1 object.';
|
|
10
|
+
const PARAM_GROUP_PREFIX = 'param_';
|
|
11
|
+
const NESTED_SCREEN_PARAM_NAMES = ['screen', 'params', 'initial', 'path', 'merge', 'pop'];
|
|
12
|
+
const REGEX_SYNTAX_CHAR = /[|\\{}()[\]^$+*?.]/;
|
|
13
|
+
const HEX_DIGITS = '0123456789ABCDEF';
|
|
14
|
+
const getStaticSegmentPattern = segment => {
|
|
15
|
+
let pattern = '';
|
|
16
|
+
for (const char of segment) {
|
|
17
|
+
const code = char.charCodeAt(0);
|
|
18
|
+
let escaped = char;
|
|
19
|
+
|
|
20
|
+
// A hyphen uses a hexadecimal escape because a regular backslash escape
|
|
21
|
+
// isn't valid outside character classes when a regex uses Unicode mode.
|
|
22
|
+
if (char === '-') {
|
|
23
|
+
escaped = '\\x2d';
|
|
24
|
+
} else {
|
|
25
|
+
// Other characters with special meaning in a regex can use a regular
|
|
26
|
+
// backslash escape so they are matched as literal characters.
|
|
27
|
+
if (REGEX_SYNTAX_CHAR.test(char)) {
|
|
28
|
+
escaped = `\\${char}`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ASCII characters have a single-byte percent-encoded form that can be
|
|
33
|
+
// built directly. Other characters need encodeURIComponent to encode all
|
|
34
|
+
// bytes in their UTF-8 representation.
|
|
35
|
+
const percentEncoded = char.length === 1 && code < 128 ? `%${HEX_DIGITS.charAt(code >> 4)}${HEX_DIGITS.charAt(code & 15)}` : encodeURIComponent(char);
|
|
36
|
+
pattern += `(?:${escaped}|${percentEncoded})`;
|
|
37
|
+
}
|
|
38
|
+
return pattern;
|
|
39
|
+
};
|
|
40
|
+
const getExplicitParamNames = parse => {
|
|
41
|
+
const names = [];
|
|
42
|
+
if (parse) {
|
|
43
|
+
for (const name in parse) {
|
|
44
|
+
if (parse[name] != null) {
|
|
45
|
+
names.push(name);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return names.length ? new Set(names) : undefined;
|
|
50
|
+
};
|
|
51
|
+
const getStandardSchema = parser => {
|
|
52
|
+
if ('~standard' in parser && typeof parser['~standard'] === 'object' && parser['~standard'] !== null && 'version' in parser['~standard'] && parser['~standard'].version === 1 && 'validate' in parser['~standard'] && typeof parser['~standard'].validate === 'function') {
|
|
53
|
+
return parser['~standard'];
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
};
|
|
57
|
+
const getValidationResult = (schema, value) => {
|
|
58
|
+
const result = schema.validate(value);
|
|
59
|
+
if (result != null && typeof result === 'object' && ('value' in result || 'issues' in result && Array.isArray(result.issues))) {
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
throw new Error(INVALID_SCHEMA_RESULT_ERROR);
|
|
63
|
+
};
|
|
64
|
+
|
|
10
65
|
/**
|
|
11
66
|
* Utility to parse a path string to initial state object accepted by the container.
|
|
12
67
|
* This is useful for deep linking when we need to handle the incoming URL.
|
|
@@ -28,40 +83,57 @@ import { validatePathConfig } from "./validatePathConfig.js";
|
|
|
28
83
|
* @param path Path string to parse and convert, e.g. /foo/bar?count=42.
|
|
29
84
|
* @param options Extra options to fine-tune how to parse the path.
|
|
30
85
|
*/
|
|
31
|
-
export function getStateFromPath(path, options) {
|
|
86
|
+
export function getStateFromPath(path, options, previous) {
|
|
32
87
|
const {
|
|
33
88
|
initialRoutes,
|
|
34
|
-
configs
|
|
89
|
+
configs,
|
|
90
|
+
configsWithZeroOrMore,
|
|
91
|
+
configsByScreen,
|
|
92
|
+
configsByPattern,
|
|
93
|
+
prefixRegex
|
|
35
94
|
} = getConfigResources(options);
|
|
36
95
|
const screens = options?.screens;
|
|
96
|
+
|
|
97
|
+
// When a matched config is shared, the same pattern can resolve to multiple branches
|
|
98
|
+
// pick the one that best matches the current navigation state
|
|
99
|
+
const resolveSharedConfig = config => {
|
|
100
|
+
if (config.shared) {
|
|
101
|
+
const candidates = configsByPattern[config.pattern];
|
|
102
|
+
if (candidates) {
|
|
103
|
+
return selectSharedConfig(config, candidates, previous, initialRoutes);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return config;
|
|
107
|
+
};
|
|
37
108
|
let remaining = path.replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
|
|
38
109
|
.replace(/^\//, '') // Remove extra leading slash
|
|
39
|
-
.replace(/\?.*$/, '')
|
|
110
|
+
.replace(/\?.*$/, '') // Remove query params which we will handle later
|
|
111
|
+
.replace(/%[0-9a-f]{2}/gi, m => m.toUpperCase()); // Normalize percent-encoding as hex digits are case-insensitive
|
|
40
112
|
|
|
41
113
|
// Make sure there is a trailing slash
|
|
42
114
|
remaining = remaining.endsWith('/') ? remaining : `${remaining}/`;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// Make sure there is a trailing slash
|
|
47
|
-
const normalizedPrefix = prefix.endsWith('/') ? prefix : `${prefix}/`;
|
|
48
|
-
|
|
49
|
-
// If the path doesn't start with the prefix, it's not a match
|
|
50
|
-
if (!remaining.startsWith(normalizedPrefix)) {
|
|
115
|
+
if (prefixRegex) {
|
|
116
|
+
const prefixMatch = remaining.match(prefixRegex);
|
|
117
|
+
if (prefixMatch == null) {
|
|
51
118
|
return undefined;
|
|
52
119
|
}
|
|
53
|
-
|
|
54
|
-
// Remove the prefix from the path
|
|
55
|
-
remaining = remaining.replace(normalizedPrefix, '');
|
|
120
|
+
remaining = remaining.slice(prefixMatch[0].length);
|
|
56
121
|
}
|
|
57
122
|
if (screens === undefined) {
|
|
58
123
|
// When no config is specified, use the path segments as route names
|
|
59
|
-
const routes =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
124
|
+
const routes = [];
|
|
125
|
+
for (const segment of remaining.split('/')) {
|
|
126
|
+
if (!segment) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
routes.push({
|
|
131
|
+
name: decodeURIComponent(segment)
|
|
132
|
+
});
|
|
133
|
+
} catch {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
65
137
|
if (routes.length) {
|
|
66
138
|
return createNestedStateObject(path, routes, initialRoutes);
|
|
67
139
|
}
|
|
@@ -70,59 +142,136 @@ export function getStateFromPath(path, options) {
|
|
|
70
142
|
if (remaining === '/') {
|
|
71
143
|
// We need to add special handling of empty path so navigation to empty path also works
|
|
72
144
|
// When handling empty path, we should only look at the root level config
|
|
73
|
-
const match = configs.find(config => config.
|
|
145
|
+
const match = configs.find(config => config.pattern === '');
|
|
74
146
|
if (match) {
|
|
75
|
-
|
|
147
|
+
const config = resolveSharedConfig(match);
|
|
148
|
+
const state = createNestedStateObject(path, config.routeNames.map(name => ({
|
|
76
149
|
name
|
|
77
|
-
})), initialRoutes,
|
|
150
|
+
})), initialRoutes, config);
|
|
151
|
+
if (state) {
|
|
152
|
+
return state;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
for (const config of configsWithZeroOrMore) {
|
|
156
|
+
const routes = matchAgainstConfig('', config, configsByScreen);
|
|
157
|
+
if (routes) {
|
|
158
|
+
let selectedConfig = resolveSharedConfig(config);
|
|
159
|
+
let selectedRoutes = routes;
|
|
160
|
+
if (selectedConfig !== config) {
|
|
161
|
+
const rematchedRoutes = matchAgainstConfig('', selectedConfig, configsByScreen);
|
|
162
|
+
if (rematchedRoutes === undefined) {
|
|
163
|
+
selectedConfig = config;
|
|
164
|
+
} else {
|
|
165
|
+
selectedRoutes = rematchedRoutes;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const state = createNestedStateObject(path, selectedRoutes, initialRoutes, selectedConfig);
|
|
169
|
+
if (state) {
|
|
170
|
+
return state;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
78
173
|
}
|
|
79
174
|
return undefined;
|
|
80
175
|
}
|
|
81
|
-
let result;
|
|
82
|
-
let current;
|
|
83
176
|
|
|
84
177
|
// We match the whole path against the regex instead of segments
|
|
85
178
|
// This makes sure matches such as wildcard will catch any unmatched routes, even if nested
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
current = createNestedStateObject(path, routes, initialRoutes, configs);
|
|
93
|
-
remaining = remainingPath;
|
|
94
|
-
result = current;
|
|
95
|
-
}
|
|
96
|
-
if (current == null || result == null) {
|
|
97
|
-
return undefined;
|
|
179
|
+
const firstRawSegment = remaining.split('/')[0];
|
|
180
|
+
let firstDecodedSegment;
|
|
181
|
+
try {
|
|
182
|
+
firstDecodedSegment = firstRawSegment != null ? decodeURIComponent(firstRawSegment) : undefined;
|
|
183
|
+
} catch {
|
|
184
|
+
firstDecodedSegment = undefined;
|
|
98
185
|
}
|
|
99
|
-
|
|
186
|
+
for (const config of configs) {
|
|
187
|
+
// Regex matching is expensive, so skip configs that can't match this path.
|
|
188
|
+
if (!canMatchFirstSegment(config.segments[0], firstDecodedSegment, firstRawSegment)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const routes = matchAgainstConfig(remaining, config, configsByScreen);
|
|
192
|
+
if (routes === undefined) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
let selectedConfig = resolveSharedConfig(config);
|
|
196
|
+
let selectedRoutes = routes;
|
|
197
|
+
if (selectedConfig !== config) {
|
|
198
|
+
const rematchedRoutes = matchAgainstConfig(remaining, selectedConfig, configsByScreen);
|
|
199
|
+
if (rematchedRoutes === undefined) {
|
|
200
|
+
selectedConfig = config;
|
|
201
|
+
} else {
|
|
202
|
+
selectedRoutes = rematchedRoutes;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const state = createNestedStateObject(path, selectedRoutes, initialRoutes, selectedConfig);
|
|
206
|
+
if (state !== undefined) {
|
|
207
|
+
return state;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
100
211
|
}
|
|
212
|
+
const canMatchFirstSegment = (configSegment, decodedSegment, rawSegment) => {
|
|
213
|
+
if (decodedSegment === undefined) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
if (configSegment === undefined) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
if (configSegment === '*' || configSegment.startsWith(':')) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
return configSegment === decodedSegment || configSegment === rawSegment;
|
|
223
|
+
};
|
|
101
224
|
|
|
102
225
|
/**
|
|
103
226
|
* Reference to the last used config resources. This is used to avoid recomputing the config resources when the options are the same.
|
|
104
227
|
*/
|
|
105
228
|
const cachedConfigResources = new WeakMap();
|
|
106
229
|
function getConfigResources(options) {
|
|
107
|
-
if (!options)
|
|
230
|
+
if (!options) {
|
|
231
|
+
return prepareConfigResources();
|
|
232
|
+
}
|
|
108
233
|
const cached = cachedConfigResources.get(options);
|
|
109
|
-
if (cached)
|
|
234
|
+
if (cached) {
|
|
235
|
+
return cached;
|
|
236
|
+
}
|
|
237
|
+
validatePathConfig(options);
|
|
110
238
|
const resources = prepareConfigResources(options);
|
|
111
239
|
cachedConfigResources.set(options, resources);
|
|
112
240
|
return resources;
|
|
113
241
|
}
|
|
114
242
|
function prepareConfigResources(options) {
|
|
115
|
-
if (options) {
|
|
116
|
-
validatePathConfig(options);
|
|
117
|
-
}
|
|
118
243
|
const initialRoutes = getInitialRoutes(options);
|
|
119
244
|
const configs = getSortedNormalizedConfigs(initialRoutes, options?.screens);
|
|
120
|
-
|
|
121
|
-
|
|
245
|
+
const prefix = options?.path?.replace(/^\//, '');
|
|
246
|
+
let prefixRegex;
|
|
247
|
+
if (prefix) {
|
|
248
|
+
const normalizedPrefix = prefix.endsWith('/') ? prefix : `${prefix}/`;
|
|
249
|
+
const prefixPattern = normalizedPrefix.split('/').map(getStaticSegmentPattern).join('/');
|
|
250
|
+
prefixRegex = new RegExp(`^${prefixPattern}`);
|
|
251
|
+
}
|
|
252
|
+
const configsByScreen = {};
|
|
253
|
+
const configsByPattern = {};
|
|
254
|
+
const configsWithZeroOrMore = [];
|
|
255
|
+
for (const c of configs) {
|
|
256
|
+
if (c.params.some(param => param.repeat === 'zero-or-more')) {
|
|
257
|
+
configsWithZeroOrMore.push(c);
|
|
258
|
+
}
|
|
259
|
+
(configsByScreen[c.screen] ??= []).push(c);
|
|
260
|
+
const existing = configsByPattern[c.pattern];
|
|
261
|
+
if (existing) {
|
|
262
|
+
checkForDuplicatedConfigs(existing[existing.length - 1], c);
|
|
263
|
+
existing.push(c);
|
|
264
|
+
} else {
|
|
265
|
+
configsByPattern[c.pattern] = [c];
|
|
266
|
+
}
|
|
267
|
+
}
|
|
122
268
|
return {
|
|
123
269
|
initialRoutes,
|
|
124
270
|
configs,
|
|
125
|
-
|
|
271
|
+
configsWithZeroOrMore,
|
|
272
|
+
configsByScreen,
|
|
273
|
+
configsByPattern,
|
|
274
|
+
prefixRegex
|
|
126
275
|
};
|
|
127
276
|
}
|
|
128
277
|
function getInitialRoutes(options) {
|
|
@@ -137,219 +286,253 @@ function getInitialRoutes(options) {
|
|
|
137
286
|
}
|
|
138
287
|
function getSortedNormalizedConfigs(initialRoutes, screens = {}) {
|
|
139
288
|
// Create a normalized configs array which will be easier to use
|
|
140
|
-
|
|
289
|
+
const configs = [];
|
|
290
|
+
for (const key in screens) {
|
|
291
|
+
setupNormalizedConfigs(key, screens, initialRoutes, configs, [], [], []);
|
|
292
|
+
}
|
|
293
|
+
for (let order = 0; order < configs.length; order++) {
|
|
294
|
+
const config = configs[order];
|
|
295
|
+
if (config) {
|
|
296
|
+
config.order = order;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return configs.sort((a, b) => {
|
|
141
300
|
// Sort config from most specific to least specific:
|
|
142
|
-
// - more segments
|
|
143
301
|
// - static segments
|
|
144
302
|
// - params with regex
|
|
145
303
|
// - regular params
|
|
304
|
+
// - one-or-more params
|
|
305
|
+
// - the end of the pattern
|
|
306
|
+
// - zero-or-more params
|
|
146
307
|
// - wildcard
|
|
147
308
|
|
|
148
309
|
// If 2 patterns are same, move the one with less route names up
|
|
149
310
|
// This is an error state, so it's only useful for consistent error messages
|
|
150
|
-
if (isArrayEqual(a.segments, b.segments)) {
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// If one of the patterns starts with the other, it's more exhaustive
|
|
155
|
-
// So move it up
|
|
156
|
-
if (arrayStartsWith(a.segments, b.segments)) {
|
|
157
|
-
return -1;
|
|
158
|
-
}
|
|
159
|
-
if (arrayStartsWith(b.segments, a.segments)) {
|
|
160
|
-
return 1;
|
|
161
|
-
}
|
|
162
|
-
for (let i = 0; i < Math.max(a.segments.length, b.segments.length); i++) {
|
|
163
|
-
// if b is longer, b gets higher priority
|
|
164
|
-
if (a.segments[i] == null) {
|
|
165
|
-
return 1;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// if a is longer, a gets higher priority
|
|
169
|
-
if (b.segments[i] == null) {
|
|
170
|
-
return -1;
|
|
171
|
-
}
|
|
172
|
-
const aWildCard = a.segments[i] === '*';
|
|
173
|
-
const bWildCard = b.segments[i] === '*';
|
|
174
|
-
const aParam = a.segments[i].startsWith(':');
|
|
175
|
-
const bParam = b.segments[i].startsWith(':');
|
|
176
|
-
const aRegex = aParam && a.segments[i].includes('(');
|
|
177
|
-
const bRegex = bParam && b.segments[i].includes('(');
|
|
178
|
-
|
|
179
|
-
// if both are wildcard or regex, we compare next component
|
|
180
|
-
if (aWildCard && bWildCard || aRegex && bRegex) {
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// if only a is wildcard, b gets higher priority
|
|
185
|
-
if (aWildCard && !bWildCard) {
|
|
186
|
-
return 1;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// if only b is wildcard, a gets higher priority
|
|
190
|
-
if (bWildCard && !aWildCard) {
|
|
311
|
+
if (a.pattern === b.pattern && isArrayEqual(a.segments, b.segments)) {
|
|
312
|
+
if (a.routeNames.length > b.routeNames.length && arrayStartsWith(a.routeNames, b.routeNames)) {
|
|
191
313
|
return -1;
|
|
192
314
|
}
|
|
193
|
-
|
|
194
|
-
// If only a has a param, b gets higher priority
|
|
195
|
-
if (aParam && !bParam) {
|
|
315
|
+
if (b.routeNames.length > a.routeNames.length && arrayStartsWith(b.routeNames, a.routeNames)) {
|
|
196
316
|
return 1;
|
|
197
317
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
// if only a has regex, a gets higher priority
|
|
205
|
-
if (aRegex && !bRegex) {
|
|
206
|
-
return -1;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// if only b has regex, b gets higher priority
|
|
210
|
-
if (bRegex && !aRegex) {
|
|
211
|
-
return 1;
|
|
318
|
+
return a.routeNames.length - b.routeNames.length || a.order - b.order;
|
|
319
|
+
}
|
|
320
|
+
for (let i = 0; i < Math.max(a.segments.length, b.segments.length); i++) {
|
|
321
|
+
const rank = (a.segmentRanks[i] ?? 5) - (b.segmentRanks[i] ?? 5);
|
|
322
|
+
if (rank !== 0) {
|
|
323
|
+
return rank;
|
|
212
324
|
}
|
|
213
325
|
}
|
|
214
|
-
return a.
|
|
326
|
+
return a.order - b.order;
|
|
215
327
|
});
|
|
216
328
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
329
|
+
const getSegmentRank = segment => {
|
|
330
|
+
if (segment === undefined) {
|
|
331
|
+
return 5;
|
|
332
|
+
}
|
|
333
|
+
if (segment === '*') {
|
|
334
|
+
return 8;
|
|
335
|
+
}
|
|
336
|
+
if (!segment.startsWith(':')) {
|
|
337
|
+
return 0;
|
|
338
|
+
}
|
|
339
|
+
const regex = segment.includes('(');
|
|
340
|
+
if (segment.endsWith('+')) {
|
|
341
|
+
return regex ? 3 : 4;
|
|
342
|
+
}
|
|
343
|
+
if (segment.endsWith('*')) {
|
|
344
|
+
return regex ? 6 : 7;
|
|
345
|
+
}
|
|
346
|
+
return regex ? 1 : 2;
|
|
347
|
+
};
|
|
224
348
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
return Object.assign(acc, {
|
|
233
|
-
[pattern]: config
|
|
234
|
-
});
|
|
235
|
-
}, {});
|
|
236
|
-
}
|
|
237
|
-
function getConfigsWithRegexes(configs) {
|
|
238
|
-
return configs.map(c => ({
|
|
239
|
-
...c,
|
|
240
|
-
// Add `$` to the regex to make sure it matches till end of the path and not just beginning
|
|
241
|
-
regex: c.regex ? new RegExp(c.regex.source + '$') : undefined
|
|
242
|
-
}));
|
|
243
|
-
}
|
|
244
|
-
const matchAgainstConfigs = (remaining, configs) => {
|
|
245
|
-
let routes;
|
|
246
|
-
let remainingPath = remaining;
|
|
349
|
+
// Throw if two configs resolve to the same pattern but conflicting screens
|
|
350
|
+
function checkForDuplicatedConfigs(existing, config) {
|
|
351
|
+
if (!existing) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const a = existing.routeNames;
|
|
355
|
+
const b = config.routeNames;
|
|
247
356
|
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
357
|
+
// It's not a problem if the path string omitted from an innermost screen
|
|
358
|
+
// For example, it's ok if a path resolves to `A > B > C` or `A > B`
|
|
359
|
+
const intersects = a.length > b.length ? arrayStartsWith(a, b) : arrayStartsWith(b, a);
|
|
360
|
+
if (!intersects && (!existing.shared || !config.shared)) {
|
|
361
|
+
throw new Error(`Found conflicting screens with the same pattern. The pattern '${config.pattern}' resolves to both '${a.join(' > ')}' and '${b.join(' > ')}'. Patterns must be unique and cannot resolve to more than one screen unless shared: true is specified.`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const matchAgainstConfig = (remaining, config, configsByScreen) => {
|
|
365
|
+
if (!config.regex) {
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
const match = remaining.match(config.regex);
|
|
369
|
+
if (!match) {
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
let validationFailed = false;
|
|
373
|
+
const matchedRoutes = [];
|
|
374
|
+
for (const [index, routeName] of config.routeNames.entries()) {
|
|
375
|
+
// Check matching name, route names and pattern in case same screen is used
|
|
376
|
+
// at different levels or branches in config
|
|
377
|
+
const routeConfig = configsByScreen[routeName]?.find(c => index === c.routeNames.length - 1 && arrayStartsWith(config.routeNames, c.routeNames) && arrayStartsWith(config.segments, c.segments));
|
|
378
|
+
let params;
|
|
379
|
+
let hasParams = false;
|
|
380
|
+
if (routeConfig && match.groups) {
|
|
381
|
+
params = {};
|
|
382
|
+
for (const param of routeConfig.params) {
|
|
383
|
+
if (param.screen !== routeName) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
let value = match.groups[`${PARAM_GROUP_PREFIX}${param.index}`];
|
|
387
|
+
if (param.repeat && value != null) {
|
|
388
|
+
value = value.replace(/\/$/, '');
|
|
389
|
+
}
|
|
390
|
+
let decoded;
|
|
391
|
+
let decodedSegments;
|
|
392
|
+
try {
|
|
393
|
+
if (param.repeat) {
|
|
394
|
+
if (value == null) {
|
|
395
|
+
decodedSegments = [];
|
|
396
|
+
} else {
|
|
397
|
+
decodedSegments = value.split('/').map(decodeURIComponent);
|
|
398
|
+
}
|
|
399
|
+
decoded = decodedSegments.join('/');
|
|
400
|
+
} else {
|
|
401
|
+
if (value == null) {
|
|
402
|
+
params[param.name] = undefined;
|
|
403
|
+
hasParams = true;
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
decoded = decodeURIComponent(value);
|
|
407
|
+
}
|
|
408
|
+
} catch {
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
254
411
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const index = Number(key.replace('param_', ''));
|
|
264
|
-
const param = routeConfig.params.find(it => it.index === index);
|
|
265
|
-
if (param?.screen === routeName && param?.name) {
|
|
266
|
-
return [param.name, value];
|
|
412
|
+
// Percent-encoded values are matched permissively
|
|
413
|
+
// So validate the decoded value against the custom regex
|
|
414
|
+
if (param.regex) {
|
|
415
|
+
const regex = param.regex;
|
|
416
|
+
const values = decodedSegments ?? [decoded];
|
|
417
|
+
const rawValues = param.repeat ? value?.split('/') ?? [] : [value];
|
|
418
|
+
if (values.some((item, index) => item !== rawValues[index] && !regex.test(item))) {
|
|
419
|
+
return undefined;
|
|
267
420
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
421
|
+
}
|
|
422
|
+
const parser = routeConfig.parse?.[param.name];
|
|
423
|
+
if (!parser) {
|
|
424
|
+
params[param.name] = decoded;
|
|
425
|
+
hasParams = true;
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
const schema = getStandardSchema(parser);
|
|
429
|
+
if (schema) {
|
|
430
|
+
const result = getValidationResult(schema, decoded);
|
|
431
|
+
if (result.issues) {
|
|
432
|
+
validationFailed = true;
|
|
433
|
+
break;
|
|
272
434
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
if (
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
};
|
|
435
|
+
params[param.name] = result.value;
|
|
436
|
+
hasParams = true;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
if (typeof parser === 'function') {
|
|
440
|
+
params[param.name] = parser(decoded);
|
|
441
|
+
hasParams = true;
|
|
442
|
+
continue;
|
|
282
443
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
444
|
+
throw new Error(INVALID_PARSER_ERROR);
|
|
445
|
+
}
|
|
446
|
+
if (validationFailed) {
|
|
447
|
+
// A failed param validation invalidates the whole nested route chain for this config.
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (params && hasParams) {
|
|
452
|
+
matchedRoutes.push({
|
|
453
|
+
name: routeName,
|
|
454
|
+
params
|
|
455
|
+
});
|
|
456
|
+
} else {
|
|
457
|
+
matchedRoutes.push({
|
|
458
|
+
name: routeName
|
|
286
459
|
});
|
|
287
|
-
remainingPath = remainingPath.replace(match[0], '');
|
|
288
|
-
break;
|
|
289
460
|
}
|
|
290
461
|
}
|
|
462
|
+
if (validationFailed) {
|
|
463
|
+
return undefined;
|
|
464
|
+
}
|
|
465
|
+
return matchedRoutes;
|
|
466
|
+
};
|
|
467
|
+
const getRoutePatternParts = (path, screen) => getPatternParts(path).map(part => {
|
|
468
|
+
const rank = getSegmentRank(part.segment);
|
|
469
|
+
if (part.name) {
|
|
470
|
+
return {
|
|
471
|
+
type: 'param',
|
|
472
|
+
screen,
|
|
473
|
+
segment: part.segment,
|
|
474
|
+
rank,
|
|
475
|
+
name: part.name,
|
|
476
|
+
...(part.pattern ? {
|
|
477
|
+
pattern: part.pattern,
|
|
478
|
+
regex: new RegExp(`^(?:${part.pattern})$`)
|
|
479
|
+
} : null),
|
|
480
|
+
...(part.optional ? {
|
|
481
|
+
optional: true
|
|
482
|
+
} : null),
|
|
483
|
+
...(part.repeat ? {
|
|
484
|
+
repeat: part.repeat
|
|
485
|
+
} : null)
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
if (part.segment === '*') {
|
|
489
|
+
return {
|
|
490
|
+
type: 'wildcard',
|
|
491
|
+
segment: part.segment,
|
|
492
|
+
rank
|
|
493
|
+
};
|
|
494
|
+
}
|
|
291
495
|
return {
|
|
292
|
-
|
|
293
|
-
|
|
496
|
+
type: 'static',
|
|
497
|
+
segment: part.segment,
|
|
498
|
+
rank,
|
|
499
|
+
pattern: getStaticSegmentPattern(part.segment)
|
|
294
500
|
};
|
|
295
|
-
};
|
|
296
|
-
const
|
|
297
|
-
const configs = [];
|
|
501
|
+
});
|
|
502
|
+
const setupNormalizedConfigs = (screen, routeConfig, initials, configs, parts, parentScreens, routeNames) => {
|
|
298
503
|
routeNames.push(screen);
|
|
299
504
|
parentScreens.push(screen);
|
|
300
505
|
const config = routeConfig[screen];
|
|
301
506
|
if (typeof config === 'string') {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
path: config
|
|
305
|
-
});
|
|
306
|
-
configs.push(createConfigItem(screen, [...routeNames], [...paths]));
|
|
507
|
+
const configParts = combinePatternParts(parts, getRoutePatternParts(config, screen));
|
|
508
|
+
configs.push(createConfigItem(screen, [...routeNames], configParts));
|
|
307
509
|
} else if (typeof config === 'object') {
|
|
308
510
|
// if an object is specified as the value (e.g. Foo: { ... }),
|
|
309
511
|
// it can have `path` property and
|
|
310
512
|
// it could have `screens` prop which has nested configs
|
|
513
|
+
const nestedScreens = 'screens' in config ? config.screens : undefined;
|
|
514
|
+
const hasNestedScreens = !!nestedScreens;
|
|
515
|
+
const currentParts = typeof config.path === 'string' ? combinePatternParts(parts, getRoutePatternParts(config.path, screen), config.exact) : parts;
|
|
311
516
|
if (typeof config.path === 'string') {
|
|
312
|
-
if (config.exact && config.path == null) {
|
|
313
|
-
throw new Error(`Screen '${screen}' doesn't specify a 'path'. A 'path' needs to be specified when specifying 'exact: true'. If you don't want this screen in the URL, specify it as empty string, e.g. \`path: ''\`.`);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
517
|
// We should add alias configs after the main config
|
|
317
518
|
// So unless they are more specific, main config will be matched first
|
|
318
519
|
const aliasConfigs = [];
|
|
319
520
|
if (config.alias) {
|
|
320
521
|
for (const alias of config.alias) {
|
|
321
522
|
if (typeof alias === 'string') {
|
|
322
|
-
aliasConfigs.push(createConfigItem(screen, [...routeNames],
|
|
323
|
-
screen,
|
|
324
|
-
path: alias
|
|
325
|
-
}], config.parse));
|
|
523
|
+
aliasConfigs.push(createConfigItem(screen, [...routeNames], combinePatternParts(parts, getRoutePatternParts(alias, screen)), config.parse, hasNestedScreens, false));
|
|
326
524
|
} else if (typeof alias === 'object') {
|
|
327
|
-
aliasConfigs.push(createConfigItem(screen, [...routeNames], alias.exact
|
|
328
|
-
screen,
|
|
329
|
-
path: alias.path
|
|
330
|
-
}] : [...paths, {
|
|
331
|
-
screen,
|
|
332
|
-
path: alias.path
|
|
333
|
-
}], alias.parse));
|
|
525
|
+
aliasConfigs.push(createConfigItem(screen, [...routeNames], combinePatternParts(parts, getRoutePatternParts(alias.path, screen), alias.exact), alias.parse, hasNestedScreens, alias.shared === true));
|
|
334
526
|
}
|
|
335
527
|
}
|
|
336
528
|
}
|
|
337
|
-
|
|
338
|
-
// If it's an exact path, we don't need to keep track of the parent screens
|
|
339
|
-
// So we can clear it
|
|
340
|
-
paths.length = 0;
|
|
341
|
-
}
|
|
342
|
-
paths.push({
|
|
343
|
-
screen,
|
|
344
|
-
path: config.path
|
|
345
|
-
});
|
|
346
|
-
configs.push(createConfigItem(screen, [...routeNames], [...paths], config.parse));
|
|
529
|
+
configs.push(createConfigItem(screen, [...routeNames], currentParts, config.parse, hasNestedScreens, config.shared === true));
|
|
347
530
|
configs.push(...aliasConfigs);
|
|
348
531
|
}
|
|
349
532
|
if (typeof config !== 'string' && typeof config.path !== 'string' && config.alias?.length) {
|
|
350
533
|
throw new Error(`Screen '${screen}' doesn't specify a 'path'. A 'path' needs to be specified in order to use 'alias'.`);
|
|
351
534
|
}
|
|
352
|
-
if (
|
|
535
|
+
if (nestedScreens) {
|
|
353
536
|
// property `initialRouteName` without `screens` has no purpose
|
|
354
537
|
if ('initialRouteName' in config && typeof config.initialRouteName === 'string') {
|
|
355
538
|
initials.push({
|
|
@@ -357,57 +540,133 @@ const createNormalizedConfigs = (screen, routeConfig, initials, paths, parentScr
|
|
|
357
540
|
parentScreens
|
|
358
541
|
});
|
|
359
542
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
});
|
|
543
|
+
for (const nestedConfig in nestedScreens) {
|
|
544
|
+
setupNormalizedConfigs(nestedConfig, nestedScreens, initials, configs, currentParts, [...parentScreens], routeNames);
|
|
545
|
+
}
|
|
364
546
|
}
|
|
365
547
|
}
|
|
366
548
|
routeNames.pop();
|
|
367
|
-
return configs;
|
|
368
549
|
};
|
|
369
|
-
const createConfigItem = (screen, routeNames,
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
550
|
+
const createConfigItem = (screen, routeNames, parts, parse, hasNestedScreens = false, shared = false) => {
|
|
551
|
+
let regex;
|
|
552
|
+
const segments = [];
|
|
553
|
+
const segmentRanks = [];
|
|
554
|
+
const params = [];
|
|
555
|
+
const pathParamNames = new Set();
|
|
556
|
+
if (parts.length) {
|
|
557
|
+
let regexString = '^(';
|
|
558
|
+
let index = 0;
|
|
559
|
+
for (const part of parts) {
|
|
560
|
+
segments.push(part.segment);
|
|
561
|
+
segmentRanks.push(part.rank);
|
|
562
|
+
if (part.type === 'param') {
|
|
563
|
+
// A custom regex may not match the percent-encoded form of the value
|
|
564
|
+
// So also accept segments containing an encoded character and validate them after decoding
|
|
565
|
+
let reg = '[^/]+';
|
|
566
|
+
if (part.pattern) {
|
|
567
|
+
const encodedReg = `(?=[^/]*%[0-9A-F]{2})[^/]+`;
|
|
568
|
+
reg = part.repeat ? `(?:${encodedReg})|(?![^/]*%[0-9A-F]{2})(?:${part.pattern})` : `(?:${part.pattern})|${encodedReg}`;
|
|
569
|
+
}
|
|
570
|
+
if (part.repeat) {
|
|
571
|
+
const repeatedReg = `(?:(?:${reg})\\/)+`;
|
|
572
|
+
regexString += `(?<${PARAM_GROUP_PREFIX}${index}>${repeatedReg})${part.repeat === 'zero-or-more' ? '?' : ''}`;
|
|
573
|
+
} else {
|
|
574
|
+
regexString += `(((?<${PARAM_GROUP_PREFIX}${index}>${reg})\\/)${part.optional ? '?' : ''})`;
|
|
575
|
+
}
|
|
576
|
+
params.push({
|
|
577
|
+
index,
|
|
578
|
+
screen: part.screen,
|
|
579
|
+
name: part.name,
|
|
580
|
+
regex: part.regex,
|
|
581
|
+
...(part.repeat ? {
|
|
582
|
+
repeat: part.repeat
|
|
583
|
+
} : {})
|
|
584
|
+
});
|
|
585
|
+
if (part.screen === screen) {
|
|
586
|
+
pathParamNames.add(part.name);
|
|
587
|
+
}
|
|
588
|
+
} else if (part.type === 'wildcard') {
|
|
589
|
+
regexString += `.*\\/`;
|
|
590
|
+
} else {
|
|
591
|
+
regexString += `${part.pattern}\\/`;
|
|
592
|
+
}
|
|
593
|
+
index++;
|
|
594
|
+
}
|
|
595
|
+
regexString += ')$';
|
|
596
|
+
regex = new RegExp(regexString);
|
|
597
|
+
}
|
|
598
|
+
const pattern = segments.join('/');
|
|
395
599
|
return {
|
|
396
600
|
screen,
|
|
601
|
+
order: 0,
|
|
397
602
|
regex,
|
|
603
|
+
pattern,
|
|
398
604
|
segments,
|
|
605
|
+
segmentRanks,
|
|
399
606
|
params,
|
|
400
607
|
routeNames,
|
|
401
|
-
parse
|
|
608
|
+
parse,
|
|
609
|
+
explicitParamNames: getExplicitParamNames(parse),
|
|
610
|
+
pathParamNames,
|
|
611
|
+
hasNestedScreens,
|
|
612
|
+
shared
|
|
402
613
|
};
|
|
403
614
|
};
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
615
|
+
const selectSharedConfig = (match, candidates, previous, initialRoutes) => {
|
|
616
|
+
// Walk the previous state down to the focused leaf to get the active route path
|
|
617
|
+
const focusedRouteNames = [];
|
|
618
|
+
let current = previous;
|
|
619
|
+
while (current?.routes.length) {
|
|
620
|
+
const index = typeof current.index === 'number' ? current.index : current.routes.length - 1;
|
|
621
|
+
const route = current.routes[index];
|
|
622
|
+
if (route == null) {
|
|
623
|
+
break;
|
|
408
624
|
}
|
|
625
|
+
focusedRouteNames.push(route.name);
|
|
626
|
+
current = route.state;
|
|
409
627
|
}
|
|
410
|
-
|
|
628
|
+
|
|
629
|
+
// Rank a candidate by how far it aligns with the focused path, then by
|
|
630
|
+
// how many of its levels match a configured initial route
|
|
631
|
+
const score = routeNames => {
|
|
632
|
+
let focused = 0;
|
|
633
|
+
while (routeNames[focused] !== undefined && routeNames[focused] === focusedRouteNames[focused]) {
|
|
634
|
+
focused++;
|
|
635
|
+
}
|
|
636
|
+
let initial = 0;
|
|
637
|
+
for (const config of initialRoutes) {
|
|
638
|
+
if (arrayStartsWith(routeNames, config.parentScreens) && routeNames[config.parentScreens.length] === config.initialRouteName) {
|
|
639
|
+
initial++;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return {
|
|
643
|
+
focused,
|
|
644
|
+
initial
|
|
645
|
+
};
|
|
646
|
+
};
|
|
647
|
+
let selected = match;
|
|
648
|
+
let best = {
|
|
649
|
+
focused: -1,
|
|
650
|
+
initial: -1
|
|
651
|
+
};
|
|
652
|
+
for (const candidate of candidates) {
|
|
653
|
+
if (!candidate.shared) {
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
const {
|
|
657
|
+
focused,
|
|
658
|
+
initial
|
|
659
|
+
} = score(candidate.routeNames);
|
|
660
|
+
const better = focused !== best.focused ? focused > best.focused : initial !== best.initial ? initial > best.initial : candidate.order < selected.order;
|
|
661
|
+
if (better) {
|
|
662
|
+
selected = candidate;
|
|
663
|
+
best = {
|
|
664
|
+
focused,
|
|
665
|
+
initial
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return selected;
|
|
411
670
|
};
|
|
412
671
|
|
|
413
672
|
// Try to find an initial route connected with the one passed
|
|
@@ -416,7 +675,9 @@ const findInitialRoute = (routeName, parentScreens, initialRoutes) => {
|
|
|
416
675
|
if (parentScreens.length === config.parentScreens.length) {
|
|
417
676
|
let sameParents = true;
|
|
418
677
|
for (let i = 0; i < parentScreens.length; i++) {
|
|
419
|
-
|
|
678
|
+
const parentScreen = parentScreens[i];
|
|
679
|
+
const configParentScreen = config.parentScreens[i];
|
|
680
|
+
if (parentScreen == null || configParentScreen == null || parentScreen.localeCompare(configParentScreen) !== 0) {
|
|
420
681
|
sameParents = false;
|
|
421
682
|
break;
|
|
422
683
|
}
|
|
@@ -470,45 +731,120 @@ const createStateObject = (initialRoute, route, isEmpty) => {
|
|
|
470
731
|
}
|
|
471
732
|
}
|
|
472
733
|
};
|
|
473
|
-
const createNestedStateObject = (path, routes, initialRoutes,
|
|
734
|
+
const createNestedStateObject = (path, routes, initialRoutes, routeConfig) => {
|
|
735
|
+
const focusedRoute = routes[routes.length - 1];
|
|
736
|
+
if (focusedRoute == null) {
|
|
737
|
+
return undefined;
|
|
738
|
+
}
|
|
739
|
+
focusedRoute.path = path.replace(/\/$/, '');
|
|
740
|
+
const queryParams = parseQueryParams(path, routeConfig?.parse, routeConfig?.pathParamNames, routeConfig?.explicitParamNames, routeConfig?.hasNestedScreens, focusedRoute.params);
|
|
741
|
+
if (!queryParams.valid) {
|
|
742
|
+
return undefined;
|
|
743
|
+
}
|
|
744
|
+
if (queryParams.params) {
|
|
745
|
+
focusedRoute.params = {
|
|
746
|
+
...focusedRoute.params,
|
|
747
|
+
...queryParams.params
|
|
748
|
+
};
|
|
749
|
+
}
|
|
474
750
|
let route = routes.shift();
|
|
751
|
+
if (route == null) {
|
|
752
|
+
return undefined;
|
|
753
|
+
}
|
|
475
754
|
const parentScreens = [];
|
|
476
755
|
let initialRoute = findInitialRoute(route.name, parentScreens, initialRoutes);
|
|
477
756
|
parentScreens.push(route.name);
|
|
478
757
|
const state = createStateObject(initialRoute, route, routes.length === 0);
|
|
479
758
|
if (routes.length > 0) {
|
|
480
759
|
let nestedState = state;
|
|
481
|
-
|
|
760
|
+
let nextRoute = routes.shift();
|
|
761
|
+
while (nextRoute != null) {
|
|
762
|
+
route = nextRoute;
|
|
482
763
|
initialRoute = findInitialRoute(route.name, parentScreens, initialRoutes);
|
|
483
|
-
const nestedStateIndex = nestedState.index
|
|
484
|
-
nestedState.routes[nestedStateIndex]
|
|
764
|
+
const nestedStateIndex = nestedState.index ?? nestedState.routes.length - 1;
|
|
765
|
+
const nestedRoute = nestedState.routes[nestedStateIndex];
|
|
766
|
+
if (nestedRoute == null) {
|
|
767
|
+
throw new Error(`Couldn't find a route at index ${nestedStateIndex}.`);
|
|
768
|
+
}
|
|
769
|
+
nestedRoute.state = createStateObject(initialRoute, route, routes.length === 0);
|
|
485
770
|
if (routes.length > 0) {
|
|
486
|
-
|
|
771
|
+
if (nestedRoute.state == null) {
|
|
772
|
+
throw new Error(`Couldn't find nested state for route '${route.name}'.`);
|
|
773
|
+
}
|
|
774
|
+
nestedState = nestedRoute.state;
|
|
487
775
|
}
|
|
488
776
|
parentScreens.push(route.name);
|
|
777
|
+
nextRoute = routes.shift();
|
|
489
778
|
}
|
|
490
779
|
}
|
|
491
|
-
route = findFocusedRoute(state);
|
|
492
|
-
route.path = path.replace(/\/$/, '');
|
|
493
|
-
const params = parseQueryParams(path, flatConfig ? findParseConfigForRoute(route.name, flatConfig) : undefined);
|
|
494
|
-
if (params) {
|
|
495
|
-
route.params = {
|
|
496
|
-
...route.params,
|
|
497
|
-
...params
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
780
|
return state;
|
|
501
781
|
};
|
|
502
|
-
const parseQueryParams = (path, parseConfig) => {
|
|
503
|
-
const
|
|
504
|
-
const
|
|
782
|
+
const parseQueryParams = (path, parseConfig, pathParamNames = new Set(), explicitParamNames, hasNestedScreens = false, routeParams) => {
|
|
783
|
+
const queryIndex = path.indexOf('?');
|
|
784
|
+
const query = queryIndex === -1 ? undefined : path.slice(queryIndex + 1);
|
|
785
|
+
const params = query ? queryString.parse(query) : {};
|
|
786
|
+
|
|
787
|
+
// Path params should always win over same-named query params.
|
|
788
|
+
for (const name of pathParamNames) {
|
|
789
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
790
|
+
delete params[name];
|
|
791
|
+
}
|
|
505
792
|
if (parseConfig) {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
793
|
+
for (const name in parseConfig) {
|
|
794
|
+
const parser = parseConfig[name];
|
|
795
|
+
if (!parser || pathParamNames.has(name)) {
|
|
796
|
+
continue;
|
|
509
797
|
}
|
|
510
|
-
|
|
798
|
+
const schema = getStandardSchema(parser);
|
|
799
|
+
if (!Object.hasOwn(params, name)) {
|
|
800
|
+
if (!schema) {
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
const result = getValidationResult(schema, undefined);
|
|
804
|
+
if (result.issues) {
|
|
805
|
+
return {
|
|
806
|
+
valid: false
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
if (result.value !== undefined) {
|
|
810
|
+
params[name] = result.value;
|
|
811
|
+
}
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
if (schema) {
|
|
815
|
+
const result = getValidationResult(schema, params[name]);
|
|
816
|
+
if (result.issues) {
|
|
817
|
+
return {
|
|
818
|
+
valid: false
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
params[name] = result.value;
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
const value = Array.isArray(params[name]) ? params[name][0] : params[name];
|
|
825
|
+
if (typeof value !== 'string') {
|
|
826
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
827
|
+
delete params[name];
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
if (typeof parser === 'function') {
|
|
831
|
+
params[name] = parser(value);
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
throw new Error(INVALID_PARSER_ERROR);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if (hasNestedScreens && !explicitParamNames?.has('screen') && (typeof params.screen === 'string' || routeParams != null && 'screen' in routeParams && typeof routeParams.screen === 'string')) {
|
|
838
|
+
for (const name of NESTED_SCREEN_PARAM_NAMES) {
|
|
839
|
+
if (!explicitParamNames?.has(name)) {
|
|
840
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
841
|
+
delete params[name];
|
|
842
|
+
}
|
|
843
|
+
}
|
|
511
844
|
}
|
|
512
|
-
return
|
|
845
|
+
return {
|
|
846
|
+
valid: true,
|
|
847
|
+
params: Object.keys(params).length ? params : undefined
|
|
848
|
+
};
|
|
513
849
|
};
|
|
514
850
|
//# sourceMappingURL=getStateFromPath.js.map
|