Skip to content

Commit 4c50ef9

Browse files
fix: TypeScript bloat (callstack#3603)
1 parent 1329ae1 commit 4c50ef9

File tree

9 files changed

+149
-6
lines changed

9 files changed

+149
-6
lines changed

Diff for: .circleci/config.yml

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ jobs:
9595
name: Build package
9696
command: |
9797
yarn prepare
98+
node ./scripts/typescript-output-lint
9899
99100
build-docs:
100101
executor: default

Diff for: scripts/typescript-output-lint.js

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
const { promises: fs } = require('fs');
2+
const path = require('path');
3+
4+
const root = path.resolve(__dirname, '..');
5+
const output = path.join(root, 'lib', 'typescript');
6+
7+
/**
8+
* List of React Native props not used by React Native Paper.
9+
* Feel free to delete props from this list when
10+
* React Native Paper has defined/uses one with the same name.
11+
* This script was originally created to detect if TypeScript
12+
* has exported a large union of props from ViewProps.
13+
* More info: https://github.com/callstack/react-native-paper/pull/3603
14+
*/
15+
const unusedViewProps = [
16+
'nativeID',
17+
'accessibilityActions',
18+
'accessibilityRole',
19+
'accessibilityValue',
20+
'onAccessibilityAction',
21+
'accessibilityLabelledBy',
22+
'accessibilityLiveRegion',
23+
'accessibilityLanguage',
24+
'accessibilityViewIsModal',
25+
'onAccessibilityEscape',
26+
'onAccessibilityTap',
27+
'onMagicTap',
28+
'accessibilityIgnoresInvertColors',
29+
'hitSlop',
30+
'removeClippedSubviews',
31+
'collapsable',
32+
'needsOffscreenAlphaCompositing',
33+
'renderToHardwareTextureAndroid',
34+
'shouldRasterizeIOS',
35+
'isTVSelectable',
36+
'hasTVPreferredFocus',
37+
'tvParallaxProperties',
38+
'tvParallaxShiftDistanceX',
39+
'tvParallaxShiftDistanceY',
40+
'tvParallaxTiltAngle',
41+
'tvParallaxMagnification',
42+
'onStartShouldSetResponder',
43+
'onMoveShouldSetResponder',
44+
'onResponderEnd',
45+
'onResponderGrant',
46+
'onResponderReject',
47+
'onResponderMove',
48+
'onResponderRelease',
49+
'onResponderStart',
50+
'onResponderTerminationRequest',
51+
'onResponderTerminate',
52+
'onStartShouldSetResponderCapture',
53+
'onMoveShouldSetResponderCapture',
54+
'onTouchStart',
55+
'onTouchMove',
56+
'onTouchEnd',
57+
'onTouchCancel',
58+
'onTouchEndCapture',
59+
'onPointerEnter',
60+
'onPointerEnterCapture',
61+
'onPointerLeave',
62+
'onPointerLeaveCapture',
63+
'onPointerMove',
64+
'onPointerMoveCapture',
65+
'onPointerCancel',
66+
'onPointerCancelCapture',
67+
'onPointerDown',
68+
'onPointerDownCapture',
69+
'onPointerUp',
70+
'onPointerUpCapture',
71+
'onHoverIn',
72+
'onHoverOut',
73+
'cancelable',
74+
'delayHoverIn',
75+
'delayHoverOut',
76+
'pressRetentionOffset',
77+
'android_disableSound',
78+
'android_ripple',
79+
'testOnly_pressed',
80+
'unstable_pressDelay',
81+
];
82+
83+
async function* getFiles(directory) {
84+
const entries = await fs.readdir(directory, { withFileTypes: true });
85+
for (const entry of entries) {
86+
const res = path.resolve(directory, entry.name);
87+
if (entry.isDirectory()) {
88+
yield* getFiles(res);
89+
} else {
90+
yield res;
91+
}
92+
}
93+
}
94+
95+
async function main() {
96+
for await (const file of getFiles(output)) {
97+
const content = await fs.readFile(file);
98+
for (const prop of unusedViewProps) {
99+
if (content.includes(prop)) {
100+
throw new Error(
101+
`Found text '${prop}' in '${file}'. Please use the wrapped 'forwardRef' in 'src/utils/forwardRef.ts', export some return types, or modify 'scripts/typescript-output-lint.js'`
102+
);
103+
}
104+
}
105+
}
106+
107+
console.log('✅ No React Native props mentioned in TypeScript files');
108+
}
109+
110+
main().catch((reason) => {
111+
console.error(reason);
112+
process.exit(1);
113+
});

Diff for: src/components/Appbar/AppbarAction.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { ThemeProp } from 'src/types';
66

77
import { useInternalTheme } from '../../core/theming';
88
import { black } from '../../styles/themes/v2/colors';
9+
import { forwardRef } from '../../utils/forwardRef';
910
import type { IconSource } from '../Icon';
1011
import IconButton from '../IconButton/IconButton';
1112

@@ -73,7 +74,7 @@ export type Props = React.ComponentPropsWithoutRef<typeof IconButton> & {
7374
* export default MyComponent;
7475
* ```
7576
*/
76-
const AppbarAction = React.forwardRef<View, Props>(
77+
const AppbarAction = forwardRef<View, Props>(
7778
(
7879
{
7980
size = 24,

Diff for: src/components/Appbar/AppbarBackAction.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
View,
77
} from 'react-native';
88

9+
import { forwardRef } from '../../utils/forwardRef';
910
import type { $Omit } from './../../types';
1011
import AppbarAction from './AppbarAction';
1112
import AppbarBackIcon from './AppbarBackIcon';
@@ -60,7 +61,7 @@ export type Props = $Omit<
6061
* export default MyComponent;
6162
* ```
6263
*/
63-
const AppbarBackAction = React.forwardRef<View, Props>(
64+
const AppbarBackAction = forwardRef<View, Props>(
6465
({ accessibilityLabel = 'Back', ...rest }: Props, ref) => (
6566
<AppbarAction
6667
accessibilityLabel={accessibilityLabel}

Diff for: src/components/FAB/FAB.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111

1212
import { useInternalTheme } from '../../core/theming';
1313
import type { $RemoveChildren, ThemeProp } from '../../types';
14+
import { forwardRef } from '../../utils/forwardRef';
1415
import ActivityIndicator from '../ActivityIndicator';
1516
import CrossFadeIcon from '../CrossFadeIcon';
1617
import Icon, { IconSource } from '../Icon';
@@ -166,7 +167,7 @@ export type Props = $RemoveChildren<typeof Surface> & {
166167
* export default MyComponent;
167168
* ```
168169
*/
169-
const FAB = React.forwardRef<View, Props>(
170+
const FAB = forwardRef<View, Props>(
170171
(
171172
{
172173
icon,

Diff for: src/components/IconButton/IconButton.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99

1010
import { useInternalTheme } from '../../core/theming';
1111
import type { $RemoveChildren, ThemeProp } from '../../types';
12+
import { forwardRef } from '../../utils/forwardRef';
1213
import CrossFadeIcon from '../CrossFadeIcon';
1314
import Icon, { IconSource } from '../Icon';
1415
import Surface from '../Surface';
@@ -112,7 +113,7 @@ export type Props = $RemoveChildren<typeof TouchableRipple> & {
112113
*
113114
* @extends TouchableRipple props https://callstack.github.io/react-native-paper/touchable-ripple.html
114115
*/
115-
const IconButton = React.forwardRef<View, Props>(
116+
const IconButton = forwardRef<View, Props>(
116117
(
117118
{
118119
icon,

Diff for: src/components/Searchbar.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import color from 'color';
1717

1818
import { useInternalTheme } from '../core/theming';
1919
import type { ThemeProp } from '../types';
20+
import { forwardRef } from '../utils/forwardRef';
2021
import ActivityIndicator from './ActivityIndicator';
2122
import type { IconSource } from './Icon';
2223
import IconButton from './IconButton/IconButton';
@@ -119,7 +120,7 @@ type TextInputHandles = Pick<
119120
120121
* ```
121122
*/
122-
const Searchbar = React.forwardRef<TextInputHandles, Props>(
123+
const Searchbar = forwardRef<TextInputHandles, Props>(
123124
(
124125
{
125126
clearAccessibilityLabel = 'clear',

Diff for: src/components/TextInput/TextInput.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010

1111
import { useInternalTheme } from '../../core/theming';
1212
import type { ThemeProp } from '../../types';
13+
import { forwardRef } from '../../utils/forwardRef';
1314
import TextInputAffix, {
1415
Props as TextInputAffixProps,
1516
} from './Adornment/TextInputAffix';
@@ -220,7 +221,7 @@ type TextInputHandles = Pick<
220221
* @extends TextInput props https://reactnative.dev/docs/textinput#props
221222
*/
222223

223-
const TextInput = React.forwardRef<TextInputHandles, Props>(
224+
const TextInput = forwardRef<TextInputHandles, Props>(
224225
(
225226
{
226227
mode = 'flat',

Diff for: src/utils/forwardRef.tsx

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {
2+
forwardRef as reactForwardRef,
3+
ForwardRefRenderFunction,
4+
PropsWithoutRef,
5+
RefAttributes,
6+
ForwardRefExoticComponent,
7+
} from 'react';
8+
9+
export type ForwarRefComponent<T, P = {}> = ForwardRefExoticComponent<
10+
PropsWithoutRef<P> & RefAttributes<T>
11+
>;
12+
13+
/**
14+
* TypeScript generated a large union of props from `ViewProps` in
15+
* `d.ts` files when using `React.forwardRef`. To prevent this
16+
* `ForwarRefComponent` was created and exported. Use this
17+
* `forwardRef` instead of `React.forwardRef` so you don't have to
18+
* import `ForwarRefComponent`.
19+
* More info: https://github.com/callstack/react-native-paper/pull/3603
20+
*/
21+
export const forwardRef: <T, P = {}>(
22+
render: ForwardRefRenderFunction<T, P>
23+
) => ForwarRefComponent<T, P> = reactForwardRef;

0 commit comments

Comments
 (0)