├── dev-app ├── .eslintignore ├── .gitignore ├── public │ ├── favicon.png │ ├── index.html │ └── global.css ├── src │ ├── main.js │ ├── store.js │ ├── components │ │ ├── displays │ │ │ ├── ProfilerDisplay.svelte │ │ │ └── ComponentDisplay.svelte │ │ ├── Hidden.svelte │ │ ├── tree_hierarchy │ │ │ ├── ComponentStats.svelte │ │ │ └── ComponentTree.svelte │ │ ├── Nav.svelte │ │ └── profiler │ │ │ └── ProfilerGraphs.svelte │ ├── Nav.test.js │ ├── utils │ │ ├── exploreCompositeDataType.js │ │ └── componentDisplayFuncs.js │ └── App.svelte ├── .babelrc ├── .eslintrc.json ├── jest.config.js ├── package.json └── rollup.config.js ├── icons ├── icon16.png ├── icon48.png └── icon128.png ├── devtools.js ├── docs ├── images │ ├── temp.png │ ├── svelcro.png │ └── svelcroEDIT.png ├── gifs │ ├── installition.gif │ ├── render-count.gif │ ├── render-times.gif │ └── component-tree-hierarchy.gif └── LICENSE.md ├── background.html ├── devtools.html ├── background.js ├── manifest.json ├── panel.html ├── README.md └── document_start.js /dev-app/.eslintignore: -------------------------------------------------------------------------------- 1 | src/utils/componentDisplayFuncs.js -------------------------------------------------------------------------------- /dev-app/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Svelcro/HEAD/icons/icon16.png -------------------------------------------------------------------------------- /icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Svelcro/HEAD/icons/icon48.png -------------------------------------------------------------------------------- /devtools.js: -------------------------------------------------------------------------------- 1 | chrome.devtools.panels.create('Svelcro', 'callback','panel.html') 2 | 3 | 4 | -------------------------------------------------------------------------------- /icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Svelcro/HEAD/icons/icon128.png -------------------------------------------------------------------------------- /docs/images/temp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Svelcro/HEAD/docs/images/temp.png -------------------------------------------------------------------------------- /docs/images/svelcro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Svelcro/HEAD/docs/images/svelcro.png -------------------------------------------------------------------------------- /dev-app/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Svelcro/HEAD/dev-app/public/favicon.png -------------------------------------------------------------------------------- /docs/gifs/installition.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Svelcro/HEAD/docs/gifs/installition.gif -------------------------------------------------------------------------------- /docs/gifs/render-count.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Svelcro/HEAD/docs/gifs/render-count.gif -------------------------------------------------------------------------------- /docs/gifs/render-times.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Svelcro/HEAD/docs/gifs/render-times.gif -------------------------------------------------------------------------------- /background.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/images/svelcroEDIT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Svelcro/HEAD/docs/images/svelcroEDIT.png -------------------------------------------------------------------------------- /docs/gifs/component-tree-hierarchy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Svelcro/HEAD/docs/gifs/component-tree-hierarchy.gif -------------------------------------------------------------------------------- /dev-app/src/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.svelte'; 2 | 3 | const app = new App({ 4 | target: document.body, 5 | }); 6 | 7 | export default app; 8 | -------------------------------------------------------------------------------- /dev-app/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["@babel/preset-env", { "targets": { "node": "current" } }]], 3 | "plugins": ["babel-plugin-transform-vite-meta-env"], 4 | "ignore": ["/node_modules/"] 5 | } 6 | -------------------------------------------------------------------------------- /dev-app/src/store.js: -------------------------------------------------------------------------------- 1 | import {writable, get} from 'svelte/store'; 2 | 3 | export const compCountsStore = writable([]); 4 | export const compInstancesStore = writable({}); 5 | export const compTimesStore = writable([]); 6 | export const compArrayStore = writable([]); 7 | export const type = writable('none'); 8 | -------------------------------------------------------------------------------- /dev-app/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "airbnb-base" 8 | ], 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "rules": { 14 | "import/no-extraneous-dependencies": ["error", {"devDependencies": true}] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /devtools.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Sidebar 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /dev-app/src/components/displays/ProfilerDisplay.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 |
9 | 10 | 18 | -------------------------------------------------------------------------------- /dev-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Svelte app 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /dev-app/src/components/Hidden.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | {#if shown} 21 | 22 | {/if} -------------------------------------------------------------------------------- /dev-app/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.js$': 'babel-jest', 4 | '^.+\\.svelte$': 'svelte-jester', 5 | '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub' 6 | }, 7 | moduleFileExtensions: ['svelte', 'js'], 8 | moduleNameMapper: { 9 | d3: '/node_modules/d3/dist/d3.min.js', 10 | }, 11 | testEnvironment: 'jsdom', 12 | setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'], 13 | testPathIgnorePatterns: ['./node_modules/'], 14 | }; 15 | -------------------------------------------------------------------------------- /dev-app/src/components/displays/ComponentDisplay.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 | 9 | 10 |
11 | 12 | 21 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | chrome.runtime.onInstalled.addListener(()=>{ 4 | //right click Svelcro devtools context menu option 5 | chrome.contextMenus.create({ 6 | "id" : "Svelcro", 7 | "title" : "Svelcro DevTools", 8 | "contexts" : ["all"] 9 | }) 10 | 11 | chrome.runtime.onMessage.addListener((msg, sender, response)=> { 12 | // Recieves msg body from DEVTOOLSscripts 13 | if(msg){ 14 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 15 | chrome.tabs.sendMessage(tabs[0].id, {header: msg}, function(response) {}); 16 | }); 17 | } 18 | return true; 19 | }) 20 | 21 | }) -------------------------------------------------------------------------------- /dev-app/src/Nav.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/svelte'; 2 | import nav from './components/nav.svelte'; 3 | 4 | describe('Nav component tests: ', () => { 5 | it('Navbar is mounted when nav component is rendered', () => { 6 | render(nav); 7 | const navBar = screen.queryByTestId('nav-bar'); 8 | expect(navBar).toBeInTheDocument(); 9 | }); 10 | 11 | it('Component button is mounted when nav component is rendered', () => { 12 | render(nav); 13 | const compButton = screen.queryByTestId('comp-button'); 14 | expect(compButton).toBeInTheDocument(); 15 | }); 16 | 17 | it('Profiler button is mounted when nav component is rendered', () => { 18 | render(nav); 19 | const profilerButton = screen.queryByTestId('profiler-button'); 20 | expect(profilerButton).toBeInTheDocument(); 21 | }); 22 | }) 23 | -------------------------------------------------------------------------------- /dev-app/src/utils/exploreCompositeDataType.js: -------------------------------------------------------------------------------- 1 | const exploreCompositeDataType = (node) => { 2 | if (node.type === 'Literal') { 3 | return node.value; 4 | } 5 | if (node.type === 'ArrayExpression') { 6 | if (node.elements[0].type === 'Literal') { 7 | return node.elements; 8 | } 9 | const arr = []; 10 | for (let i = 0; i < node.elements.length; i += 1) { 11 | arr.push(exploreCompositeDataType(node.elements[i])); 12 | } 13 | return arr; 14 | } 15 | const obj = {}; 16 | for (let i = 0; i < node.properties.length; i += 1) { 17 | if (node.properties[i].value.type === 'Literal') { 18 | obj[node.properties[i].key.name || node.properties[i].key.value] = node.properties[i].value.value; 19 | } else { 20 | obj[node.properties[i].key.name] = exploreCompositeDataType(node.properties[i].value); 21 | } 22 | } 23 | return obj; 24 | }; 25 | export default exploreCompositeDataType; 26 | -------------------------------------------------------------------------------- /dev-app/src/components/tree_hierarchy/ComponentStats.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 |
Component stats
8 |
    9 | {#each $compArrayStore as comp} 10 |
  • 11 | {`${comp.$$.id}: ${JSON.stringify(comp.$$.ctx[0])}`} 12 |
  • 13 | {/each} 14 |
15 |
16 | 17 | 43 | -------------------------------------------------------------------------------- /docs/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Svelcro 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 | -------------------------------------------------------------------------------- /dev-app/public/global.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | position: relative; 3 | width: 100%; 4 | height: 100%; 5 | } 6 | 7 | body { 8 | /* TEXT COLOR */ 9 | color: rgba(245, 245, 245, 0.543); 10 | 11 | 12 | margin: 0; 13 | box-sizing: border-box; 14 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 15 | } 16 | 17 | a { 18 | color: rgb(0,100,200); 19 | text-decoration: none; 20 | } 21 | 22 | a:hover { 23 | text-decoration: underline; 24 | } 25 | 26 | a:visited { 27 | color: rgb(0,80,160); 28 | } 29 | 30 | label { 31 | display: block; 32 | } 33 | 34 | input, button, select, textarea { 35 | font-family: inherit; 36 | font-size: inherit; 37 | -webkit-padding: 0.4em 0; 38 | padding: 0.4em; 39 | box-sizing: border-box; 40 | border: 1px solid #ccc; 41 | border-radius: 2px; 42 | } 43 | 44 | input:disabled { 45 | color: #ccc; 46 | } 47 | 48 | button { 49 | color: #333; 50 | background-color: #f4f4f4; 51 | outline: none; 52 | } 53 | 54 | button:disabled { 55 | color: #999; 56 | } 57 | 58 | button:not(:disabled):active { 59 | background-color: #ddd; 60 | } 61 | 62 | button:focus { 63 | border-color: #666; 64 | } 65 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Svelcro", 3 | "version": "1.0", 4 | "minimum_chrome_version": "10.0", 5 | "manifest_version": 3, 6 | "devtools_page": "devtools.html", 7 | "background": { 8 | "service_worker": "background.js" 9 | }, 10 | "content_security_policy": { 11 | "extension pages": "script-src 'self' https://d3js.org/d3.v7.min.js; object-src 'self'" 12 | }, 13 | "permissions": [ 14 | "contextMenus", 15 | "tabs", 16 | "activeTab" 17 | ], 18 | "content_scripts": [{ 19 | "matches": ["https://*/*", "http://*/*"], 20 | "js": ["document_start.js"], 21 | "run_at": "document_start" 22 | }], 23 | "web_accessible_resources": [{ 24 | "resources": ["contentScript.js"], 25 | "matches": ["https://*/*", "http://*/*"] 26 | }, 27 | { 28 | "resources": ["document_start.js"], 29 | "matches": ["https://*/*", "http://*/*"] 30 | }], 31 | "externally_connectable": { 32 | "matches": ["http://localhost/*"] 33 | }, 34 | "icons": { "16": "/icons/icon16.png", 35 | "48": "/icons/icon48.png", 36 | "128": "/icons/icon128.png" } 37 | } 38 | -------------------------------------------------------------------------------- /dev-app/src/components/tree_hierarchy/ComponentTree.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 |
11 | 24 |
25 |
26 | 27 | 57 | -------------------------------------------------------------------------------- /dev-app/src/components/Nav.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 31 | 32 | (child.shown = e.detail)}> 33 | 34 | 35 | 36 | (child1.shown = e.detail)}> 37 | 38 | 39 | 40 | 57 | -------------------------------------------------------------------------------- /dev-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-app", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "rollup -c", 7 | "dev": "rollup -c -w", 8 | "start": "sirv public --no-clear", 9 | "test": "npx jest", 10 | "test:watch": "npm run test -- --watch" 11 | 12 | }, 13 | "moduleFileExtensions": [ 14 | "js", 15 | "svelte" 16 | ], 17 | "devDependencies": { 18 | "@babel/core": "^7.17.9", 19 | "@babel/preset-env": "^7.16.11", 20 | "@rollup/plugin-commonjs": "^17.0.0", 21 | "@rollup/plugin-node-resolve": "^11.0.0", 22 | "@testing-library/jest-dom": "^5.16.4", 23 | "@testing-library/svelte": "^3.1.1", 24 | "@testing-library/user-event": "^14.1.1", 25 | "babel-jest": "^22.4.4", 26 | "babel-plugin-transform-vite-meta-env": "^1.0.3", 27 | "eslint": "^8.14.0", 28 | "eslint-config-airbnb-base": "^15.0.0", 29 | "eslint-plugin-import": "^2.26.0", 30 | "jest": "^26.6.3", 31 | "jest-transform-stub": "^2.0.0", 32 | "rollup": "^2.3.4", 33 | "rollup-plugin-css-only": "^3.1.0", 34 | "rollup-plugin-livereload": "^2.0.0", 35 | "rollup-plugin-svelte": "^7.0.0", 36 | "rollup-plugin-terser": "^7.0.0", 37 | "sinon-chrome": "^3.0.1", 38 | "svelte": "^3.20.1", 39 | "svelte-jester": "^1.8.2", 40 | "svelte-preprocess": "^4.10.5" 41 | }, 42 | "dependencies": { 43 | "cross-env": "^7.0.3", 44 | "d3": "^7.4.4", 45 | "jest-esm-transformer": "^1.0.0", 46 | "sirv-cli": "^2.0.0", 47 | "svelte-compiler": "^1.0.1", 48 | "svelte-preprocess": "^4.10.5", 49 | "svelte-spa-router": "^3.2.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /panel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Svelcro 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 54 | 55 |
56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /dev-app/rollup.config.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import resolve from '@rollup/plugin-node-resolve'; 4 | import livereload from 'rollup-plugin-livereload'; 5 | import { terser } from 'rollup-plugin-terser'; 6 | import css from 'rollup-plugin-css-only'; 7 | 8 | const production = !process.env.ROLLUP_WATCH; 9 | 10 | function serve() { 11 | let server; 12 | 13 | function toExit() { 14 | if (server) server.kill(0); 15 | } 16 | 17 | return { 18 | writeBundle() { 19 | if (server) return; 20 | server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { 21 | stdio: ['ignore', 'inherit', 'inherit'], 22 | shell: true 23 | }); 24 | 25 | process.on('SIGTERM', toExit); 26 | process.on('exit', toExit); 27 | } 28 | }; 29 | } 30 | 31 | export default { 32 | input: 'src/main.js', 33 | output: { 34 | sourcemap: true, 35 | format: 'iife', 36 | name: 'app', 37 | file: 'public/build/bundle.js' 38 | }, 39 | plugins: [ 40 | svelte({ 41 | compilerOptions: { 42 | // enable run-time checks when not in production 43 | dev: !production 44 | } 45 | }), 46 | // we'll extract any component CSS out into 47 | // a separate file - better for performance 48 | css({ output: 'bundle.css' }), 49 | 50 | // If you have external dependencies installed from 51 | // npm, you'll most likely need these plugins. In 52 | // some cases you'll need additional configuration - 53 | // consult the documentation for details: 54 | // https://github.com/rollup/plugins/tree/master/packages/commonjs 55 | resolve({ 56 | browser: true, 57 | dedupe: ['svelte'] 58 | }), 59 | commonjs(), 60 | 61 | // In dev mode, call `npm run start` once 62 | // the bundle has been generated 63 | !production && serve(), 64 | 65 | // Watch the `public` directory and refresh the 66 | // browser on changes when not in production 67 | !production && livereload('public'), 68 | 69 | // If we're building for production (npm run build 70 | // instead of npm run dev), minify 71 | production && terser() 72 | ], 73 | watch: { 74 | clearScreen: false 75 | } 76 | }; 77 | -------------------------------------------------------------------------------- /dev-app/src/App.svelte: -------------------------------------------------------------------------------- 1 | 67 | 68 |
69 |

Svelcro

70 |
72 | 73 | 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | 6 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/oslabs-beta/Svelcro/pulls) 7 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)]() 8 | 9 | 10 | 11 | The first component performance tracker for Svelte applications. 12 | 13 | # Features 14 | 15 | - Component Dependency Tree - View your components as a dependency tree or hierarchy. 16 | ![component tree and hierarchy](./docs/gifs/component-tree-hierarchy.gif) 17 | - Component Stats - List state and properties in your components. 18 | - Profiler 19 | - Render Performance - Monitors component render time from start to finish. 20 | ![](./docs/gifs/render-times.gif) 21 | - Render Count - Tracks how many times your components have rendered. 22 | ![](./docs/gifs/render-count.gif) 23 | 24 | # Installation 25 | 26 | Extension Coming To Chrome Store Soon... 27 | 28 | Feel free to fork and clone this repo! Otherwise, just download Svelcro and save it somewhere on your computer. 29 | 30 | Make Svelcro/dev-app your current directory, install depencencies, and run build. 31 | 32 | ``` 33 | cd Svelcro/dev-app 34 | npm install 35 | npm run build 36 | ``` 37 | 38 | ![installation](./docs/gifs/installition.gif) 39 | 40 | Navigate to Chrome's extensions page at `chrome://extensions/`. 41 | 42 | Turn on 'developer mode' in the top-right corner of the page. 43 | 44 | Click on 'load unpacked' at the top-left, and select your local copy of the Svelcro directory. 45 | 46 | Open up your Chrome DevTools, and check to make sure Svelcro is available in the dropdown menu of the navbar! 47 | 48 | # How To Use 49 | 50 | Make sure your svelte application is in development mode. 51 | 52 | You can then open up Chrome DevTools and navigate to Svelcro. 53 | 54 | Visualize component dependencies and their respective state in the "Components" tab or monitor your component render performance in the "Profiler" tab. 55 | 56 | # Troubleshooting 57 | 58 | - Have you installed your Svelcro dependencies? 59 | - Sometimes it helps to refresh your webpage or Svelcro in the Chrome extensions page. 60 | 61 | # What's to come 62 | 63 | - Monitor applications built with SvelteKit 64 | - Time Machine feature to track history of application state 65 | 66 | # Contribute 67 | 68 | We would love to hear from you! 69 | 70 | Svelcro is currently in beta. If you would like to contribute please contact the authors at svelcrodt@gmail.com. 71 | 72 | Notice any issues or bugs? Open an issue! 73 | 74 | # Learn More 75 | 76 | 77 | 78 | Visit the [Svelcro website!](https://www.svelcro.dev/ 79 | ) 80 | 81 | 82 | 83 | Read more at Medium - [Component Performance Monitoring with Selvro](https://medium.com/@svelcrodt/component-performance-monitoring-in-svelte-with-svelcro-d0bbe1d6aae0) 84 | 85 | # Contributors 86 | 87 | Sara Kivikas - [@skivikas](https://github.com/skivikas) 88 | 89 | Rankin Draa - [@rankind94](https://github.com/rankind94) 90 | 91 | Yanming Yu - [@jimmyjameswong](https://github.com/jimmyjameswong) 92 | 93 | Zachary Daniels - [@zackdaniels](https://github.com/zackdaniels) 94 | 95 | # License 96 | 97 | [MIT](./docs/LICENSE.md) 98 | -------------------------------------------------------------------------------- /document_start.js: -------------------------------------------------------------------------------- 1 | let editorExtensionId = chrome.runtime.id; 2 | 3 | const code = `let editorExtensionId = '${editorExtensionId}'; 4 | 5 | (function () { 6 | 'use strict'; 7 | 8 | const components = []; 9 | 10 | // object to hold render counts by component 11 | let nextId = 1; 12 | 13 | // Proxy object that trigger function when property values are changed 14 | let compCounts = new Proxy({}, { 15 | set: function (target, key, value) { 16 | target[key] = value; 17 | 18 | chrome.runtime.sendMessage(editorExtensionId, 19 | { header: "UPDATE_RENDER", 20 | compCounts: JSON.stringify(compCounts), 21 | compInstance: JSON.stringify(compInstance), 22 | compTimes: JSON.stringify(compTimes), 23 | compArray: JSON.stringify(components) 24 | }); 25 | return true; 26 | } 27 | }); 28 | // Object to track instances of components 29 | const compInstance = new Proxy({}, { 30 | set: function (target, key, value) { 31 | target[key] = value; 32 | 33 | // Send render count records to dev Tools 34 | chrome.runtime.sendMessage(editorExtensionId, 35 | { header: "UPDATE_INSTANCE", 36 | compCounts: JSON.stringify(compCounts), 37 | compInstance: JSON.stringify(compInstance), 38 | compTimes: JSON.stringify(compTimes), 39 | compArray: JSON.stringify(components) 40 | }); 41 | 42 | return true; 43 | } 44 | }); 45 | 46 | let compTimes = new Proxy({}, { 47 | set: function (target, key, value) { 48 | target[key] = value; 49 | 50 | chrome.runtime.sendMessage(editorExtensionId, 51 | { header: "UPDATE_TIMES", 52 | compCounts: JSON.stringify(compCounts), 53 | compInstance: JSON.stringify(compInstance), 54 | compTimes: JSON.stringify(compTimes), 55 | compArray: JSON.stringify(components) 56 | }); 57 | return true; 58 | } 59 | }); 60 | 61 | // add all Svelte components to array 62 | let start; 63 | let first = true; 64 | 65 | window.document.addEventListener('SvelteRegisterComponent', (e) => { 66 | 67 | start = window.performance.now(); 68 | 69 | let isFirstAfterUpdate = true; 70 | 71 | const { component, tagName } = e.detail; 72 | 73 | component.$$['tag_name'] = tagName; 74 | component.$$['id'] = tagName + nextId; 75 | nextId++; 76 | const curId = component.$$.id; 77 | compCounts[curId] = 1; 78 | components.push(e.detail.component) 79 | 80 | // capturing all instance of components 81 | if(!compInstance[tagName]){ 82 | compInstance[tagName] = 1; 83 | } else { 84 | compInstance[tagName] += 1; 85 | } 86 | 87 | if (first) { 88 | component.$$.on_mount.push(() => { 89 | let rendertime = window.performance.now() - start; 90 | const curId = component.$$.id; 91 | compTimes[curId] = parseFloat(rendertime).toFixed(3); 92 | compCounts[curId] = 1; 93 | }) 94 | } 95 | 96 | component.$$.before_update.push(() => { 97 | let time = window.performance.now() 98 | component.$$.before_update.time = time; 99 | }); 100 | 101 | component.$$.on_destroy.push(() => { 102 | compInstance[tagName] -= 1; 103 | // For render count 104 | delete compCounts[curId]; 105 | delete compTimes[curId]; 106 | }); 107 | 108 | component.$$.before_update.push(() => { 109 | let time = window.performance.now() 110 | component.$$.before_update.time = time; 111 | }); 112 | 113 | component.$$.after_update.push(() => { 114 | let now = window.performance.now(); 115 | const curId = component.$$.id; 116 | let rendertime = now - component.$$.before_update.time; 117 | if (isFirstAfterUpdate) { return isFirstAfterUpdate = false;} 118 | 119 | compCounts[curId] += 1; 120 | compTimes[curId] = parseFloat(rendertime).toFixed(3); 121 | }); 122 | 123 | }) 124 | 125 | window.addEventListener("message", function(event) { 126 | chrome.runtime.sendMessage(editorExtensionId, 127 | { header: "INITIAL_LOAD", compCounts: JSON.stringify(compCounts), compInstance: JSON.stringify(compInstance), compTimes: JSON.stringify(compTimes), compArray: JSON.stringify(components) }); 128 | }, false); 129 | 130 | })();` 131 | 132 | document.documentElement.setAttribute('onreset', code); 133 | document.documentElement.dispatchEvent(new CustomEvent('reset')); 134 | 135 | 136 | chrome.runtime.onMessage.addListener((msg, sender, response)=> { 137 | window.postMessage({header : "APP MOUNTED"}); 138 | return true; 139 | }) 140 | 141 | -------------------------------------------------------------------------------- /dev-app/src/components/profiler/ProfilerGraphs.svelte: -------------------------------------------------------------------------------- 1 | 214 | 215 |
216 | 230 |
231 |
232 | 233 | 300 | -------------------------------------------------------------------------------- /dev-app/src/utils/componentDisplayFuncs.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { parse, walk } from 'svelte/compiler'; 3 | import * as d3 from 'd3'; 4 | import exploreCompositeDataType from "./exploreCompositeDataType.js"; 5 | 6 | export const getData = (tab , compRecord) => { 7 | if (tab !== 'chart' && tab !== 'tree') return; 8 | let i = 0; 9 | let componentNames = []; 10 | const D3PreTree = []; 11 | // This is a pre- or partial tree with known relationships among componenets/files that go only 1 layer deep (this is all we need to build the rest of the tree) 12 | const unorderedListOfNodes = []; 13 | let componentTree; 14 | 15 | const createNode = (ast) => { 16 | const node = {}; 17 | const dependencies = {}; 18 | const state = {}; 19 | const props = {}; 20 | const elementOfD3PreTree = {}; 21 | 22 | ast.instance.content.body.forEach((el) => { 23 | // Find dependencies (via import statements) of current svelte component/file and store the dep in the node for said svelte component/file 24 | if ( 25 | el.type === "ImportDeclaration" && 26 | el.source.value.includes(".svelte") 27 | ) { 28 | const componentName = `<${el.source.value.slice( 29 | el.source.value.lastIndexOf("/") + 1, 30 | el.source.value.lastIndexOf(".") 31 | )} />`; 32 | dependencies[componentName] = {}; 33 | } 34 | // Find props (via export statements) of current svelte component/file and store the props in the node for said svelte component/file 35 | else if (el.type === "ExportNamedDeclaration") { 36 | props[el.declaration.declarations[0].id.name] = null; 37 | } 38 | }); 39 | 40 | node[componentNames[i]] = Object.keys(dependencies).length ? dependencies : {}; 41 | 42 | Object.defineProperty(node[componentNames[i]], "Props", { 43 | value: props, 44 | configurable: true, 45 | writable: true, 46 | enumerable: false 47 | }); 48 | 49 | walk(ast, { 50 | enter(ASTnode, parent, prop, index) { 51 | if (ASTnode.hasOwnProperty("declarations")) { 52 | // For variable declarations that either have not been initialized or have a value that is equal to "null" 53 | if (!ASTnode.declarations[0].init) { 54 | state[ASTnode.declarations[0].id.name] = ASTnode.declarations[0].init; 55 | } 56 | // For variable declarations that have a value that is a primitive data type or is a "Literal" 57 | else if (ASTnode.declarations[0].init.type === "Literal") { 58 | state[ASTnode.declarations[0].id.name] = ASTnode.declarations[0].init.value; 59 | } 60 | // For variable declarations that have a value that is a composite data type 61 | else if ( 62 | ASTnode.declarations[0].init.type === "ObjectExpression" || 63 | ASTnode.declarations[0].init.type === "ArrayExpression" 64 | ) { 65 | console.log("AST NODE IS:", ASTnode) 66 | state[ASTnode.declarations[0].id.name] = exploreCompositeDataType(ASTnode.declarations[0].init); 67 | } 68 | 69 | Object.defineProperty(node[componentNames[i]], "State", { 70 | value: state, 71 | configurable: true, 72 | writable: true, 73 | enumerable: false 74 | }); 75 | } 76 | }, 77 | }); 78 | 79 | if (Object.keys(node).length) { 80 | unorderedListOfNodes.push(node); 81 | 82 | // For D3 83 | const temp = {}; 84 | temp["State"] = state; 85 | temp["Props"] = props; 86 | elementOfD3PreTree[componentNames[i]] = temp; 87 | D3PreTree.push(elementOfD3PreTree); 88 | } 89 | } 90 | 91 | const createTree = (arr) => { 92 | for (let j = 0; j < arr.length; j += 1) { 93 | let success = 0; 94 | 95 | const searchTree = ( 96 | tree, 97 | keyToSearchFor, 98 | valToSubstituteIfKeyIsFound 99 | ) => { 100 | for (const key in tree) { 101 | if (key === keyToSearchFor) { 102 | tree[key] = valToSubstituteIfKeyIsFound; 103 | arr.splice(j, 1); 104 | success += 1; 105 | return true; 106 | } 107 | if ( 108 | Object.keys(tree[key]).length && 109 | searchTree( 110 | tree[key], 111 | keyToSearchFor, 112 | valToSubstituteIfKeyIsFound 113 | ) 114 | ) { 115 | return true; 116 | } 117 | } 118 | return false; 119 | } 120 | 121 | for (const key in arr[j]) { 122 | // If an unordered array node has keys that are not null (an object and therefore has dependencies) 123 | if (Object.keys(arr[j][key]).length > 0) { 124 | // testing top-most component (second level) 125 | for (const nestedKey in arr[j][key]) { 126 | for (const masterKey in componentTree) { 127 | if (nestedKey === masterKey) { 128 | arr[j][key][nestedKey] = componentTree[masterKey]; 129 | componentTree = arr[j]; 130 | arr.splice(j, 1); 131 | success += 1; 132 | } 133 | } 134 | } 135 | } 136 | } 137 | 138 | for (const key in arr[j]) { 139 | if (!success) { 140 | searchTree(componentTree, key, arr[j][key]); 141 | } 142 | } 143 | if (success) { 144 | j -= success; 145 | success = 0; 146 | } 147 | } 148 | 149 | if (arr.length !== 0) { 150 | createTree(arr); 151 | } 152 | } 153 | 154 | const filterTree = (compRecord, templateStructured) => { 155 | // comp record tag names 156 | const compRecordTagNames = {} 157 | 158 | compRecord.forEach((comp) => { 159 | const curTag = comp.$$.tag_name; 160 | if (compRecordTagNames.hasOwnProperty(curTag)) compRecordTagNames[curTag] += 1; 161 | else compRecordTagNames[curTag] = 1; 162 | }) 163 | console.log('filterTree - compRecordTagNames: ', compRecordTagNames); 164 | // helper 165 | const helper = (struc) => { 166 | // if no children, return 167 | if (!struc.children) return; 168 | // start at beginning of template structured 169 | // check that children have a match in compRecord 170 | const curChildren = struc.children; 171 | struc.children = curChildren.filter((child) => { 172 | return compRecordTagNames.hasOwnProperty(child.id); 173 | }); 174 | struc.children.forEach((child) => { 175 | helper(child); 176 | }) 177 | // console.log("filterTreeHelper - children after filter: ", struc.children); 178 | } 179 | // call helper 180 | helper(templateStructured); 181 | console.log('filterTree - templateStructured after', templateStructured) 182 | // return template structured 183 | return templateStructured; 184 | } 185 | 186 | // Get resources of inspected program and generate views 187 | chrome.devtools.inspectedWindow.getResources(resources => { 188 | const arrSvelteFiles = resources.filter(file =>file.url.includes(".svelte")); 189 | console.log("arrSvelteFiles: ", arrSvelteFiles); 190 | componentNames = arrSvelteFiles.map(svelteFile => `<${svelteFile.url.slice( 191 | svelteFile.url.lastIndexOf("/") + 1, 192 | svelteFile.url.lastIndexOf(".") 193 | )} />`); 194 | 195 | arrSvelteFiles.forEach(svelteFile => { 196 | svelteFile.getContent(source => { 197 | if (source) { 198 | const ast = parse(source); 199 | createNode(ast); 200 | 201 | if (i === componentNames.length - 1) { 202 | componentTree = unorderedListOfNodes[0]; 203 | unorderedListOfNodes.shift(); 204 | createTree(unorderedListOfNodes); 205 | } 206 | i += 1; 207 | } 208 | }); 209 | }); 210 | 211 | // For D3 component tree 212 | let AST = []; 213 | let urls = []; 214 | 215 | // retrieves URLs from Svelte files and adds them to urls array 216 | // adds each Svelte file's contents to AST array 217 | for (let i = 0; i < arrSvelteFiles.length; i++) { 218 | urls.push(JSON.parse(JSON.stringify(arrSvelteFiles[i]))); 219 | arrSvelteFiles[i].getContent(content => { 220 | AST.push(parse(content)); 221 | }); 222 | } 223 | 224 | /* ---- D3 ---- */ 225 | // executes after svelte.parse is completed 226 | setTimeout(() => { 227 | // modified D3PreTree so that it fits for D3 stratify function 228 | const newD3Pre = []; 229 | for (let eachObj of D3PreTree) { 230 | let temp = {}; 231 | let key = Object.keys(eachObj)[0]; 232 | let value = Object.values(eachObj)[0]; 233 | key = key.split(""); 234 | key.shift(); 235 | key.pop(); 236 | key.pop(); 237 | key.pop(); 238 | key = key.join(""); 239 | temp[key] = value; 240 | newD3Pre.push(temp); 241 | } 242 | 243 | // declare object to assemble component template 244 | let bigData = {}; 245 | 246 | // map out AST array so that it is easier to access the node that contains import declaration 247 | // iterated through the AST array and modified the source key to later match with url array to 248 | // combined into bigData object 249 | AST = AST.map(obj => obj.instance.content.body); 250 | for (let i = 0; i < AST.length; i++) { 251 | AST[i] = AST[i].filter(node => node.type === "ImportDeclaration"); 252 | for (let j = 0; j < AST[i].length; j++) { 253 | if (AST[i][j].source.value !== "svelte") { 254 | let obj = {}; 255 | obj.type = AST[i][j].type; 256 | obj.source = AST[i][j].source.value.split(""); 257 | obj.source.shift(); 258 | obj.source.shift(); 259 | obj.source = obj.source.join(""); 260 | obj.source = obj.source.replace(".svelte", ""); 261 | obj.source = obj.source.slice(obj.source.lastIndexOf('/') + 1) 262 | AST[i][j] = obj; 263 | } else { 264 | let obj = {}; 265 | obj.type = AST[i][j].type; 266 | obj.source = AST[i][j].source.value; 267 | AST[i][j] = obj; 268 | } 269 | } 270 | } 271 | 272 | // modified the url array to match with AST array and then combined into 273 | // bigData object 274 | for (let i = 0; i < urls.length; i++) { 275 | for (let j = urls[i].url.length - 1; j > 0; j--) { 276 | if (urls[i].url[j] === "/") { 277 | urls[i].url = urls[i].url 278 | .slice(j + 1, urls[i].url.length) 279 | .replace(".svelte", ""); 280 | } 281 | } 282 | bigData[urls[i].url] = AST[i]; 283 | } 284 | 285 | // iterate through bigData and made parent/child object and pushed into componentTemplate array 286 | let componentTemplate = []; 287 | function componentChildren(bigObj) { 288 | for (let eachKey in bigObj) { 289 | for (let eachObj of bigObj[eachKey]) { 290 | if ( 291 | eachObj.type == "ImportDeclaration" && 292 | eachObj.source !== "svelte" 293 | ) { 294 | let obj = {}; 295 | obj.parent = eachKey; 296 | obj.child = eachObj.source; 297 | componentTemplate.push(obj); 298 | } 299 | } 300 | } 301 | } 302 | componentChildren(bigData); 303 | 304 | // added special obj for the top parent component for D3 stratifyy function to successfully create relevant array 305 | for (let i = 0; i < componentTemplate.length; i++) { 306 | let obj = {}; 307 | obj.child = componentTemplate[i].parent; 308 | if (componentTemplate.every(object => object.child !== obj.child)) { 309 | if (obj.child !== "") { 310 | obj.parent = ""; 311 | componentTemplate.unshift(obj); 312 | } 313 | } 314 | } 315 | 316 | // combined data from newD3Pre into componentTemplate to render state/props onto panel with D3JS 317 | for (let i = 0; i < componentTemplate.length; i++) { 318 | for (let j = 0; j < newD3Pre.length; j++) { 319 | if (componentTemplate[i].child === Object.keys(newD3Pre[j])[0]) { 320 | componentTemplate[i].data = Object.values(newD3Pre[j])[0]; 321 | } 322 | } 323 | } 324 | 325 | // modified componentTemplate for data that has no States and/or Prop to render appropriate states for users 326 | // modified the data to show only Props keys for better user experience 327 | for (let i = 0; i < componentTemplate.length; i++) { 328 | if (!componentTemplate[i].hasOwnProperty("data")) { 329 | componentTemplate[i].data = { 330 | State: "No State", 331 | Props: "No Props" 332 | }; 333 | } else if ( 334 | Object.keys(componentTemplate[i].data.Props).length === 0 335 | ) { 336 | componentTemplate[i].data.Props = "No Props"; 337 | } else { 338 | let result = []; 339 | componentTemplate[i].data.Props = result.concat( 340 | Object.keys(componentTemplate[i].data.Props) 341 | ); 342 | } 343 | } 344 | 345 | // finally create templateStructured for D3 using D3.stratify function 346 | let templateStructured = d3 347 | .stratify() 348 | .id(function(d) { 349 | return d.child; 350 | }) 351 | .parentId(function(d) { 352 | return d.parent; 353 | })(componentTemplate); 354 | 355 | // filter through templateStructured 356 | templateStructured = filterTree(compRecord, templateStructured); 357 | switch (tab) { 358 | case "tree": 359 | let margin = {top: 40, right: 10, bottom: 50, left: 10}, 360 | width = 150 - margin.left - margin.right,//660 361 | height = 500 - margin.top - margin.bottom; 362 | 363 | // declares a tree layout and assigns the size 364 | let treemap = d3.tree() 365 | .size([width, height]); 366 | 367 | 368 | // assigns the data to a hierarchy using parent-child relationships 369 | let nodes = d3.hierarchy(templateStructured); 370 | 371 | // maps the node data to the tree layout 372 | nodes = treemap(nodes); 373 | 374 | //check if a D3 tree is already present 375 | // if so, replace tree, instead of appending tree 376 | if (!d3.select("#component-cur").empty()) { 377 | d3.select("#component-cur").remove() 378 | }; 379 | 380 | let svg = d3.select("#component-tree-display").append("svg") 381 | .attr('id', 'component-cur') 382 | .attr("width", width + margin.left + margin.right) 383 | .attr("height", height + margin.top + margin.bottom), 384 | g = svg.append("g") 385 | .attr("transform", 386 | "translate(" + margin.left + "," + margin.top + ")"); 387 | // adds the links between the nodes 388 | let link = g.selectAll(".link") 389 | .data( nodes.descendants().slice(1)) 390 | .enter().append("path") 391 | .attr("class", "link") 392 | .attr("d", function(d) { 393 | return "M" + d.x + "," + d.y 394 | + "C" + d.x + "," + (d.y + d.parent.y) / 2 395 | + " " + d.parent.x + "," + (d.y + d.parent.y) / 2 396 | + " " + d.parent.x + "," + d.parent.y; 397 | }); 398 | console.log('link', link) 399 | 400 | // adds each node as a group 401 | let node = g.selectAll(".node") 402 | .data(nodes.descendants()) 403 | .enter().append("g") 404 | .attr("class", function(d) { 405 | return "node" + 406 | (d.children ? " node--internal" : " node--leaf"); }) 407 | .attr("transform", function(d) { 408 | return "translate(" + d.x + "," + d.y + ")"; }); 409 | 410 | // adds the circle to the node 411 | node.append("circle") 412 | .attr("r", 10); 413 | 414 | // adds the text to the node 415 | node.append("text") 416 | .attr("dy", ".35em") 417 | .attr("y", function(d) { return d.children ? -20 : 20; }) 418 | .style("text-anchor", "middle") 419 | .text(function(d) { return d.data.id; }); 420 | 421 | console.log('LAST NODE', node); 422 | break; 423 | case "chart": 424 | (function () { 425 | 'use strict'; 426 | }()); 427 | let tree = d3.tree; 428 | let hierarchy = d3.hierarchy; 429 | let select = d3.select; 430 | let data = templateStructured; 431 | let MyTree = /** @class */ (function () { 432 | function MyTree() { 433 | let _this = this; 434 | this.connector = function (d) { 435 | return "M" + d.parent.y + "," + d.parent.x 436 | + "V" + d.x + "H" + d.y; 437 | }; 438 | this.collapse = function (d) { 439 | if (d.children) { 440 | d._children = d.children; 441 | d._children.forEach(_this.collapse); 442 | d.children = null; 443 | } 444 | }; 445 | this.click = function (d) { 446 | if (d.children) { 447 | d._children = d.children; 448 | d.children = null; 449 | } 450 | else { 451 | d.children = d._children; 452 | d._children = null; 453 | } 454 | _this.update(d); 455 | }; 456 | this.update = function (source) { 457 | _this.width = 100; 458 | // Compute the new tree layout. 459 | let nodes = _this.tree(_this.root); 460 | let nodesSort = []; 461 | nodes.eachBefore(function (n) { 462 | nodesSort.push(n); 463 | }); 464 | _this.height = Math.max(500, nodesSort.length * _this.barHeight + _this.margin.top + _this.margin.bottom); 465 | let links = nodesSort.slice(1); 466 | // Compute the "layout". 467 | nodesSort.forEach(function (n, i) { 468 | n.x = i * _this.barHeight; 469 | }); 470 | d3.select('svg').transition() 471 | .duration(_this.duration) 472 | .attr("height", _this.height); 473 | // Update the nodes… 474 | let node = _this.svg.selectAll('g.node') 475 | .data(nodesSort, function (d) { 476 | return d.id || (d.id = ++this.i); 477 | }); 478 | // Enter any new nodes at the parent's previous position. 479 | let nodeEnter = node.enter().append('g') 480 | .attr('class', 'node') 481 | .attr('transform', function () { 482 | return 'translate(' + source.y0 + ',' + source.x0 + ')'; 483 | }) 484 | .on('click', _this.click); 485 | nodeEnter.append('circle') 486 | .attr('r', 1e-6) 487 | .style('fill', function (d) { 488 | return d._children ? 'lightsteelblue' : '#fff'; 489 | }); 490 | nodeEnter.append('text') 491 | .attr('x', function (d) { 492 | return d.children || d._children ? 10 : 10; 493 | }) 494 | .attr('dy', '.35em') 495 | .attr('text-anchor', function (d) { 496 | return d.children || d._children ? 'start' : 'start'; 497 | }) 498 | .text(function (d) { 499 | if (d.data.id.length > 20) { 500 | return d.data.id.substring(0, 20) + '...'; 501 | } 502 | else { 503 | return d.data.id; 504 | } 505 | }) 506 | .style('fill-opacity', 1e-6); 507 | nodeEnter.append('svg:title').text(function (d) { 508 | return d.data.id; 509 | }); 510 | // Transition nodes to their new position. 511 | let nodeUpdate = node.merge(nodeEnter) 512 | .transition() 513 | .duration(_this.duration); 514 | nodeUpdate 515 | .attr('transform', function (d) { 516 | return 'translate(' + d.y + ',' + d.x + ')'; 517 | }); 518 | nodeUpdate.select('circle') 519 | .attr('r', 4.5) 520 | .style('fill', function (d) { 521 | return d._children ? 'lightsteelblue' : '#fff'; 522 | }); 523 | nodeUpdate.select('text') 524 | .style('fill-opacity', 1); 525 | // Transition exiting nodes to the parent's new position (and remove the nodes) 526 | let nodeExit = node.exit().transition() 527 | .duration(_this.duration); 528 | nodeExit 529 | .attr('transform', function (d) { 530 | return 'translate(' + source.y + ',' + source.x + ')'; 531 | }) 532 | .remove(); 533 | nodeExit.select('circle') 534 | .attr('r', 1e-6); 535 | nodeExit.select('text') 536 | .style('fill-opacity', 1e-6); 537 | // Update the links… 538 | let link = _this.svg.selectAll('path.link') 539 | .data(links, function (d) { 540 | // return d.target.id; 541 | let id = d.id + '->' + d.parent.id; 542 | return id; 543 | }); 544 | // Enter any new links at the parent's previous position. 545 | let linkEnter = link.enter().insert('path', 'g') 546 | .attr('class', 'link') 547 | .attr('d', function (d) { 548 | let o = { x: source.x0, y: source.y0, parent: { x: source.x0, y: source.y0 } }; 549 | return _this.connector(o); 550 | }); 551 | // Transition links to their new position. 552 | link.merge(linkEnter).transition() 553 | .duration(_this.duration) 554 | .attr('d', _this.connector); 555 | // Transition exiting nodes to the parent's new position. 556 | link.exit().transition() 557 | .duration(_this.duration) 558 | .attr('d', function (d) { 559 | let o = { x: source.x, y: source.y, parent: { x: source.x, y: source.y } }; 560 | return _this.connector(o); 561 | }) 562 | .remove(); 563 | // Stash the old positions for transition. 564 | nodesSort.forEach(function (d) { 565 | d.x0 = d.x; 566 | d.y0 = d.y; 567 | }); 568 | }; 569 | } 570 | MyTree.prototype.$onInit = function () { 571 | let _this = this; 572 | this.margin = { top: 20, right: 10, bottom: 20, left: 10 }; 573 | this.width = 150 - this.margin.right - this.margin.left; 574 | this.height = 100 - this.margin.top - this.margin.bottom; 575 | this.barHeight = 20; 576 | this.barWidth = this.width * .8; 577 | this.i = 0; 578 | this.duration = 750; 579 | this.tree = tree().size([this.width, this.height]); 580 | // this.tree = tree().nodeSize([0, 30]); 581 | this.tree = tree().nodeSize([0, 30]); 582 | this.root = this.tree(hierarchy(data)); 583 | this.root.each(function (d) { 584 | d.id = d.id; //transferring name to a name variable 585 | d.id = _this.i; //Assigning numerical Ids 586 | _this.i++; 587 | }); 588 | this.root.x0 = this.root.x; 589 | this.root.y0 = this.root.y; 590 | if (!d3.select("#component-cur").empty()) { 591 | console.log('hit') 592 | d3.select("#component-cur").remove() 593 | }; 594 | this.svg = select('#component-tree-display').append('svg') 595 | .attr('id', 'component-cur') 596 | .attr('width', this.width + this.margin.right + this.margin.left) 597 | .attr('height', this.height + this.margin.top + this.margin.bottom) 598 | .append('g') 599 | .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')'); 600 | this.update(this.root); 601 | }; 602 | return MyTree; 603 | }()); 604 | ; 605 | let myTree = new MyTree(); 606 | const test = myTree.$onInit(); 607 | break; 608 | } 609 | }, 100); 610 | }); 611 | }; --------------------------------------------------------------------------------