-
Notifications
You must be signed in to change notification settings - Fork 12
refactor: clean up minification benchmark data typing #150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,63 +1,150 @@ | ||
| import minificationData from "../../../data/minification-benchmarks-data.json"; | ||
|
|
||
| export const popularMinifiers = ["terser", "esbuild", "@swc/core", "uglify-js", "oxc-minify"]; | ||
| const MINIFIERS = [ | ||
| { id: "terser", name: "Terser", fill: "#a78bfa" }, | ||
| { id: "esbuild", name: "ESBuild", fill: "#10b981" }, | ||
| { id: "@swc/core", name: "SWC", fill: "#38bdf8" }, | ||
| { id: "uglify-js", name: "UglifyJS", fill: "#f87171" }, | ||
| { id: "oxc-minify", name: "OXC", fill: "#fbbf24" }, | ||
| ] as const; | ||
|
|
||
| export const libraries = Object.entries(minificationData) | ||
| .map(([name, data]: [string, any]) => ({ name, size: data.size })) | ||
| type MinifierDefinition = (typeof MINIFIERS)[number]; | ||
| type MinifierId = MinifierDefinition["id"]; | ||
| type Metric = "time" | "compression"; | ||
|
|
||
| interface BenchmarkResultData { | ||
| time?: number; | ||
| minzippedBytes?: number; | ||
| } | ||
|
|
||
| interface MinifierBenchmark { | ||
| result?: { | ||
| data?: BenchmarkResultData; | ||
| }; | ||
| } | ||
|
|
||
| interface LibraryBenchmark { | ||
| size: number; | ||
| minified?: Record<string, MinifierBenchmark | undefined>; | ||
| } | ||
|
|
||
| type MinificationBenchmarkData = Record<string, LibraryBenchmark>; | ||
|
|
||
| interface LibraryDataPoint { | ||
| name: string; | ||
| value: number; | ||
| minzippedBytes: number; | ||
| fill: string; | ||
| } | ||
|
|
||
| function isRecord(value: unknown): value is Record<string, unknown> { | ||
| return typeof value === "object" && value !== null; | ||
| } | ||
|
|
||
| function isBenchmarkResultData(value: unknown): value is BenchmarkResultData { | ||
| return ( | ||
| isRecord(value) && | ||
| (value.time === undefined || typeof value.time === "number") && | ||
| (value.minzippedBytes === undefined || typeof value.minzippedBytes === "number") | ||
| ); | ||
| } | ||
|
|
||
| function isMinifierBenchmark(value: unknown): value is MinifierBenchmark { | ||
| if (!isRecord(value)) { | ||
| return false; | ||
| } | ||
|
|
||
| if (value.result === undefined) { | ||
| return true; | ||
| } | ||
|
|
||
| return ( | ||
| isRecord(value.result) && | ||
| (value.result.data === undefined || isBenchmarkResultData(value.result.data)) | ||
| ); | ||
| } | ||
|
|
||
| function isLibraryBenchmark(value: unknown): value is LibraryBenchmark { | ||
| if (!isRecord(value) || typeof value.size !== "number") { | ||
| return false; | ||
| } | ||
|
|
||
| if (value.minified === undefined) { | ||
| return true; | ||
| } | ||
|
|
||
| return ( | ||
| isRecord(value.minified) && | ||
| Object.values(value.minified).every( | ||
| (minifierBenchmark) => | ||
| minifierBenchmark === undefined || isMinifierBenchmark(minifierBenchmark), | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| function getBenchmarkData(data: unknown): MinificationBenchmarkData { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The previous version used Unfortunately it takes 60 more lines of code to make TypeScript happy. Happy to revert back to |
||
| if (!isRecord(data)) { | ||
| return {}; | ||
| } | ||
|
|
||
| return Object.entries(data).reduce<MinificationBenchmarkData>((libraries, [name, benchmark]) => { | ||
| if (isLibraryBenchmark(benchmark)) { | ||
| libraries[name] = benchmark; | ||
| } | ||
|
|
||
| return libraries; | ||
| }, {}); | ||
| } | ||
|
|
||
| const benchmarkData = getBenchmarkData(minificationData); | ||
|
|
||
| export const popularMinifiers: MinifierId[] = MINIFIERS.map(({ id }) => id); | ||
|
|
||
| export const libraries = Object.entries(benchmarkData) | ||
| .map(([name, { size }]) => ({ name, size })) | ||
| .toSorted((a, b) => b.size - a.size) | ||
| .map((item) => item.name); | ||
|
|
||
| export const getLibraryData = (library: string, metric: "time" | "compression") => { | ||
| const libraryData = (minificationData as Record<string, any>)[library]; | ||
| const data: any[] = []; | ||
|
|
||
| popularMinifiers.forEach((minifier) => { | ||
| const minifierData = libraryData.minified?.[minifier]; | ||
| if (minifierData?.result?.data) { | ||
| let value: number; | ||
| let minzippedBytes = 0; | ||
| if (metric === "time") { | ||
| value = Math.round(minifierData.result.data.time || 0); | ||
| } else { | ||
| // compression ratio | ||
| const originalSize = libraryData.size; | ||
| minzippedBytes = minifierData.result.data.minzippedBytes || 0; | ||
| value = Math.round(((originalSize - minzippedBytes) / originalSize) * 100 * 10) / 10; | ||
| } | ||
|
|
||
| data.push({ | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The main aim was to remove this. |
||
| name: | ||
| minifier === "@swc/core" | ||
| ? "SWC" | ||
| : minifier === "uglify-js" | ||
| ? "UglifyJS" | ||
| : minifier === "oxc-minify" | ||
| ? "OXC" | ||
| : minifier === "esbuild" | ||
| ? "ESBuild" | ||
| : minifier === "terser" | ||
| ? "Terser" | ||
| : minifier, | ||
| value, | ||
| minzippedBytes: minzippedBytes || 0, | ||
| fill: | ||
| minifier === "terser" | ||
| ? "#a78bfa" | ||
| : minifier === "esbuild" | ||
| ? "#10b981" | ||
| : minifier === "@swc/core" | ||
| ? "#38bdf8" | ||
| : minifier === "uglify-js" | ||
| ? "#f87171" | ||
| : minifier === "oxc-minify" | ||
| ? "#fbbf24" | ||
| : "#9ca3af", | ||
| }); | ||
| } | ||
| }); | ||
| function getMetricValue( | ||
| metric: Metric, | ||
| librarySize: number, | ||
| resultData: BenchmarkResultData, | ||
| ): number { | ||
| if (metric === "time") { | ||
| return Math.round(resultData.time || 0); | ||
| } | ||
|
|
||
| // Sort data: time from smallest to largest (fastest to slowest), compression from largest to smallest (best to worst) | ||
| return data.toSorted((a, b) => | ||
| metric === "time" ? a.value - b.value : a.minzippedBytes - b.minzippedBytes, | ||
| ); | ||
| }; | ||
| const minzippedBytes = resultData.minzippedBytes || 0; | ||
| return Math.round(((librarySize - minzippedBytes) / librarySize) * 100 * 10) / 10; | ||
| } | ||
|
|
||
| function toLibraryDataPoint( | ||
| minifier: MinifierDefinition, | ||
| libraryData: LibraryBenchmark, | ||
| metric: Metric, | ||
| ): LibraryDataPoint | null { | ||
| const resultData = libraryData.minified?.[minifier.id]?.result?.data; | ||
| if (!resultData) { | ||
| return null; | ||
| } | ||
|
|
||
| return { | ||
| name: minifier.name, | ||
| value: getMetricValue(metric, libraryData.size, resultData), | ||
| minzippedBytes: metric === "compression" ? resultData.minzippedBytes || 0 : 0, | ||
| fill: minifier.fill, | ||
| }; | ||
| } | ||
|
|
||
| export function getLibraryData(library: string, metric: Metric): LibraryDataPoint[] { | ||
| const libraryData = benchmarkData[library]; | ||
| if (!libraryData) { | ||
| return []; | ||
| } | ||
|
|
||
| return MINIFIERS.map((minifier) => toLibraryDataPoint(minifier, libraryData, metric)) | ||
| .filter((item): item is LibraryDataPoint => item !== null) | ||
| .toSorted((a, b) => | ||
| metric === "time" ? a.value - b.value : a.minzippedBytes - b.minzippedBytes, | ||
| ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removes the need for long if/else chain in the original function.
Adding new minifiers will now be easier, just add it as a new item in the array.