react-component-snapshot 0.0.1 → 0.0.3

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
@@ -12,61 +12,54 @@ declare global {
12
12
  | FiberNodeForInstrinsicElement
13
13
  | FiberNodeForTextNode
14
14
 
15
- export interface FiberRootNode {
16
- child: FiberNode
15
+ interface FiberType {
16
+ child: FiberNode | null
17
17
  sibling: FiberNode | null
18
18
 
19
+ tag: number
20
+ return?: FiberRootNode | FiberNode
21
+ }
22
+
23
+ export interface FiberRootNode extends FiberType {
24
+ child: FiberNode
25
+
19
26
  elementType: React.FunctionComponent
20
27
  type: React.FunctionComponent
21
28
 
22
29
  stateNode: {
23
30
  current: FiberRootNode
24
31
  }
25
- return?: FiberRootNode // | null
32
+ return?: FiberRootNode
26
33
  }
27
34
 
28
- export interface FiberNodeForFunctionComponent {
29
- child: FiberNode | null
30
- sibling: FiberNode | null
31
-
35
+ export interface FiberNodeForFunctionComponent extends FiberType {
32
36
  elementType: React.FunctionComponent
33
37
  type: React.FunctionComponent
34
38
 
35
39
  stateNode: null
36
- return?: FiberNode // | null
37
40
  }
38
41
 
39
- export interface FiberNodeForClassComponent {
40
- child: FiberNode | null
41
- sibling: FiberNode | null
42
-
42
+ export interface FiberNodeForClassComponent extends FiberType {
43
43
  elementType: React.ComponentClass
44
44
  type: React.ComponentClass
45
45
 
46
46
  stateNode: React.Component
47
- return?: FiberNode // | null
48
47
  }
49
48
 
50
- export interface FiberNodeForInstrinsicElement {
51
- child: FiberNode | null
52
- sibling: FiberNode | null
53
-
49
+ export interface FiberNodeForInstrinsicElement extends FiberType {
54
50
  elementType: keyof React.JSX.IntrinsicElements
55
51
  type: keyof React.JSX.IntrinsicElements
56
52
 
57
53
  stateNode: HTMLElement
58
- return?: FiberNode
59
54
  }
60
55
 
61
- export interface FiberNodeForTextNode {
56
+ export interface FiberNodeForTextNode extends FiberType {
62
57
  child: null
63
- sibling: FiberNode | null
64
58
 
65
59
  elementType: null
66
60
  type: null
67
61
 
68
62
  stateNode: Text
69
- return?: FiberNode
70
63
  }
71
64
  }
72
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-component-snapshot",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Transform a React component to match its snapshot in Jest",
5
5
  "keywords": [
6
6
  "react",
package/src/index.mjs CHANGED
@@ -24,6 +24,14 @@ export function isReactPropsKey (key) {
24
24
  return String(key).startsWith('__reactProps$')
25
25
  }
26
26
 
27
+ /**
28
+ * @param {unknown} v
29
+ * @returns {v is object}
30
+ */
31
+ function isObject (v) {
32
+ return (v || false) instanceof Object && !Array.isArray(v)
33
+ }
34
+
27
35
  /**
28
36
  * @param {Element | Text | React.Component<any, any, any>} stateNode
29
37
  * @returns {string | undefined}
@@ -47,8 +55,8 @@ export function getReactPropsKey (stateNode) {
47
55
  }
48
56
 
49
57
  /**
50
- * @param {Element | Text | React.Component<any, any, any>} stateNode
51
- * @returns {Record<PropertyKey, unknown> | null}
58
+ * @param {Element | Text | React.Component<any, any, any>} stateNode
59
+ * @returns {Record<PropertyKey, unknown> | null}
52
60
  */
53
61
  export function getStateNodeFiber (stateNode) {
54
62
  const key = getReactFiberKey(stateNode)
@@ -65,8 +73,8 @@ export function getStateNodeFiber (stateNode) {
65
73
  }
66
74
 
67
75
  /**
68
- * @param {React.Component<any, any, any> | Element | Text} stateNode
69
- * @returns {Record<PropertyKey, unknown> | null}
76
+ * @param {React.Component<any, any, any> | Element | Text} stateNode
77
+ * @returns {Record<PropertyKey, unknown> | null}
70
78
  */
71
79
  export function getStateNodeProps (stateNode) {
72
80
  const key = getReactPropsKey(stateNode)
@@ -82,6 +90,18 @@ export function getStateNodeProps (stateNode) {
82
90
  return null
83
91
  }
84
92
 
93
+ /**
94
+ * @param {{ container: { firstElementChild: Element | null } }} component
95
+ * @returns {Element | null}
96
+ */
97
+ export function getComponentElement ({
98
+ container: {
99
+ firstElementChild: element
100
+ }
101
+ }) {
102
+ return element
103
+ }
104
+
85
105
  /**
86
106
  * @param {Element} element
87
107
  * @returns {FiberNode | null}
@@ -133,8 +153,8 @@ function getProps (fiberNode) {
133
153
  Object.fromEntries(
134
154
  Object.entries(props)
135
155
  .filter(([key]) => key !== 'children')
136
- .filter(([key, value]) => { // @ts-expect-error
137
- if ((value || false) instanceof Object && !Array.isArray(value)) return !('current' in value)
156
+ .filter(([key, value]) => {
157
+ if (isObject(value)) return !('current' in value)
138
158
  return true
139
159
  })
140
160
  )
@@ -148,7 +168,7 @@ function getProps (fiberNode) {
148
168
  * @param {FiberNode} fiberNode
149
169
  * @returns {Array<string | FiberNode>}
150
170
  */
151
- function getChildren (fiberNode) {
171
+ function getChildrenOf (fiberNode) {
152
172
  const child = fiberNode.child
153
173
 
154
174
  if (child) {
@@ -195,6 +215,17 @@ function toChildren (child) {
195
215
  )
196
216
  }
197
217
 
218
+ /**
219
+ * @param {FiberNode} fiberNode
220
+ * @returns {Array<string | Record<PropertyKey, unknown>>}
221
+ */
222
+ function getChildren (fiberNode) {
223
+ return (
224
+ getChildrenOf(fiberNode)
225
+ .map(toChildren)
226
+ )
227
+ }
228
+
198
229
  /**
199
230
  * @param {FiberNode} fiberNode
200
231
  * @returns {Record<PropertyKey, unknown>}
@@ -202,10 +233,7 @@ function toChildren (child) {
202
233
  function transform (fiberNode) {
203
234
  const type = getType(fiberNode)
204
235
  const props = getProps(fiberNode)
205
- const children = (
206
- getChildren(fiberNode)
207
- .map(toChildren)
208
- )
236
+ const children = getChildren(fiberNode)
209
237
 
210
238
  return (
211
239
  Object.defineProperty({
@@ -220,8 +248,8 @@ function transform (fiberNode) {
220
248
  }
221
249
 
222
250
  /**
223
- * @param {Element | null | undefined} element
224
- * @returns {Record<PropertyKey, unknown> | null}
251
+ * @param {Element | null | undefined} element
252
+ * @returns {Record<PropertyKey, unknown> | null}
225
253
  */
226
254
  export default function snapshotOf (element) {
227
255
  if (element instanceof HTMLElement) {
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ export const REACT_LEGACY_ELEMENT_TYPE = Symbol.for('react.element')
9
+ export const REACT_ELEMENT_TYPE = Symbol.for('react.transitional.element')
10
+ export const REACT_PORTAL_TYPE = Symbol.for('react.portal')
11
+ export const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment')
12
+ export const REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode')
13
+ export const REACT_PROFILER_TYPE = Symbol.for('react.profiler')
14
+ export const REACT_PROVIDER_TYPE = Symbol.for('react.provider')
15
+ export const REACT_CONSUMER_TYPE = Symbol.for('react.consumer')
16
+ export const REACT_CONTEXT_TYPE = Symbol.for('react.context')
17
+ export const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref')
18
+ export const REACT_SUSPENSE_TYPE = Symbol.for('react.suspense')
19
+ export const REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list')
20
+ export const REACT_MEMO_TYPE = Symbol.for('react.memo')
21
+ export const REACT_LAZY_TYPE = Symbol.for('react.lazy')
22
+ export const REACT_SCOPE_TYPE = Symbol.for('react.scope')
23
+ export const REACT_ACTIVITY_TYPE = Symbol.for('react.activity')
24
+ export const REACT_LEGACY_HIDDEN_TYPE = Symbol.for('react.legacy_hidden')
25
+ export const REACT_TRACING_MARKER_TYPE = Symbol.for('react.tracing_marker')
26
+ export const REACT_MEMO_CACHE_SENTINEL = Symbol.for('react.memo_cache_sentinel')
27
+ export const REACT_POSTPONE_TYPE = Symbol.for('react.postpone')
28
+ export const REACT_VIEW_TRANSITION_TYPE = Symbol.for('react.view_transition')
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import {
9
+ REACT_CONTEXT_TYPE,
10
+ REACT_CONSUMER_TYPE,
11
+ REACT_FORWARD_REF_TYPE,
12
+ REACT_FRAGMENT_TYPE,
13
+ REACT_PORTAL_TYPE,
14
+ REACT_MEMO_TYPE,
15
+ REACT_PROFILER_TYPE,
16
+ REACT_PROVIDER_TYPE,
17
+ REACT_STRICT_MODE_TYPE,
18
+ REACT_SUSPENSE_TYPE,
19
+ REACT_SUSPENSE_LIST_TYPE,
20
+ REACT_LAZY_TYPE,
21
+ REACT_TRACING_MARKER_TYPE,
22
+ REACT_VIEW_TRANSITION_TYPE,
23
+ REACT_ACTIVITY_TYPE
24
+ } from './ReactSymbols.mjs'
25
+
26
+ /**
27
+ * @param {{displayName?: string | null}} outerType
28
+ * @param {{displayName?: string | null, name?: string | null}} innerType
29
+ * @param {string} wrapperName
30
+ * @returns
31
+ */
32
+ function getWrappedName (
33
+ outerType,
34
+ innerType,
35
+ wrapperName
36
+ ) {
37
+ const outerDisplayName = outerType.displayName
38
+
39
+ if (outerDisplayName) {
40
+ return outerDisplayName
41
+ }
42
+
43
+ const innerDisplayName = innerType.displayName || innerType.name || null
44
+
45
+ if (innerDisplayName) {
46
+ return `${wrapperName}(${innerDisplayName})`
47
+ }
48
+
49
+ return wrapperName
50
+ }
51
+
52
+ /**
53
+ * @param {{displayName?: string | null}} type
54
+ * @returns {string}
55
+ */
56
+ function getContextName (type) {
57
+ return type.displayName || 'Context'
58
+ }
59
+
60
+ const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference')
61
+
62
+ /**
63
+ * @param {unknown} type
64
+ * @returns {string | null}
65
+ */
66
+ export default function getComponentNameFromType (type) {
67
+ if (type == null) return null
68
+
69
+ if (typeof type === 'function') { // @ts-expect-error
70
+ if (type.$$typeof === REACT_CLIENT_REFERENCE) return null
71
+ return ( // @ts-expect-error
72
+ type.displayName || type.name || null
73
+ )
74
+ }
75
+
76
+ if (typeof type === 'string') return type
77
+
78
+ switch (type) {
79
+ case REACT_FRAGMENT_TYPE:
80
+ return 'Fragment'
81
+ case REACT_PROFILER_TYPE:
82
+ return 'Profiler'
83
+ case REACT_STRICT_MODE_TYPE:
84
+ return 'StrictMode'
85
+ case REACT_SUSPENSE_TYPE:
86
+ return 'Suspense'
87
+ case REACT_SUSPENSE_LIST_TYPE:
88
+ return 'SuspenseList'
89
+ case REACT_ACTIVITY_TYPE:
90
+ return 'Activity'
91
+ case REACT_VIEW_TRANSITION_TYPE:
92
+ return 'ViewTransition'
93
+ case REACT_TRACING_MARKER_TYPE:
94
+ return 'TracingMarker'
95
+ }
96
+
97
+ if (typeof type === 'object') { // @ts-expect-error
98
+ switch (type.$$typeof) {
99
+ case REACT_PORTAL_TYPE:
100
+ return 'Portal'
101
+
102
+ case REACT_PROVIDER_TYPE: // @ts-expect-error
103
+ return getContextName(type._context) + '.Provider'
104
+
105
+ case REACT_CONTEXT_TYPE:
106
+ return getContextName(type) + '.Context'
107
+
108
+ case REACT_CONSUMER_TYPE: // @ts-expect-error
109
+ return getContextName(type._context) + '.Consumer'
110
+
111
+ case REACT_FORWARD_REF_TYPE: // @ts-expect-error
112
+ return getWrappedName(type, type.render, 'ForwardRef')
113
+
114
+ case REACT_MEMO_TYPE:
115
+ {
116
+ // @ts-expect-error
117
+ const outerName = type.displayName || null
118
+
119
+ if (outerName !== null) {
120
+ return outerName
121
+ }
122
+
123
+ // @ts-expect-error
124
+ return getComponentNameFromType(type.type) || 'Memo'
125
+ }
126
+
127
+ case REACT_LAZY_TYPE:
128
+ {
129
+ // @ts-expect-error
130
+ const payload = type._payload
131
+
132
+ // @ts-expect-error
133
+ const init = type._init
134
+
135
+ try {
136
+ return getComponentNameFromType(init(payload))
137
+ } catch {
138
+ return null
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ return null
145
+ }
package/src/react.mjs CHANGED
@@ -1,16 +1,17 @@
1
1
  // @ts-nocheck
2
2
 
3
+ /**
4
+ * @typedef {ReactComponentSnapshotTypes.FiberNode} FiberNode
5
+ */
6
+
3
7
  import debug from 'debug'
4
8
 
5
9
  const log = debug('react-component-snapshot')
6
10
 
7
- // export const REACT_LEGACY_ELEMENT_TYPE = Symbol.for('react.element')
8
- // export const REACT_ELEMENT_TYPE = Symbol.for('react.transitional.element')
9
11
  export const REACT_PORTAL_TYPE = Symbol.for('react.portal')
10
12
  export const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment')
11
13
  export const REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode')
12
14
  export const REACT_PROFILER_TYPE = Symbol.for('react.profiler')
13
- // export const REACT_PROVIDER_TYPE = Symbol.for('react.provider')
14
15
  export const REACT_CONSUMER_TYPE = Symbol.for('react.consumer')
15
16
  export const REACT_CONTEXT_TYPE = Symbol.for('react.context')
16
17
  export const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref')
@@ -18,15 +19,30 @@ export const REACT_SUSPENSE_TYPE = Symbol.for('react.suspense')
18
19
  export const REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list')
19
20
  export const REACT_MEMO_TYPE = Symbol.for('react.memo')
20
21
  export const REACT_LAZY_TYPE = Symbol.for('react.lazy')
21
- // Symbol.for('react.scope')
22
- // Symbol.for('react.debug_trace_mode')
23
- export const REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen')
24
- // Symbol.for('react.legacy_hidden')
25
- // Symbol.for('react.tracing_marker')
26
- // export const REACT_MEMO_CACHE_SENTINEL = Symbol.for('react.memo_cache_sentinel')
27
- // export const MAYBE_ITERATOR_SYMBOL = Symbol.iterator
28
22
  export const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference')
29
23
 
24
+ /**
25
+ * @param {{displayName?: string | null}} outerType
26
+ * @param {{displayName?: string | null, name?: string | null}} innerType
27
+ * @param {string} refKey
28
+ * @returns {string | null}
29
+ */
30
+ function getNameFromRef (outerType, innerType, refKey) {
31
+ const outerName = outerType.displayName || null
32
+
33
+ if (outerName) return outerName
34
+
35
+ const innerName = innerType.displayName || innerType.name || null
36
+
37
+ if (innerName) return `${refKey}(${innerName})`
38
+
39
+ return refKey
40
+ }
41
+
42
+ /**
43
+ * @param {FiberNode['type']} type
44
+ * @returns {string | null}
45
+ */
30
46
  export function getNameFromType (type) {
31
47
  if (type == null) return null
32
48
 
@@ -62,39 +78,22 @@ export function getNameFromType (type) {
62
78
  case REACT_CONSUMER_TYPE:
63
79
  return (type._context.displayName || 'Context') + '.Consumer'
64
80
  case REACT_FORWARD_REF_TYPE:
65
- {
66
- const innerType = type.render
67
- type = type.displayName
68
-
69
- if (type) return type
81
+ return getNameFromRef(type, type.render, 'ForwardRef')
70
82
 
71
- type = innerType.displayName || innerType.name || ''
72
-
73
- return (
74
- type !== ''
75
- ? `ForwardRef(${type})`
76
- : 'ForwardRef'
77
- )
78
- }
79
83
  case REACT_MEMO_TYPE:
80
- {
81
- const innerType = type.displayName || null
82
-
83
84
  return (
84
- innerType !== null
85
- ? innerType
86
- : getNameFromType(type.type) || 'Memo'
85
+ type.displayName || getNameFromType(type.type) || 'Memo'
87
86
  )
88
- }
87
+
89
88
  case REACT_LAZY_TYPE:
90
89
  {
91
- const innerType = type._payload
92
- type = type._init
90
+ const payload = type._payload
91
+ const init = type._init
93
92
 
94
93
  try {
95
- return getNameFromType(type(innerType))
96
- } catch (message) {
97
- log(`Lazy error. Message was "${message}"`)
94
+ return getNameFromType(init(payload))
95
+ } catch ({ message }) {
96
+ log(`Lazy Component error. Message was "${message}"`)
98
97
  }
99
98
  }
100
99
  }
@@ -103,6 +102,10 @@ export function getNameFromType (type) {
103
102
  return null
104
103
  }
105
104
 
105
+ /**
106
+ * @param {FiberNode} fiberNode
107
+ * @returns {string | null}
108
+ */
106
109
  export function getType (fiberNode) {
107
110
  const type = fiberNode.type
108
111
 
@@ -116,14 +119,7 @@ export function getType (fiberNode) {
116
119
  case 18:
117
120
  return 'DehydratedFragment'
118
121
  case 11:
119
- {
120
- return (
121
- (fiberNode = type.render),
122
- (fiberNode = fiberNode.displayName || fiberNode.name || ''),
123
- type.displayName ||
124
- (fiberNode !== '' ? `ForwardRef(${fiberNode})` : 'ForwardRef')
125
- )
126
- }
122
+ return getNameFromRef(type, type.render, 'ForwardRef')
127
123
  case 7:
128
124
  return 'Fragment'
129
125
  case 26:
@@ -162,16 +158,16 @@ export function getType (fiberNode) {
162
158
  case 15:
163
159
  if (typeof type === 'function') return type.displayName || type.name || null
164
160
  if (typeof type === 'string') return type
165
- break
161
+ return null
166
162
  case 29:
167
163
  {
168
- const type = fiberNode._debugInfo || null
164
+ const info = fiberNode._debugInfo || null
169
165
 
170
- if (type != null) {
171
- let i = type.length - 1
166
+ if (info != null) {
167
+ let i = info.length - 1
172
168
 
173
169
  for (i; i >= 0; i--) {
174
- const t = type[i]
170
+ const t = info[i]
175
171
  const n = t.name
176
172
 
177
173
  if (typeof n === 'string') return n
@@ -181,5 +177,6 @@ export function getType (fiberNode) {
181
177
  if (fiberNode.return !== null) return getType(fiberNode.return)
182
178
  }
183
179
  }
180
+
184
181
  return null
185
182
  }