├── mockData ├── treeGraph.js ├── tinyFlameGraph.js ├── fiber.js ├── ticTacToe.js ├── devToolData.js └── ticTacToeFiberRoot.js ├── .gitignore ├── src ├── assets │ ├── rm-icon16.png │ ├── readmeDemo.gif │ ├── rm-icon-128.png │ ├── rm-icon-48.png │ ├── play-button-svgrepo-com.svg │ ├── pause-svgrepo-com.svg │ └── reload-svgrepo-com.svg ├── extension │ ├── devtools.js │ ├── devtools.html │ ├── manifest.json │ ├── contentScript.js │ └── backgroundScript.js └── app │ ├── index.html │ ├── components │ ├── timeTravel.js │ ├── record.js │ ├── mainContainer.js │ ├── stateChange.js │ ├── componentsList.js │ ├── flameChart.js │ ├── stateAndProps.js │ ├── playButton.js │ ├── helperFunctions.js │ └── d3tree.js │ ├── App.js │ └── style.scss ├── package ├── sendContentScript.js ├── deleteParent.js ├── containerWrapper.js ├── JSONStringify.js ├── makeTreeCreator.test.js ├── ReactWorkTags.js ├── createTree.js ├── treeGraphFactory.test.js ├── deleteParent.test.js ├── makeTreeCreator.js ├── newNode.js ├── package.json ├── compareStateAndProps.js ├── README.md └── treeGraphFactory.js ├── uml ├── highLevelDiagram.plantuml ├── devtools.plantuml └── treeCreator.plantuml ├── __test__ ├── stateChange.test.js ├── record.test.js ├── stateAndProps.test.js ├── componentsList.test.js ├── playButton.test.js ├── timeTravel.test.js └── helperFunctions.test.js ├── LICENSE ├── webpack.config.js ├── package.json └── README.md /mockData/treeGraph.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /dist 3 | .vscode/ -------------------------------------------------------------------------------- /src/assets/rm-icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ReactMonitor/HEAD/src/assets/rm-icon16.png -------------------------------------------------------------------------------- /src/assets/readmeDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ReactMonitor/HEAD/src/assets/readmeDemo.gif -------------------------------------------------------------------------------- /src/assets/rm-icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ReactMonitor/HEAD/src/assets/rm-icon-128.png -------------------------------------------------------------------------------- /src/assets/rm-icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ReactMonitor/HEAD/src/assets/rm-icon-48.png -------------------------------------------------------------------------------- /src/extension/devtools.js: -------------------------------------------------------------------------------- 1 | chrome.devtools.panels.create( 2 | 'React Monitor', // panel title 3 | './assets/rm-icon-128.png', // logo path 4 | 'index.html', // initial HTML page for the dev panel 5 | null 6 | ); 7 | -------------------------------------------------------------------------------- /package/sendContentScript.js: -------------------------------------------------------------------------------- 1 | module.exports = function(treeCreator, prevTree, currentTree) 2 | { 3 | const treeGraph = treeCreator(currentTree); 4 | window.postMessage({ action: 'npmToContent', payload: treeGraph }); 5 | } 6 | -------------------------------------------------------------------------------- /package/deleteParent.js: -------------------------------------------------------------------------------- 1 | 2 | function deleteParent(root){ 3 | 4 | if (root.parent) { 5 | delete root.parent; 6 | } 7 | if (root.children) { 8 | root.children.forEach((child) => deleteParent(child)); 9 | } 10 | 11 | } 12 | 13 | 14 | 15 | module.exports = deleteParent; -------------------------------------------------------------------------------- /src/extension/devtools.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React Monitor 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React Monitor 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /mockData/tinyFlameGraph.js: -------------------------------------------------------------------------------- 1 | const flameGraphData = { 2 | "children": [ 3 | { 4 | "name": "genunix`syscall_mstate", 5 | "value": 89 6 | }, 7 | { 8 | "children": [ 9 | { 10 | "name": "unix`page_lookup_create", 11 | "value": 1 12 | } 13 | ], 14 | "name": "america", 15 | "value": 99 16 | }], 17 | "name": "codesmith", 18 | "value": 56 19 | } 20 | 21 | export default flameGraphData; -------------------------------------------------------------------------------- /package/containerWrapper.js: -------------------------------------------------------------------------------- 1 | const reactMonitor = require("./createTree.js"); 2 | 3 | //grabbing the Dom element that has the property of react_RootContiner 4 | function get_fiber_root(document_children) { 5 | for (let ele of document_children) { 6 | if (ele.hasOwnProperty("_reactRootContainer")) return ele; 7 | } 8 | } 9 | 10 | const container = get_fiber_root(document.body.children); 11 | 12 | //invoking the createTree function by passing the DOM element that contain the property of react_RootContainer 13 | reactMonitor(container); 14 | -------------------------------------------------------------------------------- /package/JSONStringify.js: -------------------------------------------------------------------------------- 1 | module.exports = function (object){ 2 | 3 | let cache = []; 4 | const string = JSON.stringify( 5 | object, 6 | // custom replacer - gets around "TypeError: Converting circular structure to JSON" 7 | (key, value) => { 8 | if (typeof value === 'object' && value !== null) { 9 | if (cache.indexOf(value) !== -1) { 10 | // Circular reference found, discard key 11 | return; 12 | } 13 | // Store value in collection 14 | cache.push(value); 15 | } 16 | return value; 17 | }, 18 | 4 19 | ); 20 | cache = null; // garbage collection 21 | return string; 22 | } -------------------------------------------------------------------------------- /package/makeTreeCreator.test.js: -------------------------------------------------------------------------------- 1 | const makeTreeCreator = require('./makeTreeCreator'); 2 | const fiber = require('../mockData/fiber'); 3 | 4 | 5 | describe('test makeTreeCreator', () => { 6 | 7 | 8 | it('test makeTreeCreator returns a function', () => { 9 | 10 | const treeCreator = makeTreeCreator(); 11 | const treeGraph = treeCreator(fiber); 12 | 13 | expect( treeCreator ).toBeInstanceOf( Function ) 14 | expect( treeGraph.name ).toBe('div'); 15 | }); 16 | 17 | it('test makeTreeCreator has valid value', () => { 18 | 19 | const treeCreator = makeTreeCreator(); 20 | const treeGraph = treeCreator(fiber); 21 | 22 | expect( treeGraph.name ).toBe('div'); 23 | }); 24 | 25 | 26 | }); 27 | -------------------------------------------------------------------------------- /uml/highLevelDiagram.plantuml: -------------------------------------------------------------------------------- 1 | @startuml windowUML 2 | 3 | boundary App 4 | boundary Container 5 | control Window 6 | control TreeCreator 7 | entity D3Tree 8 | entity D3Tree 9 | 10 | App -> Container : setState is called 11 | Container -> Container : render method called \nfiberRoot = container._reactRootContainer._internalRoot 12 | Container -> Window: check every 20ms \nif Virtual DOM has changed. 13 | Window -> TreeCreator : treeCreator(fiberTree) 14 | Window <- TreeCreator : returns treeGraph 15 | Window -> Container: sends treeGraph 16 | Container -> D3Tree: recieves treeGraph 17 | D3Tree -> D3Tree: present tree using \nD3Tree or D3Flame visual. 18 | 19 | @enduml -------------------------------------------------------------------------------- /__test__/stateChange.test.js: -------------------------------------------------------------------------------- 1 | import StateChange from '../src/app/components/stateChange' 2 | import React from 'react' 3 | import { configure, shallow, mount } from 'enzyme'; 4 | import Adapter from 'enzyme-adapter-react-16'; 5 | configure({ adapter: new Adapter() }); 6 | 7 | describe(' StateChanges component rendered correctly ', () => { 8 | let wrapper; 9 | let props = { 10 | currentState: [{}], 11 | index: 0, 12 | } 13 | wrapper = shallow() 14 | it(' Record rendered correctly ', () => { 15 | expect(wrapper.type()).toEqual('div') 16 | expect(wrapper.find('h2').text()).toEqual('State Diff') 17 | expect(wrapper.find('ComponentsList2')).not.toEqual(undefined) 18 | }) 19 | }) -------------------------------------------------------------------------------- /mockData/fiber.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: { 3 | name: "h1" 4 | }, 5 | tag: 5, 6 | effectTag: 1, 7 | actualDuration: 9.00, 8 | actualStartTime: 400, 9 | memorizedProps: {}, 10 | memorizedState: null, 11 | child: { 12 | type: { name: "div"}, 13 | tag: 1, 14 | effectTag: 2, 15 | actualDuration: 8.00, 16 | actualStartTime: 300, 17 | memorizedProps: {}, 18 | memorizedState: null, 19 | child: { 20 | type: "div", 21 | tag: 1, 22 | effectTag: 2, 23 | actualDuration: 8.00, 24 | actualStartTime: 300, 25 | memorizedProps: {}, 26 | memorizedState: null, 27 | } 28 | }, 29 | } 30 | -------------------------------------------------------------------------------- /package/ReactWorkTags.js: -------------------------------------------------------------------------------- 1 | export const FunctionComponent = 0; 2 | export const ClassComponent = 1; 3 | export const IndeterminateComponent = 2; // Before we know whether it is function or class 4 | export const HostRoot = 3; // Root of a host tree. Could be nested inside another node. 5 | export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer. 6 | export const HostComponent = 5; 7 | export const HostText = 6; 8 | export const Fragment = 7; 9 | export const Mode = 8; 10 | export const ContextConsumer = 9; 11 | export const ContextProvider = 10; 12 | export const ForwardRef = 11; 13 | export const Profiler = 12; 14 | export const SuspenseComponent = 13; 15 | export const MemoComponent = 14; 16 | export const SimpleMemoComponent = 15; 17 | export const LazyComponent = 16; -------------------------------------------------------------------------------- /package/createTree.js: -------------------------------------------------------------------------------- 1 | const sendContentScript = require("./sendContentScript"); 2 | 3 | //this function will be invoked in containerWrapper.js 4 | 5 | module.exports = function (container) { 6 | const fiberRoot = container._reactRootContainer._internalRoot; 7 | let hostRoot = fiberRoot.current; 8 | const treeCreator = require("./makeTreeCreator")(); 9 | let time_last = -Infinity; 10 | // console.log("[info] actualStartTime:", hostRoot); 11 | 12 | setInterval( function() { 13 | hostRoot = fiberRoot.current; 14 | if (hostRoot.actualStartTime !== time_last) { 15 | sendContentScript( treeCreator, undefined, hostRoot); 16 | time_last = hostRoot.actualStartTime; 17 | console.log("[2] actualStartTime:", time_last); 18 | console.log("[2] actualStartTime:", hostRoot.actualStartTime); 19 | } 20 | }, 100 ); 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /package/treeGraphFactory.test.js: -------------------------------------------------------------------------------- 1 | const fiber = require('../mockData/fiber'); 2 | const treeGraphFactory = require('./treeGraphFactory'); 3 | 4 | 5 | describe('test treeGraphFactory', () => { 6 | 7 | it('test treeGraph was created properly', () => { 8 | 9 | const treeGraph = treeGraphFactory(fiber); 10 | expect(treeGraph.name).toBe('h1'); 11 | expect(treeGraph.value).toBe(9); 12 | expect(treeGraph.tag).toBe(5); 13 | expect(treeGraph.stats.effectTag).toBe(1); 14 | expect(treeGraph.stats.renderStart).toBe('400.00'); 15 | expect(treeGraph.stats.renderTotal).toBe('9.00'); 16 | expect(treeGraph.children[0].name).toBe('div'); 17 | expect(treeGraph.children[0].value).toBe(8); 18 | expect(treeGraph.children[0].tag).toBe(1); 19 | expect(treeGraph.children[0].stats.effectTag).toBe(2) 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "React Monitor", 4 | "version": "0.0.0.1", 5 | "devtools_page": "devtools.html", 6 | "permissions": ["activeTab", "contextMenus", ""], 7 | "content_scripts": [ 8 | { 9 | "matches": [""], 10 | "js": ["contentScript.js"] 11 | } 12 | ], 13 | "background": { 14 | "scripts": ["backgroundScript.js"], 15 | "persistent": false 16 | }, 17 | "icons": { 18 | "16": "./assets/rm-icon16.png", 19 | "48": "./assets/rm-icon-48.png", 20 | "128": "./assets/rm-icon-128.png" 21 | }, 22 | "externally_connectable": { 23 | "ids": ["*"] 24 | }, 25 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", 26 | "web_accessible_resources": ["/containerWrapper.js", "/fiberTreeAnalyzer.js"] 27 | } 28 | -------------------------------------------------------------------------------- /__test__/record.test.js: -------------------------------------------------------------------------------- 1 | import Record from '../src/app/components/record' 2 | import React from 'react' 3 | import { configure, shallow, mount } from 'enzyme'; 4 | import Adapter from 'enzyme-adapter-react-16'; 5 | configure({ adapter: new Adapter() }); 6 | 7 | describe(' Record component rendered correctly ', () => { 8 | let wrapper; 9 | let props = { 10 | logofTime: [1], 11 | index: 0, 12 | } 13 | wrapper = shallow() 17 | it(' Record rendered correctly ', () => { 18 | expect(wrapper.type()).toEqual('div') 19 | expect(wrapper.find('h2').text()).toEqual('Time') 20 | expect(wrapper.find('ul')).not.toEqual(undefined) 21 | expect(wrapper.find('span')).not.toEqual(undefined) 22 | expect(wrapper.find('li')).not.toEqual(undefined) 23 | }) 24 | }) -------------------------------------------------------------------------------- /__test__/stateAndProps.test.js: -------------------------------------------------------------------------------- 1 | import StateAndProps from '../src/app/components/stateAndProps' 2 | import React from 'react' 3 | import { configure, shallow, mount } from 'enzyme'; 4 | import Adapter from 'enzyme-adapter-react-16'; 5 | configure({ adapter: new Adapter() }); 6 | 7 | describe(' StateAndProps component rendered correctly ', () => { 8 | let wrapper; 9 | let props = { 10 | state: [], 11 | props: [], 12 | selected:[], 13 | label:'', 14 | onChange:() => true 15 | } 16 | wrapper = shallow() 17 | it(' StateAndProps component rendered correctly ', () => { 18 | expect(wrapper.type()).toEqual('div') 19 | expect(wrapper.find('li')).not.toEqual(undefined) 20 | expect(wrapper.find('span').at(0).text()).toEqual(' State ') 21 | expect(wrapper.find('span').at(1).text()).toEqual(' Props ') 22 | }) 23 | }) -------------------------------------------------------------------------------- /__test__/componentsList.test.js: -------------------------------------------------------------------------------- 1 | import ComponentsList from '../src/app/components/componentsList' 2 | import React from 'react' 3 | import { configure, shallow, mount } from 'enzyme'; 4 | import Adapter from 'enzyme-adapter-react-16'; 5 | configure({ adapter: new Adapter() }); 6 | 7 | describe(' ComponentsList component rendered correctly ', () => { 8 | let wrapper; 9 | let props = { 10 | components: [{children:[]}], 11 | onChange: () => true, 12 | selectedComponents: 0, 13 | isFirst:true 14 | } 15 | wrapper = shallow() 16 | it(' ComponentsList Component rendered correctly ', () => { 17 | expect(wrapper.type()).toEqual('div') 18 | expect(wrapper.find('StateAndProps')).not.toEqual(undefined) 19 | expect(wrapper.find('ComponentsList')).not.toEqual(undefined) 20 | expect(wrapper.find('ul')).not.toEqual(undefined) 21 | }) 22 | }) -------------------------------------------------------------------------------- /uml/devtools.plantuml: -------------------------------------------------------------------------------- 1 | @startuml devtools 2 | entity npmPackage 3 | boundary Window 4 | control contentScript 5 | boundary Chrome 6 | boundary ChromePort 7 | 8 | control backgroundScript 9 | control MainContainer 10 | control D3Tree 11 | 12 | 13 | backgroundScript -> ChromePort : chrome.runtime.onConnect.\naddListener((port)); 14 | contentScript -> Window : window.\naddEventListener('message'); 15 | 16 | npmPackage -> Window : 1.) postMessage({action:'NpmToContent', \npayload: treeGraph}) 17 | Window <- contentScript : 2.) receives Window action \n'NpmToContent' 18 | contentScript -> Chrome : 3.) chrome.runtime.sendMessage 19 | Chrome <- backgroundScript : 4.) receives Chrome Message \nvia chrome.runtime.onMessage.\naddListener 20 | backgroundScript -> ChromePort : 5.) post message to ChromePort. 21 | ChromePort <- MainContainer : 6.) receives message. 22 | MainContainer -> D3Tree : 7.) get treeGraph and \npresent information on D3Tree 23 | 24 | @enduml -------------------------------------------------------------------------------- /package/deleteParent.test.js: -------------------------------------------------------------------------------- 1 | const treeGraph = require('../mockData/treeGraph'); 2 | const deleteParent = require('./deleteParent'); 3 | 4 | 5 | describe('test deleteParent', () => { 6 | 7 | 8 | xit('test deleteParent deletes Parents', () => { 9 | 10 | const prunedTreeGraph = deleteParent(treeGraph); 11 | 12 | console.log("prunedTreeGraph -", prunedTreeGraph); 13 | expect(prunedTreeGraph.name).toBe('h1'); 14 | expect(prunedTreeGraph.value).toBe(9); 15 | expect(prunedTreeGraph.tag).toBe(5); 16 | expect(prunedTreeGraph.stats.effectTag).toBe(1); 17 | expect(prunedTreeGraph.stats.renderStart).toBe('400.00'); 18 | expect(prunedTreeGraph.stats.renderTotal).toBe('9.00'); 19 | expect(prunedTreeGraph.children[0].name).toBe('div'); 20 | expect(prunedTreeGraph.children[0].value).toBe(8); 21 | expect(prunedTreeGraph.children[0].tag).toBe(1); 22 | expect(prunedTreeGraph.children[0].stats.effectTag).toBe(2) 23 | }); 24 | 25 | 26 | }); 27 | -------------------------------------------------------------------------------- /src/app/components/timeTravel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Playbutton from './playButton' 3 | import Record from './record' 4 | import Statechange from './stateChange' 5 | 6 | export default class timeTravel extends Component { 7 | constructor(props) { 8 | super(props); 9 | } 10 | render() { 11 | return ( 12 |
13 |
14 |
15 | 16 |
17 |
18 | 19 |
20 |
21 | 22 |
23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/app/components/record.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | 3 | export default class record extends Component { 4 | constructor(props){ 5 | super(props) 6 | } 7 | render() { 8 | if(this.props.logofTime){ 9 | return ( 10 |
11 |

Time

12 |
    13 | {this.props.logofTime.map((elem,i) =>{ 14 | if(this.props.index===i){ 15 | return
  • Name:{elem[0]} Time:{elem[1]}
  • 16 | }else{ 17 | return
  • Name:{elem[0]} Time:{elem[1]}
  • 18 | } 19 | })} 20 |
21 |
22 | ) 23 | }else{ 24 | return ( 25 |

26 | ) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /__test__/playButton.test.js: -------------------------------------------------------------------------------- 1 | import PlayButton from '../src/app/components/playButton' 2 | import React from 'react' 3 | import { configure, shallow, mount } from 'enzyme'; 4 | import Adapter from 'enzyme-adapter-react-16'; 5 | configure({ adapter: new Adapter() }); 6 | 7 | describe(' PlayButton rendering correctly', () => { 8 | let wrapper; 9 | let props = { 10 | logOfTime: 1, 11 | index: 0, 12 | handelPlay: () => true, 13 | } 14 | beforeAll(() => { 15 | wrapper = shallow() 20 | }) 21 | it(' PlayButton rendered correctly ', () => { 22 | const label = ['Play','Pause','Reset'] 23 | expect(wrapper.type()).toEqual('div') 24 | expect(wrapper.find('button')).toHaveLength(3) 25 | for(let i=0; i<3;i+=1){ 26 | expect(wrapper.find('button').at(i).text()).toEqual(label[i]) 27 | } 28 | }) 29 | }) -------------------------------------------------------------------------------- /src/app/components/mainContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom'; 4 | import D3Tree from './d3tree'; 5 | import FlameChart from './flameChart' 6 | 7 | export default class MainContainer extends Component { 8 | constructor(props) { 9 | super(props); 10 | } 11 | render() { 12 | return ( 13 | 14 |
15 |
16 |
  • Tree
  • 17 |
  • Chart
  • 18 |
    19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
    28 |
    29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 OSLabs Beta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /__test__/timeTravel.test.js: -------------------------------------------------------------------------------- 1 | 2 | import TimeTravel from '../src/app/components/timeTravel' 3 | import React from 'react' 4 | import { configure, shallow, mount } from 'enzyme'; 5 | import Adapter from 'enzyme-adapter-react-16'; 6 | configure({ adapter: new Adapter() }); 7 | 8 | describe('Time travel rendered correctly', () => { 9 | let wrapper 10 | let props = { 11 | currentState:[{}], 12 | handelPlay : () => 'hello world', 13 | logofTime: [['App',1.5]], 14 | index:0 15 | } 16 | beforeAll(() => { 17 | wrapper = shallow() 23 | }) 24 | it('Timetravel rendered correctly', () => { 25 | expect(wrapper.type()).toEqual('div') 26 | expect(wrapper.find('Playbutton')).toEqual({}) 27 | expect(wrapper.find('Record')).toEqual({}) 28 | expect(wrapper.find('Statechange')).toEqual({}) 29 | expect(wrapper.find('Playbutton')).toEqual({}) 30 | }) 31 | }) -------------------------------------------------------------------------------- /package/makeTreeCreator.js: -------------------------------------------------------------------------------- 1 | const treeGraphFactory = require('./treeGraphFactory'); 2 | const deleteParent = require('./deleteParent'); 3 | const compareStateAndProps = require('./compareStateAndProps'); 4 | 5 | 6 | module.exports = function (){ 7 | 8 | // first time load these closure variables 9 | // need to be initialized accordingly. 10 | let wasMounted = false; 11 | let prevTreeGraph = null; 12 | 13 | function treeCreator(hostRoot, treeGraph = null) { 14 | 15 | // 1.) create treeGraph 16 | if (hostRoot.child) { 17 | // recursively traverse App Fiber Tree and create treeGraph 18 | treeGraph = treeGraphFactory(hostRoot.child); 19 | } 20 | 21 | // 2.) prune treeGraph 22 | deleteParent(treeGraph); 23 | delete treeGraph.parent; 24 | 25 | // 3.) enhance treeGraph 26 | // by comparing state and props in prevTreeGraph and treeGraph(current) 27 | const tempTreeGraph = JSON.parse(JSON.stringify(treeGraph)); 28 | compareStateAndProps(wasMounted, treeGraph, prevTreeGraph, null); 29 | prevTreeGraph = tempTreeGraph; 30 | wasMounted = true; 31 | 32 | 33 | return treeGraph; 34 | 35 | } 36 | return treeCreator; 37 | } 38 | -------------------------------------------------------------------------------- /src/assets/play-button-svgrepo-com.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /package/newNode.js: -------------------------------------------------------------------------------- 1 | const JSONStringify = require('./JSONStringify'); 2 | 3 | function Node(name, parent, children, fiber) { 4 | console.log(' fiber.actualDuration hi',fiber.actualDuration) 5 | console.log(' fiber.actualStartTime hi',fiber.actualStartTime) 6 | this.name = name; 7 | this.parent = parent; 8 | this.value = Number(fiber.actualDuration.toFixed(2)); 9 | this.children = children; 10 | this.stats = { 11 | state: JSON.stringify((fiber.memoizedState)?( (fiber.memoizedState.memoizedState)?fiber.memoizedState.memoizedState: fiber.memoizedState):fiber.memoizedState ), 12 | props: JSONStringify(fiber.memoizedProps), 13 | effectTag: fiber.effectTag, 14 | type: typeof fiber.type, 15 | renderStart: fiber.actualStartTime.toFixed(2), 16 | renderTotal: fiber.actualDuration.toFixed(2), 17 | }; 18 | this.nodeSvgShape = { 19 | shape: 'ellipse', 20 | shapeProps: { 21 | rx: 10, 22 | ry: 10, 23 | fill: 'lightgreen', 24 | }, 25 | }; 26 | if (typeof this.type === "string") { 27 | this.type = fiber.type; 28 | } 29 | this.tag = fiber.tag; 30 | 31 | } 32 | 33 | module.exports = { 34 | Node: Node 35 | } 36 | -------------------------------------------------------------------------------- /uml/treeCreator.plantuml: -------------------------------------------------------------------------------- 1 | @startuml treeCreatorUML 2 | 3 | control MakeTreeCreator 4 | control TreeCreator 5 | control TreeGraphFactory 6 | entity Node 7 | control helper 8 | entity deleteParent 9 | entity compareStateAndProps 10 | entity Window 11 | 12 | MakeTreeCreator -> TreeCreator : closed over \nwasMounted, prevTreeGraph, \nreturns function 13 | TreeCreator -> TreeGraphFactory : treeGraphFactor(fiberRoot); 14 | TreeGraphFactory -> Node : treeGraph = new Node(name, null, [], fiber); 15 | TreeGraphFactory -> helper: helper(fiber, treeGraph) 16 | helper -> helper: recursively reads Fiber child, sibling \nand creates array of children, parent.children 17 | TreeCreator <- TreeGraphFactory: return treeGraph 18 | TreeCreator -> deleteParent: delete parents from treeGraph 19 | TreeCreator <- deleteParent: return pruned treeGraph 20 | TreeCreator -> compareStateAndProps: compareStateAndProps(treeGraph, prevTreeGraph, null) 21 | compareStateAndProps -> compareStateAndProps: update node colors based on \n1.)initial load \n2.) when nodes diff \n3.)no prevNode 22 | TreeCreator <- compareStateAndProps: return TreeGraph with colored nodes 23 | TreeCreator -> TreeCreator: prevTreeGraph = tempTreeGraph \nwasMounted = true; 24 | TreeCreator -> Window: send TreeGraph 25 | @enduml -------------------------------------------------------------------------------- /mockData/ticTacToe.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function TicTacToe() { 4 | return ( 5 |
    6 |

    Tic Tac Toe

    7 |
    8 |
    9 | 12 | 15 | 18 |
    19 |
    20 | 23 | 26 | 29 |
    30 |
    31 | 34 | 37 | 40 |
    41 |
    42 |
    43 | ); 44 | } 45 | 46 | export default TicTacToe; 47 | -------------------------------------------------------------------------------- /src/app/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { render } from 'react-dom'; 3 | import MainContainer from './components/mainContainer'; 4 | import './style.scss'; 5 | 6 | let port; 7 | 8 | export default class App extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | name: '', 13 | children: [], 14 | stats: '', 15 | value: 0, 16 | oldState:[] 17 | }; 18 | } 19 | 20 | componentDidMount() { 21 | if (!port) port = chrome.runtime.connect(); 22 | 23 | port.onMessage.addListener((message) => { 24 | let array=this.state.oldState 25 | array.push(message.payload.payload) 26 | this.setState({ 27 | value: message.payload.payload.value, 28 | name: message.payload.payload.name, 29 | children: message.payload.payload.children, 30 | stats: message.payload.payload.stats, 31 | oldstate:array 32 | }); 33 | }); 34 | } 35 | 36 | render() { 37 | 38 | return ( 39 |
    40 | 47 |
    48 | ); 49 | } 50 | } 51 | 52 | render(, document.getElementById('app')); 53 | -------------------------------------------------------------------------------- /src/assets/pause-svgrepo-com.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 10 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/extension/contentScript.js: -------------------------------------------------------------------------------- 1 | // listen for message from npm package 2 | window.addEventListener("message", (msg) => { 3 | // filter the incoming msg.data 4 | if (msg.data.action === "npmToContent") { 5 | // send the message to the chrome - backgroundScript 6 | chrome.runtime.sendMessage({ 7 | action: "ContentToBackground", 8 | payload: msg.data, 9 | }); 10 | } 11 | }); 12 | 13 | function injectScript(file, node) { 14 | // test - inject javascript, access fibergraph 15 | const body0 = document.getElementsByTagName(node)[0]; 16 | 17 | // iterating the file from web accessible resources 18 | for (let i = 0; i < file.length; i++) { 19 | const s0 = document.createElement("script"); // 20 | 21 | s0.setAttribute("type", "text/javascript"); 22 | 23 | //built in chrome method to get the path of the file to be injected to the user's app 24 | s0.setAttribute("src", chrome.extension.getURL(file[i])); 25 | // console.log(chrome.extension.getURL(file[0]), "injectedfile"); 26 | body0.appendChild(s0); 27 | } 28 | } 29 | /* 30 | ... this will inject the following HTML tags into index.html: 31 | 32 |