react-component-instance 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.mts CHANGED
@@ -7,7 +7,7 @@ declare global {
7
7
  */
8
8
 
9
9
  export type FiberNode =
10
- | FiberNodeForComponentClass
10
+ | FiberNodeForClassComponent
11
11
  | FiberNodeForFunctionComponent
12
12
  | FiberNodeForInstrinsicElement
13
13
  | FiberNodeForTextNode
@@ -22,7 +22,7 @@ declare global {
22
22
  stateNode: {
23
23
  current: FiberRootNode
24
24
  }
25
- return: FiberRootNode
25
+ return?: FiberRootNode
26
26
  }
27
27
 
28
28
  export interface FiberNodeForFunctionComponent {
@@ -33,10 +33,10 @@ declare global {
33
33
  type: React.FunctionComponent
34
34
 
35
35
  stateNode: null
36
- return: FiberNode
36
+ return?: FiberNode
37
37
  }
38
38
 
39
- export interface FiberNodeForComponentClass {
39
+ export interface FiberNodeForClassComponent {
40
40
  child: FiberNode | null
41
41
  sibling: FiberNode | null
42
42
 
@@ -44,7 +44,7 @@ declare global {
44
44
  type: React.ComponentClass
45
45
 
46
46
  stateNode: React.Component
47
- return: FiberNode
47
+ return?: FiberNode
48
48
  }
49
49
 
50
50
  export interface FiberNodeForInstrinsicElement {
@@ -55,7 +55,7 @@ declare global {
55
55
  type: keyof React.JSX.IntrinsicElements
56
56
 
57
57
  stateNode: HTMLElement
58
- return: FiberNode
58
+ return?: FiberNode
59
59
  }
60
60
 
61
61
  export interface FiberNodeForTextNode {
@@ -66,7 +66,7 @@ declare global {
66
66
  type: null
67
67
 
68
68
  stateNode: Text
69
- return: FiberNode
69
+ return?: FiberNode
70
70
  }
71
71
  }
72
72
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-component-instance",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Get a React class component instance from its DOM Element",
5
5
  "keywords": [
6
6
  "react",
@@ -41,7 +41,8 @@
41
41
  "@testing-library/react": "^16.2.0",
42
42
  "@types/debug": "^4.1.12",
43
43
  "@types/jest": "^29.5.14",
44
- "@types/react": "^19.0.10",
44
+ "@types/prop-types": "^15.7.14",
45
+ "@types/react": "^19.0.11",
45
46
  "core-js": "^3.41.0",
46
47
  "eslint": "^9.22.0",
47
48
  "eslint-plugin-react": "^7.37.4",
@@ -53,10 +54,12 @@
53
54
  },
54
55
  "imports": {
55
56
  "#react-component-instance": "./src/index.mjs",
57
+ "#react-component-instance/common": "./src/common/index.mjs",
56
58
  "#react-component-instance/container": "./src/container/index.mjs"
57
59
  },
58
60
  "exports": {
59
61
  ".": "./src/index.mjs",
62
+ "./common": "./src/common/index.mjs",
60
63
  "./container": "./src/container/index.mjs"
61
64
  }
62
65
  }
@@ -0,0 +1,198 @@
1
+ /**
2
+ * @typedef {ReactComponentInstanceTypes.FiberNode} FiberNode
3
+ * @typedef {ReactComponentInstanceTypes.FiberRootNode} FiberRootNode
4
+ */
5
+
6
+ /**
7
+ * @param {PropertyKey} key
8
+ * @returns {boolean}
9
+ */
10
+ export function isReactFiberKey (key) {
11
+ return String(key).startsWith('__reactFiber$')
12
+ }
13
+
14
+ /**
15
+ * @param {PropertyKey} key
16
+ * @returns {boolean}
17
+ */
18
+ export function isReactPropsKey (key) {
19
+ return String(key).startsWith('__reactProps$')
20
+ }
21
+
22
+ /**
23
+ * @param {PropertyKey} key
24
+ * @returns {boolean}
25
+ */
26
+ export function isReactContainerFiberKey (key) {
27
+ return String(key).startsWith('__reactContainer$')
28
+ }
29
+
30
+ /**
31
+ * @param {Element} element
32
+ * @returns {string | undefined}
33
+ */
34
+ export function getReactFiberKey (element) {
35
+ return (
36
+ Object.keys(element)
37
+ .find(isReactFiberKey)
38
+ )
39
+ }
40
+
41
+ /**
42
+ * @param {Element} element
43
+ * @returns {string | undefined}
44
+ */
45
+ export function getReactPropsKey (element) {
46
+ return (
47
+ Object.keys(element)
48
+ .find(isReactPropsKey)
49
+ )
50
+ }
51
+
52
+ /**
53
+ * @param {Element} element
54
+ * @returns {string | undefined}
55
+ */
56
+ export function getReactContainerFiberKey (element) {
57
+ return (
58
+ Object.keys(element)
59
+ .find(isReactContainerFiberKey)
60
+ )
61
+ }
62
+
63
+ /**
64
+ * @param {Element} element
65
+ * @returns {FiberNode | null | undefined}
66
+ */
67
+ export function getFiberFrom (element) {
68
+ const key = getReactFiberKey(element)
69
+
70
+ if (key) {
71
+ const { // @ts-expect-error
72
+ [key]: fiber
73
+ } = element
74
+
75
+ return fiber
76
+ }
77
+
78
+ throw new Error('Fiber is not found')
79
+ }
80
+
81
+ /**
82
+ * @param {Element} element
83
+ * @returns {FiberNode | null | undefined}
84
+ */
85
+ export function getParentFiberFrom (element) {
86
+ const key = getReactFiberKey(element)
87
+
88
+ if (key) {
89
+ const { // @ts-expect-error
90
+ [key]: {
91
+ return: fiber
92
+ }
93
+ } = element
94
+
95
+ return fiber
96
+ }
97
+
98
+ throw new Error('Fiber is not found')
99
+ }
100
+
101
+ /**
102
+ * @param {Element} element
103
+ * @returns {FiberNode | null | undefined}
104
+ */
105
+ export function getChildFiberFrom (element) {
106
+ const key = getReactFiberKey(element)
107
+
108
+ if (key) {
109
+ const { // @ts-expect-error
110
+ [key]: {
111
+ child: fiber
112
+ }
113
+ } = element
114
+
115
+ return fiber
116
+ }
117
+
118
+ throw new Error('Fiber is not found')
119
+ }
120
+
121
+ /**
122
+ * @param {Element} element
123
+ * @returns {FiberRootNode | null | undefined}
124
+ */
125
+ export function getContainerFiberFrom (element) {
126
+ const key = getReactContainerFiberKey(element)
127
+
128
+ if (key) {
129
+ const { // @ts-expect-error
130
+ [key]: fiber
131
+ } = element
132
+
133
+ return fiber
134
+ }
135
+
136
+ throw new Error('Fiber is not found')
137
+ }
138
+
139
+ /**
140
+ * @param {FiberNode} fiber
141
+ * @returns {FiberNode | null | undefined}
142
+ */
143
+ export function getParentFiber ({ return: parentFiber }) {
144
+ return parentFiber
145
+ }
146
+
147
+ /**
148
+ * @param {FiberNode} fiber
149
+ * @returns {FiberNode | null | undefined}
150
+ */
151
+ export function getChildFiber ({ child: childFiber }) {
152
+ return childFiber
153
+ }
154
+
155
+ /**
156
+ * @param {FiberNode} fiber
157
+ * @returns {React.Component<any, any, any> | HTMLElement | Text | null}
158
+ */
159
+ export function getComponentInstance ({ stateNode: instance }) {
160
+ return instance
161
+ }
162
+
163
+ /**
164
+ * @param {(() => React.JSX.Element) | typeof React.Component} Component
165
+ * @param {FiberNode} fiber
166
+ * @returns {boolean}
167
+ */
168
+ export function isComponentType (Component, { type }) {
169
+ return Component === type
170
+ }
171
+
172
+ /**
173
+ * @param {(() => React.JSX.Element) | typeof React.Component} Component
174
+ * @returns {(fiber: FiberNode) => boolean}
175
+ */
176
+ export function getIsComponentType (Component) {
177
+ return function isComponentType ({ type }) {
178
+ return Component === type
179
+ }
180
+ }
181
+
182
+ /**
183
+ * @param {FiberNode} fiber
184
+ * @returns {Generator<FiberNode | null | undefined, null, unknown>}
185
+ */
186
+ export function * traverseFiberParents (fiber) {
187
+ let currentFiber = getParentFiber(fiber)
188
+
189
+ while (currentFiber) {
190
+ yield currentFiber
191
+
192
+ if (currentFiber) {
193
+ currentFiber = getParentFiber(currentFiber)
194
+ }
195
+ }
196
+
197
+ return null
198
+ }
@@ -2,28 +2,31 @@
2
2
  * @typedef {ReactComponentInstanceTypes.FiberRootNode} FiberRootNode
3
3
  */
4
4
 
5
+ import {
6
+ getContainerFiberFrom
7
+ } from '#react-component-instance/common'
8
+
5
9
  /**
6
- * @param {string} key
7
- * @returns {boolean}
10
+ * @param {Element | null | undefined} container
11
+ * @returns {FiberRootNode | null | undefined}
8
12
  */
9
- function isReactContainerKey (key) {
10
- return key.startsWith('__reactContainer$')
13
+ export function getContainerFiberNodeFrom (container) {
14
+ if (container instanceof HTMLElement) {
15
+ return (
16
+ getContainerFiberFrom(container)
17
+ )
18
+ }
19
+
20
+ throw new Error('Container is not an Element')
11
21
  }
12
22
 
13
23
  /**
14
- * @param {HTMLElement} container
24
+ * @param {Element | null | undefined} container
15
25
  * @returns {React.Component<any, any, any> | HTMLElement | Text | null}
16
26
  */
17
- export default function getReactComponentInstanceFrom (container) {
18
- const key = (
19
- Object.keys(container)
20
- .find(isReactContainerKey)
21
- )
22
-
23
- if (key) {
24
- const { // @ts-expect-error
25
- [key]: fiberNode
26
- } = container
27
+ export default function getComponentInstanceFrom (container) {
28
+ if (container instanceof HTMLElement) {
29
+ const fiberNode = getContainerFiberFrom(container)
27
30
 
28
31
  if (fiberNode) {
29
32
  const {
@@ -38,7 +41,9 @@ export default function getReactComponentInstanceFrom (container) {
38
41
 
39
42
  return stateNode
40
43
  }
44
+
45
+ throw new Error('Component instance is not found')
41
46
  }
42
47
 
43
- throw new Error('Component instance is not found')
48
+ throw new Error('Container is not an Element')
44
49
  }
package/src/index.mjs CHANGED
@@ -2,96 +2,102 @@
2
2
  * @typedef {ReactComponentInstanceTypes.FiberNode} FiberNode
3
3
  */
4
4
 
5
- /**
6
- * @param {PropertyKey} key
7
- * @returns {boolean}
8
- */
9
- function isReactFiberKey (key) {
10
- return String(key).startsWith('__reactFiber$')
11
- }
5
+ import {
6
+ getFiberFrom,
7
+ getParentFiberFrom,
8
+ getChildFiberFrom,
9
+ getComponentInstance,
10
+ isComponentType,
11
+ traverseFiberParents
12
+ } from '#react-component-instance/common'
12
13
 
13
14
  /**
14
- * @param {FiberNode} fiberNode
15
- * @returns {Generator<FiberNode, void, unknown>}
15
+ * @param {Element | null | undefined} element
16
+ * @param {(() => React.JSX.Element) | typeof React.Component} Component
17
+ * @returns {React.Component<any, any, any> | HTMLElement | Text | null}
16
18
  */
17
- function * traverseFiberNodeTree (fiberNode) {
18
- let currentNode = fiberNode
19
+ export function findComponentInstanceFor (element, Component) {
20
+ if (element instanceof HTMLElement) {
21
+ const fiber = getFiberFrom(element)
19
22
 
20
- while (true) {
21
- yield currentNode
23
+ if (fiber) {
24
+ if (isComponentType(Component, fiber)) {
25
+ return (
26
+ getComponentInstance(fiber)
27
+ )
28
+ }
22
29
 
23
- currentNode = currentNode.return
30
+ for (const parentFiber of traverseFiberParents(fiber)) {
31
+ if (parentFiber) {
32
+ if (isComponentType(Component, parentFiber)) {
33
+ return (
34
+ getComponentInstance(parentFiber)
35
+ )
36
+ }
37
+ }
38
+ }
39
+ }
40
+
41
+ throw new Error('Component instance is not found')
24
42
  }
43
+
44
+ throw new Error('Element is not an Element')
25
45
  }
26
46
 
27
47
  /**
28
48
  * @param {Element | null | undefined} element
29
- * @param {(() => React.JSX.Element) | typeof React.Component} Component
30
- * @returns {React.Component<any, any, any> | HTMLElement | Text | null}
49
+ * @returns {FiberNode | null | undefined}
31
50
  */
32
- export function findReactComponentInstanceFor (element, Component) {
51
+ export function getFiberNodeFrom (element) {
33
52
  if (element instanceof HTMLElement) {
34
- const key = (
35
- Object.keys(element)
36
- .find(isReactFiberKey)
53
+ return (
54
+ getFiberFrom(element)
37
55
  )
56
+ }
38
57
 
39
- if (key) {
40
- const { // @ts-expect-error
41
- [key]: fiberNode
42
- } = element
43
-
44
- if (fiberNode) {
45
- for (const fiber of traverseFiberNodeTree(fiberNode)) {
46
- const {
47
- type
48
- } = fiber
58
+ throw new Error('Element is not an Element')
59
+ }
49
60
 
50
- if (type === Component) {
51
- const {
52
- stateNode
53
- } = fiber
61
+ /**
62
+ * @param {Element | null | undefined} element
63
+ * @returns {FiberNode | null | undefined}
64
+ */
65
+ export function getParentFiberNodeFrom (element) {
66
+ if (element instanceof HTMLElement) {
67
+ return (
68
+ getParentFiberFrom(element)
69
+ )
70
+ }
54
71
 
55
- return stateNode
56
- }
57
- }
58
- }
59
- }
72
+ throw new Error('Element is not an Element')
73
+ }
60
74
 
61
- throw new Error('Component instance is not found')
75
+ /**
76
+ * @param {Element | null | undefined} element
77
+ * @returns {FiberNode | null | undefined}
78
+ */
79
+ export function getChildFiberNodeFrom (element) {
80
+ if (element instanceof HTMLElement) {
81
+ return (
82
+ getChildFiberFrom(element)
83
+ )
62
84
  }
63
85
 
64
86
  throw new Error('Element is not an Element')
65
87
  }
66
88
 
67
89
  /**
68
- * @param {Element | null} element
90
+ * @param {Element | null | undefined} element
69
91
  * @returns {React.Component<any, any, any> | HTMLElement | Text | null}
70
92
  */
71
- export default function getReactComponentInstanceFrom (element) {
93
+ export default function getComponentInstanceFrom (element) {
72
94
  if (element instanceof HTMLElement) {
73
- const key = (
74
- Object.keys(element)
75
- .find(isReactFiberKey)
76
- )
95
+ const parentFiber = getParentFiberFrom(element)
77
96
 
78
- if (key) {
79
- const { // @ts-expect-error
80
- [key]: fiberNode
81
- } = element
82
-
83
- if (fiberNode) {
84
- /**
85
- * `return` is the component which owns `element`
86
- */
87
- const {
88
- return: {
89
- stateNode
90
- }
91
- } = fiberNode
92
-
93
- return stateNode
94
- }
97
+ if (parentFiber) {
98
+ return (
99
+ getComponentInstance(parentFiber)
100
+ )
95
101
  }
96
102
 
97
103
  throw new Error('Component instance is not found')