Require Flow types in all react-native source files (#45786)

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

Following D60377082, `packages/react-native/Libraries/` is now 100% parsable by `flow-api-translator` and covered by `flow-api-test`. This diff increases test strictness to preserve this state going forward.

Changelog: [Internal]

Reviewed By: cortinico

Differential Revision: D60377123

fbshipit-source-id: 8c48bdd73d9d0d97a114d553eb70cc01faf18e89
This commit is contained in:
Alex Hunt
2024-09-27 12:02:37 -07:00
committed by Facebook GitHub Bot
parent 776e822db5
commit d17373c4ef
2 changed files with 29 additions and 376 deletions
@@ -7739,341 +7739,6 @@ exports[`public API should not change unintentionally Libraries/ReactPrivate/Rea
"
`;
exports[`public API should not change unintentionally Libraries/Renderer/implementations/ReactFabric-dev.js 1`] = `"UNTYPED MODULE"`;
exports[`public API should not change unintentionally Libraries/Renderer/implementations/ReactFabric-prod.js 1`] = `"UNTYPED MODULE"`;
exports[`public API should not change unintentionally Libraries/Renderer/implementations/ReactFabric-profiling.js 1`] = `"UNTYPED MODULE"`;
exports[`public API should not change unintentionally Libraries/Renderer/implementations/ReactNativeRenderer-dev.js 1`] = `"UNTYPED MODULE"`;
exports[`public API should not change unintentionally Libraries/Renderer/implementations/ReactNativeRenderer-prod.js 1`] = `"UNTYPED MODULE"`;
exports[`public API should not change unintentionally Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js 1`] = `"UNTYPED MODULE"`;
exports[`public API should not change unintentionally Libraries/Renderer/shims/ReactFabric.js 1`] = `
"declare module.exports: ReactFabricType;
"
`;
exports[`public API should not change unintentionally Libraries/Renderer/shims/ReactFeatureFlags.js 1`] = `
"declare const ReactFeatureFlags: { debugRenderPhaseSideEffects: false };
declare module.exports: ReactFeatureFlags;
"
`;
exports[`public API should not change unintentionally Libraries/Renderer/shims/ReactNative.js 1`] = `
"declare module.exports: ReactNativeType;
"
`;
exports[`public API should not change unintentionally Libraries/Renderer/shims/ReactNativeTypes.js 1`] = `
"export type MeasureOnSuccessCallback = (
x: number,
y: number,
width: number,
height: number,
pageX: number,
pageY: number
) => void;
export type MeasureInWindowOnSuccessCallback = (
x: number,
y: number,
width: number,
height: number
) => void;
export type MeasureLayoutOnSuccessCallback = (
left: number,
top: number,
width: number,
height: number
) => void;
export type AttributeType<T, V> =
| true
| $ReadOnly<{
diff?: (arg1: T, arg2: T) => boolean,
process?: (arg1: V) => T,
}>;
export type AnyAttributeType = AttributeType<$FlowFixMe, $FlowFixMe>;
export type AttributeConfiguration = $ReadOnly<{
[propName: string]: AnyAttributeType,
style: $ReadOnly<{
[propName: string]: AnyAttributeType,
...
}>,
...
}>;
export type PartialAttributeConfiguration = $ReadOnly<{
[propName: string]: AnyAttributeType,
style?: $ReadOnly<{
[propName: string]: AnyAttributeType,
...
}>,
...
}>;
export type ViewConfig = $ReadOnly<{
Commands?: $ReadOnly<{ [commandName: string]: number, ... }>,
Constants?: $ReadOnly<{ [name: string]: mixed, ... }>,
Manager?: string,
NativeProps?: $ReadOnly<{ [propName: string]: string, ... }>,
baseModuleName?: ?string,
bubblingEventTypes?: $ReadOnly<{
[eventName: string]: $ReadOnly<{
phasedRegistrationNames: $ReadOnly<{
captured: string,
bubbled: string,
skipBubbling?: ?boolean,
}>,
}>,
...
}>,
directEventTypes?: $ReadOnly<{
[eventName: string]: $ReadOnly<{
registrationName: string,
}>,
...
}>,
supportsRawText?: boolean,
uiViewClassName: string,
validAttributes: AttributeConfiguration,
}>;
export type PartialViewConfig = $ReadOnly<{
bubblingEventTypes?: $PropertyType<ViewConfig, \\"bubblingEventTypes\\">,
directEventTypes?: $PropertyType<ViewConfig, \\"directEventTypes\\">,
supportsRawText?: boolean,
uiViewClassName: string,
validAttributes?: PartialAttributeConfiguration,
}>;
export interface INativeMethods {
blur(): void;
focus(): void;
measure(callback: MeasureOnSuccessCallback): void;
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void;
measureLayout(
relativeToNativeNode: number | ElementRef<HostComponent<mixed>>,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail?: () => void
): void;
setNativeProps(nativeProps: { ... }): void;
}
export type NativeMethods = $ReadOnly<{|
blur(): void,
focus(): void,
measure(callback: MeasureOnSuccessCallback): void,
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void,
measureLayout(
relativeToNativeNode: number | ElementRef<HostComponent<mixed>>,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail?: () => void
): void,
setNativeProps(nativeProps: { ... }): void,
|}>;
export type HostComponent<T> = AbstractComponent<T, $ReadOnly<NativeMethods>>;
type SecretInternalsType = {
computeComponentStackForErrorReporting(tag: number): string,
...
};
type InspectorDataProps = $ReadOnly<{
[propName: string]: string,
...
}>;
type InspectorDataGetter = (
<TElementType: ElementType>(
componentOrHandle: ElementRef<TElementType> | number
) => ?number
) => $ReadOnly<{
measure: (callback: MeasureOnSuccessCallback) => void,
props: InspectorDataProps,
}>;
export type InspectorData = $ReadOnly<{
closestInstance?: mixed,
hierarchy: Array<{
name: ?string,
getInspectorData: InspectorDataGetter,
}>,
selectedIndex: ?number,
props: InspectorDataProps,
componentStack: string,
}>;
export type TouchedViewDataAtPoint = $ReadOnly<{
pointerY: number,
touchedViewTag?: number,
frame: $ReadOnly<{
top: number,
left: number,
width: number,
height: number,
}>,
closestPublicInstance?: PublicInstance,
...InspectorData,
}>;
export type RenderRootOptions = {
onUncaughtError?: (
error: mixed,
errorInfo: { +componentStack?: ?string }
) => void,
onCaughtError?: (
error: mixed,
errorInfo: {
+componentStack?: ?string,
+errorBoundary?: ?React$Component<any, any>,
}
) => void,
onRecoverableError?: (
error: mixed,
errorInfo: { +componentStack?: ?string }
) => void,
};
export type ReactNativeType = {
findHostInstance_DEPRECATED<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number)
): ?ElementRef<HostComponent<mixed>>,
findNodeHandle<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number)
): ?number,
isChildPublicInstance(
parent: PublicInstance | HostComponent<mixed>,
child: PublicInstance | HostComponent<mixed>
): boolean,
dispatchCommand(
handle: ElementRef<HostComponent<mixed>>,
command: string,
args: Array<mixed>
): void,
sendAccessibilityEvent(
handle: ElementRef<HostComponent<mixed>>,
eventType: string
): void,
render(
element: MixedElement,
containerTag: number,
callback: ?() => void,
options: ?RenderRootOptions
): ?ElementRef<ElementType>,
unmountComponentAtNode(containerTag: number): void,
unmountComponentAtNodeAndRemoveContainer(containerTag: number): void,
+unstable_batchedUpdates: <T>(fn: (T) => void, bookkeeping: T) => void,
+__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: SecretInternalsType,
...
};
declare export opaque type Node;
declare export opaque type InternalInstanceHandle;
type PublicInstance = mixed;
type PublicTextInstance = mixed;
export type ReactFabricType = {
findHostInstance_DEPRECATED<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number)
): ?ElementRef<HostComponent<mixed>>,
findNodeHandle<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number)
): ?number,
dispatchCommand(
handle: ElementRef<HostComponent<mixed>>,
command: string,
args: Array<mixed>
): void,
isChildPublicInstance(parent: PublicInstance, child: PublicInstance): boolean,
sendAccessibilityEvent(
handle: ElementRef<HostComponent<mixed>>,
eventType: string
): void,
render(
element: MixedElement,
containerTag: number,
callback: ?() => void,
concurrentRoot: ?boolean,
options: ?RenderRootOptions
): ?ElementRef<ElementType>,
unmountComponentAtNode(containerTag: number): void,
getNodeFromInternalInstanceHandle(
internalInstanceHandle: InternalInstanceHandle
): ?Node,
getPublicInstanceFromInternalInstanceHandle(
internalInstanceHandle: InternalInstanceHandle
): PublicInstance | PublicTextInstance | null,
...
};
export type ReactFabricEventTouch = {
identifier: number,
locationX: number,
locationY: number,
pageX: number,
pageY: number,
screenX: number,
screenY: number,
target: number,
timestamp: number,
force: number,
...
};
export type ReactFabricEvent = {
touches: Array<ReactFabricEventTouch>,
changedTouches: Array<ReactFabricEventTouch>,
targetTouches: Array<ReactFabricEventTouch>,
target: number,
...
};
export type LayoutAnimationType =
| \\"spring\\"
| \\"linear\\"
| \\"easeInEaseOut\\"
| \\"easeIn\\"
| \\"easeOut\\"
| \\"keyboard\\";
export type LayoutAnimationProperty =
| \\"opacity\\"
| \\"scaleX\\"
| \\"scaleY\\"
| \\"scaleXY\\";
export type LayoutAnimationAnimationConfig = $ReadOnly<{
duration?: number,
delay?: number,
springDamping?: number,
initialVelocity?: number,
type?: LayoutAnimationType,
property?: LayoutAnimationProperty,
}>;
export type LayoutAnimationConfig = $ReadOnly<{
duration: number,
create?: LayoutAnimationAnimationConfig,
update?: LayoutAnimationAnimationConfig,
delete?: LayoutAnimationAnimationConfig,
}>;
"
`;
exports[`public API should not change unintentionally Libraries/Renderer/shims/ReactNativeViewConfigRegistry.js 1`] = `
"declare export const customBubblingEventTypes: {
[eventName: string]: $ReadOnly<{
phasedRegistrationNames: $ReadOnly<{
captured: string,
bubbled: string,
skipBubbling?: ?boolean,
}>,
}>,
};
declare export const customDirectEventTypes: {
[eventName: string]: $ReadOnly<{
registrationName: string,
}>,
};
declare export function register(
name: string,
callback: () => ViewConfig
): string;
declare export function get(name: string): ViewConfig;
"
`;
exports[`public API should not change unintentionally Libraries/Renderer/shims/createReactNativeComponentClass.js 1`] = `
"declare const createReactNativeComponentClass: (
name: string,
callback: () => ViewConfig
) => string;
declare module.exports: createReactNativeComponentClass;
"
`;
exports[`public API should not change unintentionally Libraries/Settings/NativeSettingsManager.js 1`] = `
"export * from \\"../../src/private/specs/modules/NativeSettingsManager\\";
declare export default typeof NativeSettingsManager;
+29 -41
View File
@@ -26,12 +26,11 @@ const IGNORE_PATTERNS = [
'**/*.fb.js',
'**/*.macos.js',
'**/*.windows.js',
// Non source files
'Libraries/Renderer/implementations/**',
'Libraries/Renderer/shims/**',
];
// Exclude list for files that fail to parse under flow-api-translator. Please
// review your changes before adding new entries.
const FILES_WITH_KNOWN_ERRORS = new Set<string>([]);
const sourceFiles = [
'index.js',
...glob.sync(JS_FILES_PATTERN, {
@@ -46,47 +45,36 @@ describe('public API', () => {
test.each(sourceFiles)('%s', async (file: string) => {
const source = await fs.readFile(path.join(PACKAGE_ROOT, file), 'utf-8');
if (/@flow/.test(source)) {
// Require and use adjacent .js.flow file when source file includes an
// unsupported-syntax suppression
if (source.includes('// $FlowFixMe[unsupported-syntax]')) {
const flowDefPath = path.join(
PACKAGE_ROOT,
file.replace('.js', '.js.flow'),
if (!/@flow/.test(source)) {
throw new Error(
file +
' is untyped. All source files in the react-native package must be written using Flow (// @flow).',
);
}
// Require and use adjacent .js.flow file when source file includes an
// unsupported-syntax suppression
if (source.includes('// $FlowFixMe[unsupported-syntax]')) {
const flowDefPath = path.join(
PACKAGE_ROOT,
file.replace('.js', '.js.flow'),
);
if (!existsSync(flowDefPath)) {
throw new Error(
'Found an unsupported-syntax suppression in ' +
file +
', meaning types cannot be parsed. Add an adjacent <module>.js.flow file to fix this!',
);
if (!existsSync(flowDefPath)) {
throw new Error(
'Found an unsupported-syntax suppression in ' +
file +
', meaning types cannot be parsed. Add an adjacent <module>.js.flow file to fix this!',
);
}
return;
}
let success = false;
try {
expect(await translateFlowToExportedAPI(source)).toMatchSnapshot();
return;
}
success = true;
} catch (e) {
if (!FILES_WITH_KNOWN_ERRORS.has(file)) {
throw new Error(
'Unable to parse file: ' + file + '\n\n' + e.message,
);
}
} finally {
if (success && FILES_WITH_KNOWN_ERRORS.has(file)) {
throw new Error(
'Expected parse error, please remove file exclude from FILES_WITH_KNOWN_ERRORS: ' +
file,
);
}
}
} else {
expect('UNTYPED MODULE').toMatchSnapshot();
try {
expect(await translateFlowToExportedAPI(source)).toMatchSnapshot();
} catch (e) {
throw new Error('Unable to parse file: ' + file + '\n\n' + e.message);
}
});
});