Skip to content

Commit b28862f

Browse files
committed
chore(react): Inline hoist-non-react-statics package
1 parent ea20d8d commit b28862f

File tree

4 files changed

+465
-16
lines changed

4 files changed

+465
-16
lines changed

packages/react/package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@
4040
},
4141
"dependencies": {
4242
"@sentry/browser": "10.22.0",
43-
"@sentry/core": "10.22.0",
44-
"hoist-non-react-statics": "^3.3.2"
43+
"@sentry/core": "10.22.0"
4544
},
4645
"peerDependencies": {
4746
"react": "^16.14.0 || 17.x || 18.x || 19.x"
@@ -51,7 +50,6 @@
5150
"@testing-library/react-hooks": "^7.0.2",
5251
"@types/history-4": "npm:@types/history@4.7.8",
5352
"@types/history-5": "npm:@types/history@4.7.8",
54-
"@types/hoist-non-react-statics": "^3.3.5",
5553
"@types/node-fetch": "^2.6.11",
5654
"@types/react": "17.0.3",
5755
"@types/react-router-4": "npm:@types/react-router@4.0.25",
Lines changed: 170 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,171 @@
1-
import * as hoistNonReactStaticsImport from 'hoist-non-react-statics';
1+
/**
2+
* Inlined implementation of hoist-non-react-statics
3+
* Original library: https://github.com/mridgway/hoist-non-react-statics
4+
* License: BSD-3-Clause
5+
* Copyright 2015, Yahoo! Inc.
6+
*
7+
* This is an inlined version to avoid ESM compatibility issues with the original package.
8+
*/
29

3-
// Ensure we use the default export from hoist-non-react-statics if available,
4-
// falling back to the module itself. This handles both ESM and CJS usage.
5-
export const hoistNonReactStatics = hoistNonReactStaticsImport.default || hoistNonReactStaticsImport;
10+
import type * as React from 'react';
11+
12+
/**
13+
* React statics that should not be hoisted
14+
*/
15+
const REACT_STATICS = {
16+
childContextTypes: true,
17+
contextType: true,
18+
contextTypes: true,
19+
defaultProps: true,
20+
displayName: true,
21+
getDefaultProps: true,
22+
getDerivedStateFromError: true,
23+
getDerivedStateFromProps: true,
24+
mixins: true,
25+
propTypes: true,
26+
type: true,
27+
} as const;
28+
29+
/**
30+
* Known JavaScript function statics that should not be hoisted
31+
*/
32+
const KNOWN_STATICS = {
33+
name: true,
34+
length: true,
35+
prototype: true,
36+
caller: true,
37+
callee: true,
38+
arguments: true,
39+
arity: true,
40+
} as const;
41+
42+
/**
43+
* Statics specific to ForwardRef components
44+
*/
45+
const FORWARD_REF_STATICS = {
46+
$$typeof: true,
47+
render: true,
48+
defaultProps: true,
49+
displayName: true,
50+
propTypes: true,
51+
} as const;
52+
53+
/**
54+
* Statics specific to Memo components
55+
*/
56+
const MEMO_STATICS = {
57+
$$typeof: true,
58+
compare: true,
59+
defaultProps: true,
60+
displayName: true,
61+
propTypes: true,
62+
type: true,
63+
} as const;
64+
65+
/**
66+
* Inlined react-is utilities
67+
* We only need to detect ForwardRef and Memo types
68+
*/
69+
const ForwardRefType = Symbol.for('react.forward_ref');
70+
const MemoType = Symbol.for('react.memo');
71+
72+
/**
73+
* Check if a component is a Memo component
74+
*/
75+
function isMemo(component: unknown): boolean {
76+
return typeof component === 'object' && component !== null && (component as { $$typeof?: symbol }).$$typeof === MemoType;
77+
}
78+
79+
/**
80+
* Map of React component types to their specific statics
81+
*/
82+
const TYPE_STATICS: Record<symbol, Record<string, boolean>> = {};
83+
TYPE_STATICS[ForwardRefType] = FORWARD_REF_STATICS;
84+
TYPE_STATICS[MemoType] = MEMO_STATICS;
85+
86+
/**
87+
* Get the appropriate statics object for a given component
88+
*/
89+
function getStatics(component: React.ComponentType<unknown>): Record<string, boolean> {
90+
// React v16.11 and below
91+
if (isMemo(component)) {
92+
return MEMO_STATICS;
93+
}
94+
95+
// React v16.12 and above
96+
const componentType = (component as { $$typeof?: symbol }).$$typeof;
97+
return (componentType && TYPE_STATICS[componentType]) || REACT_STATICS;
98+
}
99+
100+
const defineProperty = Object.defineProperty.bind(Object);
101+
const getOwnPropertyNames = Object.getOwnPropertyNames.bind(Object);
102+
const getOwnPropertySymbols = Object.getOwnPropertySymbols?.bind(Object);
103+
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor.bind(Object);
104+
const getPrototypeOf = Object.getPrototypeOf.bind(Object);
105+
const objectPrototype = Object.prototype;
106+
107+
/**
108+
* Copies non-react specific statics from a child component to a parent component.
109+
* Similar to Object.assign, but copies all static properties from source to target,
110+
* excluding React-specific statics and known JavaScript statics.
111+
*
112+
* @param targetComponent - The component to copy statics to
113+
* @param sourceComponent - The component to copy statics from
114+
* @param excludelist - An optional object of keys to exclude from hoisting
115+
* @returns The target component with hoisted statics
116+
*/
117+
export function hoistNonReactStatics<
118+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
119+
T extends React.ComponentType<any>,
120+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
121+
S extends React.ComponentType<any>,
122+
C extends Record<string, boolean> = Record<string, never>
123+
>(
124+
targetComponent: T,
125+
sourceComponent: S,
126+
excludelist?: C
127+
): T {
128+
if (typeof sourceComponent !== 'string') {
129+
// Don't hoist over string (html) components
130+
if (objectPrototype) {
131+
const inheritedComponent = getPrototypeOf(sourceComponent);
132+
133+
if (inheritedComponent && inheritedComponent !== objectPrototype) {
134+
hoistNonReactStatics(targetComponent, inheritedComponent, excludelist);
135+
}
136+
}
137+
138+
let keys: (string | symbol)[] = getOwnPropertyNames(sourceComponent);
139+
140+
if (getOwnPropertySymbols) {
141+
keys = keys.concat(getOwnPropertySymbols(sourceComponent));
142+
}
143+
144+
const targetStatics = getStatics(targetComponent);
145+
const sourceStatics = getStatics(sourceComponent);
146+
147+
for (const key of keys) {
148+
const keyStr = String(key);
149+
if (
150+
!KNOWN_STATICS[keyStr as keyof typeof KNOWN_STATICS] &&
151+
!excludelist?.[keyStr] &&
152+
!sourceStatics?.[keyStr] &&
153+
!targetStatics?.[keyStr] &&
154+
!getOwnPropertyDescriptor(targetComponent, key) // Don't overwrite existing properties
155+
) {
156+
const descriptor = getOwnPropertyDescriptor(sourceComponent, key);
157+
158+
if (descriptor) {
159+
try {
160+
// Avoid failures from read-only properties
161+
defineProperty(targetComponent, key, descriptor);
162+
} catch (e) {
163+
// Silently ignore errors
164+
}
165+
}
166+
}
167+
}
168+
}
169+
170+
return targetComponent;
171+
}

0 commit comments

Comments
 (0)