Skip to content

Commit 2f2e249

Browse files
authored
Merge pull request #155 from open-source-labs/david/dev-update
Bringing dev branch up to date with the latest master code
2 parents 2340dc7 + afc59e4 commit 2f2e249

22 files changed

+275
-131
lines changed

.gitignore

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@ parents
77
coverage
88
src/extension/build.zip
99
src/extension/build.crx
10-
src/extension/build/key.pem
11-
bower_components
12-
package/__tests__/astParser.test.js
10+
src/extension/build.pem
11+
bower_components

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
FROM node:10.16.2
2-
WORKDIR /usr/src/app
2+
WORKDIR /usr/src/app
33
COPY package*.json ./
44
RUN npm i

build.zip

228 KB
Binary file not shown.

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"build": "webpack --mode production",
66
"dev": "webpack --mode development --watch",
77
"test": "jest --verbose --coverage --watchAll",
8-
"lint": "eslint --ext .js --ext .jsx src"
8+
"docker-test-lint": "eslint --ext .js --ext .jsx src"
99
},
1010
"keywords": [
1111
"react",
@@ -22,8 +22,12 @@
2222
"contributors": [
2323
"Andy Wong",
2424
"Bryan Lee",
25+
"Chris Flannery",
2526
"David Chai",
2627
"Josh Kim",
28+
"Prasanna Malla",
29+
"Rajeeb Banstola",
30+
"Rocky Lin",
2731
"Ruth Anam",
2832
"Ryan Dang",
2933
"Sierra Swaby",

package/astParser.js

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
const acorn = require('acorn');
1+
/* eslint-disable no-inner-declarations */
2+
const acorn = require('acorn'); // javascript parser
23
// eslint-disable-next-line import/newline-after-import
34
const jsx = require('acorn-jsx');
45
const JSXParser = acorn.Parser.extend(jsx());
@@ -8,30 +9,32 @@ module.exports = elementType => {
89
// Initialize empty object to store the setters and getter
910
let ast = JSXParser.parse(elementType);
1011
const hookState = {};
11-
// All module exports will always start off as a single 'FunctionDeclaration' type
12+
1213
while (Object.hasOwnProperty.call(ast, 'body')) {
13-
// Traverse down .body once before invoking parsing logic and will loop through any .body after
1414
ast = ast.body;
15-
// Iterate through AST of every function declaration
16-
// Check within each function declaration if there are hook declarations
15+
const statements = [];
16+
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 */
1721
ast.forEach(functionDec => {
18-
const { body } = functionDec.body;
19-
const statements = [];
22+
let body;
23+
if (functionDec.expression) body = functionDec.expression.body.body;
24+
else body = functionDec.body.body;
2025
// Traverse through the function's funcDecs and Expression Statements
21-
body.forEach(program => {
22-
// Hook Declarations will only be under 'VariableDeclaration' type
23-
if (program.type === 'VariableDeclaration') {
24-
program.declarations.forEach(dec => {
25-
statements.push(dec.id.name);
26+
body.forEach(elem => {
27+
if (elem.type === 'VariableDeclaration') {
28+
elem.declarations.forEach(hook => {
29+
statements.push(hook.id.name);
2630
});
2731
}
2832
});
29-
// Iterate through the array and determine getter/setters based on pattern
30-
for (let i = 0; i < statements.length; i += 1) {
31-
if (statements[i].match(/_use/)) {
32-
hookState[statements[i]] = statements[i + 2];
33-
}
34-
}
33+
34+
// Iterate array and determine getter/setters based on pattern
35+
statements.forEach((el, i) => {
36+
if (el.match(/_use/)) hookState[el] = statements[i + 2];
37+
});
3538
});
3639
}
3740
return hookState;

package/index.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,25 @@ const mode = {
99
const linkFiber = require('./linkFiber')(snapShot, mode);
1010
const timeJump = require('./timeJump')(snapShot, mode);
1111

12-
window.addEventListener('message', ({ data: { action, payload } }) => {
12+
function getRouteURL(node) {
13+
if (node.name === 'Router') {
14+
return node.state.location.pathname;
15+
}
16+
if (node.children && node.children.length >= 1) {
17+
const tempNode = node.children;
18+
for (let index = 0; index < tempNode.length; index += 1) {
19+
return getRouteURL(tempNode[index]);
20+
}
21+
}
22+
}
23+
24+
window.addEventListener('message', ({ data: { action, payload } }) => { // runs automatically twice per second with inspectedElement
1325
switch (action) {
1426
case 'jumpToSnap':
1527
timeJump(payload);
28+
// Get the pathname from payload and add new entry to browser history
29+
// MORE: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
30+
window.history.pushState('', '', getRouteURL(payload));
1631
break;
1732
case 'setLock':
1833
mode.locked = payload;

package/linkFiber.js

Lines changed: 48 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
@@ -31,7 +33,7 @@ module.exports = (snap, mode) => {
3133
const oldSetState = component.setState.bind(component);
3234
// replace component's setState so developer doesn't change syntax
3335
// component.setState = newSetState.bind(component);
34-
component.setState = (state, callback = () => { }) => {
36+
component.setState = (state, callback = () => {}) => {
3537
// don't do anything if state is locked
3638
// UNLESS we are currently jumping through time
3739
if (mode.locked && !mode.jumping) return;
@@ -54,10 +56,10 @@ module.exports = (snap, mode) => {
5456
// don't do anything if state is locked
5557
if (mode.locked && !mode.jumping) return;
5658
oldDispatch(fiber, queue, action);
57-
setTimeout(() => {
58-
updateSnapShotTree();
59-
sendSnapshot();
60-
}, 100);
59+
// setTimeout(() => {
60+
updateSnapShotTree();
61+
sendSnapshot();
62+
// }, 100);
6163
};
6264
component.queue.dispatch.linkFiberChanged = true;
6365
}
@@ -70,8 +72,11 @@ 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) {
75+
while (memoizedState && memoizedState.queue) {
76+
// prevents useEffect from crashing on load
77+
// if (memoizedState.next.queue === null) { // prevents double pushing snapshot updates
7478
changeUseState(memoizedState);
79+
// }
7580
// memoized[astHooks[index]] = memoizedState.memoizedState;
7681
memoized[astHooks[index]] = memoizedState.memoizedState;
7782
// Reassign memoizedState to its next value
@@ -102,7 +107,14 @@ module.exports = (snap, mode) => {
102107
changeSetState(stateNode);
103108
}
104109
// Check if the component uses hooks
105-
if (memoizedState && Object.hasOwnProperty.call(memoizedState, 'baseState')) {
110+
// console.log("memoizedState", memoizedState);
111+
112+
if (
113+
memoizedState &&
114+
Object.hasOwnProperty.call(memoizedState, 'baseState')
115+
) {
116+
// 'catch-all' for suspense elements (experimental)
117+
if (typeof elementType.$$typeof === 'symbol') return;
106118
// Traverse through the currentFiber and extract the getters/setters
107119
astHooks = astParser(elementType);
108120
saveState(astHooks);
@@ -120,20 +132,39 @@ module.exports = (snap, mode) => {
120132
}
121133
// runs when page initially loads
122134
// but skips 1st hook click
123-
function updateSnapShotTree() {
124-
const { current } = fiberRoot;
135+
async function updateSnapShotTree() {
136+
let current;
137+
// if concurrent mode, grab current.child'
138+
if (concurrent) {
139+
// we need a way to wait for current child to populate
140+
const promise = new Promise((resolve, reject) => {
141+
setTimeout(() => resolve(fiberRoot.current.child), 400);
142+
});
143+
144+
current = await promise;
145+
146+
current = fiberRoot.current.child;
147+
} else {
148+
current = fiberRoot.current;
149+
}
150+
125151
snap.tree = createTree(current);
126152
}
127153

128-
return container => {
129-
const {
130-
_reactRootContainer: { _internalRoot },
131-
_reactRootContainer,
132-
} = container;
133-
// only assign internal rootp if it actually exists
134-
fiberRoot = _internalRoot || _reactRootContainer;
154+
return async container => {
155+
if (container._internalRoot) {
156+
fiberRoot = container._internalRoot;
157+
concurrent = true;
158+
} else {
159+
const {
160+
_reactRootContainer: { _internalRoot },
161+
_reactRootContainer,
162+
} = container;
163+
// only assign internal root if it actually exists
164+
fiberRoot = _internalRoot || _reactRootContainer;
165+
}
135166

136-
updateSnapShotTree();
167+
await updateSnapShotTree();
137168
// send the initial snapshot once the content script has started up
138169
window.addEventListener('message', ({ data: { action } }) => {
139170
if (action === 'contentScriptStarted') sendSnapshot();

package/package-lock.json

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

package/package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "reactime",
3-
"version": "2.0.4",
3+
"version": "2.0.9",
44
"description": "A library that helps debug React by memorizing the state of components with every render.",
55
"main": "index.js",
66
"repository": {
@@ -26,9 +26,13 @@
2626
"contributors": [
2727
"Andy Wong",
2828
"Bryan Lee",
29+
"Chris Flannery",
2930
"David Chai",
3031
"Josh Kim",
31-
"Ruthba Anam",
32+
"Prasanna Malla",
33+
"Rajeeb Banstola",
34+
"Rocky Lin",
35+
"Ruth Anam",
3236
"Ryan Dang",
3337
"Sierra Swaby",
3438
"Yujin Kang"
@@ -38,4 +42,4 @@
3842
"acorn": "^7.1.0",
3943
"acorn-jsx": "^5.0.2"
4044
}
41-
}
45+
}

package/readme.md

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<p align="center">
2-
<img src ="./assets/readme_logo.png" width="400"/>
2+
<img src ="./assets/readme-logo-svg.svg" width="300"/>
33
</p>
44

5-
# Reactime: A Time Travel Debugger for React
5+
<h1 align="center">State Debugger for React</h1>
66

77
[![GitHub](https://img.shields.io/github/license/oslabs-beta/reactime)](https://github.com/oslabs-beta/reactime)
88
[![Build Status](https://travis-ci.com/oslabs-beta/reactime.svg?branch=master)](https://travis-ci.com/oslabs-beta/reactime)
@@ -11,24 +11,24 @@
1111
[![DevDependencies](https://david-dm.org/oslabs-beta/reactime/dev-status.svg)](https://david-dm.org/oslabs-beta/reactime?type=dev)
1212
[![Vulnerabilities](https://snyk.io/test/github/oslabs-beta/reactime/badge.svg)](https://snyk.io/test/github/oslabs-beta/reactime)
1313

14-
[![NPM](https://nodei.co/npm/reactime.png)](https://nodei.co/npm/reactime/)
15-
1614
<p align="center">
15+
<a href="https://nodei.co/npm/reactime/"><img src="https://nodei.co/npm/reactime.png"></a>
16+
1717
<img src="demo.gif" alt="Demo of Reactime" style="width: 55%">
1818
</p>
1919

20-
Reactime is a debugging tool for React developers. It records state whenever state is changed and allows user to jump to any previous recorded state.
20+
Reactime is a debugging tool for React developers. It records state whenever it is changed and allows the user to jump to any previously recorded state.
2121

22-
One important thing to note. This devtool is for React apps using only stateful components and prop drilling. If you're using Redux, Hooks, Context, or functional components, this devtool will not function on your app.
22+
This dev tool is for React apps using stateful components and prop drilling, and has beta support for Context API, conditional state routing, Hooks (useState & useEffect) and functional components.
2323

24-
Another thing is that this library does not work well when mixing React with direct DOM manipulation. Since DOM manipulation doesn't change any React state, this library cannot record or even detect that change. Of course, you should be avoiding mixing the two in the first place.
24+
One thing to note is that this library does not work well when mixing React with direct DOM manipulation.
2525

26-
Two parts are needed for this tool to function. The <a href="https://chrome.google.com/webstore/detail/react-time-travel/cgibknllccemdnfhfpmjhffpjfeidjga">chrome extension</a> must be installed, and the NPM package must be installed and used in the React code.
26+
Two parts are needed for this tool to function. The [**chrome extension**](https://chrome.google.com/webstore/detail/react-time-travel/cgibknllccemdnfhfpmjhffpjfeidjga) must be installed, and the [**NPM package**](https://www.npmjs.com/package/reactime) must be installed and used in the React code.
2727

2828
After successfully installing the chrome extension, you can test Reactime functionalities in the demo repositories below.
2929

30-
- <a href="http://reactime-demo1.us-east-1.elasticbeanstalk.com/">Calculator</a>
31-
- <a href="http://reactime-demo2.us-east-1.elasticbeanstalk.com/">Bitcoin Price Index</a>
30+
- [Calculator](http://reactime-demo1.us-east-1.elasticbeanstalk.com)
31+
- [Bitcoin Price Index](http://reactime-demo2.us-east-1.elasticbeanstalk.com)
3232

3333
## Installing
3434

@@ -63,7 +63,7 @@ Then open up your Chrome DevTools. There'll be a new tab called Reactime.
6363

6464
### Recording
6565

66-
Whenever state is changed (whenever setState is called), this extension will create a snapshot of the current state tree and record it. Each snapshot will be displayed in Chrome DevTools under the Reactime panel.
66+
Whenever state is changed (whenever setState or useState is called), this extension will create a snapshot of the current state tree and record it. Each snapshot will be displayed in Chrome DevTools under the Reactime panel.
6767

6868
### Viewing
6969

@@ -73,14 +73,15 @@ You can click on a snapshot to view your app's state. State can be visualized in
7373

7474
Jumping is the most important feature of all. It allows you to jump to any previous recorded snapshots. Hitting the jump button on any snapshot will change the DOM by setting their state.
7575

76-
### Others
77-
78-
Other handy features include:
76+
### And Much More
7977

78+
- multiple tree graph branches depicting state changes
79+
- tree graph hover functionality to view state changes
80+
- ability to pan and zoom tree graph
8081
- multiple tabs support
8182
- a slider to move through snapshots quickly
8283
- a play button to move through snapshots automatically
83-
- a pause which button stops recording each snapshot
84+
- a pause button, which stops recording each snapshot
8485
- a lock button to freeze the DOM in place. setState will lose all functionality while the extension is locked
8586
- a persist button to keep snapshots upon refresh (handy when changing code and debugging)
8687
- export/import the current snapshots in memory
@@ -91,6 +92,14 @@ Other handy features include:
9192
- **Bryan Lee** - [@mylee1995](https://github.com/mylee1995)
9293
- **Josh Kim** - [@joshua0308](https://github.com/joshua0308)
9394
- **Sierra Swaby** - [@starkspark](https://github.com/starkspark)
95+
- **Ruth Anam** - [@peachiecodes](https://github.com/peachiecodes)
96+
- **David Chai** - [@davidchaidev](https://github.com/davidchai717)
97+
- **Yujin Kang** - [@yujinkay](https://github.com/yujinkay)
98+
- **Andy Wong** - [@andywongdev](https://github.com/andywongdev)
99+
- **Chris Flannery** - [@chriswillsflannery](https://github.com/chriswillsflannery)
100+
- **Rajeeb Banstola** - [@rajeebthegreat](https://github.com/rajeebthegreat)
101+
- **Prasanna Malla** - [@prasmalla](https://github.com/prasmalla)
102+
- **Rocky Lin** - [@rocky9413](https://github.com/rocky9413)
94103

95104
## License
96105

package/timeJump.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ module.exports = (origin, mode) => {
2828
let index = 0;
2929
const hooks = returnState();
3030
// while loop through the memoize tree
31-
while (current) {
31+
while (current && current.queue) { // allows time travel with useEffect
3232
current.queue.dispatch(target.state[hooks[index]]);
3333
// Reassign the current value
3434
current = current.next;

0 commit comments

Comments
 (0)