Disallow usage of "NativeProps" symbol in internal components (#51889)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51889

Changelog: [Internal]

Reviewed By: huntie

Differential Revision: D76244543

fbshipit-source-id: 0ccbc29f99e3fac992b8f0040e16a73f72350969
This commit is contained in:
Jakub Piasecki
2025-06-09 23:14:38 -07:00
committed by Facebook GitHub Bot
parent b649791920
commit daff0c99d5
18 changed files with 110 additions and 78 deletions
@@ -63,7 +63,7 @@ export type ReturnKeyType =
export type SubmitBehavior = 'submit' | 'blurAndSubmit' | 'newline';
export type NativeProps = $ReadOnly<{
export type AndroidTextInputNativeProps = $ReadOnly<{
// This allows us to inherit everything from ViewProps except for style (see below)
// This must be commented for Fabric codegen to work.
...Omit<ViewProps, 'style'>,
@@ -606,7 +606,7 @@ export type NativeProps = $ReadOnly<{
text?: ?string,
}>;
type NativeType = HostComponent<NativeProps>;
type NativeType = HostComponent<AndroidTextInputNativeProps>;
type NativeCommands = TextInputNativeCommands<NativeType>;
@@ -725,10 +725,11 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = {
},
};
let AndroidTextInputNativeComponent = NativeComponentRegistry.get<NativeProps>(
'AndroidTextInput',
() => __INTERNAL_VIEW_CONFIG,
);
let AndroidTextInputNativeComponent =
NativeComponentRegistry.get<AndroidTextInputNativeProps>(
'AndroidTextInput',
() => __INTERNAL_VIEW_CONFIG,
);
// flowlint-next-line unclear-type:off
export default ((AndroidTextInputNativeComponent: any): HostComponent<NativeProps>);
export default ((AndroidTextInputNativeComponent: any): HostComponent<AndroidTextInputNativeProps>);
@@ -18,7 +18,7 @@ import type {ImageResizeMode} from './ImageResizeMode';
import * as NativeComponentRegistry from '../NativeComponent/NativeComponentRegistry';
type NativeProps = $ReadOnly<{
type RCTTextInlineImageNativeProps = $ReadOnly<{
...ViewProps,
resizeMode?: ?ImageResizeMode,
src?: ?$ReadOnlyArray<?$ReadOnly<{uri?: ?string, ...}>>,
@@ -40,8 +40,8 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = {
},
};
const TextInlineImage: HostComponent<NativeProps> =
NativeComponentRegistry.get<NativeProps>(
const TextInlineImage: HostComponent<RCTTextInlineImageNativeProps> =
NativeComponentRegistry.get<RCTTextInlineImageNativeProps>(
'RCTTextInlineImage',
() => __INTERNAL_VIEW_CONFIG,
);
@@ -2436,7 +2436,7 @@ export type ReturnKeyType =
| \\"route\\"
| \\"yahoo\\";
export type SubmitBehavior = \\"submit\\" | \\"blurAndSubmit\\" | \\"newline\\";
export type NativeProps = $ReadOnly<{
export type AndroidTextInputNativeProps = $ReadOnly<{
...Omit<ViewProps, \\"style\\">,
autoComplete?: WithDefault<
| \\"birthdate-day\\"
@@ -2594,11 +2594,11 @@ export type NativeProps = $ReadOnly<{
mostRecentEventCount: Int32,
text?: ?string,
}>;
type NativeType = HostComponent<NativeProps>;
type NativeType = HostComponent<AndroidTextInputNativeProps>;
type NativeCommands = TextInputNativeCommands<NativeType>;
declare export const Commands: NativeCommands;
declare export const __INTERNAL_VIEW_CONFIG: PartialViewConfig;
declare export default HostComponent<NativeProps>;
declare export default HostComponent<AndroidTextInputNativeProps>;
"
`;
@@ -4616,7 +4616,7 @@ declare module.exports: typeof RelativeImageStub;
`;
exports[`public API should not change unintentionally Libraries/Image/TextInlineImageNativeComponent.js 1`] = `
"type NativeProps = $ReadOnly<{
"type RCTTextInlineImageNativeProps = $ReadOnly<{
...ViewProps,
resizeMode?: ?ImageResizeMode,
src?: ?$ReadOnlyArray<?$ReadOnly<{ uri?: ?string, ... }>>,
@@ -4624,7 +4624,7 @@ exports[`public API should not change unintentionally Libraries/Image/TextInline
headers?: ?{ [string]: string },
}>;
declare export const __INTERNAL_VIEW_CONFIG: PartialViewConfig;
declare const TextInlineImage: HostComponent<NativeProps>;
declare const TextInlineImage: HostComponent<RCTTextInlineImageNativeProps>;
declare export default typeof TextInlineImage;
"
`;
@@ -15,7 +15,7 @@ import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type NativeProps = $ReadOnly<{
type RCTActivityIndicatorViewNativeProps = $ReadOnly<{
...ViewProps,
/**
@@ -48,6 +48,9 @@ type NativeProps = $ReadOnly<{
size?: WithDefault<'small' | 'large', 'small'>,
}>;
export default (codegenNativeComponent<NativeProps>('ActivityIndicatorView', {
paperComponentName: 'RCTActivityIndicatorView',
}): HostComponent<NativeProps>);
export default (codegenNativeComponent<RCTActivityIndicatorViewNativeProps>(
'ActivityIndicatorView',
{
paperComponentName: 'RCTActivityIndicatorView',
},
): HostComponent<RCTActivityIndicatorViewNativeProps>);
@@ -30,7 +30,7 @@ type DrawerSlideEvent = $ReadOnly<{
offset: Float,
}>;
type NativeProps = $ReadOnly<{
type AndroidDrawerLayoutNativeProps = $ReadOnly<{
...ViewProps,
/**
* Determines whether the keyboard gets dismissed in response to a drag.
@@ -108,7 +108,7 @@ type NativeProps = $ReadOnly<{
statusBarBackgroundColor?: ?ColorValue,
}>;
type NativeType = HostComponent<NativeProps>;
type NativeType = HostComponent<AndroidDrawerLayoutNativeProps>;
interface NativeCommands {
+openDrawer: (viewRef: React.ElementRef<NativeType>) => void;
@@ -119,6 +119,6 @@ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['openDrawer', 'closeDrawer'],
});
export default (codegenNativeComponent<NativeProps>(
export default (codegenNativeComponent<AndroidDrawerLayoutNativeProps>(
'AndroidDrawerLayout',
): NativeType);
@@ -13,15 +13,15 @@ import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type NativeProps = $ReadOnly<{
type AndroidHorizontalScrollContentViewNativeProps = $ReadOnly<{
...ViewProps,
removeClippedSubviews?: ?boolean,
}>;
type NativeType = HostComponent<NativeProps>;
type NativeType = HostComponent<AndroidHorizontalScrollContentViewNativeProps>;
export default (codegenNativeComponent<NativeProps>(
export default (codegenNativeComponent<AndroidHorizontalScrollContentViewNativeProps>(
'AndroidHorizontalScrollContentView',
{interfaceOnly: true},
): NativeType);
@@ -21,7 +21,7 @@ import codegenNativeCommands from '../../../../Libraries/Utilities/codegenNative
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
import * as React from 'react';
type NativeProps = $ReadOnly<{
type AndroidSwipeRefreshLayoutNativeProps = $ReadOnly<{
...ViewProps,
/**
@@ -56,7 +56,7 @@ type NativeProps = $ReadOnly<{
refreshing: boolean,
}>;
type NativeType = HostComponent<NativeProps>;
type NativeType = HostComponent<AndroidSwipeRefreshLayoutNativeProps>;
interface NativeCommands {
+setNativeRefreshing: (
@@ -69,6 +69,6 @@ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['setNativeRefreshing'],
});
export default (codegenNativeComponent<NativeProps>(
export default (codegenNativeComponent<AndroidSwipeRefreshLayoutNativeProps>(
'AndroidSwipeRefreshLayout',
): NativeType);
@@ -21,12 +21,12 @@ import codegenNativeCommands from '../../../../Libraries/Utilities/codegenNative
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
import * as React from 'react';
type SwitchChangeEvent = $ReadOnly<{
type AndroidSwitchChangeEvent = $ReadOnly<{
value: boolean,
target: Int32,
}>;
type NativeProps = $ReadOnly<{
type AndroidSwitchNativeProps = $ReadOnly<{
...ViewProps,
// Props
@@ -41,10 +41,10 @@ type NativeProps = $ReadOnly<{
trackTintColor?: ?ColorValue,
// Events
onChange?: BubblingEventHandler<SwitchChangeEvent>,
onChange?: BubblingEventHandler<AndroidSwitchChangeEvent>,
}>;
type NativeType = HostComponent<NativeProps>;
type NativeType = HostComponent<AndroidSwitchNativeProps>;
interface NativeCommands {
+setNativeValue: (
@@ -57,6 +57,9 @@ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['setNativeValue'],
});
export default (codegenNativeComponent<NativeProps>('AndroidSwitch', {
interfaceOnly: true,
}): NativeType);
export default (codegenNativeComponent<AndroidSwitchNativeProps>(
'AndroidSwitch',
{
interfaceOnly: true,
},
): NativeType);
@@ -16,10 +16,11 @@ import codegenNativeCommands from '../../../../Libraries/Utilities/codegenNative
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
import * as React from 'react';
type NativeProps = $ReadOnly<{
type DebuggingOverlayNativeProps = $ReadOnly<{
...ViewProps,
}>;
export type DebuggingOverlayNativeComponentType = HostComponent<NativeProps>;
export type DebuggingOverlayNativeComponentType =
HostComponent<DebuggingOverlayNativeProps>;
export type TraceUpdate = {
id: number,
@@ -56,6 +57,6 @@ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
],
});
export default (codegenNativeComponent<NativeProps>(
export default (codegenNativeComponent<DebuggingOverlayNativeProps>(
'DebuggingOverlay',
): HostComponent<NativeProps>);
): HostComponent<DebuggingOverlayNativeProps>);
@@ -18,7 +18,7 @@ import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type NativeProps = $ReadOnly<{
type AndroidProgressBarNativeProps = $ReadOnly<{
...ViewProps,
//Props
@@ -31,6 +31,9 @@ type NativeProps = $ReadOnly<{
testID?: WithDefault<string, ''>,
}>;
export default (codegenNativeComponent<NativeProps>('AndroidProgressBar', {
interfaceOnly: true,
}): HostComponent<NativeProps>);
export default (codegenNativeComponent<AndroidProgressBarNativeProps>(
'AndroidProgressBar',
{
interfaceOnly: true,
},
): HostComponent<AndroidProgressBarNativeProps>);
@@ -21,7 +21,7 @@ import codegenNativeCommands from '../../../../Libraries/Utilities/codegenNative
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
import * as React from 'react';
type NativeProps = $ReadOnly<{
type PullToRefreshNativeProps = $ReadOnly<{
...ViewProps,
/**
@@ -52,7 +52,7 @@ type NativeProps = $ReadOnly<{
refreshing: boolean,
}>;
type ComponentType = HostComponent<NativeProps>;
type ComponentType = HostComponent<PullToRefreshNativeProps>;
interface NativeCommands {
+setNativeRefreshing: (
@@ -65,7 +65,10 @@ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['setNativeRefreshing'],
});
export default (codegenNativeComponent<NativeProps>('PullToRefreshView', {
paperComponentName: 'RCTRefreshControl',
excludedPlatforms: ['android'],
}): HostComponent<NativeProps>);
export default (codegenNativeComponent<PullToRefreshNativeProps>(
'PullToRefreshView',
{
paperComponentName: 'RCTRefreshControl',
excludedPlatforms: ['android'],
},
): HostComponent<PullToRefreshNativeProps>);
@@ -14,13 +14,16 @@ import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type NativeProps = $ReadOnly<{
type InputAccessoryNativeProps = $ReadOnly<{
...ViewProps,
backgroundColor?: ?ColorValue,
}>;
export default (codegenNativeComponent<NativeProps>('InputAccessory', {
interfaceOnly: true,
paperComponentName: 'RCTInputAccessoryView',
excludedPlatforms: ['android'],
}): HostComponent<NativeProps>);
export default (codegenNativeComponent<InputAccessoryNativeProps>(
'InputAccessory',
{
interfaceOnly: true,
paperComponentName: 'RCTInputAccessoryView',
excludedPlatforms: ['android'],
},
): HostComponent<InputAccessoryNativeProps>);
@@ -22,7 +22,7 @@ type OrientationChangeEvent = $ReadOnly<{
orientation: 'portrait' | 'landscape',
}>;
type NativeProps = $ReadOnly<{
type RCTModalHostViewNativeProps = $ReadOnly<{
...ViewProps,
/**
@@ -141,7 +141,10 @@ type NativeProps = $ReadOnly<{
identifier?: WithDefault<Int32, 0>,
}>;
export default (codegenNativeComponent<NativeProps>('ModalHostView', {
interfaceOnly: true,
paperComponentName: 'RCTModalHostView',
}): HostComponent<NativeProps>);
export default (codegenNativeComponent<RCTModalHostViewNativeProps>(
'ModalHostView',
{
interfaceOnly: true,
paperComponentName: 'RCTModalHostView',
},
): HostComponent<RCTModalHostViewNativeProps>);
@@ -13,13 +13,16 @@ import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type NativeProps = $ReadOnly<{
type RCTSafeAreaViewNativeProps = $ReadOnly<{
...ViewProps,
// No props
}>;
export default (codegenNativeComponent<NativeProps>('SafeAreaView', {
paperComponentName: 'RCTSafeAreaView',
interfaceOnly: true,
}): HostComponent<NativeProps>);
export default (codegenNativeComponent<RCTSafeAreaViewNativeProps>(
'SafeAreaView',
{
paperComponentName: 'RCTSafeAreaView',
interfaceOnly: true,
},
): HostComponent<RCTSafeAreaViewNativeProps>);
@@ -26,7 +26,7 @@ type SwitchChangeEvent = $ReadOnly<{
target: Int32,
}>;
type NativeProps = $ReadOnly<{
type SwitchNativeProps = $ReadOnly<{
...ViewProps,
// Props
@@ -45,7 +45,7 @@ type NativeProps = $ReadOnly<{
onChange?: ?BubblingEventHandler<SwitchChangeEvent>,
}>;
type ComponentType = HostComponent<NativeProps>;
type ComponentType = HostComponent<SwitchNativeProps>;
interface NativeCommands {
+setValue: (viewRef: React.ElementRef<ComponentType>, value: boolean) => void;
@@ -55,7 +55,7 @@ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['setValue'],
});
export default (codegenNativeComponent<NativeProps>('Switch', {
export default (codegenNativeComponent<SwitchNativeProps>('Switch', {
paperComponentName: 'RCTSwitch',
excludedPlatforms: ['android'],
}): ComponentType);
@@ -14,13 +14,13 @@ import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type NativeProps = $ReadOnly<{
type UnimplementedNativeViewNativeProps = $ReadOnly<{
...ViewProps,
name?: WithDefault<string, ''>,
}>;
// NOTE: This component is not implemented in paper
// Do not require this file in paper builds
export default (codegenNativeComponent<NativeProps>(
export default (codegenNativeComponent<UnimplementedNativeViewNativeProps>(
'UnimplementedNativeView',
): HostComponent<NativeProps>);
): HostComponent<UnimplementedNativeViewNativeProps>);
@@ -30,6 +30,16 @@ describe('ensureNoUnprefixedProps', () => {
await expect(translate(code)).rejects.toThrow();
});
test('should throw when encountering unprefixed NativeProps type', async () => {
const code = `type NativeProps = {}`;
await expect(translate(code)).rejects.toThrow();
});
test('should throw when encountering unprefixed NativeProps interface', async () => {
const code = `interface NativeProps {}`;
await expect(translate(code)).rejects.toThrow();
});
test('should not throw when encountering prefixed Props type', async () => {
const code = `type ViewProps = {}`;
await expect(translate(code)).resolves.toBeDefined();
@@ -16,25 +16,24 @@ const {transformAST} = require('hermes-transform/dist/transform/transformAST');
const visitors: TransformVisitor = context => ({
TypeAlias(node): void {
if (node.id.name === 'Props') {
if (node.id.name === 'Props' || node.id.name === 'NativeProps') {
throw new Error(
`Type alias 'Props' is not allowed. Use more descriptive name.`,
`Type aliases 'Props' and 'NativeProps' are not allowed. Use more descriptive name.`,
);
}
},
InterfaceDeclaration(node): void {
if (node.id.name === 'Props') {
if (node.id.name === 'Props' || node.id.name === 'NativeProps') {
throw new Error(
`Type alias 'Props' is not allowed. Use more descriptive name.`,
`Type aliases 'Props' and 'NativeProps' are not allowed. Use more descriptive name.`,
);
}
},
});
/**
* flow-api-translator doesn't translate empty type to never due to difference
* in semantics between the two. This is desirable behavtior in this case,
* as it's the closest approximation of the empty type.
* Prevents the usage of 'Props' and 'NativeProps' type aliases across the
* public API of React Native.
*/
async function ensureNoUnprefixedProps(
source: ParseResult,