1
+ /**
2
+ * This file contains core module functionality.
3
+ *
4
+ * It exports an anonymous
5
+ * @function
6
+ * that is invoked on
7
+ * @param snap --> Current snapshot
8
+ * @param mode --> Current mode (jumping i.e. time-traveling, locked, or paused)
9
+ * and @returns a function to be invoked on the rootContainer HTMLElement
10
+ *
11
+ * @function updateSnapShotTree
12
+ * --> Middleware #1: Updates snap object with latest snapshot
13
+ *
14
+ * @function sendSnapshot
15
+ * --> Middleware #2: Gets a copy of the current snapshot state tree and posts it to the window
16
+ *
17
+ * @function changeSetState
18
+ * @param component : stateNode property on a stateful class component's FiberNode object
19
+ * --> Binds class component setState method to the component
20
+ * --> Injects middleware into class component's setState method
21
+ *
22
+ * @function changeUseState
23
+ * @param component : memoizedState property on a stateful functional component's FiberNode object
24
+ * --> Binds functional component dispatch method to the component
25
+ * --> Injects middleware into component's dispatch method
26
+ * Note: dispatch is hook equivalent to setState()
27
+ *
28
+ * @function traverseHooks
29
+ * @param memoizedState : memoizedState property on a stateful functional component's FiberNode object
30
+ * --> Helper function to traverse through memoizedState
31
+ * --> Invokes @changeUseState on each stateful functional component
32
+ *
33
+ * @function createTree
34
+ * @param currentFiber : a FiberNode object
35
+ * --> Recursive function to traverse from FiberRootNode and create
36
+ * an instance of custom Tree class and build up state snapshot
37
+ */
38
+
1
39
/* eslint-disable no-underscore-dangle */
2
40
/* eslint-disable func-names */
3
41
/* eslint-disable no-use-before-define */
4
42
/* eslint-disable no-param-reassign */
5
- // links component state tree to library
6
- // changes the setState method to also update our snapshot
43
+
7
44
const Tree = require ( './tree' ) ;
8
45
const astParser = require ( './astParser' ) ;
9
46
const { saveState } = require ( './masterState' ) ;
@@ -27,67 +64,69 @@ module.exports = (snap, mode) => {
27
64
}
28
65
29
66
function changeSetState ( component ) {
30
- // check that setState hasn't been changed yet
31
67
if ( component . setState . linkFiberChanged ) return ;
32
- // make a copy of setState
68
+
69
+ // Persist the old setState and bind to component so we can continue to setState({})
33
70
const oldSetState = component . setState . bind ( component ) ;
34
- // replace component's setState so developer doesn't change syntax
35
- // component.setState = newSetState.bind(component);
71
+
36
72
component . setState = ( state , callback = ( ) => { } ) => {
37
- // don't do anything if state is locked
38
- // UNLESS we are currently jumping through time
73
+ // Don't do anything if state is locked UNLESS we are currently jumping through time
39
74
if ( mode . locked && ! mode . jumping ) return ;
40
- // continue normal setState functionality, except add sending message middleware
75
+ // Continue normal setState functionality, with middleware in callback
41
76
oldSetState ( state , ( ) => {
42
77
updateSnapShotTree ( ) ;
43
78
sendSnapshot ( ) ;
44
79
callback . bind ( component ) ( ) ;
45
80
} ) ;
46
81
} ;
82
+ // Set a custom property to ensure we don't change this method again
47
83
component . setState . linkFiberChanged = true ;
48
84
}
49
85
50
86
function changeUseState ( component ) {
51
87
if ( component . queue . dispatch . linkFiberChanged ) return ;
52
- // store the original dispatch function definition
88
+
89
+ // Persist the old dispatch and bind to component so we can continue to dispatch()
53
90
const oldDispatch = component . queue . dispatch . bind ( component . queue ) ;
54
- // redefine the dispatch function so we can inject our code
91
+
55
92
component . queue . dispatch = ( fiber , queue , action ) => {
56
- // don't do anything if state is locked
57
93
if ( mode . locked && ! mode . jumping ) return ;
58
94
oldDispatch ( fiber , queue , action ) ;
95
+ // * Uncomment setTimeout to prevent snapshot lag-effect
96
+ // * (i.e. getting the prior snapshot on each state change)
59
97
// setTimeout(() => {
60
98
updateSnapShotTree ( ) ;
61
99
sendSnapshot ( ) ;
62
100
// }, 100);
63
101
} ;
102
+ // Set a custom property to ensure we don't change this method again
64
103
component . queue . dispatch . linkFiberChanged = true ;
65
104
}
66
105
67
- // Helper function to traverse through the memoized state
68
106
// TODO: WE NEED TO CLEAN IT UP A BIT
69
107
function traverseHooks ( memoizedState ) {
70
108
// Declare variables and assigned to 0th index and an empty object, respectively
71
109
const memoized = { } ;
72
110
let index = 0 ;
73
111
astHooks = Object . values ( astHooks ) ;
74
- // while memoizedState is truthy, save the value to the object
112
+ // While memoizedState is truthy, save the value to the object
75
113
while ( memoizedState && memoizedState . queue ) {
76
- // prevents useEffect from crashing on load
114
+ // // prevents useEffect from crashing on load
77
115
// if (memoizedState.next.queue === null) { // prevents double pushing snapshot updates
78
116
changeUseState ( memoizedState ) ;
79
117
// }
80
118
// memoized[astHooks[index]] = memoizedState.memoizedState;
81
119
memoized [ astHooks [ index ] ] = memoizedState . memoizedState ;
82
120
// Reassign memoizedState to its next value
83
121
memoizedState = memoizedState . next ;
84
- // Increment the index by 2
122
+ // See astParser.js for explanation of this increment
85
123
index += 2 ;
86
124
}
87
125
return memoized ;
88
126
}
89
127
90
128
function createTree ( currentFiber , tree = new Tree ( 'root' ) ) {
129
+ // Base case: child or sibling pointed to null
91
130
if ( ! currentFiber ) return tree ;
92
131
93
132
const {
@@ -99,16 +138,14 @@ module.exports = (snap, mode) => {
99
138
} = currentFiber ;
100
139
101
140
let nextTree = tree ;
102
- // check if stateful component
141
+
142
+ // Check if stateful component
103
143
if ( stateNode && stateNode . state ) {
104
- // add component to tree
105
- nextTree = tree . appendChild ( stateNode ) ;
106
- // change setState functionality
107
- changeSetState ( stateNode ) ;
144
+ nextTree = tree . appendChild ( stateNode ) ; // Add component to tree
145
+ changeSetState ( stateNode ) ; // Change setState functionality
108
146
}
109
- // Check if the component uses hooks
110
- // console.log("memoizedState", memoizedState);
111
147
148
+ // Check if the component uses hooks
112
149
if (
113
150
memoizedState &&
114
151
Object . hasOwnProperty . call ( memoizedState , 'baseState' )
@@ -123,18 +160,19 @@ module.exports = (snap, mode) => {
123
160
memoizedState . traversed = traverseHooks ( memoizedState ) ;
124
161
nextTree = tree . appendChild ( memoizedState ) ;
125
162
}
126
- // iterate through siblings
163
+
164
+ // Recurse on siblings
127
165
createTree ( sibling , tree ) ;
128
- // iterate through children
166
+ // Recurse on children
129
167
createTree ( child , nextTree ) ;
130
168
131
169
return tree ;
132
170
}
133
- // runs when page initially loads
134
- // but skips 1st hook click
171
+
172
+ // ! BUG: skips 1st hook click
135
173
async function updateSnapShotTree ( ) {
136
174
let current ;
137
- // if concurrent mode, grab current.child'
175
+ // If concurrent mode, grab current.child
138
176
if ( concurrent ) {
139
177
// we need a way to wait for current child to populate
140
178
const promise = new Promise ( ( resolve , reject ) => {
@@ -152,6 +190,7 @@ module.exports = (snap, mode) => {
152
190
}
153
191
154
192
return async container => {
193
+ // Point fiberRoot to FiberRootNode
155
194
if ( container . _internalRoot ) {
156
195
fiberRoot = container . _internalRoot ;
157
196
concurrent = true ;
@@ -160,12 +199,13 @@ module.exports = (snap, mode) => {
160
199
_reactRootContainer : { _internalRoot } ,
161
200
_reactRootContainer,
162
201
} = container ;
163
- // only assign internal root if it actually exists
202
+ // Only assign internal root if it actually exists
164
203
fiberRoot = _internalRoot || _reactRootContainer ;
165
204
}
166
205
167
206
await updateSnapShotTree ( ) ;
168
- // send the initial snapshot once the content script has started up
207
+ // Send the initial snapshot once the content script has started up
208
+ // This message is sent from contentScript.js in chrome extension bundles
169
209
window . addEventListener ( 'message' , ( { data : { action } } ) => {
170
210
if ( action === 'contentScriptStarted' ) sendSnapshot ( ) ;
171
211
} ) ;
0 commit comments