UNPKG

@alithanar/react-automation-profiler

Version:

Automated React profiling and data visualization using React's Profiler API, Puppeteer, and D3.

335 lines (314 loc) 10.9 kB
<html><head> <meta charset="utf-8"> <title>React Automation Profiler</title> <style> body { background: #333; color: #fff; font-family: Arial; font-weight: lighter; margin: 0 auto; margin-bottom: 25px; } div.arrow { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; color: #888; } div.arrow:hover { color: #52c9f2; } div.arrow-wrapper { bottom: 10px; cursor: pointer; display: flex; font-size: 18px; font-weight: lighter; height: 25px; width: 150px; justify-content: space-between; position: absolute; } div.carousel { display: flex; height: 575px; justify-content: center; margin: 0 auto; padding: 25px; position: relative; } div#export { border: 1px solid #56a6fc; border-radius: 4px; bottom: 25px; font-size: 14px; right: 25px; color: #56a6fc; cursor: pointer; padding: 5px; position: fixed; z-index: 100; } div#export:hover { background: white; border: 1px solid white; color: #333; } h2, h3, h4, h5 { font-weight: lighter; } h2 { bottom: 25px; font-size: 20px; font-weight: lighter; position: absolute; text-align: center; } h3 { font-size: 16px; margin-top: -30px; position: fixed; z-index: 1000; } h4 { font-size: 11px; bottom: 25px; left: 25px; margin: 0; position: fixed; opacity: 0; width: 275px; z-index: 1000; } h5 { margin: 0; } rect { cursor: pointer; } span { background: #373; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); border-radius: 3px; padding: 5px; opacity: 0; } svg { position: absolute; } svg.hidden { display: none; } text { fill: #fff; } text.interactions, text.total { cursor: pointer; } text.interactions:hover, text.total:hover { text-decoration: underline; fill: #fff; } </style> </head> <body> <h3 id="big-tooltip"><span></span></h3> <h4></h4> <div id="export">Export</div> <script id="charts" type="module">Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const d3 = (0, tslib_1.__importStar)(require("d3")); const charts_util_1 = require("./charts.util"); const { INTERACTIONS, RECT, TOTAL } = charts_util_1.MouseEventSelectors; const { ACTUAL_DURATION, BASE_DURATION, NUMBER_OF_INTERACTIONS, TOTAL_AUTOMATION_TIME_ELAPSED, } = charts_util_1.ParagraphMap; (async function () { const height = 500; let width = 1000; let bodyWidth = 0; const margin = { bottom: 20, left: 40, right: 10, top: 10, }; const allJsonValues = []; const carouselIds = []; const interactions = []; const itemIdLengths = []; d3.select('html').on('mousemove', (e) => (0, charts_util_1.updateTooltipPosition)(e)); const jsonFiles = document.querySelectorAll('.json'); const jsonMap = new Map(); jsonFiles.forEach((file) => { const contents = JSON.parse(file.innerHTML); const id = file.id; interactions.push(contents.numberOfInteractions); contents.logs = contents.logs.map((item, index) => { item[ACTUAL_DURATION] = item.actualDuration; item[BASE_DURATION] = item.baseDuration; item.Render = `${index + 1}: ${item.id}`; allJsonValues.push(item[ACTUAL_DURATION], item[BASE_DURATION]); itemIdLengths.push(item.id.length); delete item.actualDuration; delete item.baseDuration; delete item.id; delete item.interactions; delete item.phase; return item; }); jsonMap.set(id, [...contents.logs]); }); const longestId = Math.max(...itemIdLengths); const tallestRect = Math.max(...allJsonValues); const scaleMax = Math.round((tallestRect + tallestRect * 0.05) * 10) / 10; for (const [key, value] of jsonMap.entries()) { const [singularId, multipleId] = key.split('-'); const carouselId = singularId === 'average' ? multipleId : singularId; carouselIds.push(carouselId); let carouselEl = document.getElementById(carouselId); if (!carouselEl) { carouselEl = (0, charts_util_1.createCarousel)(carouselId); const h2El = document.createElement('h2'); let innerText = carouselId.replace(/([^A-Z])([A-Z])/g, '$1 $2'); innerText = innerText[0].toUpperCase() + innerText.substring(1); h2El.innerText = innerText; carouselEl.appendChild(h2El); document.body.appendChild(carouselEl); } const svgEl = (0, charts_util_1.createSVG)(key); carouselEl.appendChild(svgEl); const data = Object.assign(value, { columns: ['Render', BASE_DURATION, ACTUAL_DURATION], }); const totalTimeElapsed = data[data.length - 1].commitTime - data[0].startTime; const potentialWidth = ~~(data.length / 10) * 875 + Math.abs(longestId - 5) * 175 - 1000; width = potentialWidth > 1000 ? potentialWidth : 1000; const potentialBodyWidth = width + 600; if (bodyWidth < potentialBodyWidth) bodyWidth = potentialBodyWidth; svgEl.setAttribute('width', `${width}`); const [groupKey, ...keys] = data.columns; const color = d3.scaleOrdinal().range(['#5A78E6', '#56A6FC', '#52C9F2']); const legend = (svg) => { const g = svg .attr('transform', `translate(${width},0)`) .attr('text-anchor', 'end') .attr('font-family', 'sans-serif') .attr('font-size', 10) .selectAll('g') .data(color.domain().slice().reverse()) .join('g') .attr('transform', (d, i) => `translate(0,${i * 20})`); g.append('rect') .attr('x', -19) .attr('width', 19) .attr('height', 19) .attr('fill', color); g.append('text') .attr('x', -24) .attr('y', 9.5) .attr('dy', '0.35em') .text((d) => d); }; const xAxis = (g) => g .attr('transform', `translate(0,${height - margin.bottom})`) .call(d3.axisBottom(x0).tickSizeOuter(0)) .call((g) => g.select('.domain').remove()); const yAxis = (g) => g .attr('transform', `translate(${margin.left},0)`) .call(d3.axisLeft(y).ticks(null, 's')) .call((g) => g.select('.domain').remove()) .call((g) => g .select('.tick:last-of-type text') .clone() .attr('x', 3) .attr('text-anchor', 'start') .attr('font-weight', 'bold') .text('Milliseconds')); const x0 = d3 .scaleBand() .domain(data.map((d) => d['Render'])) .rangeRound([margin.left, width - margin.right]) .paddingInner(0.1); const x1 = d3 .scaleBand() .domain(keys) .rangeRound([0, x0.bandwidth()]) .padding(0.05); const y = d3 .scaleLinear() .domain([0, scaleMax]) .nice() .rangeRound([height - margin.bottom, margin.top]); const chart = () => { const svg = d3.select(`#chart-${key}`); svg .append('g') .selectAll('g') .data(data) .join('g') .attr('transform', (d) => `translate(${x0(d[groupKey])},0)`) .selectAll('rect') .data((d) => keys.map((key) => ({ key, value: d[key] }))) .join('rect') .attr('x', (d) => x1(d.key)) .attr('y', (d) => y(d.value)) .attr('width', x1.bandwidth()) .attr('height', (d) => y(0) - y(d.value)) .attr('fill', (d) => color(d.key)); svg.append('g').call(xAxis); svg.append('g').call(yAxis); svg.append('g').call(legend); svg .append('text') .attr('transform', `translate(45,${height - margin.bottom + 35})`) .attr('text-align', 'start') .attr('font-size', '11') .attr('font-weight', 'bold') .text('Renders'); svg .append('text') .attr('transform', `translate(${width - 12},${height - margin.bottom + 35})`) .attr('text-anchor', 'end') .attr('font-size', '11') .attr('font-weight', 'bold') .attr('class', 'total') .text(`${TOTAL_AUTOMATION_TIME_ELAPSED}: ${totalTimeElapsed} ms`); svg .append('text') .attr('transform', `translate(${width - 12},${height - margin.bottom + 55})`) .attr('text-anchor', 'end') .attr('font-size', '11') .attr('font-weight', 'bold') .attr('class', 'interactions') .text(`${NUMBER_OF_INTERACTIONS}: ${interactions.shift()}`); svg .select('.total') .on('mouseover', () => (0, charts_util_1.handleMouseOver)(TOTAL)) .on('mouseout', () => (0, charts_util_1.handleMouseOut)()); svg .select('.interactions') .on('mouseover', () => (0, charts_util_1.handleMouseOver)(INTERACTIONS)) .on('mouseout', () => (0, charts_util_1.handleMouseOut)()); svg .selectAll('rect') .on('mouseover', (e) => (0, charts_util_1.handleMouseOver)(RECT, e)) .on('mouseout', (e) => (0, charts_util_1.handleMouseOut)(e)); return svg.node(); }; chart(); } (0, charts_util_1.initCarouselState)(carouselIds); (0, charts_util_1.addExportListener)(); if (bodyWidth) document.body.style.width = `${bodyWidth}px`; })(); </script></body></html>