Skip to content

Commit f66edcb

Browse files
authored
Merge pull request #54 from crperezt/feature2
Added hooks useState state names to snapshots; performance changes
2 parents e839e90 + 007c7d1 commit f66edcb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+6889
-16312
lines changed

.vscode/settings.json

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
11
{
2-
"eslint.enable": true
2+
"eslint.enable": true,
3+
"workbench.colorCustomizations": {
4+
"activityBar.activeBackground": "#93e6fc",
5+
"activityBar.activeBorder": "#fa45d4",
6+
"activityBar.background": "#93e6fc",
7+
"activityBar.foreground": "#15202b",
8+
"activityBar.inactiveForeground": "#15202b99",
9+
"activityBarBadge.background": "#fa45d4",
10+
"activityBarBadge.foreground": "#15202b",
11+
"statusBar.background": "#61dafb",
12+
"statusBar.border": "#61dafb",
13+
"statusBar.foreground": "#15202b",
14+
"statusBarItem.hoverBackground": "#2fcefa",
15+
"titleBar.activeBackground": "#61dafb",
16+
"titleBar.activeForeground": "#15202b",
17+
"titleBar.border": "#61dafb",
18+
"titleBar.inactiveBackground": "#61dafb99",
19+
"titleBar.inactiveForeground": "#15202b99"
20+
},
21+
"peacock.color": "#61dafb"
322
}

dev-reactime/__tests__/astParser.test.js

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@
77
import { configure } from 'enzyme';
88
import Adapter from 'enzyme-adapter-react-16';
99
// import toJson from 'enzyme-to-json';
10-
import astParser from '../astParser';
10+
import { getHooksNames } from '../helpers';
1111

1212
// Newer Enzyme versions require an adapter to a particular version of React
1313
configure({ adapter: new Adapter() });
1414

