Skip to content

Commit

Permalink
fix: validate property names in linking config
Browse files Browse the repository at this point in the history
  • Loading branch information
satya164 committed Nov 27, 2022
1 parent 04d82c3 commit 4f00cd2
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 0 deletions.
54 changes: 54 additions & 0 deletions packages/core/src/__tests__/getStateFromPath.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2420,3 +2420,57 @@ it('correctly applies initialRouteName for config with similar route names v2',
getStateFromPath<object>(getPathFromState<object>(state, config), config)
).toEqual(state);
});

it('throws when invalid properties are specified in the config', () => {
expect(() =>
getStateFromPath<object>('', {
Foo: 'foo',
Bar: {
path: 'bar',
},
} as any)
).toThrowErrorMatchingInlineSnapshot(`
"Found invalid properties in the configuration:
- Foo
- Bar
Did you forget to specify them under a 'screens' property?
You can only specify the following properties:
- initialRouteName
- screens
See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration."
`);

expect(() =>
getStateFromPath<object>('', {
screens: {
Foo: 'foo',
Bar: {
path: 'bar',
},
Baz: {
Qux: {
path: 'qux',
},
},
},
} as any)
).toThrowErrorMatchingInlineSnapshot(`
"Found invalid properties in the configuration:
- Qux
Did you forget to specify them under a 'screens' property?
You can only specify the following properties:
- initialRouteName
- screens
- path
- exact
- stringify
- parse
See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration."
`);
});
5 changes: 5 additions & 0 deletions packages/core/src/getPathFromState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as queryString from 'query-string';

import fromEntries from './fromEntries';
import type { PathConfig, PathConfigMap } from './types';
import validatePathConfig from './validatePathConfig';

type Options<ParamList> = {
initialRouteName?: string;
Expand Down Expand Up @@ -75,6 +76,10 @@ export default function getPathFromState<ParamList extends {}>(
);
}

if (options) {
validatePathConfig(options);
}

// Create a normalized configs object which will be easier to use
const configs: Record<string, ConfigItem> = options?.screens
? createNormalizedConfigs(options?.screens)
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/getStateFromPath.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as queryString from 'query-string';

import findFocusedRoute from './findFocusedRoute';
import type { PathConfigMap } from './types';
import validatePathConfig from './validatePathConfig';

type Options<ParamList extends {}> = {
initialRouteName?: string;
Expand Down Expand Up @@ -65,6 +66,10 @@ export default function getStateFromPath<ParamList extends {}>(
path: string,
options?: Options<ParamList>
): ResultState | undefined {
if (options) {
validatePathConfig(options);
}

let initialRoutes: InitialRouteConfig[] = [];

if (options?.initialRouteName) {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export { default as useNavigationBuilder } from './useNavigationBuilder';
export { default as useNavigationContainerRef } from './useNavigationContainerRef';
export { default as useNavigationState } from './useNavigationState';
export { default as useRoute } from './useRoute';
export { default as validatePathConfig } from './validatePathConfig';
export * from '@react-navigation/routers';
32 changes: 32 additions & 0 deletions packages/core/src/validatePathConfig.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const formatToList = (items: string[]) =>
items.map((key) => `- ${key}`).join('\n');

export default function validatePathConfig(config: any, root = true) {
const validKeys = ['initialRouteName', 'screens'];

if (!root) {
validKeys.push('path', 'exact', 'stringify', 'parse');
}

const invalidKeys = Object.keys(config).filter(
(key) => !validKeys.includes(key)
);

if (invalidKeys.length) {
throw new Error(
`Found invalid properties in the configuration:\n${formatToList(
invalidKeys
)}\n\nDid you forget to specify them under a 'screens' property?\n\nYou can only specify the following properties:\n${formatToList(
validKeys
)}\n\nSee https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration.`
);
}

if (config.screens) {
Object.entries(config.screens).forEach(([_, value]) => {
if (typeof value !== 'string') {
validatePathConfig(value, false);
}
});
}
}
5 changes: 5 additions & 0 deletions packages/native/src/NavigationContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
NavigationContainerProps,
NavigationContainerRef,
ParamListBase,
validatePathConfig,
} from '@react-navigation/core';
import * as React from 'react';

Expand Down Expand Up @@ -62,6 +63,10 @@ function NavigationContainerInner(
) {
const isLinkingEnabled = linking ? linking.enabled !== false : false;

if (linking?.config) {
validatePathConfig(linking.config);
}

const refContainer = React.useRef<NavigationContainerRef<ParamListBase>>(
null
);
Expand Down

0 comments on commit 4f00cd2

Please sign in to comment.