Skip to content

Commit

Permalink
fix: TypeScript bloat (callstack#3603)
Browse files Browse the repository at this point in the history
  • Loading branch information
DimitarNestorov committed Jan 23, 2023
1 parent 1329ae1 commit 4c50ef9
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 6 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ jobs:
name: Build package
command: |
yarn prepare
node ./scripts/typescript-output-lint
build-docs:
executor: default
Expand Down
113 changes: 113 additions & 0 deletions scripts/typescript-output-lint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
const { promises: fs } = require('fs');
const path = require('path');

const root = path.resolve(__dirname, '..');
const output = path.join(root, 'lib', 'typescript');

/**
* List of React Native props not used by React Native Paper.
* Feel free to delete props from this list when
* React Native Paper has defined/uses one with the same name.
* This script was originally created to detect if TypeScript
* has exported a large union of props from ViewProps.
* More info: https://github.com/callstack/react-native-paper/pull/3603
*/
const unusedViewProps = [
'nativeID',
'accessibilityActions',
'accessibilityRole',
'accessibilityValue',
'onAccessibilityAction',
'accessibilityLabelledBy',
'accessibilityLiveRegion',
'accessibilityLanguage',
'accessibilityViewIsModal',
'onAccessibilityEscape',
'onAccessibilityTap',
'onMagicTap',
'accessibilityIgnoresInvertColors',
'hitSlop',
'removeClippedSubviews',
'collapsable',
'needsOffscreenAlphaCompositing',
'renderToHardwareTextureAndroid',
'shouldRasterizeIOS',
'isTVSelectable',
'hasTVPreferredFocus',
'tvParallaxProperties',
'tvParallaxShiftDistanceX',
'tvParallaxShiftDistanceY',
'tvParallaxTiltAngle',
'tvParallaxMagnification',
'onStartShouldSetResponder',
'onMoveShouldSetResponder',
'onResponderEnd',
'onResponderGrant',
'onResponderReject',
'onResponderMove',
'onResponderRelease',
'onResponderStart',
'onResponderTerminationRequest',
'onResponderTerminate',
'onStartShouldSetResponderCapture',
'onMoveShouldSetResponderCapture',
'onTouchStart',
'onTouchMove',
'onTouchEnd',
'onTouchCancel',
'onTouchEndCapture',
'onPointerEnter',
'onPointerEnterCapture',
'onPointerLeave',
'onPointerLeaveCapture',
'onPointerMove',
'onPointerMoveCapture',
'onPointerCancel',
'onPointerCancelCapture',
'onPointerDown',
'onPointerDownCapture',
'onPointerUp',
'onPointerUpCapture',
'onHoverIn',
'onHoverOut',
'cancelable',
'delayHoverIn',
'delayHoverOut',
'pressRetentionOffset',
'android_disableSound',
'android_ripple',
'testOnly_pressed',
'unstable_pressDelay',
];

async function* getFiles(directory) {
const entries = await fs.readdir(directory, { withFileTypes: true });
for (const entry of entries) {
const res = path.resolve(directory, entry.name);
if (entry.isDirectory()) {
yield* getFiles(res);
} else {
yield res;
}
}
}

async function main() {
for await (const file of getFiles(output)) {
const content = await fs.readFile(file);
for (const prop of unusedViewProps) {
if (content.includes(prop)) {
throw new Error(
`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'`
);
}
}
}

console.log('✅ No React Native props mentioned in TypeScript files');
}

main().catch((reason) => {
console.error(reason);
process.exit(1);
});
3 changes: 2 additions & 1 deletion src/components/Appbar/AppbarAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { ThemeProp } from 'src/types';

import { useInternalTheme } from '../../core/theming';
import { black } from '../../styles/themes/v2/colors';
import { forwardRef } from '../../utils/forwardRef';
import type { IconSource } from '../Icon';
import IconButton from '../IconButton/IconButton';

Expand Down Expand Up @@ -73,7 +74,7 @@ export type Props = React.ComponentPropsWithoutRef<typeof IconButton> & {
* export default MyComponent;
* ```
*/
const AppbarAction = React.forwardRef<View, Props>(
const AppbarAction = forwardRef<View, Props>(
(
{
size = 24,
Expand Down
3 changes: 2 additions & 1 deletion src/components/Appbar/AppbarBackAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
View,
} from 'react-native';

import { forwardRef } from '../../utils/forwardRef';
import type { $Omit } from './../../types';
import AppbarAction from './AppbarAction';
import AppbarBackIcon from './AppbarBackIcon';
Expand Down Expand Up @@ -60,7 +61,7 @@ export type Props = $Omit<
* export default MyComponent;
* ```
*/
const AppbarBackAction = React.forwardRef<View, Props>(
const AppbarBackAction = forwardRef<View, Props>(
({ accessibilityLabel = 'Back', ...rest }: Props, ref) => (
<AppbarAction
accessibilityLabel={accessibilityLabel}
Expand Down
3 changes: 2 additions & 1 deletion src/components/FAB/FAB.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {

import { useInternalTheme } from '../../core/theming';
import type { $RemoveChildren, ThemeProp } from '../../types';
import { forwardRef } from '../../utils/forwardRef';
import ActivityIndicator from '../ActivityIndicator';
import CrossFadeIcon from '../CrossFadeIcon';
import Icon, { IconSource } from '../Icon';
Expand Down Expand Up @@ -166,7 +167,7 @@ export type Props = $RemoveChildren<typeof Surface> & {
* export default MyComponent;
* ```
*/
const FAB = React.forwardRef<View, Props>(
const FAB = forwardRef<View, Props>(
(
{
icon,
Expand Down
3 changes: 2 additions & 1 deletion src/components/IconButton/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {

import { useInternalTheme } from '../../core/theming';
import type { $RemoveChildren, ThemeProp } from '../../types';
import { forwardRef } from '../../utils/forwardRef';
import CrossFadeIcon from '../CrossFadeIcon';
import Icon, { IconSource } from '../Icon';
import Surface from '../Surface';
Expand Down Expand Up @@ -112,7 +113,7 @@ export type Props = $RemoveChildren<typeof TouchableRipple> & {
*
* @extends TouchableRipple props https://callstack.github.io/react-native-paper/touchable-ripple.html
*/
const IconButton = React.forwardRef<View, Props>(
const IconButton = forwardRef<View, Props>(
(
{
icon,
Expand Down
3 changes: 2 additions & 1 deletion src/components/Searchbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import color from 'color';

import { useInternalTheme } from '../core/theming';
import type { ThemeProp } from '../types';
import { forwardRef } from '../utils/forwardRef';
import ActivityIndicator from './ActivityIndicator';
import type { IconSource } from './Icon';
import IconButton from './IconButton/IconButton';
Expand Down Expand Up @@ -119,7 +120,7 @@ type TextInputHandles = Pick<
* ```
*/
const Searchbar = React.forwardRef<TextInputHandles, Props>(
const Searchbar = forwardRef<TextInputHandles, Props>(
(
{
clearAccessibilityLabel = 'clear',
Expand Down
3 changes: 2 additions & 1 deletion src/components/TextInput/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {

import { useInternalTheme } from '../../core/theming';
import type { ThemeProp } from '../../types';
import { forwardRef } from '../../utils/forwardRef';
import TextInputAffix, {
Props as TextInputAffixProps,
} from './Adornment/TextInputAffix';
Expand Down Expand Up @@ -220,7 +221,7 @@ type TextInputHandles = Pick<
* @extends TextInput props https://reactnative.dev/docs/textinput#props
*/

const TextInput = React.forwardRef<TextInputHandles, Props>(
const TextInput = forwardRef<TextInputHandles, Props>(
(
{
mode = 'flat',
Expand Down
23 changes: 23 additions & 0 deletions src/utils/forwardRef.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
forwardRef as reactForwardRef,
ForwardRefRenderFunction,
PropsWithoutRef,
RefAttributes,
ForwardRefExoticComponent,
} from 'react';

export type ForwarRefComponent<T, P = {}> = ForwardRefExoticComponent<
PropsWithoutRef<P> & RefAttributes<T>
>;

/**
* TypeScript generated a large union of props from `ViewProps` in
* `d.ts` files when using `React.forwardRef`. To prevent this
* `ForwarRefComponent` was created and exported. Use this
* `forwardRef` instead of `React.forwardRef` so you don't have to
* import `ForwarRefComponent`.
* More info: https://github.com/callstack/react-native-paper/pull/3603
*/
export const forwardRef: <T, P = {}>(
render: ForwardRefRenderFunction<T, P>
) => ForwarRefComponent<T, P> = reactForwardRef;

0 comments on commit 4c50ef9

Please sign in to comment.