Skip to content

Commit 321efa9

Browse files
coadofacebook-github-bot
authored andcommitted
Add lint rule preventing "Node" imports from react and migrate "Node" types to use React namespace in react-native (#50030)
Summary: Pull Request resolved: #50030 In Flow importing Node from react is equivalent importing ReactNode in Typescript. During translation Node is not translated to ReactNode but React.Node is translated to React.ReactNode. The easiest solution is to migrate all "Node" types to "React.Node" so that the translation is correct. To make sure that everyone follow the lint rule is added that checks for Node imports from react. Changelog: [Internal] - Added lint rule preventing "Node" imports from react and migrated "Node" types to use React namespace in react-native Reviewed By: javache, huntie Differential Revision: D71189533 fbshipit-source-id: baea8feb46be3dc30b6e58bcefe140655ec0530a
1 parent 8ae4bbc commit 321efa9

File tree

12 files changed

+102
-25
lines changed

12 files changed

+102
-25
lines changed

.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ module.exports = {
7474
'lint/no-haste-imports': 2,
7575
'lint/no-react-native-imports': 2,
7676
'lint/require-extends-error': 2,
77+
'lint/no-react-node-imports': 2,
7778
},
7879
},
7980
{

packages/react-native/Libraries/Components/View/ViewPropTypes.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,18 @@ import type {ViewStyleProp} from '../../StyleSheet/StyleSheet';
1515
import type {
1616
BlurEvent,
1717
FocusEvent,
18+
GestureResponderEvent,
1819
LayoutChangeEvent,
1920
LayoutRectangle,
2021
MouseEvent,
2122
PointerEvent,
22-
GestureResponderEvent,
2323
} from '../../Types/CoreEventTypes';
2424
import type {
2525
AccessibilityActionEvent,
2626
AccessibilityProps,
2727
} from './ViewAccessibility';
28-
import type {Node} from 'react';
28+
29+
import React from 'react';
2930

3031
export type ViewLayout = LayoutRectangle;
3132
export type ViewLayoutEvent = LayoutChangeEvent;
@@ -357,7 +358,7 @@ export type ViewPropsIOS = $ReadOnly<{
357358
}>;
358359

359360
type ViewBaseProps = $ReadOnly<{
360-
children?: Node,
361+
children?: React.Node,
361362
style?: ?ViewStyleProp,
362363

363364
/**

packages/react-native/Libraries/Image/ImageProps.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import type {
2424
import typeof Image from './Image';
2525
import type {ImageResizeMode} from './ImageResizeMode';
2626
import type {ImageSource, ImageURISource} from './ImageSource';
27-
import type {ElementRef, Node, RefSetter} from 'react';
27+
import type React from 'react';
28+
import type {ElementRef, RefSetter} from 'react';
2829

2930
export type ImageSourcePropType = ImageSource;
3031

@@ -341,7 +342,7 @@ export type ImageProps = $ReadOnly<{
341342

342343
export type ImageBackgroundProps = $ReadOnly<{
343344
...ImageProps,
344-
children?: Node,
345+
children?: React.Node,
345346

346347
/**
347348
* Style applied to the outer View component

packages/react-native/Libraries/NewAppScreen/components/DebugInstructions.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
* @format
99
*/
1010

11-
import type {Node} from 'react';
12-
1311
import StyleSheet from '../../StyleSheet/StyleSheet';
1412
import Text from '../../Text/Text';
1513
import Platform from '../../Utilities/Platform';
@@ -21,7 +19,7 @@ const styles = StyleSheet.create({
2119
},
2220
});
2321

24-
const DebugInstructions: () => Node = Platform.select({
22+
const DebugInstructions: () => React.Node = Platform.select({
2523
ios: () => (
2624
<Text>
2725
Press <Text style={styles.highlight}>Cmd + D</Text> in the simulator or{' '}

packages/react-native/Libraries/NewAppScreen/components/Header.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
* @format
99
*/
1010

11-
import type {Node} from 'react';
12-
1311
import ImageBackground from '../../Image/ImageBackground';
1412
import StyleSheet from '../../StyleSheet/StyleSheet';
1513
import Text from '../../Text/Text';
@@ -18,7 +16,7 @@ import Colors from './Colors';
1816
import HermesBadge from './HermesBadge';
1917
import React from 'react';
2018

21-
const Header = (): Node => {
19+
const Header = (): React.Node => {
2220
const isDarkMode = useColorScheme() === 'dark';
2321
return (
2422
<ImageBackground

packages/react-native/Libraries/NewAppScreen/components/HermesBadge.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,14 @@
88
* @format
99
*/
1010

11-
import type {Node} from 'react';
12-
1311
import View from '../../Components/View/View';
1412
import StyleSheet from '../../StyleSheet/StyleSheet';
1513
import Text from '../../Text/Text';
1614
import useColorScheme from '../../Utilities/useColorScheme';
1715
import Colors from './Colors';
1816
import React from 'react';
1917

20-
const HermesBadge = (): Node => {
18+
const HermesBadge = (): React.Node => {
2119
const isDarkMode = useColorScheme() === 'dark';
2220
const version =
2321
global.HermesInternal?.getRuntimeProperties?.()['OSS Release Version'] ??

packages/react-native/Libraries/NewAppScreen/components/LearnMoreLinks.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
* @format
99
*/
1010

11-
import type {Node} from 'react';
12-
1311
import TouchableOpacity from '../../Components/Touchable/TouchableOpacity';
1412
import View from '../../Components/View/View';
1513
import openURLInBrowser from '../../Core/Devtools/openURLInBrowser';
@@ -81,7 +79,7 @@ const links = [
8179
},
8280
];
8381

84-
const LinkList = (): Node => {
82+
const LinkList = (): React.Node => {
8583
const isDarkMode = useColorScheme() === 'dark';
8684
return (
8785
<View style={styles.container}>

packages/react-native/Libraries/NewAppScreen/components/ReloadInstructions.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
* @format
99
*/
1010

11-
import type {Node} from 'react';
12-
1311
import StyleSheet from '../../StyleSheet/StyleSheet';
1412
import Text from '../../Text/Text';
1513
import Platform from '../../Utilities/Platform';
@@ -21,7 +19,7 @@ const styles = StyleSheet.create({
2119
},
2220
});
2321

24-
const ReloadInstructions: () => Node = Platform.select({
22+
const ReloadInstructions: () => React.Node = Platform.select({
2523
ios: () => (
2624
<Text>
2725
Press <Text style={styles.highlight}>Cmd + R</Text> in the simulator to

packages/react-native/Libraries/Text/TextProps.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import type {
2424
PointerEvent,
2525
TextLayoutEvent,
2626
} from '../Types/CoreEventTypes';
27-
import type {Node} from 'react';
27+
28+
import React from 'react';
2829

2930
export type PressRetentionOffset = $ReadOnly<{
3031
top: number,
@@ -169,7 +170,7 @@ type TextBaseProps = $ReadOnly<{
169170
*/
170171
'aria-labelledby'?: ?string,
171172

172-
children?: ?Node,
173+
children?: ?React.Node,
173174

174175
/**
175176
* When `numberOfLines` is set, this prop defines how text will be

packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap

+3-3
Original file line numberDiff line numberDiff line change
@@ -4035,7 +4035,7 @@ export type ViewPropsIOS = $ReadOnly<{
40354035
shouldRasterizeIOS?: ?boolean,
40364036
}>;
40374037
type ViewBaseProps = $ReadOnly<{
4038-
children?: Node,
4038+
children?: React.Node,
40394039
style?: ?ViewStyleProp,
40404040
collapsable?: ?boolean,
40414041
collapsableChildren?: ?boolean,
@@ -4679,7 +4679,7 @@ export type ImageProps = $ReadOnly<{
46794679
}>;
46804680
export type ImageBackgroundProps = $ReadOnly<{
46814681
...ImageProps,
4682-
children?: Node,
4682+
children?: React.Node,
46834683
style?: ?ViewStyleProp,
46844684
imageStyle?: ?ImageStyleProp,
46854685
imageRef?: RefSetter<ElementRef<Image>>,
@@ -8136,7 +8136,7 @@ type TextBaseProps = $ReadOnly<{
81368136
\\"aria-expanded\\"?: ?boolean,
81378137
\\"aria-selected\\"?: ?boolean,
81388138
\\"aria-labelledby\\"?: ?string,
8139-
children?: ?Node,
8139+
children?: ?React.Node,
81408140
ellipsizeMode?: ?(\\"clip\\" | \\"head\\" | \\"middle\\" | \\"tail\\"),
81418141
id?: string,
81428142
maxFontSizeMultiplier?: ?number,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
*/
9+
10+
'use strict';
11+
12+
const rule = require('../no-react-node-imports');
13+
const {RuleTester} = require('eslint');
14+
15+
const ruleTester = new RuleTester({
16+
parser: require.resolve('hermes-eslint'),
17+
parserOptions: {
18+
ecmaVersion: 6,
19+
sourceType: 'module',
20+
},
21+
});
22+
23+
ruleTester.run('import(...)', rule, {
24+
valid: [
25+
{
26+
code: `import React from "react";`,
27+
},
28+
{
29+
code: `import { Foo } from "react";`,
30+
},
31+
],
32+
invalid: [
33+
{
34+
code: `import { Node } from "react";`,
35+
errors: [{messageId: 'nodeImport'}],
36+
output: null,
37+
},
38+
],
39+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
*/
9+
10+
'use strict';
11+
12+
module.exports = {
13+
meta: {
14+
type: 'problem',
15+
docs: {
16+
description: 'Disallow named Node imports from react',
17+
},
18+
messages: {
19+
nodeImport:
20+
'React.Node is React.ReactNode in TypeScript. To help us replace these correctly during type translation, prefer `import * as React` and using `React.Node`.',
21+
},
22+
schema: [],
23+
},
24+
25+
create(context) {
26+
return {
27+
ImportDeclaration(node) {
28+
if (node.source.value === 'react') {
29+
node.specifiers.forEach(specifier => {
30+
if (
31+
specifier.type === 'ImportSpecifier' &&
32+
specifier.imported.name === 'Node'
33+
) {
34+
context.report({
35+
node: specifier,
36+
messageId: 'nodeImport',
37+
});
38+
}
39+
});
40+
}
41+
},
42+
};
43+
},
44+
};

0 commit comments

Comments
 (0)