-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bad362d
commit ccdc0a2
Showing
26 changed files
with
1,789 additions
and
4,810 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1 @@ | ||
node_modules/ | ||
test.* | ||
|
||
packages/**/*-dist | ||
.parcel-cache | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { Entry } from "./types"; | ||
type DataFetcher = () => Promise<string>; | ||
interface DataSource { | ||
fetchData(): Promise<Entry[]>; | ||
} | ||
|
||
class NetWorkDataSource implements DataSource { | ||
constructor(private url: string) {} | ||
|
||
async fetchData(): Promise<Entry[]> { | ||
const res = await fetch(this.url); | ||
const data = await res.text(); | ||
const entries: Entry[] = data | ||
.split("\n") | ||
.filter(Boolean) | ||
.map((it) => JSON.parse(it)); | ||
return entries; | ||
} | ||
} | ||
|
||
class BasicDataSource implements DataSource { | ||
constructor( | ||
private fetch: DataFetcher, | ||
private normalized: (source: string) => Entry[], | ||
) {} | ||
|
||
async fetchData(): Promise<Entry[]> { | ||
const source = await this.fetch(); | ||
return this.normalized(source); | ||
} | ||
} | ||
|
||
export async function initDataSource(): Promise<Entry[]> { | ||
const res = await Promise.all([ | ||
new NetWorkDataSource( | ||
"https://raw.githubusercontent.com/rolldown/metric/main/metric.json", | ||
).fetchData(), | ||
new BasicDataSource( | ||
async () => { | ||
const res = await fetch("https://raw.githubusercontent.com/rolldown/benchmark-results-storage/main/benchmark-node-output.json"); | ||
const data = await res.text(); | ||
return data; | ||
}, | ||
(source) => { | ||
const json = JSON.parse(source); | ||
let entries: Entry[] = []; | ||
json["entries"]?.["Node Benchmark"].forEach((item) => { | ||
let { commit, date, benches } = item; | ||
for (let i = 0, len = benches.length; i < len; i++) { | ||
entries.push({ | ||
case: benches[i].name, | ||
metric: "production build time", | ||
unit: "ms", | ||
commit: commit.id, | ||
records: { | ||
rolldown: +benches[i].value, | ||
}, | ||
timestamp: date, | ||
}); | ||
} | ||
}); | ||
return entries; | ||
}, | ||
).fetchData(), | ||
]); | ||
return res.flat(); | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>rolldown Dashboard</title> | ||
<script src="https://cdn.plot.ly/plotly-1.58.4.min.js"></script> | ||
<script type="module" crossorigin src="/metric/assets/index-sOms_8P3.js"></script> | ||
<link rel="stylesheet" crossorigin href="/metric/assets/index-BOdhcf7Z.css"> | ||
</head> | ||
|
||
<body> | ||
<div id="inner"> | ||
<h1>rolldown dashboard</h1> | ||
|
||
<p> | ||
<a href="..">Home</a> | ||
</p> | ||
|
||
<details> | ||
<summary>Change time frame</summary> | ||
|
||
<form method="get" action="."> | ||
Start: <input name="start" type="date"><br> | ||
End: <input name="end" type="date"><br> | ||
<button>Submit</button><br> | ||
<button onclick="setDays(1)">Last day</button> | ||
<button onclick="setDays(7)">Last 7 days</button> | ||
<button onclick="setDays(30)">Last 30 days</button> | ||
</form> | ||
</details> | ||
|
||
<div id="notification" class="notification hidden"></div> | ||
</div> | ||
|
||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>rolldown Dashboard</title> | ||
<script src="https://cdn.plot.ly/plotly-1.58.4.min.js"></script> | ||
<link rel="stylesheet" href="./style.css"> | ||
</head> | ||
|
||
<body> | ||
<div id="inner"> | ||
<h1>rolldown dashboard</h1> | ||
|
||
<p> | ||
<a href="..">Home</a> | ||
</p> | ||
|
||
<details> | ||
<summary>Change time frame</summary> | ||
|
||
<form method="get" action="."> | ||
Start: <input name="start" type="date"><br> | ||
End: <input name="end" type="date"><br> | ||
<button>Submit</button><br> | ||
<button onclick="setDays(1)">Last day</button> | ||
<button onclick="setDays(7)">Last 7 days</button> | ||
<button onclick="setDays(30)">Last 30 days</button> | ||
</form> | ||
</details> | ||
|
||
<div id="notification" class="notification hidden"></div> | ||
</div> | ||
|
||
<script src="./index.ts" type="module"></script> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
import { groupBy } from "lodash-es"; | ||
import { Entry, Metric, Plots } from "./types"; | ||
import { initDataSource } from "./data-source"; | ||
import "./style.css"; | ||
|
||
function parseQueryString(): [Date?, Date?] { | ||
let start: Date | undefined = undefined; | ||
let end: Date | undefined = undefined; | ||
|
||
let query = new URLSearchParams(location.search); | ||
let startQuery = query.get("start"); | ||
let endQuery = query.get("end"); | ||
if (startQuery && endQuery) { | ||
start = new Date(startQuery); | ||
end = new Date(endQuery); | ||
} | ||
return [start, end]; | ||
} | ||
|
||
function show_notification(html_text: string) { | ||
var notificationElem = document.getElementById("notification")!; | ||
notificationElem.innerHTML = html_text; | ||
notificationElem.classList.remove("hidden"); | ||
setTimeout(() => { | ||
notificationElem.classList.add("hidden"); | ||
}, 3000); | ||
} | ||
|
||
async function main() { | ||
let entries = await initDataSource(); | ||
const [start, end] = parseQueryString(); | ||
setTimeFrameInputs(start, end); | ||
const metrics = groupBy(entries, (item: Entry) => { | ||
return `${item.case}/${item.metric}`; | ||
}); | ||
|
||
let [normalizedEntryDict, metricSet] = normalizeEntryDict(metrics); | ||
let metricContainerMap = initMetricContainer(metricSet); | ||
const plots = new Map<string, Plots>(); | ||
|
||
for (let [ | ||
series, | ||
{ unit, data, commit, timestamp, metric }, | ||
] of Object.entries(normalizedEntryDict)) { | ||
let plotName = series; | ||
let seriesName: string; | ||
seriesName = series; | ||
let plot = plots.get(plotName); | ||
if (!plot) { | ||
plot = { | ||
data: [], | ||
layout: { | ||
title: plotName, | ||
xaxis: { | ||
type: "date", | ||
tickformat: "%Y-%m-%d", | ||
}, | ||
yaxis: { | ||
title: unit, | ||
rangemode: "tozero", | ||
}, | ||
width: Math.min(1200, window.innerWidth - 30), | ||
margin: { | ||
l: 50, | ||
r: 20, | ||
b: 100, | ||
t: 100, | ||
pad: 4, | ||
}, | ||
legend: { | ||
orientation: window.innerWidth < 700 ? "h" : "v", | ||
}, | ||
}, | ||
}; | ||
plots.set(plotName, plot); | ||
} | ||
|
||
Object.entries(data).forEach(([key, value]) => { | ||
console.log(`data: `, value, key); | ||
plot?.data.push({ | ||
name: key, | ||
line: { | ||
// width:1.5, | ||
shape: 'hv' | ||
}, | ||
x: timestamp.map((n) => new Date(n)), | ||
y: value, | ||
hovertext: commit, | ||
hovertemplate: `%{y} ${unit}<br>(%{hovertext})`, | ||
}); | ||
}); | ||
} | ||
const sortedPlots = Array.from(plots.entries()); | ||
sortedPlots.sort(([t], [t2]) => t.localeCompare(t2)); | ||
for (const [key, definition] of sortedPlots) { | ||
let [_, metric] = key.split("/"); | ||
const plotDiv = document.createElement( | ||
"div", | ||
) as any as Plotly.PlotlyHTMLElement; | ||
|
||
definition.data.sort((a, b) => { | ||
if (a.name < b.name) { | ||
return -1; | ||
} else if (a.name > b.name) { | ||
return 1; | ||
} else { | ||
return 0; | ||
} | ||
}); | ||
|
||
// @ts-ignore | ||
Plotly.newPlot(plotDiv, definition.data, definition.layout); | ||
// plotDiv.on("plotly_click", (data) => { | ||
// const commit_hash: string = (data.points[0] as any).hovertext; | ||
// const url = `https://github.com/rolldown-rs/rolldown/commit/${commit_hash}`; | ||
// const notification_text = `Commit <b>${commit_hash}</b> URL copied to clipboard`; | ||
// navigator.clipboard.writeText(url); | ||
// show_notification(notification_text); | ||
// }); | ||
metricContainerMap.get(metric)?.appendChild(plotDiv); | ||
// bodyElement.appendChild(plotDiv); | ||
} | ||
} | ||
|
||
function initMetricContainer(metricSet: Set<string>) { | ||
let metricNames = Array.from(metricSet); | ||
// metric name: name => HTMLElement | ||
let metricContainerMap = new Map<string, HTMLElement>(); | ||
metricNames.sort(); | ||
for (let i = 0; i < metricNames.length; i++) { | ||
let metricName = metricNames[i]; | ||
let metricContainer = document.createElement("div"); | ||
metricContainer.id = `metric-${metricName}`; | ||
|
||
let divider = document.createElement("div"); | ||
divider.classList.add("divider"); | ||
divider.innerText = metricName; | ||
metricContainer.appendChild(divider); | ||
|
||
metricContainerMap.set(metricName, metricContainer); | ||
document.getElementById("inner")!.appendChild(metricContainer); | ||
} | ||
return metricContainerMap; | ||
} | ||
|
||
function normalizeEntryDict( | ||
entries: Record<string, Entry[]>, | ||
): [Record<string, Metric>, Set<string>] { | ||
let map = Object.create(null); | ||
let metricSet = new Set<string>(); | ||
Object.entries(entries).map(([key, value]) => { | ||
value.sort((a, b) => { | ||
return +a.timestamp - +b.timestamp; | ||
}); | ||
let data: Record<string, number[]> = {}; | ||
let commit: string[] = []; | ||
let timestamp: number[] = []; | ||
|
||
for (let i = 0; i < value.length; i++) { | ||
let v = value[i]; | ||
Object.entries(v.records).forEach(([key, value]) => { | ||
if (data[key] === undefined) { | ||
data[key] = []; | ||
} | ||
data[key].push(value); | ||
}); | ||
commit.push(v.commit); | ||
timestamp.push(v.timestamp); | ||
} | ||
metricSet.add(value[0].metric); | ||
map[key] = { | ||
data, | ||
commit, | ||
timestamp, | ||
unit: value[0].unit, | ||
metric: value[0].metric, | ||
}; | ||
}); | ||
return [map, metricSet]; | ||
} | ||
|
||
function setDays(n: number) { | ||
const timestamp = +new Date() - n * 1000 * 60 * 60 * 24; | ||
const date = new Date(timestamp); | ||
setTimeFrameInputs(date, undefined); | ||
} | ||
|
||
function getTimeFrameInputs(): [HTMLInputElement, HTMLInputElement] { | ||
const start = document.getElementsByName("start")[0] as HTMLInputElement; | ||
const end = document.getElementsByName("end")[0] as HTMLInputElement; | ||
return [start, end]; | ||
} | ||
|
||
function setTimeFrameInputs(start?: Date, end?: Date) { | ||
const [startInput, endInput] = getTimeFrameInputs(); | ||
(startInput as any).value = start ? start.toISOString().split("T")[0] : ""; | ||
(endInput as any).value = end ? end.toISOString().split("T")[0] : ""; | ||
} | ||
|
||
main(); |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.