Elm Minification Benchmarks

Three years ago, I made a post about minifying Elm code:

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:

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

Very helpful. Thanks for this. :slight_smile:

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' │
└─────────┴───────────────────────┴───────────┴─────────────┴────────┴───────────────┴─────────┴───────────────┴─────────┴───────────────────────────────────────────────────────┘

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 :sweat_smile:

1 Like