Skip to content

Commit b079e27

Browse files
authored
Merge pull request #24 from oslabs-beta/staging
Staging
2 parents a2b5286 + 8e77467 commit b079e27

File tree

13 files changed

+337
-173
lines changed

13 files changed

+337
-173
lines changed

dev-reactime/astParser.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ const JSXParser = acorn.Parser.extend(jsx());
88
// Helper function to grab the getters/setters from `elementType`
99
module.exports = elementType => {
1010
// Initialize empty object to store the setters and getter
11+
console.log('entered ast parser');
12+
//console.log('elementType: ', elementType);
1113
let ast = JSXParser.parse(elementType);
14+
//console.log('ast:', ast);
1215
const hookState = {};
1316

1417
while (Object.hasOwnProperty.call(ast, 'body')) {

dev-reactime/index.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,31 @@
44
*/
55
import 'core-js';
66
import 'regenerator-runtime/runtime';
7+
import { exist } from 'acorn-jsx/xhtml';
78

89
// * State snapshot object initialized here
910
const snapShot = { tree: null };
1011

12+
1113
const mode = {
1214
jumping: false,
1315
paused: false,
1416
locked: false,
1517
};
1618

1719
const linkFiber = require('./linkFiber')(snapShot, mode);
20+
console.log('import timeJump in index.js:', JSON.parse(JSON.stringify(snapShot)));
1821
const timeJump = require('./timeJump')(snapShot, mode);
1922

23+
2024
function getRouteURL(node) {
2125
if (node.name === 'Router') {
2226
return node.state.location.pathname;
2327
}
2428
if (node.children && node.children.length >= 1) {
2529
const tempNode = node.children;
2630
for (let index = 0; index < tempNode.length; index += 1) {
27-
return getRouteURL(tempNode[index]);
31+
return getRouteURL(tempNode[index]); // Carlos: ???
2832
}
2933
}
3034
}
@@ -33,9 +37,12 @@ function getRouteURL(node) {
3337
window.addEventListener('message', ({ data: { action, payload } }) => {
3438
switch (action) {
3539
case 'jumpToSnap':
40+
console.log('payload in jumpToSnap', payload);
3641
timeJump(payload); // * This sets state with given payload
3742
// Get the pathname from payload and add new entry to browser history
3843
// MORE: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
44+
45+
// try to modify workInProgress tree from here
3946
window.history.pushState('', '', getRouteURL(payload));
4047
break;
4148
case 'setLock':
@@ -49,5 +56,5 @@ window.addEventListener('message', ({ data: { action, payload } }) => {
4956
}
5057
});
5158

52-
//module.exports = linkFiber;
59+
// module.exports = linkFiber;
5360
export default linkFiber;

dev-reactime/linkFiber.js

Lines changed: 84 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -42,149 +42,123 @@
4242
/* eslint-disable no-param-reassign */
4343

4444
const Tree = require('./tree');
45-
const astParser = require('./astParser');
46-
const { saveState } = require('./masterState');
45+
const componentActionsRecord = require('./masterState');
4746

4847
module.exports = (snap, mode) => {
4948
let fiberRoot = null;
5049
let astHooks;
5150
let concurrent = false; // flag to check if we are in concurrent mode
5251

53-
function sendSnapshot() {
52+
async function sendSnapshot() {
5453
// Don't send messages while jumping or while paused
55-
// DEV: So that when we are jumping to an old snapshot it
5654
if (mode.jumping || mode.paused) return;
57-
const payload = snap.tree.getCopy();
58-
window.postMessage({
59-
action: 'recordSnap',
60-
payload,
61-
});
62-
}
63-
64-
function changeSetState(component) {
65-
if (component.setState.linkFiberChanged) return;
66-
67-
// Persist the old setState and bind to component so we can continue to setState({})
68-
const oldSetState = component.setState.bind(component);
69-
70-
component.setState = (state, callback = () => {}) => {
71-
// Don't do anything if state is locked UNLESS we are currently jumping through time
72-
if (mode.locked && !mode.jumping) return;
73-
// Continue normal setState functionality, with middleware in callback
74-
oldSetState(state, () => {
75-
updateSnapShotTree();
76-
sendSnapshot();
77-
callback.bind(component)();
55+
// console.log('PAYLOAD: before cleaning', snap.tree);
56+
const payload = snap.tree.cleanTreeCopy();// snap.tree.getCopy();
57+
// console.log('PAYLOAD: after cleaning', payload);
58+
try {
59+
await window.postMessage({
60+
action: 'recordSnap',
61+
payload,
7862
});
79-
};
80-
// Set a custom property to ensure we don't change this method again
81-
component.setState.linkFiberChanged = true;
82-
}
83-
84-
function changeUseState(component) {
85-
if (component.queue.dispatch.linkFiberChanged) return;
86-
87-
// Persist the old dispatch and bind to component so we can continue to dispatch()
88-
const oldDispatch = component.queue.dispatch.bind(component.queue);
89-
90-
component.queue.dispatch = (fiber, queue, action) => {
91-
if (mode.locked && !mode.jumping) return;
92-
oldDispatch(fiber, queue, action);
93-
// * Uncomment setTimeout to prevent snapshot lag-effect
94-
// * (i.e. getting the prior snapshot on each state change)
95-
// setTimeout(() => {
96-
updateSnapShotTree();
97-
sendSnapshot();
98-
// }, 100);
99-
};
100-
// Set a custom property to ensure we don't change this method again
101-
component.queue.dispatch.linkFiberChanged = true;
63+
} catch (e) {
64+
console.log('failed to send postMessage:', e);
65+
}
10266
}
10367

104-
// TODO: WE NEED TO CLEAN IT UP A BIT
68+
// Carlos: Injects instrumentation to update our state tree every time
69+
// a hooks component changes state
10570
function traverseHooks(memoizedState) {
106-
// Declare variables and assigned to 0th index and an empty object, respectively
107-
const memoized = {};
108-
let index = 0;
109-
astHooks = Object.values(astHooks);
110-
// While memoizedState is truthy, save the value to the object
71+
const hooksComponents = [];
11172
while (memoizedState && memoizedState.queue) {
112-
// // prevents useEffect from crashing on load
73+
// Carlos: these two are legacy comments, we should look into them later
74+
// prevents useEffect from crashing on load
11375
// if (memoizedState.next.queue === null) { // prevents double pushing snapshot updates
114-
changeUseState(memoizedState);
115-
// }
116-
// memoized[astHooks[index]] = memoizedState.memoizedState;
117-
memoized[astHooks[index]] = memoizedState.memoizedState;
118-
// Reassign memoizedState to its next value
119-
memoizedState = memoizedState.next;
120-
// See astParser.js for explanation of this increment
121-
index += 2;
76+
if (memoizedState.memoizedState) {
77+
console.log('memoizedState in traverseHooks is:', memoizedState);
78+
hooksComponents.push({
79+
component: memoizedState.queue,
80+
state: memoizedState.memoizedState,
81+
});
82+
}
83+
// console.log('GOT STATE', memoizedState.memoizedState);
84+
memoizedState = memoizedState.next !== memoizedState
85+
? memoizedState.next : null;
12286
}
123-
return memoized;
87+
return hooksComponents;
12488
}
12589

90+
// Carlos: This runs after EVERY Fiber commit. It creates a new snapshot,
91+
//
12692
function createTree(currentFiber, tree = new Tree('root')) {
12793
// Base case: child or sibling pointed to null
12894
if (!currentFiber) return tree;
12995

96+
// These have the newest state. We update state and then
97+
// called updateSnapshotTree()
13098
const {
13199
sibling,
132100
stateNode,
133101
child,
134102
memoizedState,
135103
elementType,
104+
tag,
136105
} = currentFiber;
137106

138-
let nextTree = tree;
139-
140-
// Check if stateful component
141-
if (stateNode && stateNode.state) {
142-
nextTree = tree.appendChild(stateNode); // Add component to tree
143-
changeSetState(stateNode); // Change setState functionality
107+
let index;
108+
// Check if node is a stateful component
109+
if (stateNode && stateNode.state && (tag === 0 || tag === 1)) {
110+
// Save component's state and setState() function to our record for future
111+
// time-travel state changing. Add record index to snapshot so we can retrieve.
112+
index = componentActionsRecord.saveNew(stateNode.state, stateNode);
113+
tree.appendChild(stateNode.state, elementType.name, index); // Add component to tree
114+
} else {
115+
// grab stateless components here
144116
}
145117

146-
// Check if the component uses hooks
147-
if (
148-
memoizedState
149-
&& Object.hasOwnProperty.call(memoizedState, 'baseState')
150-
) {
151-
// 'catch-all' for suspense elements (experimental)
152-
if (typeof elementType.$$typeof === 'symbol') return;
153-
// Traverse through the currentFiber and extract the getters/setters
154-
astHooks = astParser(elementType);
155-
saveState(astHooks);
156-
// Create a traversed property and assign to the evaluated result of
157-
// invoking traverseHooks with memoizedState
158-
memoizedState.traversed = traverseHooks(memoizedState);
159-
nextTree = tree.appendChild(memoizedState);
118+
// Check if node is a hooks function
119+
if (memoizedState && (tag === 0 || tag === 1 || tag === 10)) {
120+
if (memoizedState.queue) {
121+
const hooksComponents = traverseHooks(memoizedState);
122+
hooksComponents.forEach(c => {
123+
if (elementType.name) {
124+
index = componentActionsRecord.saveNew(c.state, c.component);
125+
tree.appendChild(c.state, elementType.name ? elementType.name : 'nameless', index);
126+
}
127+
});
128+
}
160129
}
161130

162131
// Recurse on siblings
163132
createTree(sibling, tree);
164133
// Recurse on children
165-
createTree(child, nextTree);
134+
if (tree.children.length > 0) {
135+
createTree(child, tree.children[0]);
136+
} else {
137+
createTree(child, tree);
138+
}
166139

167140
return tree;
168141
}
169142

170143
// ! BUG: skips 1st hook click
171-
async function updateSnapShotTree() {
172-
let current;
144+
function updateSnapShotTree() {
145+
/* let current;
173146
// If concurrent mode, grab current.child
174147
if (concurrent) {
175148
// we need a way to wait for current child to populate
176149
const promise = new Promise((resolve, reject) => {
177150
setTimeout(() => resolve(fiberRoot.current.child), 400);
178151
});
179-
180152
current = await promise;
181-
182153
current = fiberRoot.current.child;
183154
} else {
184155
current = fiberRoot.current;
185-
}
156+
} */
157+
const { current } = fiberRoot; // Carlos: get rid of concurrent mode for now
186158

187-
snap.tree = createTree(current);
159+
// console.log('FIBER COMMITTED, new fiber is:', util.inspect(current, false, 4));
160+
// fs.appendFile('fiberlog.txt', util.inspect(current, false, 10));
161+
snap.tree = createTree(current); // Carlos: pass new hooks state here?
188162
}
189163

190164
return async container => {
@@ -199,15 +173,30 @@ module.exports = (snap, mode) => {
199173
} = container;
200174
// Only assign internal root if it actually exists
201175
fiberRoot = _internalRoot || _reactRootContainer;
202-
console.log('linkFiber.js, fiberRoot:', fiberRoot);
176+
// console.log('_reactRootContainer is:', _reactRootContainer);
177+
// console.log('linkFiber.js, fiberRoot:', fiberRoot);
203178
}
204-
205-
await updateSnapShotTree();
179+
const devTools = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
180+
const reactInstance = devTools ? devTools.renderers.get(1) : null;
181+
const overrideHookState = reactInstance ? reactInstance.overrideHookState : null;
182+
console.log('DEVTOOLS:', devTools);
183+
console.log('roots:', reactInstance.getCurrentFiber())
184+
185+
if (reactInstance && reactInstance.version) {
186+
devTools.onCommitFiberRoot = (function (original) {
187+
return function (...args) {
188+
fiberRoot = args[1];
189+
updateSnapShotTree();
190+
sendSnapshot();
191+
return original(...args);
192+
};
193+
}(devTools.onCommitFiberRoot));
194+
}
195+
updateSnapShotTree();
206196
// Send the initial snapshot once the content script has started up
207197
// This message is sent from contentScript.js in chrome extension bundles
208198
window.addEventListener('message', ({ data: { action } }) => {
209199
if (action === 'contentScriptStarted') {
210-
console.log('linkFiber.js received contentScriptStarted message, sending snapshot');
211200
sendSnapshot();
212201
}
213202
});

dev-reactime/masterState.js

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,25 @@
1+
/* eslint-disable no-plusplus */
12
/* eslint-disable guard-for-in */
23
/* eslint-disable no-restricted-syntax */
3-
// Export two functions that either saves the AST state object into an array
4-
// or returns the array for use
5-
const masterState = [];
4+
5+
const componentActionsRecord = {};
6+
let index = 0;
7+
8+
module.exports = {
9+
saveNew: (state, component) => {
10+
componentActionsRecord[index] = { state, component };
11+
index++;
12+
return index - 1;
13+
},
14+
getRecordByIndex: inputIndex => componentActionsRecord[inputIndex],
15+
getComponentByIndex: inputIndex => (componentActionsRecord[inputIndex]
16+
? componentActionsRecord[inputIndex].component
17+
: undefined),
18+
};
19+
20+
21+
/* const masterState = [];
22+
const hooksComponentsActions = {};
623
724
module.exports = {
825
saveState: state => {
@@ -12,4 +29,10 @@ module.exports = {
1229
return masterState;
1330
},
1431
returnState: () => masterState,
32+
saveHooksComponent: stateAndAction => {
33+
for (const elementName in stateAndAction) {
34+
hooksComponentsActions[elementName] = stateAndAction[elementName];
35+
}
36+
},
1537
};
38+
*/

dev-reactime/node_modules/fs/README.md

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)