react-component-instance 0.0.0 → 0.0.2
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/README.md +27 -0
- package/index.d.mts +70 -1
- package/package.json +38 -9
- package/src/container/index.mjs +44 -0
- package/src/index.mjs +101 -0
- package/tsconfig.json +2 -1
- package/eslint.config.mjs +0 -36
- package/index.mjs +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# react-component-instance
|
|
2
|
+
|
|
3
|
+
Get a React class component instance from its `DOM` element
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
import getInstance from 'react-component-instance'
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## With `@testing-library/react`
|
|
10
|
+
|
|
11
|
+
**Note** that for use with _Testing Library_ import from `react-component-instance/container`
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import getInstance from 'react-component-instance/container'
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
render
|
|
18
|
+
} from '@testing-library/react'
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
container
|
|
22
|
+
} = render(
|
|
23
|
+
<Component />
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
const instance = getInstance(container)
|
|
27
|
+
```
|
package/index.d.mts
CHANGED
|
@@ -1,5 +1,74 @@
|
|
|
1
|
+
import type React from 'react'
|
|
2
|
+
|
|
1
3
|
declare global {
|
|
2
|
-
namespace ReactComponentInstanceTypes {
|
|
4
|
+
namespace ReactComponentInstanceTypes {
|
|
5
|
+
/**
|
|
6
|
+
* https://github.com/bendtherules/react-fiber-traverse
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type FiberNode =
|
|
10
|
+
| FiberNodeForComponentClass
|
|
11
|
+
| FiberNodeForFunctionComponent
|
|
12
|
+
| FiberNodeForInstrinsicElement
|
|
13
|
+
| FiberNodeForTextNode
|
|
14
|
+
|
|
15
|
+
export interface FiberRootNode {
|
|
16
|
+
child: FiberNode
|
|
17
|
+
sibling: FiberNode | null
|
|
18
|
+
|
|
19
|
+
elementType: React.FunctionComponent
|
|
20
|
+
type: React.FunctionComponent
|
|
21
|
+
|
|
22
|
+
stateNode: {
|
|
23
|
+
current: FiberRootNode
|
|
24
|
+
}
|
|
25
|
+
return: FiberRootNode
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface FiberNodeForFunctionComponent {
|
|
29
|
+
child: FiberNode | null
|
|
30
|
+
sibling: FiberNode | null
|
|
31
|
+
|
|
32
|
+
elementType: React.FunctionComponent
|
|
33
|
+
type: React.FunctionComponent
|
|
34
|
+
|
|
35
|
+
stateNode: null
|
|
36
|
+
return: FiberNode
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface FiberNodeForComponentClass {
|
|
40
|
+
child: FiberNode | null
|
|
41
|
+
sibling: FiberNode | null
|
|
42
|
+
|
|
43
|
+
elementType: React.ComponentClass
|
|
44
|
+
type: React.ComponentClass
|
|
45
|
+
|
|
46
|
+
stateNode: React.Component
|
|
47
|
+
return: FiberNode
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface FiberNodeForInstrinsicElement {
|
|
51
|
+
child: FiberNode | null
|
|
52
|
+
sibling: FiberNode | null
|
|
53
|
+
|
|
54
|
+
elementType: keyof React.JSX.IntrinsicElements
|
|
55
|
+
type: keyof React.JSX.IntrinsicElements
|
|
56
|
+
|
|
57
|
+
stateNode: HTMLElement
|
|
58
|
+
return: FiberNode
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface FiberNodeForTextNode {
|
|
62
|
+
child: null
|
|
63
|
+
sibling: FiberNode | null
|
|
64
|
+
|
|
65
|
+
elementType: null
|
|
66
|
+
type: null
|
|
67
|
+
|
|
68
|
+
stateNode: Text
|
|
69
|
+
return: FiberNode
|
|
70
|
+
}
|
|
71
|
+
}
|
|
3
72
|
}
|
|
4
73
|
|
|
5
74
|
export {}
|
package/package.json
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-component-instance",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Get a React class component instance from its DOM Element",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"class",
|
|
8
|
+
"component",
|
|
9
|
+
"instance"
|
|
10
|
+
],
|
|
11
|
+
"main": "./src/index.mjs",
|
|
5
12
|
"type": "module",
|
|
6
13
|
"types": "./index.d.mts",
|
|
7
14
|
"author": {
|
|
8
|
-
"name": "Jonathan Perry for
|
|
9
|
-
"email": "jonathanperry@
|
|
10
|
-
"url": "https://
|
|
15
|
+
"name": "Jonathan Perry for Modern Poacher Limited",
|
|
16
|
+
"email": "jonathanperry@modernpoacher.com",
|
|
17
|
+
"url": "https://modernpoacher.com"
|
|
11
18
|
},
|
|
12
19
|
"license": "ISC",
|
|
13
20
|
"engines": {
|
|
@@ -15,19 +22,41 @@
|
|
|
15
22
|
},
|
|
16
23
|
"repository": {
|
|
17
24
|
"type": "git",
|
|
18
|
-
"url": "git://github.com/
|
|
25
|
+
"url": "git://github.com/modernpoacher/react-component-instance.git"
|
|
19
26
|
},
|
|
20
27
|
"scripts": {
|
|
21
28
|
"lint": "eslint",
|
|
22
|
-
"lint:fix": "npm run lint -- --fix"
|
|
29
|
+
"lint:fix": "npm run lint -- --fix",
|
|
30
|
+
"prepare": "husky",
|
|
31
|
+
"test": "NODE_ENV=test jest"
|
|
23
32
|
},
|
|
24
33
|
"devDependencies": {
|
|
34
|
+
"@babel/core": "^7.26.10",
|
|
35
|
+
"@babel/eslint-parser": "^7.26.10",
|
|
36
|
+
"@babel/preset-env": "^7.26.9",
|
|
37
|
+
"@babel/preset-react": "^7.26.3",
|
|
25
38
|
"@sequencemedia/eslint-config-standard": "^0.2.42",
|
|
26
39
|
"@sequencemedia/eslint-config-typescript": "^0.1.60",
|
|
40
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
41
|
+
"@testing-library/react": "^16.2.0",
|
|
42
|
+
"@types/debug": "^4.1.12",
|
|
43
|
+
"@types/jest": "^29.5.14",
|
|
44
|
+
"@types/react": "^19.0.10",
|
|
45
|
+
"core-js": "^3.41.0",
|
|
27
46
|
"eslint": "^9.22.0",
|
|
28
|
-
"
|
|
47
|
+
"eslint-plugin-react": "^7.37.4",
|
|
48
|
+
"globals": "^16.0.0",
|
|
49
|
+
"husky": "^9.1.7",
|
|
50
|
+
"jest": "^29.7.0",
|
|
51
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
52
|
+
"react": "^19.0.0"
|
|
53
|
+
},
|
|
54
|
+
"imports": {
|
|
55
|
+
"#react-component-instance": "./src/index.mjs",
|
|
56
|
+
"#react-component-instance/container": "./src/container/index.mjs"
|
|
29
57
|
},
|
|
30
58
|
"exports": {
|
|
31
|
-
".": "./src/index.mjs"
|
|
59
|
+
".": "./src/index.mjs",
|
|
60
|
+
"./container": "./src/container/index.mjs"
|
|
32
61
|
}
|
|
33
62
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {ReactComponentInstanceTypes.FiberRootNode} FiberRootNode
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} key
|
|
7
|
+
* @returns {boolean}
|
|
8
|
+
*/
|
|
9
|
+
function isReactContainerKey (key) {
|
|
10
|
+
return key.startsWith('__reactContainer$')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {HTMLElement} container
|
|
15
|
+
* @returns {React.Component<any, any, any> | HTMLElement | Text | null}
|
|
16
|
+
*/
|
|
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
|
+
|
|
28
|
+
if (fiberNode) {
|
|
29
|
+
const {
|
|
30
|
+
stateNode: {
|
|
31
|
+
current: {
|
|
32
|
+
child: {
|
|
33
|
+
stateNode
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} = fiberNode
|
|
38
|
+
|
|
39
|
+
return stateNode
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
throw new Error('Component instance is not found')
|
|
44
|
+
}
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {ReactComponentInstanceTypes.FiberNode} FiberNode
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {PropertyKey} key
|
|
7
|
+
* @returns {boolean}
|
|
8
|
+
*/
|
|
9
|
+
function isReactFiberKey (key) {
|
|
10
|
+
return String(key).startsWith('__reactFiber$')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {FiberNode} fiberNode
|
|
15
|
+
* @returns {Generator<FiberNode, void, unknown>}
|
|
16
|
+
*/
|
|
17
|
+
function * traverseFiberNodeTree (fiberNode) {
|
|
18
|
+
let currentNode = fiberNode
|
|
19
|
+
|
|
20
|
+
while (true) {
|
|
21
|
+
yield currentNode
|
|
22
|
+
|
|
23
|
+
currentNode = currentNode.return
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {Element | null | undefined} element
|
|
29
|
+
* @param {(() => React.JSX.Element) | typeof React.Component} Component
|
|
30
|
+
* @returns {React.Component<any, any, any> | HTMLElement | Text | null}
|
|
31
|
+
*/
|
|
32
|
+
export function findReactComponentInstanceFor (element, Component) {
|
|
33
|
+
if (element instanceof HTMLElement) {
|
|
34
|
+
const key = (
|
|
35
|
+
Object.keys(element)
|
|
36
|
+
.find(isReactFiberKey)
|
|
37
|
+
)
|
|
38
|
+
|
|
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
|
|
49
|
+
|
|
50
|
+
if (type === Component) {
|
|
51
|
+
const {
|
|
52
|
+
stateNode
|
|
53
|
+
} = fiber
|
|
54
|
+
|
|
55
|
+
return stateNode
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
throw new Error('Component instance is not found')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
throw new Error('Element is not an Element')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {Element | null} element
|
|
69
|
+
* @returns {React.Component<any, any, any> | HTMLElement | Text | null}
|
|
70
|
+
*/
|
|
71
|
+
export default function getReactComponentInstanceFrom (element) {
|
|
72
|
+
if (element instanceof HTMLElement) {
|
|
73
|
+
const key = (
|
|
74
|
+
Object.keys(element)
|
|
75
|
+
.find(isReactFiberKey)
|
|
76
|
+
)
|
|
77
|
+
|
|
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
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
throw new Error('Component instance is not found')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
throw new Error('Element is not an Element')
|
|
101
|
+
}
|
package/tsconfig.json
CHANGED
package/eslint.config.mjs
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import globals from 'globals'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
configs as standard
|
|
5
|
-
} from '@sequencemedia/eslint-config-standard'
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
configs as typescript
|
|
9
|
-
} from '@sequencemedia/eslint-config-typescript'
|
|
10
|
-
|
|
11
|
-
export default [
|
|
12
|
-
{
|
|
13
|
-
...standard.recommended,
|
|
14
|
-
files: [
|
|
15
|
-
'**/*.{mjs,cjs,mts,cts}'
|
|
16
|
-
],
|
|
17
|
-
languageOptions: {
|
|
18
|
-
...standard.recommended.languageOptions,
|
|
19
|
-
globals: {
|
|
20
|
-
...globals.node
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
...typescript.recommended,
|
|
26
|
-
files: [
|
|
27
|
-
'**/*.{mts,cts}'
|
|
28
|
-
],
|
|
29
|
-
languageOptions: {
|
|
30
|
-
...typescript.recommended.languageOptions,
|
|
31
|
-
globals: {
|
|
32
|
-
...globals.node
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
]
|
package/index.mjs
DELETED
|
File without changes
|