Skip to content

Commit e7c03b4

Browse files
authored
Merge pull request #13 from oslabs-beta/chris-10-29
added concurrent support 11/13
2 parents a70d752 + 979d5bd commit e7c03b4

File tree

3 files changed

+68
-49
lines changed

3 files changed

+68
-49
lines changed

package/astParser.js

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,47 +9,40 @@ module.exports = elementType => {
99
// Initialize empty object to store the setters and getter
1010
let ast = JSXParser.parse(elementType);
1111
const hookState = {};
12-
// All module exports will always start off as a single 'FunctionDeclaration' type
12+
1313
while (Object.hasOwnProperty.call(ast, 'body')) {
14-
// Traverse down .body once before invoking parsing logic and will loop through any .body after
1514
ast = ast.body;
1615
const statements = [];
1716

18-
function saveAstHooks(st) {
19-
st.forEach((el, i) => {
20-
if (el.match(/_use/)) hookState[el] = statements[i + 2];
21-
});
22-
}
23-
24-
function findHookDeclarations(astVal) {
25-
astVal.forEach(elem => {
17+
/** All module exports always start off as a single 'FunctionDeclaration' type
18+
* Other types: "BlockStatement" / "ExpressionStatement" / "ReturnStatement"
19+
* Iterate through AST of every function declaration
20+
* Check within each function declaration if there are hook declarations */
21+
ast.forEach(functionDec => {
22+
let body;
23+
if (functionDec.expression) body = functionDec.expression.body.body;
24+
else body = functionDec.body.body;
25+
// Traverse through the function's funcDecs and Expression Statements
26+
body.forEach(elem => {
2627
if (elem.type === 'VariableDeclaration') {
27-
elem.declarations.forEach(decClar => {
28-
statements.push(decClar.id.name);
28+
elem.declarations.forEach(hook => {
29+
statements.push(hook.id.name);
2930
});
3031
}
3132
});
32-
}
3333

34-
// handle useState useContext
35-
if (ast[0].expression.body.body) {
36-
ast = ast[0].expression.body.body;
37-
// Hook Declarations will only be under 'VariableDeclaration' type
38-
findHookDeclarations(ast);
34+
/* body will look something like:
35+
[ 0: "_useState"
36+
1: "_useState2"
37+
2: "character"
38+
3: "setCharacter" ]
39+
*/
40+
3941
// Iterate array and determine getter/setters based on pattern
40-
saveAstHooks(statements); // add key-value to hookState
41-
} else {
42-
// TODO test if this is needed, backward compatibility?
43-
// Iterate through AST of every function declaration
44-
// Check within each function declaration if there are hook declarations
45-
ast.forEach(functionDec => {
46-
const { body } = functionDec.body;
47-
// Traverse through the function's funcDecs and Expression Statements
48-
findHookDeclarations(body);
49-
// Iterate array and determine getter/setters based on pattern
50-
saveAstHooks(statements); // add key-value to hookState
42+
statements.forEach((el, i) => {
43+
if (el.match(/_use/)) hookState[el] = statements[i + 2];
5144
});
52-
}
45+
});
5346
}
5447
return hookState;
5548
};

package/linkFiber.js

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-underscore-dangle */
12
/* eslint-disable func-names */
23
/* eslint-disable no-use-before-define */
34
/* eslint-disable no-param-reassign */
@@ -10,6 +11,7 @@ const { saveState } = require('./masterState');
1011
module.exports = (snap, mode) => {
1112
let fiberRoot = null;
1213
let astHooks;
14+
let concurrent = false; // flag to check if we are in concurrent mode
1315

1416
function sendSnapshot() {
1517
// don't send messages while jumping or while paused
@@ -70,20 +72,24 @@ module.exports = (snap, mode) => {
7072
let index = 0;
7173
astHooks = Object.values(astHooks);
7274
// while memoizedState is truthy, save the value to the object
73-
while (memoizedState && memoizedState.queue) {
75+
while (memoizedState && memoizedState.queue) { // prevents useEffect from crashing on load
76+
// if (memoizedState.next.queue === null) { // prevents double pushing snapshot updates
7477
changeUseState(memoizedState);
75-
78+
// }
79+
// memoized[astHooks[index]] = memoizedState.memoizedState;
7680
memoized[astHooks[index]] = memoizedState.memoizedState;
7781
// Reassign memoizedState to its next value
7882
memoizedState = memoizedState.next;
79-
index += 2; // Increment the index by 2
83+
// Increment the index by 2
84+
index += 2;
8085
}
8186
return memoized;
8287
}
8388

8489
function createTree(currentFiber, tree = new Tree('root')) {
8590
if (!currentFiber) return tree;
8691

92+
8793
const {
8894
sibling,
8995
stateNode,
@@ -101,10 +107,11 @@ module.exports = (snap, mode) => {
101107
changeSetState(stateNode);
102108
}
103109
// Check if the component uses hooks
104-
if (
105-
memoizedState
106-
&& Object.hasOwnProperty.call(memoizedState, 'baseState')
107-
) {
110+
// console.log("memoizedState", memoizedState);
111+
112+
if (memoizedState && Object.hasOwnProperty.call(memoizedState, 'baseState')) {
113+
// 'catch-all' for suspense elements (experimental)
114+
if (typeof elementType.$$typeof === 'symbol') return;
108115
// Traverse through the currentFiber and extract the getters/setters
109116
astHooks = astParser(elementType);
110117
saveState(astHooks);
@@ -122,20 +129,39 @@ module.exports = (snap, mode) => {
122129
}
123130
// runs when page initially loads
124131
// but skips 1st hook click
125-
function updateSnapShotTree() {
126-
const { current } = fiberRoot;
132+
async function updateSnapShotTree() {
133+
let current;
134+
// if concurrent mode, grab current.child'
135+
if (concurrent) {
136+
// we need a way to wait for current child to populate
137+
const promise = new Promise((resolve, reject) => {
138+
setTimeout(() => resolve(fiberRoot.current.child), 400);
139+
});
140+
141+
current = await promise;
142+
143+
current = fiberRoot.current.child;
144+
} else {
145+
current = fiberRoot.current;
146+
}
147+
127148
snap.tree = createTree(current);
128149
}
129150

130-
return container => {
131-
const {
132-
_reactRootContainer: { _internalRoot },
133-
_reactRootContainer,
134-
} = container;
135-
// only assign internal rootp if it actually exists
136-
fiberRoot = _internalRoot || _reactRootContainer;
151+
return async container => {
152+
if (container._internalRoot) {
153+
fiberRoot = container._internalRoot;
154+
concurrent = true;
155+
} else {
156+
const {
157+
_reactRootContainer: { _internalRoot },
158+
_reactRootContainer,
159+
} = container;
160+
// only assign internal rootp if it actually exists
161+
fiberRoot = _internalRoot || _reactRootContainer;
162+
}
137163

138-
updateSnapShotTree();
164+
await updateSnapShotTree();
139165
// send the initial snapshot once the content script has started up
140166
window.addEventListener('message', ({ data: { action } }) => {
141167
if (action === 'contentScriptStarted') sendSnapshot();

src/extension/contentScript.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ let firstMessage = true;
33
// listening for messages from npm package
44
window.addEventListener('message', msg => { // runs automatically every second
55
// window listener picks up the message it sends, so we should filter
6-
// messages sent by contentscript
6+
// messages sent by contentscrip
7+
78
if (msg.data.action !== 'contentScriptStarted' && firstMessage) {
89
// since contentScript is run everytime a page is refreshed
910
// tell the background script that the tab has reloaded
@@ -21,7 +22,6 @@ window.addEventListener('message', msg => { // runs automatically every second
2122

2223
// listening for messages from the UI
2324
chrome.runtime.onMessage.addListener(request => { // seems to never fire
24-
2525
// send the message to npm package
2626
const { action } = request;
2727
switch (action) {

0 commit comments

Comments
 (0)