Skip to content

Commit

Permalink
Inline hoist-non-react-statics to eliminate a dep and help shaking
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson committed Aug 26, 2023
1 parent bec3fa7 commit a55e112
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/components/connect.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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 {
Expand Down
136 changes: 136 additions & 0 deletions src/utils/hoistStatics.ts
Original file line number Diff line number Diff line change
@@ -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
}

type NonReactStatics<
S extends React.ComponentType<any>,
C extends {
[key: string]: true
} = {}
> = {
[key in Exclude<
keyof S,
S extends React.MemoExoticComponent<any>
? keyof typeof MEMO_STATICS | keyof C
: S extends React.ForwardRefExoticComponent<any>
? 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<any>,
S extends React.ComponentType<any>,
C extends {
[key: string]: true
} = {}
>(targetComponent: T, sourceComponent: S): T & NonReactStatics<S, C> {
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
}

0 comments on commit a55e112

Please sign in to comment.