1515
describe('AST Unit Tests', () => {
16-
describe('astParser', () => {
16+
describe('getHooksNames', () => {
1717
it.skip('Should return object with one getter/setter for a single useState instance', () => {
1818
const useState = 'const singleUseStateTest = () => { const [testCount, setTestCount] = useState(0); return ( <div> <p> You clicked this {testCount} times </p> <button onClick={() => setTestCount(testCount + 1)}>+1</button> <button onClick={() => setTestCount(testCount - 1)}>-1</button> <hr /> </div> )';
1919

2020
const expectedObject = {
2121
_useState: 'testCount',
2222
_useState2: 'setTestCount',
2323
};
24-
expect(astParser(useState)).toEqual(expectedObject);
24+
expect(getHooksNames(useState)).toEqual(expectedObject);
2525
});
2626

2727
it.skip('Should output the right number of properties when taking in multiple function definitions', () => {
@@ -33,26 +33,94 @@ describe('AST Unit Tests', () => {
3333
_useState3: 'age',
3434
_useState4: 'setAge',
3535
};
36-
expect(astParser(useState)).toEqual(expectedObject);
37-
expect(Object.keys(astParser(useState))).toHaveLength(4);
36+
expect(getHooksNames(useState)).toEqual(expectedObject);
37+
expect(Object.keys(getHooksNames(useState))).toHaveLength(4);
3838
});
3939

4040
it.skip('Should ignore any non-hook definitions', () => {
4141
const useState = 'const singleUseStateTest = () => { const [testCount, setTestCount] = useState(0); const age = 20; return ( <div> <p> You clicked this {testCount} times </p> <button onClick={() => setTestCount(testCount + 1)}>+1</button> <button onClick={() => setTestCount(testCount - 1)}>-1</button> <p> You are {age} years old! </p> <button onClick={age => age + 1}>Get Older</button> <hr /> </div>)';
4242

43-
expect(Object.keys(astParser(useState))).toHaveLength(2);
43+
expect(Object.keys(getHooksNames(useState))).toHaveLength(2);
4444
});
4545

4646
it.skip('Should return an empty object if no hooks found', () => {
4747
const useState = 'const singleUseStateTest = () => { const age = 20; return ( <div> <p> You are {age} years old! </p> <button onClick={age => age + 1}>Get Older</button> <hr /> </div>)';
4848

49-
expect(astParser(useState)).toBe({});
49+
expect(getHooksNames(useState)).toBe({});
5050
});
5151

5252
it.skip('Should throw an error if input is invalid javascript', () => {
5353
const useState = 'const singleUseStateTest = () => { age: 20; return ( <div> <p> You are {age} years old! </p> <button onClick={age + 1}>Get Older</button></div>) }';
5454

55-
expect(astParser(useState)).toThrow();
55+
expect(getHooksNames(useState)).toThrow();
5656
});
5757
});
5858
});
59+
60+
61+
62+
/* /*
63+
console.log('getHooksNames: ', getHooksNames(`function LastSnapshot(props) {
64+
var _useState = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(''),
65+
_useState2 = _slicedToArray(_useState, 2),
66+
currentSnapshot = _useState2[0],
67+
setCurrentSnapshot = _useState2[1];
68+
69+
var _useState3 = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(25),
70+
_useState4 = _slicedToArray(_useState3, 2),
71+
testState = _useState4[0],
72+
setTestState = _useState4[1];
73+
74+
var _useState5 = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(50),
75+
_useState6 = _slicedToArray(_useState5, 2),
76+
testState2 = _useState6[0],
77+
setTestState2 = _useState6[1];
78+
79+
function replacer(name, val) {
80+
// Ignore the key that is the name of the state variable
81+
if (name === 'currentSnapshot') {
82+
console.log('filtering currentSnapshot from display');
83+
return undefined;
84+
}
85+
86+
return val;
87+
}
88+
89+
Object(react__WEBPACK_IMPORTED_MODULE_0__["useEffect"])(function () {
90+
window.addEventListener('message', function (_ref) {
91+
var _ref$data = _ref.data,
92+
action = _ref$data.action,
93+
payload = _ref$data.payload;
94+
95+
if (action === 'recordSnap') {
96+
console.log('stringifying payload:', payload);
97+
var payloadContent = JSON.stringify(payload, replacer, 1);
98+
setCurrentSnapshot(payloadContent);
99+
setTestState(function (state) {
100+
return state * 2;
101+
});
102+
setTestState2(function (state) {
103+
return state * 2;
104+
});
105+
console.log('current snapshot', currentSnapshot);
106+
}
107+
});
108+
}, []);
109+
/*
110+
// This method is for testing. Setting state after the activeSandbox is changed modifies the overall behavior of the sandbox environment.
111+
const { activeSandbox } = props;
112+
useEffect(() => {
113+
// Reset the current snapshot when a new sandbox is entered
114+
setCurrentSnapshot('');
115+
}, [activeSandbox]);
116+
*/
117+
/*
118+
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
119+
id: "lastSnapshot",
120+
className: "ml-5 mt-2",
121+
style: {
122+
whiteSpace: 'pre'
123+
}
124+
}, testState, testState2, currentSnapshot));
125+
};`));
126+
*/

dev-reactime/__tests__/index.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Testing LinkFiber</title>
7+
</head>
8+
<body>
9+
10+
</body>
11+
</html>

dev-reactime/__tests__/linkFiber.test.js

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
/* eslint-disable react/jsx-filename-extension */
22
import React, { Component } from 'react';
33
import { render } from 'react-dom';
4+
import linkFiberStart from '../linkFiber';
5+
// import 'expect-puppeteer';
6+
import puppeteer from 'puppeteer';
47

5-
const linkFiberRequire = require('../linkFiber');
8+
const SERVER = require('./puppeteerServer');
9+
10+
const APP = 'http://localhost:3002';
611

712
let linkFiber;
813
let mode;
914
let snapShot;
1015

16+
let browser;
17+
let page;
18+
1119
class App extends Component {
1220
constructor(props) {
1321
super(props);
@@ -21,24 +29,59 @@ class App extends Component {
2129
}
2230

2331
describe('unit test for linkFiber', () => {
32+
beforeAll(async () => {
33+
await SERVER;
34+
const args = puppeteer.defaultArgs().filter(arg => String(arg).toLowerCase() !== '--disable-extensions');
35+
browser = await puppeteer.launch({
36+
args: args.concat(['--no-sandbox', '--disable-setuid-sandbox',
37+
'---extensions-on-chrome-urls',
38+
'--whitelisted-extension-id=fmkadmapgofadopljbjfkapdkoienihi',
39+
'--whitelisted-extension-id=hilpbahfbckghckaiafiiinjkeagmfhn',
40+
'--load-extension=/mnt/d/Libraries/Documents/codeRepos/reactime/src/extension/build']),
41+
devtools: true,
42+
ignoreDefaultArgs: true,
43+
// '--load-extension', '../../src/extension/build'],
44+
45+
// headless: false,
46+
});
47+
48+
const c = await puppeteer.connect({
49+
browserWSEndpoint: browser.wsEndpoint(), //`ws://${host}:${port}/devtools/browser/<id>`,
50+
ignoreHTTPSErrors: false
51+
});
52+
53+
page = await browser.newPage();
54+
});
55+
56+
afterAll(async () => {
57+
await SERVER.close();
58+
59+
await browser.close();
60+
});
61+
62+
2463
beforeEach(() => {
2564
snapShot = { tree: null };
2665
mode = {
2766
jumping: false,
2867
paused: false,
2968
locked: false,
3069
};
31-
linkFiber = linkFiberRequire(snapShot, mode);
70+
linkFiber = linkFiberStart(snapShot, mode);
3271

33-
const container = document.createElement('div');
34-
render(<App />, container);
35-
linkFiber(container);
72+
page.waitForFunction(async lf => {
73+
const container = document.createElement('div');
74+
render(<App />, container);
75+
lf(container);
76+
}, {}, linkFiber);
3677
});
3778

3879
test('linkFiber should mutate the snapshot tree property', () => {
3980
// linkFiber mutates the snapshot
81+
4082
expect(typeof snapShot.tree).toBe('object');
41-
expect(snapShot.tree.component.state).toBe('root');
83+
//expect(snapShot.tree.component.state).toBe('root');
84+
expect(snapShot.tree.state).toBe('root');
4285
expect(snapShot.tree.children).toHaveLength(1);
4386
expect(snapShot.tree.children[0].component.state.foo).toBe('bar');
4487
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const express = require('express');
2+
const path = require('path');
3+
4+
const app = express();
5+
6+
app.use(express.static(path.resolve(__dirname)));
7+
8+
const server = app.listen(3002);
9+
// () => {console.log('Express listening on port 3000');}
10+
11+
module.exports = server;

dev-reactime/astParser.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'core-js';
12
/* eslint-disable no-inner-declarations, no-loop-func */
23
// eslint-disable-next-line import/newline-after-import
34
const acorn = require('acorn'); // javascript parser

dev-reactime/helpers.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/* eslint-disable linebreak-style */
2+
/* eslint-disable no-inner-declarations, no-loop-func */
3+
// eslint-disable-next-line import/newline-after-import
4+
const acorn = require('acorn');
5+
const jsx = require('acorn-jsx');
6+
// import { acorn } from 'acorn'; // javascript parser
7+
// import { jsx } from 'acorn-jsx';
8+
9+
const JSXParser = acorn.Parser.extend(jsx());
10+
11+
// Returns a throttled version of an input function
12+
// The returned throttled function only executes at most once every t milliseconds
13+
export const throttle = (f, t) => {
14+
let isOnCooldown = false;
15+
let isCallQueued = false;
16+
const throttledFunc = () => {
17+
if (isOnCooldown && isCallQueued) return;
18+
if (isOnCooldown) {
19+
isCallQueued = true;
20+
return;
21+
}
22+
f();
23+
isOnCooldown = true;
24+
isCallQueued = false;
25+
26+
const runAfterTimeout = () => {
27+
if (isCallQueued) {
28+
isCallQueued = false;
29+
isOnCooldown = true; // not needed I think
30+
setTimeout(runAfterTimeout, t);
31+
return;
32+
}
33+
isOnCooldown = false;
34+
};
35+
setTimeout(runAfterTimeout, t);
36+
};
37+
return throttledFunc;
38+
};
39+
40+
// Helper function to grab the getters/setters from `elementType`
41+
export const getHooksNames = elementType => {
42+
43+
// Initialize empty object to store the setters and getter
44+
let ast = JSXParser.parse(elementType);
45+
const hookState = {};
46+
const hooksNames = {};
47+
48+
while (Object.hasOwnProperty.call(ast, 'body')) {
49+
let tsCount = 0; // Counter for the number of TypeScript hooks seen (to distinguish in masterState)
50+
ast = ast.body;
51+
const statements = [];
52+
53+
/** All module exports always start off as a single 'FunctionDeclaration' type
54+
* Other types: "BlockStatement" / "ExpressionStatement" / "ReturnStatement"
55+
* Iterate through AST of every function declaration
56+
* Check within each function declaration if there are hook declarations */
57+
ast.forEach(functionDec => {
58+
let body;
59+
if (functionDec.expression && functionDec.expression.body) body = functionDec.expression.body.body;
60+
else body = functionDec.body ? functionDec.body.body : [];
61+
// Traverse through the function's funcDecs and Expression Statements
62+
body.forEach(elem => {
63+
if (elem.type === 'VariableDeclaration') {
64+
elem.declarations.forEach(hook => {
65+
// * TypeScript hooks appear to have no "VariableDeclarator"
66+
// * with id.name of _useState, _useState2, etc...
67+
// * hook.id.type relevant for TypeScript applications
68+
// *
69+
// * Works for useState hooks
70+
if (hook.id.type === 'ArrayPattern') {
71+
hook.id.elements.forEach(hook => {
72+
statements.push(hook.name);
73+
// * Unshift a wildcard name to achieve similar functionality as before
74+
statements.unshift(`_useWildcard${tsCount}`);
75+
tsCount += 1;
76+
});
77+
} else {
78+
if (hook.init.object && hook.init.object.name) {
79+
const varName = hook.init.object.name;
80+
if (!hooksNames[varName] && varName.match(/_use/)) {
81+
hooksNames[varName] = hook.id.name;
82+
}
83+
}
84+
statements.push(hook.id.name);
85+
}
86+
});
87+
}
88+
});
89+
90+
// Iterate array and determine getter/setters based on pattern
91+
statements.forEach((el, i) => {
92+
if (el.match(/_use/)) hookState[el] = statements[i + 2];
93+
});
94+
});
95+
}
96+
return Object.values(hooksNames);
97+
};

dev-reactime/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import timeJumpStart from './timeJump';
1010
// * State snapshot object initialized here
1111
const snapShot = {
1212
tree: null,
13-
unfilteredTree: null
13+
unfilteredTree: null,
1414
};
1515

1616
const mode = {

0 commit comments

Comments
 (0)