├── .babelrc
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── build
├── backgroundScript.js
├── contentScript.js
├── devtools.html
├── hexagonFAT.png
├── lucid.zip
├── lucidlogo-card-transparent.png
├── manifest.json
└── reactTraverser.js
├── devtools.html
├── package-lock.json
├── package.json
├── public
├── ReqResJson.gif
├── StateDiff.gif
├── TreeDisplay.gif
├── TreeFilter.gif
├── assets
│ └── lucidlogo-transparent.png
├── demo-gif.gif
├── demo-photo-graphql.png
└── demo-photo-react.png
├── src
├── components
│ ├── AppNav
│ │ ├── AppNav.js
│ │ └── app_nav.css
│ ├── GraphQLResponse
│ │ ├── GraphQLResponse.css
│ │ └── GraphQLResponse.js
│ ├── GraphQLSchema
│ │ ├── GraphQLSchema.css
│ │ └── GraphQLSchema.js
│ ├── GraphQLTab
│ │ ├── GraphQLTab.css
│ │ └── GraphQLTab.js
│ ├── LogComponent
│ │ ├── Log.css
│ │ ├── Log.jsx
│ │ └── __tests__
│ │ │ ├── __snapshots__
│ │ │ └── log-component.render.js.snap
│ │ │ └── log-component.render.js
│ ├── LogContainer
│ │ ├── LogContainer.css
│ │ ├── LogContainer.js
│ │ └── __tests__
│ │ │ ├── LogContainer.render.js
│ │ │ └── __snapshots__
│ │ │ └── LogContainer.render.js.snap
│ ├── ReactTab
│ │ ├── ReactTab.css
│ │ └── ReactTab.js
│ ├── StateContainer
│ │ ├── StateContainer.jsx
│ │ └── stateContainer.css
│ ├── StatePropsBox
│ │ ├── StatePropsBox.css
│ │ └── StatePropsBox.js
│ ├── Tool
│ │ └── Tool.js
│ └── TreeDiagram
│ │ ├── TreeDiagram.css
│ │ └── TreeDiagram.jsx
├── devtools.css
├── devtools.js
├── filterComponents.js
├── filterDOM.js
└── stateDiff.js
├── stats.json
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "test": {
4 | "presets": [
5 | [
6 | "@babel/preset-env",
7 | {
8 | "modules": "commonjs",
9 | "debug": false
10 | }
11 | ],
12 | "@babel/preset-flow",
13 | "@babel/preset-react"
14 | ],
15 | "plugins": [
16 | "@babel/plugin-syntax-dynamic-import",
17 | "@babel/plugin-proposal-class-properties"
18 | ]
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 | src/devtools/.DS_Store
63 | .DS_Store
64 | src/devtools/.DS_Store
65 | src/devtools/.DS_Store
66 |
67 | build/webpack-bundle.js
68 | webpack-bundle.js
69 | build/webpack-bundle.js.map
70 | webpack-bundle.js.map
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 'stable'
4 | cache:
5 | directories:
6 | - node_modules
7 | script:
8 | - npm test
9 | - npm run build
10 | on:
11 | branch: master
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at gossamer.lucid@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Lucid
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | [](https://twitter.com/intent/tweet?text=Make%20development%20easier%20with%20Lucid.&url=https://github.com/Gossamer-React/Lucid&hashtags=react,graphql,apollographql,javascript,programming,developers,chrome)
9 | 
10 | 
11 |
12 | ## **Lucid**: *a React-GraphQL developer tool*
13 |
14 | Lucid is a Chrome Developer Tool designed to help engineers debug their React-GraphQL applications.
15 | - Visualize the component hierarchy, state/props data and state changes of your React application
16 | - See your GraphQL schema, queries, and mutations in real-time
17 |
18 | ## Underlying Technology
19 | ### React Tab
20 | Lucid parses through your React app to generate an interactive tree graph representing your __React component hierarchy__, with node-specific __state and props__ data. The tree updates upon each change to the React app's state, and displays a log of __state diffs__ on the left. This is done by creating a persistent data bridge to the user's React application via the Javascript API for WebExtensions' background and content scripts. Lucid injects scripts utilizing React DevTool's Global Hook to recursively traverse through the React DOM each time setState is called, resulting in a tree and a log that each display real-time feedback. Our app itself uses React internally so as the state of your live app changes, the Lucid tree graph will also provide visual feedback of data flow and state changes through the React components immediately.
21 |
22 | ### GraphQL Tab
23 | Lucid intercepts HTTP requests using Chrome Devtool APIs to display a log of real-time __Apollo client queries and mutations__, along with associated __response__ objects. Lucid also uses GraphQL schema introspection to display __schema__ information from the server. This allows full-stack developers to debug their app from the front-end to the back-end, as requests are generated, responses are returned from the server, and data flows through React components to be rendered in the DOM.
24 |
25 | ## Setup
26 | | Install from Chrome Extension Store | Build your own extension |
27 | | ------------- | ------------- |
28 | | 1. Install React Developer Tools. | 1. Clone the repo and ```npm install``` |
29 | | 2. Install Lucid or __Build your own extension__. | 2. ```npm run build ```|
30 | |3. Restart Chrome Browser. |3. Navigate to chrome://extensions. |
31 | |4. Run your React v16+ application using GraphQL.|4. Click **Load Unpacked** and select your './react-lucid/build' folder.|
32 | |5. Open Chrome Developer Tools (Inspect) and click on the Lucid panel. Trigger a re-render! |
33 |
34 | **IMPORTANT:** Lucid is in *BETA* mode and works best for React v16+ local projects in development environments.
35 |
36 | ## How to Use
37 | ### GraphQL Tab
38 | * In the GraphQL panel, a chronological log of API __requests__ is shown on the left.
39 | * A GraphQL __schema__ of all available types, queries, and mutations is also automatically fetched from the GraphQL server when Lucid is initialized and displayed on the bottom.
40 | * Click each request log to see its associated HTTP __response__.
41 |
42 | **NOTE:** Lucid only listens for HTTP requests while it is open in the Chrome Developer Tool panel. To see any requests that were made upon initial page load, reload your React page after opening Lucid in your Chrome browser.
43 |
44 | 
45 |
46 | ### React Tab
47 | * In the React panel is a tree graph representing your application's __component hierarchy__.
48 | * Hovering over any React Component in the tree displays the __state and props data__ of that component in the top left.
49 | * The __State Diff__ Log tracks changes in state whenever setState() is triggered.
50 | * Filter out specified higher-order components from your tree graph (e.g. Redux, Apollo-GraphQL, and React Router) by clicking the buttons.
51 |
52 | 
53 |
54 | ## Contributing
55 |
56 | Lucid is currently in beta release. Please let us know about bugs and suggestions at gossamer.lucid@gmail.com. Feel free to fork this repo and submit pull requests!
57 |
58 | ## Coming Soon
59 |
60 |
61 | ## Team
62 |
63 | Yong-Nicholas Kim (https://github.com/yongnicholaskim)
64 |
65 | Nian Liu (https://github.com/nianliu18)
66 |
67 | Eterna Tsai (https://github.com/eternalee)
68 |
69 | Neyser Zana (https://github.com/neyser95)
70 |
71 | ## License
72 | MIT
73 |
--------------------------------------------------------------------------------
/build/backgroundScript.js:
--------------------------------------------------------------------------------
1 | var _DevtoolPort;
2 | var _ContentscriptPort;
3 |
4 | // * store tab-port connections from multiple open tabs to lucid panel in an array
5 | const connections = {};
6 |
7 | // * listens to ports being connected
8 | chrome.runtime.onConnect.addListener(port => {
9 | if (port.name === 'devtool-background-port') {
10 | _DevtoolPort = port;
11 |
12 | // * receive message from devtools to trigger reactTraverse
13 | let extensionListener = (message, sender, res) => {
14 | if (message.name === 'connect' && message.tabId) {
15 | chrome.tabs.sendMessage(message.tabId, message);
16 | connections[message.tabId] = port;
17 | return;
18 | }
19 | };
20 |
21 | port.onMessage.addListener(extensionListener);
22 | }
23 | });
24 |
25 | // Receives message from content-script and checks for valid connections before posting to devtools
26 | chrome.runtime.onMessage.addListener(function (req, sender, res) {
27 | if (req.type === 'content-script') {
28 | if (sender.tab) {
29 | let tabId = sender.tab.id;
30 | if (tabId in connections) {
31 | //send the request to the specific port in the connections object associated to our tabId
32 | connections[tabId].postMessage({ type: 'appState', msg: req.message });
33 | } else console.log('ATTENTION:: Tab not found in connection list');
34 | } else console.log('ATTENTION:: sender.tab not defined');
35 | return true;
36 | }
37 | });
38 |
39 | //Remove tabId/port from connection object after tab is closed.
40 | chrome.tabs.onRemoved.addListener(function (tabId) {
41 | delete connections[tabId];
42 | });
43 |
44 | //* When react router is invoked a tab change happens and the traverser is lost. This sends a message to the content script so it can check if the traverser needs to be reinjected.
45 | chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
46 | if (!connections[tabId]) {
47 | return;
48 | }
49 | if (changeInfo.status === 'complete' && _DevtoolPort) {
50 | chrome.tabs.sendMessage(tabId, { type: 'tabChange' });
51 | }
52 | });
53 |
54 | //* This will reload extension when it is first installed.
55 | chrome.runtime.onInstalled.addListener(details => {
56 | const currentVersion = chrome.runtime.getManifest().version;
57 | if (details.reason === 'install') {
58 | //* Alert is for debugging purposes, shows install message.
59 | // alert('This is a first install!');
60 | chrome.storage.local.set({ lastKnownVersion: currentVersion });
61 | chrome.runtime.reload();
62 | }
63 | });
64 | //* This will reload extension when there is a new update.
65 | chrome.runtime.onUpdateAvailable.addListener(details => {
66 | //* Alert is for debugging purposes, shows update message.
67 | // alert('This is an update!');
68 | const newVersion = details.version;
69 | chrome.storage.local.get('lastKnownVersion', function (result) {
70 | const currentVersion = result.version;
71 | if (newVersion !== currentVersion) {
72 | //* Alert is for debugging purposes, shows update message.
73 | // alert('This is an update!');
74 | chrome.storage.local.set({ lastKnownVersion: newVersion });
75 | chrome.runtime.reload();
76 | }
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/build/contentScript.js:
--------------------------------------------------------------------------------
1 | // * opens new port to connect to our backendscript
2 | let contentScriptPort;
3 |
4 | //To access the DOM & reactDevToolsGlobalHook, inject the script into the document body:
5 | function injectScript(file) {
6 | //this adds to the DOM's body
7 | const body = document.getElementsByTagName('body')[0];
8 | const scriptFile = document.createElement('script');
9 | scriptFile.id = 'traverser';
10 | scriptFile.setAttribute('type', 'text/javascript');
11 | scriptFile.setAttribute('src', file);
12 | body.appendChild(scriptFile);
13 | }
14 |
15 | chrome.runtime.onMessage.addListener((message, sender, res) => {
16 | //* listen for message.name connect to inject the traverser
17 | if (message.name === 'connect') {
18 | if (!document.getElementById('traverser')) {
19 | injectScript(chrome.extension.getURL('reactTraverser.js'));
20 | }
21 | }
22 | // * receives message about tab update
23 | // TODO: change message.type to message.name
24 | if (message.type && message.type === 'tabChange') {
25 | if (!document.getElementById('traverser')) {
26 | injectScript(chrome.extension.getURL('reactTraverser.js'));
27 | }
28 | }
29 | });
30 |
31 | //listen for messages from the reactTraverser
32 | window.addEventListener('message', e => {
33 | if (e.data === undefined) return;
34 | if (e.data.type == 'reactTraverser') {
35 | reactDocObj = e.data.data;
36 |
37 | chrome.runtime.sendMessage(
38 | {
39 | type: "content-script",
40 | message: reactDocObj
41 | }
42 | )
43 | } else {
44 | return;
45 | }
46 | });
47 |
48 | // * code below is in case we want to purposely invoke the traverser. May or may not be used.
49 | // const newEvent = new Event('run-traverser');
50 | // window.dispatchEvent(newEvent);
--------------------------------------------------------------------------------
/build/devtools.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/build/hexagonFAT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gossamer-React/Lucid/ce6e2d7e57ff5dc19e77d661bf733f7bfa1cfd3d/build/hexagonFAT.png
--------------------------------------------------------------------------------
/build/lucid.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gossamer-React/Lucid/ce6e2d7e57ff5dc19e77d661bf733f7bfa1cfd3d/build/lucid.zip
--------------------------------------------------------------------------------
/build/lucidlogo-card-transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gossamer-React/Lucid/ce6e2d7e57ff5dc19e77d661bf733f7bfa1cfd3d/build/lucidlogo-card-transparent.png
--------------------------------------------------------------------------------
/build/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "lucid",
4 | "description": "A React-GraphQL developer tool.",
5 | "version": "1.0.3",
6 | "devtools_page": "devtools.html",
7 | "homepage_url": "https://github.com/Gossamer-React/React-Lucid",
8 | "content_scripts": [
9 | {
10 | "run_at": "document_end",
11 | "matches": [""],
12 | "js": ["contentScript.js"]
13 | }
14 | ],
15 | "background": {
16 | "scripts": ["backgroundScript.js"]
17 | },
18 | "permissions": ["storage"],
19 | "web_accessible_resources": ["reactTraverser.js"],
20 | "externally_connectable": {
21 | "ids": ["*"]
22 | },
23 | "icons": {
24 | "128": "hexagonFAT.png"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/build/reactTraverser.js:
--------------------------------------------------------------------------------
1 | let timeout;
2 | let reactGlobalHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
3 |
4 | if (reactGlobalHook) {
5 | const reactInstance = reactGlobalHook.renderers.get(1);
6 | let virtualdom;
7 | var reactDOMArr = [];
8 |
9 | // window.addEventListener('run-traverser', () => {
10 | // console.log('run the traverser!')
11 | // setHook();
12 | // reactGlobalHook.onCommitFiberRoot();
13 | // })
14 | function setHook() {
15 | //React 16+
16 | if (reactInstance && reactInstance.version) {
17 | reactGlobalHook.onCommitFiberRoot = (function(oCFR) {
18 | return function(...args) {
19 | if (args[1] !== undefined) {
20 | clearTimeout(timeout);
21 | timeout = setTimeout(() => {
22 | virtualdom = args[1];
23 | let nodeToTraverse = virtualdom.current.stateNode.current;
24 | traverse(nodeToTraverse);
25 | window.postMessage(
26 | JSON.parse(
27 | JSON.stringify({
28 | type: 'reactTraverser',
29 | data: reactDOMArr
30 | })
31 | ),
32 | '*'
33 | );
34 |
35 | reactDOMArr = [];
36 | }, 750);
37 |
38 | return oCFR(...args);
39 | }
40 | };
41 | })(reactGlobalHook.onCommitFiberRoot);
42 | } else if (reactInstance && reactInstance.Reconciler) {
43 | console.log('React version 16+ (Fiber) is required to use React-Lucid');
44 | } else {
45 | console.log('React not found- React is required to use React-Lucid');
46 | }
47 | }
48 | setHook();
49 | const traverse = (node, childrenarr = reactDOMArr, sib = false) => {
50 | if (node.type) {
51 | if (node.type.name) {
52 | //if desired node, create obj and push into reactDOMArr
53 | obj = {
54 | name: node.type.name,
55 | attributes: {
56 | Id: node._debugID
57 | },
58 | children: [],
59 | State: stateAndPropParser(node.memoizedState),
60 | Props: stateAndPropParser(node.memoizedProps)
61 | };
62 |
63 | //Create parent node in reactDOMArr
64 | if (reactDOMArr.length === 0) {
65 | reactDOMArr.push(obj);
66 | childrenarr = reactDOMArr[reactDOMArr.length - 1]['children'];
67 | } else {
68 | childrenarr.push(obj);
69 | if (!sib) {
70 | childrenarr = obj['children'];
71 | }
72 | }
73 | }
74 | }
75 |
76 | if (node.child !== null) {
77 | traverse(node.child, childrenarr, false);
78 | }
79 | if (node.sibling) {
80 | traverse(node.sibling, childrenarr, true);
81 | }
82 | return;
83 | };
84 | } else {
85 | console.log('React devtool is required to use React-Lucid');
86 | }
87 |
88 | // * Parsing Functions
89 |
90 | //* This function will try to parser the state and props objects and catch any circular json errors.
91 | /** @param reactObj - this will be the components state or prop object
92 | * @return object/error - this will return a parsered object or a catched error.
93 | */
94 | const stateAndPropParser = reactObj => {
95 | try {
96 | let result = {};
97 | if (typeof reactObj === 'object') {
98 | for (let key in reactObj) {
99 | const val = reactObj[key];
100 | if (typeof val === 'function' || typeof val === 'object') {
101 | result[key] = JSON.parse(
102 | JSON.stringify(val, (key, value) => {
103 | try {
104 | return JSON.parse(JSON.stringify(parserObject(value)));
105 | } catch (error) {
106 | return error;
107 | }
108 | })
109 | );
110 | } else {
111 | result[key] = val;
112 | }
113 | }
114 | } else {
115 | result = reactObj;
116 | }
117 | return result;
118 | } catch (e) {
119 | return {};
120 | }
121 | };
122 |
123 | // * This function will try to parser an object inside a component state or props objecct and catch any circular json errors.
124 | /** @param propObj - this will be an obj within a component state or prop object
125 | * @return object/error - this will return a parsered object or a catched error.
126 | */
127 | const parserObject = propObj => {
128 | try {
129 | let result = {};
130 | if (typeof propObj === 'object') {
131 | for (let key in propObj) {
132 | const val = propObj[key];
133 | if (typeof val === 'function' || typeof val === 'object') {
134 | result[key] = JSON.parse(
135 | JSON.stringify(val, (key, value) => {
136 | try {
137 | return JSON.parse(JSON.stringify(value));
138 | } catch (err) {
139 | return 'unable to parser circular reference';
140 | }
141 | })
142 | );
143 | } else {
144 | result[key] = val;
145 | }
146 | }
147 | } else {
148 | result = propObj;
149 | }
150 | return result;
151 | } catch (err) {
152 | return 'unable to parser circular reference';
153 | }
154 | };
155 |
--------------------------------------------------------------------------------
/devtools.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lucid",
3 | "version": "1.0.0",
4 | "description": "A devtool for React and GraphQL developers",
5 | "main": "devtools.js",
6 | "author": "Gossamer",
7 | "license": "ISC",
8 | "scripts": {
9 | "build": "webpack",
10 | "client": "webpack-dev-server --mode development",
11 | "test": "jest"
12 | },
13 | "jest": {
14 | "transform": {
15 | "^.+\\.(js|jsx)$": "babel-jest",
16 | ".+\\.(css|styl|less|sass|scss)$": "/node_modules/jest-css-modules-transform"
17 | },
18 | "transformIgnorePatterns": [
19 | "/node_modules/(?!test-component).+\\.js$"
20 | ]
21 | },
22 | "dependencies": {
23 | "graphql": "^14.7.0",
24 | "graphql-syntax-highlighter-react": "^0.4.0",
25 | "react": "^16.6.3",
26 | "react-d3-tree": "^1.12.0",
27 | "react-dom": "^16.6.3",
28 | "react-json-view": "^1.19.1"
29 | },
30 | "devDependencies": {
31 | "@babel/core": "^7.15.8",
32 | "@babel/plugin-proposal-class-properties": "^7.14.5",
33 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
34 | "@babel/preset-env": "^7.15.8",
35 | "@babel/preset-flow": "^7.14.5",
36 | "@babel/preset-react": "^7.14.5",
37 | "@babel/register": "^7.15.3",
38 | "autoprefixer": "^9.8.8",
39 | "babel-core": "^7.0.0-bridge.0",
40 | "babel-jest": "^27.2.5",
41 | "babel-loader": "^7.1.5",
42 | "css-loader": "^1.0.1",
43 | "html-webpack-plugin": "^5.3.2",
44 | "jest": "^27.2.5",
45 | "jest-css-modules-transform": "^2.5.0",
46 | "path": "^0.12.7",
47 | "postcss-loader": "^3.0.0",
48 | "react-test-renderer": "^16.14.0",
49 | "style-loader": "^0.23.1",
50 | "terser-webpack-plugin": "^1.2.3",
51 | "webpack": "^5.58.2",
52 | "webpack-cli": "^4.9.0",
53 | "webpack-dev-server": "^4.3.1"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/public/ReqResJson.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gossamer-React/Lucid/ce6e2d7e57ff5dc19e77d661bf733f7bfa1cfd3d/public/ReqResJson.gif
--------------------------------------------------------------------------------
/public/StateDiff.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gossamer-React/Lucid/ce6e2d7e57ff5dc19e77d661bf733f7bfa1cfd3d/public/StateDiff.gif
--------------------------------------------------------------------------------
/public/TreeDisplay.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gossamer-React/Lucid/ce6e2d7e57ff5dc19e77d661bf733f7bfa1cfd3d/public/TreeDisplay.gif
--------------------------------------------------------------------------------
/public/TreeFilter.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gossamer-React/Lucid/ce6e2d7e57ff5dc19e77d661bf733f7bfa1cfd3d/public/TreeFilter.gif
--------------------------------------------------------------------------------
/public/assets/lucidlogo-transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gossamer-React/Lucid/ce6e2d7e57ff5dc19e77d661bf733f7bfa1cfd3d/public/assets/lucidlogo-transparent.png
--------------------------------------------------------------------------------
/public/demo-gif.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gossamer-React/Lucid/ce6e2d7e57ff5dc19e77d661bf733f7bfa1cfd3d/public/demo-gif.gif
--------------------------------------------------------------------------------
/public/demo-photo-graphql.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gossamer-React/Lucid/ce6e2d7e57ff5dc19e77d661bf733f7bfa1cfd3d/public/demo-photo-graphql.png
--------------------------------------------------------------------------------
/public/demo-photo-react.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gossamer-React/Lucid/ce6e2d7e57ff5dc19e77d661bf733f7bfa1cfd3d/public/demo-photo-react.png
--------------------------------------------------------------------------------
/src/components/AppNav/AppNav.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './app_nav.css';
3 |
4 | const appNav = (props) => {
5 | return (
6 |