@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
<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>