diff --git a/package.json b/package.json index b36063a0f..3b754253a 100644 --- a/package.json +++ b/package.json @@ -70,9 +70,7 @@ } }, "dependencies": { - "@types/hoist-non-react-statics": "^3.3.1", "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", "react-is": "^18.0.0", "use-sync-external-store": "^1.0.0" }, diff --git a/src/components/connect.tsx b/src/components/connect.tsx index d76025eeb..723381df8 100644 --- a/src/components/connect.tsx +++ b/src/components/connect.tsx @@ -1,5 +1,4 @@ /* eslint-disable valid-jsdoc, @typescript-eslint/no-unused-vars */ -import hoistStatics from 'hoist-non-react-statics' import type { ComponentType } from 'react' import * as React from 'react' import { isValidElementType, isContextConsumer } from 'react-is' @@ -31,6 +30,7 @@ import type { Subscription } from '../utils/Subscription' import { createSubscription } from '../utils/Subscription' import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect' import shallowEqual from '../utils/shallowEqual' +import hoistStatics from '../utils/hoistStatics' import warning from '../utils/warning' import type { diff --git a/src/types.ts b/src/types.ts index 2ce54ac8e..92775a104 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,7 +7,7 @@ import type { import type { Action, UnknownAction, Dispatch } from 'redux' -import type { NonReactStatics } from 'hoist-non-react-statics' +import type { NonReactStatics } from './utils/hoistStatics' import type { ConnectProps } from './components/connect' diff --git a/src/utils/hoistStatics.ts b/src/utils/hoistStatics.ts new file mode 100644 index 000000000..7a2d8b0a6 --- /dev/null +++ b/src/utils/hoistStatics.ts @@ -0,0 +1,136 @@ +// Copied directly from: +// https://github.com/mridgway/hoist-non-react-statics/blob/main/src/index.js +// https://unpkg.com/browse/@types/hoist-non-react-statics@3.3.1/index.d.ts + +/** + * Copyright 2015, Yahoo! Inc. + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. + */ +import type * as React from 'react' +import { ForwardRef, Memo, isMemo } from 'react-is' + +const REACT_STATICS = { + childContextTypes: true, + contextType: true, + contextTypes: true, + defaultProps: true, + displayName: true, + getDefaultProps: true, + getDerivedStateFromError: true, + getDerivedStateFromProps: true, + mixins: true, + propTypes: true, + type: true, +} as const + +const KNOWN_STATICS = { + name: true, + length: true, + prototype: true, + caller: true, + callee: true, + arguments: true, + arity: true, +} as const + +const FORWARD_REF_STATICS = { + $$typeof: true, + render: true, + defaultProps: true, + displayName: true, + propTypes: true, +} as const + +const MEMO_STATICS = { + $$typeof: true, + compare: true, + defaultProps: true, + displayName: true, + propTypes: true, + type: true, +} as const + +const TYPE_STATICS = { + [ForwardRef]: FORWARD_REF_STATICS, + [Memo]: MEMO_STATICS, +} as const + +function getStatics(component: any) { + // React v16.11 and below + if (isMemo(component)) { + return MEMO_STATICS + } + + // React v16.12 and above + return TYPE_STATICS[component['$$typeof']] || REACT_STATICS +} + +export type NonReactStatics< + S extends React.ComponentType, + C extends { + [key: string]: true + } = {} +> = { + [key in Exclude< + keyof S, + S extends React.MemoExoticComponent + ? keyof typeof MEMO_STATICS | keyof C + : S extends React.ForwardRefExoticComponent + ? keyof typeof FORWARD_REF_STATICS | keyof C + : keyof typeof REACT_STATICS | keyof typeof KNOWN_STATICS | keyof C + >]: S[key] +} + +const defineProperty = Object.defineProperty +const getOwnPropertyNames = Object.getOwnPropertyNames +const getOwnPropertySymbols = Object.getOwnPropertySymbols +const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor +const getPrototypeOf = Object.getPrototypeOf +const objectPrototype = Object.prototype + +export default function hoistNonReactStatics< + T extends React.ComponentType, + S extends React.ComponentType, + C extends { + [key: string]: true + } = {} +>(targetComponent: T, sourceComponent: S): T & NonReactStatics { + if (typeof sourceComponent !== 'string') { + // don't hoist over string (html) components + + if (objectPrototype) { + const inheritedComponent = getPrototypeOf(sourceComponent) + if (inheritedComponent && inheritedComponent !== objectPrototype) { + hoistNonReactStatics(targetComponent, inheritedComponent) + } + } + + let keys: (string | symbol)[] = getOwnPropertyNames(sourceComponent) + + if (getOwnPropertySymbols) { + keys = keys.concat(getOwnPropertySymbols(sourceComponent)) + } + + const targetStatics = getStatics(targetComponent) + const sourceStatics = getStatics(sourceComponent) + + for (let i = 0; i < keys.length; ++i) { + const key = keys[i] + if ( + !KNOWN_STATICS[key as keyof typeof KNOWN_STATICS] && + !(sourceStatics && sourceStatics[key as keyof typeof sourceStatics]) && + !(targetStatics && targetStatics[key as keyof typeof targetStatics]) + ) { + const descriptor = getOwnPropertyDescriptor(sourceComponent, key) + try { + // Avoid failures from read-only properties + defineProperty(targetComponent, key, descriptor!) + } catch (e) { + // ignore + } + } + } + } + + return targetComponent as any +} diff --git a/yarn.lock b/yarn.lock index 22d59cfaf..96f86580e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3263,16 +3263,6 @@ __metadata: languageName: node linkType: hard -"@types/hoist-non-react-statics@npm:^3.3.1": - version: 3.3.1 - resolution: "@types/hoist-non-react-statics@npm:3.3.1" - dependencies: - "@types/react": "*" - hoist-non-react-statics: ^3.3.0 - checksum: 2c0778570d9a01d05afabc781b32163f28409bb98f7245c38d5eaf082416fdb73034003f5825eb5e21313044e8d2d9e1f3fe2831e345d3d1b1d20bcd12270719 - languageName: node - linkType: hard - "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": version: 2.0.3 resolution: "@types/istanbul-lib-coverage@npm:2.0.3" @@ -6588,15 +6578,6 @@ __metadata: languageName: node linkType: hard -"hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.2": - version: 3.3.2 - resolution: "hoist-non-react-statics@npm:3.3.2" - dependencies: - react-is: ^16.7.0 - checksum: b1538270429b13901ee586aa44f4cc3ecd8831c061d06cb8322e50ea17b3f5ce4d0e2e66394761e6c8e152cd8c34fb3b4b690116c6ce2bd45b18c746516cb9e8 - languageName: node - linkType: hard - "hosted-git-info@npm:^2.1.4": version: 2.8.9 resolution: "hosted-git-info@npm:2.8.9" @@ -10025,7 +10006,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^16.13.1, react-is@npm:^16.7.0, react-is@npm:^16.8.1, react-is@npm:^16.8.4": +"react-is@npm:^16.13.1, react-is@npm:^16.8.1, react-is@npm:^16.8.4": version: 16.13.1 resolution: "react-is@npm:16.13.1" checksum: f7a19ac3496de32ca9ae12aa030f00f14a3d45374f1ceca0af707c831b2a6098ef0d6bdae51bd437b0a306d7f01d4677fcc8de7c0d331eb47ad0f46130e53c5f @@ -10126,7 +10107,6 @@ __metadata: "@testing-library/react-12": "npm:@testing-library/react@^12" "@testing-library/react-hooks": ^3.4.2 "@testing-library/react-native": ^7.1.0 - "@types/hoist-non-react-statics": ^3.3.1 "@types/object-assign": ^4.0.30 "@types/react": ^18 "@types/react-dom": ^18 @@ -10145,7 +10125,6 @@ __metadata: eslint-plugin-prettier: ^3.1.4 eslint-plugin-react: ^7.21.5 glob: ^7.1.6 - hoist-non-react-statics: ^3.3.2 jest: ^29 jest-environment-jsdom: ^29.5.0 metro-react-native-babel-preset: ^0.76.6