lydell
September 15, 2024, 12:00am
1
Three years ago, I made a post about minifying Elm code:
Summary
The Elm Guide has a page on minification , but it’s not the end of the story.
The UglifyJS command in the Elm Guide can be tweaked to produce slightly smaller code a tiny bit faster.
But you can get even smaller by first running just parts of UglifyJS and then esbuild – and much faster!
Gzip is really unpredictable. Slightly increasing the minified size can sometimes decrease the gzipped size.
If you’re OK with ~2% more of the original JS, you can run only esbuild in less than a second …
Now I’m back on that topic again! This time, I created a tool for benchmarking different minifiers against each other, on a compiled Elm JS file of choice:
A tool to help you evaluate what the best minifier for your Elm program is.
The winner?
If you use SWC with the settings from the Elm Guide , you seem to get about the smallest size in very little time. It’s not the fastest, but still not even close to a second even on a large Elm app (depending on the computer of course).
import * as swc from "@swc/core";
const pureFuncs = [ "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9"];
async function minify(code) {
return (
await swc.minify(code, {
compress: {
pure_funcs: pureFuncs,
pure_getters: true,
unsafe_comps: true,
unsafe: true,
},
mangle: {
reserved: pureFuncs,
},
})
).code;
}
Example
❯ node bench.js example-azimutt.js
┌─────────┬───────────────────────┬───────────┬─────────────┬───────┬───────────────┬─────────┬───────────────┬─────────┬───────────────────────────────────────────────────────┐
│ (index) │ name │ version │ time │ x │ size │ % │ brotli ⬇ │ % │ installation size and dependencies │
├─────────┼───────────────────────┼───────────┼─────────────┼───────┼───────────────┼─────────┼───────────────┼─────────┼───────────────────────────────────────────────────────┤
│ 0 │ '(none)' │ '' │ '' │ '' │ ' 1.35 MiB' │ '' │ ' 167 KiB' │ '' │ '' │
│ 1 │ '@swc/core_elm-guide' │ '1.7.26' │ ' 196 ms' │ 'x6' │ ' 411 KiB' │ '-70 %' │ '🏆 101 KiB' │ '-40 %' │ 'https://packagephobia.com/result?p=@swc/core' │
│ 2 │ 'uglify-js_elm-guide' │ '3.19.3' │ ' 2.43 s' │ 'x78' │ '🏆 402 KiB' │ '-71 %' │ ' 102 KiB' │ '-39 %' │ 'https://packagephobia.com/result?p=uglify-js' │
│ 3 │ '@swc/core' │ '1.7.26' │ ' 198 ms' │ 'x6' │ ' 410 KiB' │ '-70 %' │ ' 102 KiB' │ '-39 %' │ 'https://packagephobia.com/result?p=@swc/core' │
│ 4 │ 'terser_elm-guide' │ '5.32.0' │ ' 1.64 s' │ 'x53' │ ' 416 KiB' │ '-70 %' │ ' 103 KiB' │ '-39 %' │ 'https://packagephobia.com/result?p=terser' │
│ 5 │ 'uglify-js' │ '3.19.3' │ ' 2.29 s' │ 'x74' │ ' 405 KiB' │ '-71 %' │ ' 103 KiB' │ '-38 %' │ 'https://packagephobia.com/result?p=uglify-js' │
│ 6 │ 'uglify-js+esbuild' │ '' │ ' 1.75 s' │ 'x56' │ ' 403 KiB' │ '-71 %' │ ' 104 KiB' │ '-38 %' │ '' │
│ 7 │ 'terser' │ '5.32.0' │ ' 1.34 s' │ 'x43' │ ' 419 KiB' │ '-70 %' │ ' 104 KiB' │ '-38 %' │ 'https://packagephobia.com/result?p=terser' │
│ 8 │ 'esbuild_tweaked' │ '0.23.1' │ ' 61 ms' │ 'x2' │ ' 423 KiB' │ '-69 %' │ ' 107 KiB' │ '-36 %' │ 'https://packagephobia.com/result?p=esbuild' │
│ 9 │ 'esbuild' │ '0.23.1' │ ' 56 ms' │ 'x2' │ ' 430 KiB' │ '-69 %' │ ' 110 KiB' │ '-34 %' │ 'https://packagephobia.com/result?p=esbuild' │
│ 10 │ 'bun' │ '1.1.27' │ '🏆 31 ms' │ 'x1' │ ' 433 KiB' │ '-69 %' │ ' 110 KiB' │ '-34 %' │ 'https://packagephobia.com/result?p=bun' │
│ 11 │ '@tdewolff/minify' │ '2.20.37' │ ' 48 ms' │ 'x2' │ ' 434 KiB' │ '-69 %' │ ' 114 KiB' │ '-32 %' │ 'https://packagephobia.com/result?p=@tdewolff/minify' │
└─────────┴───────────────────────┴───────────┴─────────────┴───────┴───────────────┴─────────┴───────────────┴─────────┴───────────────────────────────────────────────────────┘
24 Likes
dirkbj
September 19, 2024, 1:26pm
2
Very helpful. Thanks for this.
1 Like
Got slightly different results (first place remain unchanged):
wc -l app.js
27868 app.js
┌─────────┬───────────────────────┬───────────┬─────────────┬────────┬───────────────┬─────────┬───────────────┬─────────┬───────────────────────────────────────────────────────┐
│ (index) │ name │ version │ time │ x │ size │ % │ brotli ⬇ │ % │ installation size and dependencies │
├─────────┼───────────────────────┼───────────┼─────────────┼────────┼───────────────┼─────────┼───────────────┼─────────┼───────────────────────────────────────────────────────┤
│ 0 │ '(none)' │ '' │ '' │ '' │ ' 789 KiB' │ '' │ ' 77.6 KiB' │ '' │ '' │
│ 1 │ '@swc/core_elm-guide' │ '1.7.26' │ ' 169 ms' │ 'x5' │ ' 210 KiB' │ '-73 %' │ '🏆 43.1 KiB' │ '-44 %' │ 'https://packagephobia.com/result?p=@swc/core' │
│ 2 │ 'uglify-js_elm-guide' │ '3.19.3' │ ' 2.76 s' │ 'x89' │ '🏆 208 KiB' │ '-74 %' │ ' 43.6 KiB' │ '-44 %' │ 'https://packagephobia.com/result?p=uglify-js' │
│ 3 │ 'terser_elm-guide' │ '5.32.0' │ ' 1.84 s' │ 'x59' │ ' 211 KiB' │ '-73 %' │ ' 43.7 KiB' │ '-44 %' │ 'https://packagephobia.com/result?p=terser' │
│ 4 │ '@swc/core' │ '1.7.26' │ ' 164 ms' │ 'x5' │ ' 213 KiB' │ '-73 %' │ ' 43.9 KiB' │ '-43 %' │ 'https://packagephobia.com/result?p=@swc/core' │
│ 5 │ 'uglify-js+esbuild' │ '' │ ' 2.27 s' │ 'x73' │ ' 213 KiB' │ '-73 %' │ ' 44.3 KiB' │ '-43 %' │ '' │
│ 6 │ 'uglify-js' │ '3.19.3' │ ' 3.09 s' │ 'x100' │ ' 215 KiB' │ '-73 %' │ ' 45.0 KiB' │ '-42 %' │ 'https://packagephobia.com/result?p=uglify-js' │
│ 7 │ 'terser' │ '5.32.0' │ ' 1.44 s' │ 'x46' │ ' 218 KiB' │ '-72 %' │ ' 45.1 KiB' │ '-42 %' │ 'https://packagephobia.com/result?p=terser' │
│ 8 │ 'esbuild_tweaked' │ '0.23.1' │ ' 62 ms' │ 'x2' │ ' 227 KiB' │ '-71 %' │ ' 46.6 KiB' │ '-40 %' │ 'https://packagephobia.com/result?p=esbuild' │
│ 9 │ 'esbuild' │ '0.23.1' │ ' 59 ms' │ 'x2' │ ' 232 KiB' │ '-71 %' │ ' 47.8 KiB' │ '-38 %' │ 'https://packagephobia.com/result?p=esbuild' │
│ 10 │ 'bun' │ '1.1.27' │ ' 49 ms' │ 'x2' │ ' 234 KiB' │ '-70 %' │ ' 48.3 KiB' │ '-38 %' │ 'https://packagephobia.com/result?p=bun' │
│ 11 │ '@tdewolff/minify' │ '2.20.37' │ '🏆 31 ms' │ 'x1' │ ' 231 KiB' │ '-71 %' │ ' 50.3 KiB' │ '-35 %' │ 'https://packagephobia.com/result?p=@tdewolff/minify' │
└─────────┴───────────────────────┴───────────┴─────────────┴────────┴───────────────┴─────────┴───────────────┴─────────┴───────────────────────────────────────────────────────┘
lydell
September 26, 2024, 4:16pm
4
Yeah, different apps have different characteristics, which highlight each minifier’s strengths and weaknesses in different ways.
For people who want to be absolutely certain what minifier is best for them, I recommend running the tool on their apps, and maybe even tweak the minifier configs.
1 Like
For reference, here are my results (for my own app and for the azimutt example):
~/Code/elm-minification-benchmarks λ node bench.js elm.js
┌─────────┬───────────────────────┬───────────┬─────────────┬───────┬───────────────┬─────────┬───────────────┬─────────┬───────────────────────────────────────────────────────┐
│ (index) │ name │ version │ time │ x │ size │ % │ brotli ⬇ │ % │ installation size and dependencies │
├─────────┼───────────────────────┼───────────┼─────────────┼───────┼───────────────┼─────────┼───────────────┼─────────┼───────────────────────────────────────────────────────┤
│ 0 │ '(none)' │ '' │ '' │ '' │ ' 3.60 MiB' │ '' │ ' 271 KiB' │ '' │ '' │
│ 1 │ '@swc/core_elm-guide' │ '1.7.26' │ ' 876 ms' │ 'x8' │ ' 757 KiB' │ '-79 %' │ '🏆 144 KiB' │ '-47 %' │ 'https://packagephobia.com/result?p=@swc/core' │
│ 2 │ '@swc/core' │ '1.7.26' │ ' 847 ms' │ 'x8' │ ' 752 KiB' │ '-80 %' │ ' 145 KiB' │ '-47 %' │ 'https://packagephobia.com/result?p=@swc/core' │
│ 3 │ 'uglify-js_elm-guide' │ '3.19.3' │ ' 9.62 s' │ 'x89' │ '🏆 742 KiB' │ '-80 %' │ ' 146 KiB' │ '-46 %' │ 'https://packagephobia.com/result?p=uglify-js' │
│ 4 │ 'terser_elm-guide' │ '5.32.0' │ ' 6.45 s' │ 'x60' │ ' 763 KiB' │ '-79 %' │ ' 148 KiB' │ '-46 %' │ 'https://packagephobia.com/result?p=terser' │
│ 5 │ 'uglify-js' │ '3.19.3' │ ' 9.02 s' │ 'x84' │ ' 749 KiB' │ '-80 %' │ ' 148 KiB' │ '-45 %' │ 'https://packagephobia.com/result?p=uglify-js' │
│ 6 │ 'terser' │ '5.32.0' │ ' 5.03 s' │ 'x47' │ ' 763 KiB' │ '-79 %' │ ' 149 KiB' │ '-45 %' │ 'https://packagephobia.com/result?p=terser' │
│ 7 │ 'uglify-js+esbuild' │ '' │ ' 6.43 s' │ 'x60' │ ' 747 KiB' │ '-80 %' │ ' 150 KiB' │ '-45 %' │ '' │
│ 8 │ 'esbuild_tweaked' │ '0.23.1' │ ' 162 ms' │ 'x2' │ ' 786 KiB' │ '-79 %' │ ' 157 KiB' │ '-42 %' │ 'https://packagephobia.com/result?p=esbuild' │
│ 9 │ 'esbuild' │ '0.23.1' │ ' 156 ms' │ 'x1' │ ' 795 KiB' │ '-78 %' │ ' 160 KiB' │ '-41 %' │ 'https://packagephobia.com/result?p=esbuild' │
│ 10 │ 'bun' │ '1.1.27' │ '🏆 108 ms' │ 'x1' │ ' 802 KiB' │ '-78 %' │ ' 161 KiB' │ '-41 %' │ 'https://packagephobia.com/result?p=bun' │
│ 11 │ '@tdewolff/minify' │ '2.20.37' │ ' 207 ms' │ 'x2' │ ' 787 KiB' │ '-79 %' │ ' 168 KiB' │ '-38 %' │ 'https://packagephobia.com/result?p=@tdewolff/minify' │
└─────────┴───────────────────────┴───────────┴─────────────┴───────┴───────────────┴─────────┴───────────────┴─────────┴───────────────────────────────────────────────────────┘
~/Code/elm-minification-benchmarks λ node bench.js example-azimutt.js
┌─────────┬───────────────────────┬───────────┬─────────────┬───────┬───────────────┬─────────┬───────────────┬─────────┬───────────────────────────────────────────────────────┐
│ (index) │ name │ version │ time │ x │ size │ % │ brotli ⬇ │ % │ installation size and dependencies │
├─────────┼───────────────────────┼───────────┼─────────────┼───────┼───────────────┼─────────┼───────────────┼─────────┼───────────────────────────────────────────────────────┤
│ 0 │ '(none)' │ '' │ '' │ '' │ ' 1.35 MiB' │ '' │ ' 167 KiB' │ '' │ '' │
│ 1 │ '@swc/core_elm-guide' │ '1.7.26' │ ' 313 ms' │ 'x5' │ ' 411 KiB' │ '-70 %' │ '🏆 101 KiB' │ '-40 %' │ 'https://packagephobia.com/result?p=@swc/core' │
│ 2 │ 'uglify-js_elm-guide' │ '3.19.3' │ ' 4.06 s' │ 'x65' │ '🏆 402 KiB' │ '-71 %' │ ' 102 KiB' │ '-39 %' │ 'https://packagephobia.com/result?p=uglify-js' │
│ 3 │ '@swc/core' │ '1.7.26' │ ' 308 ms' │ 'x5' │ ' 410 KiB' │ '-70 %' │ ' 102 KiB' │ '-39 %' │ 'https://packagephobia.com/result?p=@swc/core' │
│ 4 │ 'terser_elm-guide' │ '5.32.0' │ ' 2.84 s' │ 'x46' │ ' 416 KiB' │ '-70 %' │ ' 103 KiB' │ '-39 %' │ 'https://packagephobia.com/result?p=terser' │
│ 5 │ 'uglify-js' │ '3.19.3' │ ' 4.02 s' │ 'x65' │ ' 405 KiB' │ '-71 %' │ ' 103 KiB' │ '-38 %' │ 'https://packagephobia.com/result?p=uglify-js' │
│ 6 │ 'uglify-js+esbuild' │ '' │ ' 3.02 s' │ 'x49' │ ' 403 KiB' │ '-71 %' │ ' 104 KiB' │ '-38 %' │ '' │
│ 7 │ 'terser' │ '5.32.0' │ ' 2.16 s' │ 'x35' │ ' 419 KiB' │ '-70 %' │ ' 104 KiB' │ '-38 %' │ 'https://packagephobia.com/result?p=terser' │
│ 8 │ 'esbuild_tweaked' │ '0.23.1' │ ' 77 ms' │ 'x1' │ ' 423 KiB' │ '-69 %' │ ' 107 KiB' │ '-36 %' │ 'https://packagephobia.com/result?p=esbuild' │
│ 9 │ 'esbuild' │ '0.23.1' │ ' 68 ms' │ 'x1' │ ' 430 KiB' │ '-69 %' │ ' 110 KiB' │ '-34 %' │ 'https://packagephobia.com/result?p=esbuild' │
│ 10 │ 'bun' │ '1.1.27' │ '🏆 62 ms' │ 'x1' │ ' 433 KiB' │ '-69 %' │ ' 110 KiB' │ '-34 %' │ 'https://packagephobia.com/result?p=bun' │
│ 11 │ '@tdewolff/minify' │ '2.20.37' │ ' 64 ms' │ 'x1' │ ' 434 KiB' │ '-69 %' │ ' 114 KiB' │ '-32 %' │ 'https://packagephobia.com/result?p=@tdewolff/minify' │
└─────────┴───────────────────────┴───────────┴─────────────┴───────┴───────────────┴─────────┴───────────────┴─────────┴───────────────────────────────────────────────────────┘
So, now: anyone got a simple guide on how to use swc instead of esbuild/uglify when using vite? Asking for a friend
1 Like