Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
753b852
Merge pull request #2 from reactrewind/dev
victorvrv Mar 1, 2019
1027fd2
Merge pull request #3 from victorvrv/master
victorvrv Mar 1, 2019
fcb0c1e
commit to handle merges
victorvrv Mar 1, 2019
bbdf7d7
Minor change in how React app connect actions to the state and action…
victorvrv Mar 1, 2019
509c3dc
Delete devtools_bundle.js
victorvrv Mar 2, 2019
1bc9f2e
Delete devtools_bundle.js.map
victorvrv Mar 2, 2019
8537156
added doubly linked list data structure
victorvrv Mar 2, 2019
8afe649
Merge branch 'master' of https://github.com/victorvrv/react-rewind
victorvrv Mar 2, 2019
aecd748
Merge pull request #4 from reactrewind/dev
victorvrv Mar 2, 2019
6c07872
Solved merge conficts
victorvrv Mar 2, 2019
9a0dae3
Merge pull request #6 from reactrewind/dev
victorvrv Mar 2, 2019
d95fea0
Added buttons back and forth time travel functionality
victorvrv Mar 2, 2019
1f59e3b
Merge branch 'dev' of https://github.com/victorvrv/react-rewind
victorvrv Mar 2, 2019
43d9933
Merge branch 'victor-dev'
victorvrv Mar 2, 2019
0039064
Merge last PR with the current chrome dev tools version.
victorvrv Mar 2, 2019
7a0780f
removed console.log from extension
victorvrv Mar 2, 2019
86e2dbd
deleted console.log
victorvrv Mar 2, 2019
e4fea21
Merge pull request #7 from reactrewind/dev
victorvrv Mar 4, 2019
73e4455
Minor change to background, extension and inject_script. Commiting be…
victorvrv Mar 4, 2019
9eed7c1
merged parser pull
victorvrv Mar 4, 2019
81bc3a9
Merge pull request #8 from reactrewind/dev
victorvrv Mar 4, 2019
28e3349
Merge pull request #9 from reactrewind/dev
victorvrv Mar 4, 2019
49ecd68
commiting handle of merge conflicts before new pull
victorvrv Mar 4, 2019
85a6236
Merge branch 'dev' of https://github.com/victorvrv/react-rewind into …
victorvrv Mar 4, 2019
457d39a
Connecting parser and chrome dev tools. Code is full of console.logs.
victorvrv Mar 4, 2019
fec8eae
Merge pull request #10 from victorvrv/victor-dev
victorvrv Mar 4, 2019
d064c77
Merge pull request #11 from reactrewind/dev
victorvrv Mar 6, 2019
208fa48
Connected dev tools with page. Injected script working, parsing worki…
victorvrv Mar 6, 2019
c989af1
Merge branch 'dev' of https://github.com/victorvrv/react-rewind into …
victorvrv Mar 6, 2019
4d3b80e
fixed wrong type for expected Number on TimeSlider
victorvrv Mar 7, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,646 changes: 2,598 additions & 48 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"main": "index.js",
"scripts": {
"lint": "eslint \"*/**/*.{js,jsx}\"",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "echo \"Error: no test specified\" && exit 1",
"bundle_bg": "browserify src/browser/chrome/background.js -o src/browser/chrome/devtools_bundle/bg_bundle.js"
},
"repository": {
"type": "git",
Expand Down
19 changes: 11 additions & 8 deletions src/app/components/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ class App extends Component {

// functionality to change 'play' button to 'stop'
setIsPlaying() {
if (this.state.isPlayingIndex === this.state.data.length - 1) {
this.state.isPlayingIndex = 0;
if (this.state.isPlayingIndex > this.state.data.length - 1) {
this.setState({ isPlayingIndex: 0 });
}

console.log('isplaying')
console.log('isplaying');
let { isPlaying } = this.state;
isPlaying = !isPlaying;
this.setState({ isPlaying });
Expand All @@ -98,14 +98,17 @@ class App extends Component {
}

actionInPlay() {
this.isPlayingIndex++;
let { isPlayingIndex } = this.state;
if (isPlayingIndex >= this.state.data.length - 1) isPlayingIndex = 0;

this.setState({ isPlayingIndex: isPlayingIndex + 1 });
const { id, action, state } = this.state.data[isPlayingIndex + 1];

const { id, action, state } = this.state.data[this.isPlayingIndex];
setTimeout(() => {
this.setState((prev, props) => {
return { ...prev, id, action, state }
return { ...prev, id, action, state };
});
if (this.state.isPlaying && this.isPlayingIndex < this.state.data.length - 1) {
if (this.state.isPlaying && isPlayingIndex + 1 < this.state.data.length - 1) {
this.actionInPlay();
} else {
this.setState({ isPlaying: false });
Expand Down Expand Up @@ -142,7 +145,7 @@ class App extends Component {
id,
action,
state,
isPlayingIndex: e.target.value,
isPlayingIndex: parseInt(e.target.value),
});
}

Expand Down
6 changes: 3 additions & 3 deletions src/app/container/TimeSlider.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ const TimeSlider = (props) => {
<>
<SliderWrapper>
<Button onClick={setIsRecording}>{isRecording ? 'PAUSE' : 'RECORD'}</Button>
<Button onClick={setIsPlaying}>{ isPlaying ? <text>||</text> : <text>&#9658;</text> }</Button>
<input type="range" min="0" max={data.length - 1} value={isPlayingIndex}
onChange={handleBarChange} />
<Button onClick={setIsPlaying}>{isPlaying ? '||' : '►'}</Button>
<input type="range" min="0" max={data.length - 1} value={isPlayingIndex}
onChange={handleBarChange} />
</SliderWrapper>
</>
);
Expand Down
50 changes: 28 additions & 22 deletions src/browser/chrome/background.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const parseAndGenerate = require('./parser');
const parseAndGenerate = require('./scripts/parser');

chrome.tabs.onUpdated.addListener((id, info, tab) => {
if (tab.status !== 'complete' || tab.url.startsWith('chrome')) return;
Expand All @@ -10,31 +10,37 @@ chrome.tabs.onUpdated.addListener((id, info, tab) => {
});
});

chrome.runtime.onMessage.addListener((msg) => {
if (msg.react_check) {
console.log('Background got the react_check! Resending...');

chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, msg);
});
}
});

let reqIndex = 0;
const urlCache = {};
chrome.webRequest.onBeforeRequest.addListener(
(request) => {
if (request.type === 'script' && !request.url.startsWith('chrome')) {
console.log('redirecting... ORIGINAL: ', request);
fetch(request.url)
.then(r => r.text())
.then((codeString) => {
const editedCode = parseAndGenerate(codeString);
if (!editedCode) return { redirectUrl: request.url };
//TODO: Define sendMessage and redirectURL
// sendMessageToContent(editedCode);
// return { redirectUrl: 'javascript:' };
});
if (request.type === 'script' && !request.url.startsWith('chrome')
&& request.frameId === 0) {
// TODO: adjust comment
// Else we need to check wether or not this contains the react
// library. If it does, we need to send the edit javascript to
// out content script, so it can inject into the page. If it doesnt,
// we need to send the url to our content script so that it can
// add it to the page <script src=URL> AND add it to our cache, so
// that when we intercept it, we dont block it.
const syncRequest = new XMLHttpRequest();
syncRequest.open('GET', request.url, false);
syncRequest.send(null);
console.log(`Status: ${syncRequest.status} - Size of response: ${syncRequest.responseText.length}`);

sendMessageToContent(parseAndGenerate(syncRequest.responseText));

return { redirectUrl: 'javascript:' };
}
},
{ urls: ['<all_urls>'] },
['blocking'],
);

function sendMessageToContent(codeString) {
const index = reqIndex++;
console.log(`Sending request ${index}.`);
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, { codeString, index });
});
}
2 changes: 2 additions & 0 deletions src/browser/chrome/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ const port = chrome.runtime.connect({
});

port.onMessage.addListener((msg) => {
// This is where we get messages from the App component.
// We get an object { type: 'TIMETRAVEL', direction: 'forward' }
window.postMessage(msg);
});

Expand Down
4 changes: 2 additions & 2 deletions src/browser/chrome/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
"default_popup": "page_action.html"
},
"background": {
"scripts": ["bundle.js"]
"scripts": ["devtools_bundle/bg_bundle.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>", "http://*/*", "https://*/*"],
"matches": ["<all_urls>"],
"js": ["scripts/inject_script_tags.js"],
"run_at":"document_start"
}
Expand Down
35 changes: 35 additions & 0 deletions src/browser/chrome/scripts/inject_script_tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,38 @@ timeTravelScript.src = chrome.runtime.getURL('scripts/time_travel.js');
linkedListScript.onload = timeTravelScript.onload = function removeScriptTag() {
this.remove();
};

const scriptsToParse = [];
let lastReceivedMsgTime = null;

chrome.runtime.onMessage.addListener(scheduleWork);

function scheduleWork(work) {
// We want to add the scripts to a work array. When its been 100s
// without receiving any new scripts, then we sort the array and
// add all scripts to the page.
if (work) scriptsToParse.push(work);

if (Date.now() - lastReceivedMsgTime > 100 && lastReceivedMsgTime !== null) {
lastReceivedMsgTime = null;
addScriptToPage();
} else {
if (work) lastReceivedMsgTime = Date.now();
setTimeout(scheduleWork, 50);
}
}

function addScriptToPage() {
// First we sort the array by index, then we add everything to page
scriptsToParse.sort((a, b) => a.index - b.index);
while (scriptsToParse.length > 0) {
const { codeString } = scriptsToParse.shift();

const script = document.createElement('script');
script.innerHTML = codeString;
(document.head || document.documentElement).appendChild(script);
script.onload = function removeScriptTag() {
this.remove();
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ const escodegen = require('escodegen');
const _ = require('lodash');

// declare functions to insert
// TODO: Un-comment timeTravelTracker
function useReducerReplacement() {
const dispatcher = resolveDispatcher();
function reducerWithTracker(state, action) {
const newState = reducer(state, action);
// timeTravelTracker[timeTravelTracker.length - 1].actionDispatched = true;
timeTravelTracker[timeTravelTracker.length - 1].actionDispatched = true;
window.postMessage({
type: 'DISPATCH',
data: {
Expand All @@ -22,7 +21,15 @@ function useReducerReplacement() {
}
return dispatcher.useReducer(reducerWithTracker, initialArg, init);
}

function commitAllHostEffectsReplacement() {
if (Object.keys(funcStorage).length === 0) {
funcStorage.commitDeletion = commitDeletion;
funcStorage.commitPlacement = commitPlacement;
funcStorage.commitWork = commitWork;
funcStorage.prepareUpdate = prepareUpdate;
}

while (nextEffect !== null) {
{
setCurrentFiber(nextEffect);
Expand Down Expand Up @@ -51,12 +58,9 @@ function commitAllHostEffectsReplacement() {
case Placement:
{
// editbyme
window.postMessage({
type: 'EFFECT',
data: {
timeTravelTracker.push({
primaryEffectTag: 'PLACEMENT',
effect: _.cloneDeep(nextEffect),
},
});

commitPlacement(nextEffect);
Expand Down Expand Up @@ -84,13 +88,10 @@ function commitAllHostEffectsReplacement() {
case Update:
{
// editbyme
window.postMessage({
type: 'EFFECT',
data: {
primaryEffectTag: 'UPDATE',
effect: _.cloneDeep(nextEffect),
current: _.cloneDeep(nextEffect.alternate),
},
timeTravelTracker.push({
primaryEffectTag: 'UPDATE',
effect: _.cloneDeep(nextEffect),
current: _.cloneDeep(nextEffect.alternate),
});

let _current2 = nextEffect.alternate;
Expand All @@ -100,12 +101,9 @@ function commitAllHostEffectsReplacement() {
case Deletion:
{
// editbyme
window.postMessage({
type: 'EFFECT',
data: {
primaryEffectTag: 'DELETION',
effect: _.cloneDeep(nextEffect),
},
timeTravelTracker.push({
primaryEffectTag: 'DELETION',
effect: _.cloneDeep(nextEffect),
});

commitDeletion(nextEffect);
Expand All @@ -122,34 +120,37 @@ function commitAllHostEffectsReplacement() {

// traverse ast to find method and replace body with our node's body
function traverseTree(replacementNode, functionName, ast) {
console.log('traverse called');
estraverse.replace(ast, {
enter(node) {
if (node.type === 'FunctionDeclaration') {
if (node.id.name === functionName) {
node.body = replacementNode.body[0].body;
console.log('From parser. REPLACING!', node.id.name);
}
}
},
});
}

const parseAndGenerate = (codeString) => {
if (codeString.search('react')) {
if (codeString.search('react') !== -1) {
const ast = esprima.parseModule(codeString);

// parse react-dom code
if (codeString.search('react-dom')) {
const injectableCommitAllHostEffects = esprima.parseScript(commitAllHostEffectsReplacement.toString());
traverseTree(injectableCommitAllHostEffects, 'commitAllHostEffects', ast);
} else {
// parse react code
const injectableUseReducer = esprima.parseScript(useReducerReplacement.toString());
traverseTree(injectableUseReducer, 'useReducer', ast);
}
const injectableCommitAllHostEffects = esprima.parseScript(commitAllHostEffectsReplacement.toString());
traverseTree(injectableCommitAllHostEffects, 'commitAllHostEffects', ast);

// parse react code
const injectableUseReducer = esprima.parseScript(useReducerReplacement.toString());
traverseTree(injectableUseReducer, 'useReducer', ast);

const code = escodegen.generate(ast);
console.log('returning code.');
return code;
}
return -1;
console.log('returning string.');
return codeString;
};

// }
module.exports = parseAndGenerate;
Loading