Skip to content

Commit badb2c3

Browse files
authored
refactor: clean up minification benchmark data typing (#150)
1 parent b623dd2 commit badb2c3

File tree

1 file changed

+143
-56
lines changed

1 file changed

+143
-56
lines changed
Lines changed: 143 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,150 @@
11
import minificationData from "../../../data/minification-benchmarks-data.json";
22

3-
export const popularMinifiers = ["terser", "esbuild", "@swc/core", "uglify-js", "oxc-minify"];
3+
const MINIFIERS = [
4+
{ id: "terser", name: "Terser", fill: "#a78bfa" },
5+
{ id: "esbuild", name: "ESBuild", fill: "#10b981" },
6+
{ id: "@swc/core", name: "SWC", fill: "#38bdf8" },
7+
{ id: "uglify-js", name: "UglifyJS", fill: "#f87171" },
8+
{ id: "oxc-minify", name: "OXC", fill: "#fbbf24" },
9+
] as const;
410

5-
export const libraries = Object.entries(minificationData)
6-
.map(([name, data]: [string, any]) => ({ name, size: data.size }))
11+
type MinifierDefinition = (typeof MINIFIERS)[number];
12+
type MinifierId = MinifierDefinition["id"];
13+
type Metric = "time" | "compression";
14+
15+
interface BenchmarkResultData {
16+
time?: number;
17+
minzippedBytes?: number;
18+
}
19+
20+
interface MinifierBenchmark {
21+
result?: {
22+
data?: BenchmarkResultData;
23+
};
24+
}
25+
26+
interface LibraryBenchmark {
27+
size: number;
28+
minified?: Record<string, MinifierBenchmark | undefined>;
29+
}
30+
31+
type MinificationBenchmarkData = Record<string, LibraryBenchmark>;
32+
33+
interface LibraryDataPoint {
34+
name: string;
35+
value: number;
36+
minzippedBytes: number;
37+
fill: string;
38+
}
39+
40+
function isRecord(value: unknown): value is Record<string, unknown> {
41+
return typeof value === "object" && value !== null;
42+
}
43+
44+
function isBenchmarkResultData(value: unknown): value is BenchmarkResultData {
45+
return (
46+
isRecord(value) &&
47+
(value.time === undefined || typeof value.time === "number") &&
48+
(value.minzippedBytes === undefined || typeof value.minzippedBytes === "number")
49+
);
50+
}
51+
52+
function isMinifierBenchmark(value: unknown): value is MinifierBenchmark {
53+
if (!isRecord(value)) {
54+
return false;
55+
}
56+
57+
if (value.result === undefined) {
58+
return true;
59+
}
60+
61+
return (
62+
isRecord(value.result) &&
63+
(value.result.data === undefined || isBenchmarkResultData(value.result.data))
64+
);
65+
}
66+
67+
function isLibraryBenchmark(value: unknown): value is LibraryBenchmark {
68+
if (!isRecord(value) || typeof value.size !== "number") {
69+
return false;
70+
}
71+
72+
if (value.minified === undefined) {
73+
return true;
74+
}
75+
76+
return (
77+
isRecord(value.minified) &&
78+
Object.values(value.minified).every(
79+
(minifierBenchmark) =>
80+
minifierBenchmark === undefined || isMinifierBenchmark(minifierBenchmark),
81+
)
82+
);
83+
}
84+
85+
function getBenchmarkData(data: unknown): MinificationBenchmarkData {
86+
if (!isRecord(data)) {
87+
return {};
88+
}
89+
90+
return Object.entries(data).reduce<MinificationBenchmarkData>((libraries, [name, benchmark]) => {
91+
if (isLibraryBenchmark(benchmark)) {
92+
libraries[name] = benchmark;
93+
}
94+
95+
return libraries;
96+
}, {});
97+
}
98+
99+
const benchmarkData = getBenchmarkData(minificationData);
100+
101+
export const popularMinifiers: MinifierId[] = MINIFIERS.map(({ id }) => id);
102+
103+
export const libraries = Object.entries(benchmarkData)
104+
.map(([name, { size }]) => ({ name, size }))
7105
.toSorted((a, b) => b.size - a.size)
8106
.map((item) => item.name);
9107

10-
export const getLibraryData = (library: string, metric: "time" | "compression") => {
11-
const libraryData = (minificationData as Record<string, any>)[library];
12-
const data: any[] = [];
13-
14-
popularMinifiers.forEach((minifier) => {
15-
const minifierData = libraryData.minified?.[minifier];
16-
if (minifierData?.result?.data) {
17-
let value: number;
18-
let minzippedBytes = 0;
19-
if (metric === "time") {
20-
value = Math.round(minifierData.result.data.time || 0);
21-
} else {
22-
// compression ratio
23-
const originalSize = libraryData.size;
24-
minzippedBytes = minifierData.result.data.minzippedBytes || 0;
25-
value = Math.round(((originalSize - minzippedBytes) / originalSize) * 100 * 10) / 10;
26-
}
27-
28-
data.push({
29-
name:
30-
minifier === "@swc/core"
31-
? "SWC"
32-
: minifier === "uglify-js"
33-
? "UglifyJS"
34-
: minifier === "oxc-minify"
35-
? "OXC"
36-
: minifier === "esbuild"
37-
? "ESBuild"
38-
: minifier === "terser"
39-
? "Terser"
40-
: minifier,
41-
value,
42-
minzippedBytes: minzippedBytes || 0,
43-
fill:
44-
minifier === "terser"
45-
? "#a78bfa"
46-
: minifier === "esbuild"
47-
? "#10b981"
48-
: minifier === "@swc/core"
49-
? "#38bdf8"
50-
: minifier === "uglify-js"
51-
? "#f87171"
52-
: minifier === "oxc-minify"
53-
? "#fbbf24"
54-
: "#9ca3af",
55-
});
56-
}
57-
});
108+
function getMetricValue(
109+
metric: Metric,
110+
librarySize: number,
111+
resultData: BenchmarkResultData,
112+
): number {
113+
if (metric === "time") {
114+
return Math.round(resultData.time || 0);
115+
}
58116

59-
// Sort data: time from smallest to largest (fastest to slowest), compression from largest to smallest (best to worst)
60-
return data.toSorted((a, b) =>
61-
metric === "time" ? a.value - b.value : a.minzippedBytes - b.minzippedBytes,
62-
);
63-
};
117+
const minzippedBytes = resultData.minzippedBytes || 0;
118+
return Math.round(((librarySize - minzippedBytes) / librarySize) * 100 * 10) / 10;
119+
}
120+
121+
function toLibraryDataPoint(
122+
minifier: MinifierDefinition,
123+
libraryData: LibraryBenchmark,
124+
metric: Metric,
125+
): LibraryDataPoint | null {
126+
const resultData = libraryData.minified?.[minifier.id]?.result?.data;
127+
if (!resultData) {
128+
return null;
129+
}
130+
131+
return {
132+
name: minifier.name,
133+
value: getMetricValue(metric, libraryData.size, resultData),
134+
minzippedBytes: metric === "compression" ? resultData.minzippedBytes || 0 : 0,
135+
fill: minifier.fill,
136+
};
137+
}
138+
139+
export function getLibraryData(library: string, metric: Metric): LibraryDataPoint[] {
140+
const libraryData = benchmarkData[library];
141+
if (!libraryData) {
142+
return [];
143+
}
144+
145+
return MINIFIERS.map((minifier) => toLibraryDataPoint(minifier, libraryData, metric))
146+
.filter((item): item is LibraryDataPoint => item !== null)
147+
.toSorted((a, b) =>
148+
metric === "time" ? a.value - b.value : a.minzippedBytes - b.minzippedBytes,
149+
);
150+
}

0 commit comments

Comments
 (0)