Skip to content

Commit

Permalink
Merge pull request open-source-labs#24 from oslabs-beta/staging
Browse files Browse the repository at this point in the history
Staging
  • Loading branch information
crperezt committed Jun 29, 2020
2 parents a2b5286 + 8e77467 commit b079e27
Show file tree
Hide file tree
Showing 13 changed files with 337 additions and 173 deletions.
3 changes: 3 additions & 0 deletions dev-reactime/astParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ const JSXParser = acorn.Parser.extend(jsx());
// Helper function to grab the getters/setters from `elementType`
module.exports = elementType => {
// Initialize empty object to store the setters and getter
console.log('entered ast parser');
//console.log('elementType: ', elementType);
let ast = JSXParser.parse(elementType);
//console.log('ast:', ast);
const hookState = {};

while (Object.hasOwnProperty.call(ast, 'body')) {
Expand Down
11 changes: 9 additions & 2 deletions dev-reactime/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,31 @@
*/
import 'core-js';
import 'regenerator-runtime/runtime';
import { exist } from 'acorn-jsx/xhtml';

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


const mode = {
jumping: false,
paused: false,
locked: false,
};

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


function getRouteURL(node) {
if (node.name === 'Router') {
return node.state.location.pathname;
}
if (node.children && node.children.length >= 1) {
const tempNode = node.children;
for (let index = 0; index < tempNode.length; index += 1) {
return getRouteURL(tempNode[index]);
return getRouteURL(tempNode[index]); // Carlos: ???
}
}
}
Expand All @@ -33,9 +37,12 @@ function getRouteURL(node) {
window.addEventListener('message', ({ data: { action, payload } }) => {
switch (action) {
case 'jumpToSnap':
console.log('payload in jumpToSnap', payload);
timeJump(payload); // * This sets state with given payload
// Get the pathname from payload and add new entry to browser history
// MORE: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState

// try to modify workInProgress tree from here
window.history.pushState('', '', getRouteURL(payload));
break;
case 'setLock':
Expand All @@ -49,5 +56,5 @@ window.addEventListener('message', ({ data: { action, payload } }) => {
}
});

//module.exports = linkFiber;
// module.exports = linkFiber;
export default linkFiber;
179 changes: 84 additions & 95 deletions dev-reactime/linkFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,149 +42,123 @@
/* eslint-disable no-param-reassign */

const Tree = require('./tree');
const astParser = require('./astParser');
const { saveState } = require('./masterState');
const componentActionsRecord = require('./masterState');

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

function sendSnapshot() {
async function sendSnapshot() {
// Don't send messages while jumping or while paused
// DEV: So that when we are jumping to an old snapshot it
if (mode.jumping || mode.paused) return;
const payload = snap.tree.getCopy();
window.postMessage({
action: 'recordSnap',
payload,
});
}

function changeSetState(component) {
if (component.setState.linkFiberChanged) return;

// Persist the old setState and bind to component so we can continue to setState({})
const oldSetState = component.setState.bind(component);

component.setState = (state, callback = () => {}) => {
// Don't do anything if state is locked UNLESS we are currently jumping through time
if (mode.locked && !mode.jumping) return;
// Continue normal setState functionality, with middleware in callback
oldSetState(state, () => {
updateSnapShotTree();
sendSnapshot();
callback.bind(component)();
// console.log('PAYLOAD: before cleaning', snap.tree);
const payload = snap.tree.cleanTreeCopy();// snap.tree.getCopy();
// console.log('PAYLOAD: after cleaning', payload);
try {
await window.postMessage({
action: 'recordSnap',
payload,
});
};
// Set a custom property to ensure we don't change this method again
component.setState.linkFiberChanged = true;
}

function changeUseState(component) {
if (component.queue.dispatch.linkFiberChanged) return;

// Persist the old dispatch and bind to component so we can continue to dispatch()
const oldDispatch = component.queue.dispatch.bind(component.queue);

component.queue.dispatch = (fiber, queue, action) => {
if (mode.locked && !mode.jumping) return;
oldDispatch(fiber, queue, action);
// * Uncomment setTimeout to prevent snapshot lag-effect
// * (i.e. getting the prior snapshot on each state change)
// setTimeout(() => {
updateSnapShotTree();
sendSnapshot();
// }, 100);
};
// Set a custom property to ensure we don't change this method again
component.queue.dispatch.linkFiberChanged = true;
} catch (e) {
console.log('failed to send postMessage:', e);
}
}

// TODO: WE NEED TO CLEAN IT UP A BIT
// Carlos: Injects instrumentation to update our state tree every time
// a hooks component changes state
function traverseHooks(memoizedState) {
// Declare variables and assigned to 0th index and an empty object, respectively
const memoized = {};
let index = 0;
astHooks = Object.values(astHooks);
// While memoizedState is truthy, save the value to the object
const hooksComponents = [];
while (memoizedState && memoizedState.queue) {
// // prevents useEffect from crashing on load
// Carlos: these two are legacy comments, we should look into them later
// prevents useEffect from crashing on load
// if (memoizedState.next.queue === null) { // prevents double pushing snapshot updates
changeUseState(memoizedState);
// }
// memoized[astHooks[index]] = memoizedState.memoizedState;
memoized[astHooks[index]] = memoizedState.memoizedState;
// Reassign memoizedState to its next value
memoizedState = memoizedState.next;
// See astParser.js for explanation of this increment
index += 2;
if (memoizedState.memoizedState) {
console.log('memoizedState in traverseHooks is:', memoizedState);
hooksComponents.push({
component: memoizedState.queue,
state: memoizedState.memoizedState,
});
}
// console.log('GOT STATE', memoizedState.memoizedState);
memoizedState = memoizedState.next !== memoizedState
? memoizedState.next : null;
}
return memoized;
return hooksComponents;
}

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

// These have the newest state. We update state and then
// called updateSnapshotTree()
const {
sibling,
stateNode,
child,
memoizedState,
elementType,
tag,
} = currentFiber;

let nextTree = tree;

// Check if stateful component
if (stateNode && stateNode.state) {
nextTree = tree.appendChild(stateNode); // Add component to tree
changeSetState(stateNode); // Change setState functionality
let index;
// Check if node is a stateful component
if (stateNode && stateNode.state && (tag === 0 || tag === 1)) {
// Save component's state and setState() function to our record for future
// time-travel state changing. Add record index to snapshot so we can retrieve.
index = componentActionsRecord.saveNew(stateNode.state, stateNode);
tree.appendChild(stateNode.state, elementType.name, index); // Add component to tree
} else {
// grab stateless components here
}

// Check if the component uses hooks
if (
memoizedState
&& Object.hasOwnProperty.call(memoizedState, 'baseState')
) {
// 'catch-all' for suspense elements (experimental)
if (typeof elementType.$$typeof === 'symbol') return;
// Traverse through the currentFiber and extract the getters/setters
astHooks = astParser(elementType);
saveState(astHooks);
// Create a traversed property and assign to the evaluated result of
// invoking traverseHooks with memoizedState
memoizedState.traversed = traverseHooks(memoizedState);
nextTree = tree.appendChild(memoizedState);
// Check if node is a hooks function
if (memoizedState && (tag === 0 || tag === 1 || tag === 10)) {
if (memoizedState.queue) {
const hooksComponents = traverseHooks(memoizedState);
hooksComponents.forEach(c => {
if (elementType.name) {
index = componentActionsRecord.saveNew(c.state, c.component);
tree.appendChild(c.state, elementType.name ? elementType.name : 'nameless', index);
}
});
}
}

// Recurse on siblings
createTree(sibling, tree);
// Recurse on children
createTree(child, nextTree);
if (tree.children.length > 0) {
createTree(child, tree.children[0]);
} else {
createTree(child, tree);
}

return tree;
}

// ! BUG: skips 1st hook click
async function updateSnapShotTree() {
let current;
function updateSnapShotTree() {
/* let current;
// If concurrent mode, grab current.child
if (concurrent) {
// we need a way to wait for current child to populate
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(fiberRoot.current.child), 400);
});

current = await promise;

current = fiberRoot.current.child;
} else {
current = fiberRoot.current;
}
} */
const { current } = fiberRoot; // Carlos: get rid of concurrent mode for now

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

return async container => {
Expand All @@ -199,15 +173,30 @@ module.exports = (snap, mode) => {
} = container;
// Only assign internal root if it actually exists
fiberRoot = _internalRoot || _reactRootContainer;
console.log('linkFiber.js, fiberRoot:', fiberRoot);
// console.log('_reactRootContainer is:', _reactRootContainer);
// console.log('linkFiber.js, fiberRoot:', fiberRoot);
}

await updateSnapShotTree();
const devTools = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
const reactInstance = devTools ? devTools.renderers.get(1) : null;
const overrideHookState = reactInstance ? reactInstance.overrideHookState : null;
console.log('DEVTOOLS:', devTools);
console.log('roots:', reactInstance.getCurrentFiber())

if (reactInstance && reactInstance.version) {
devTools.onCommitFiberRoot = (function (original) {
return function (...args) {
fiberRoot = args[1];
updateSnapShotTree();
sendSnapshot();
return original(...args);
};
}(devTools.onCommitFiberRoot));
}
updateSnapShotTree();
// Send the initial snapshot once the content script has started up
// This message is sent from contentScript.js in chrome extension bundles
window.addEventListener('message', ({ data: { action } }) => {
if (action === 'contentScriptStarted') {
console.log('linkFiber.js received contentScriptStarted message, sending snapshot');
sendSnapshot();
}
});
Expand Down
29 changes: 26 additions & 3 deletions dev-reactime/masterState.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
/* eslint-disable no-plusplus */
/* eslint-disable guard-for-in */
/* eslint-disable no-restricted-syntax */
// Export two functions that either saves the AST state object into an array
// or returns the array for use
const masterState = [];

const componentActionsRecord = {};
let index = 0;

module.exports = {
saveNew: (state, component) => {
componentActionsRecord[index] = { state, component };
index++;
return index - 1;
},
getRecordByIndex: inputIndex => componentActionsRecord[inputIndex],
getComponentByIndex: inputIndex => (componentActionsRecord[inputIndex]
? componentActionsRecord[inputIndex].component
: undefined),
};


/* const masterState = [];
const hooksComponentsActions = {};
module.exports = {
saveState: state => {
Expand All @@ -12,4 +29,10 @@ module.exports = {
return masterState;
},
returnState: () => masterState,
saveHooksComponent: stateAndAction => {
for (const elementName in stateAndAction) {
hooksComponentsActions[elementName] = stateAndAction[elementName];
}
},
};
*/
9 changes: 9 additions & 0 deletions dev-reactime/node_modules/fs/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b079e27

Please sign in to comment.