Optimize elm compilation with webpack

I am using Elm with webpack, and trying to reduce the compiled file size using the steps described here https://elm-lang.org/0.19.0/optimize.

The issue is that I cannot seem to get webpack to treat the elm code any differently than the rest of the js I have. Would anyone mind sharing their config if they managed to achieve something similar? Particularly if they have a codebase with a mix of elm and non elm code to be uglified.

I found the following issue https://github.com/elm-community/elm-webpack-loader/issues/140 but didn’t get anywhere after trying that config and tweaking it

2 Likes

Have you seen Simon’s elm-webpack-starter?

1 Like

Perfect timing, as this is also the topic in which I am interested today.

In the elm-webpack-starter, the rule for handling the Elm code in production mode seems to be:

test: /\.elm$/,
exclude: [/elm-stuff/, /node_modules/],
use: [
    { loader: "elm-webpack-loader" }
]

Does this ‘loader’ also carry out the uglify/minify step? I suspect not, does that need to be added somewhere else within the webpack config?

In another ‘webpack.config.js’ that I have, which is very similar to the starter, I also have these 2 plugins in the production section:

  new UglifyJsPlugin(),
  new CompressionPlugin(),

I haven’t even tried this yet, I copied my build from somewhere else to get me started, but that seems likely to be what is needed?

2 Likes

No, elm-webpack-loader does not uglify code by default (https://github.com/elm-community/elm-webpack-loader/blob/master/index.js#L15). By default webpack will uglify your code, but it will not apply the flags which are optimal for Elm.

You need to use https://github.com/webpack-contrib/uglifyjs-webpack-plugin (at least for webpack 4) with custom config. The problem for me is I am not sure what that config should be

1 Like

This guide:

https://guide.elm-lang.org/optimization/asset_size.html

Says to use:

elm make src/Main.elm --optimize --output=elm.js
uglifyjs elm.js --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' | uglifyjs --mangle --output=elm.min.js

But that is 2 passes of uglify. So yeah, how to set this up right with webpack?

There was some discussion about this on the PR to upgrade elm-webpack-loader to 0.19. Here you go: https://github.com/elm-community/elm-webpack-loader/pull/142#issuecomment-416584398

1 Like

I guess give this a go:

new UglifyJsPlugin({
  uglifyOptions: {
    compress: {
      pure_funcs: ['F2','F3','F4','F5','F6','F7','F8','F9','A2','A3','A4','A5','A6','A7','A8','A9'],
      pure_getters: true,
      keep_fargs: false,
      unsafe_comps: true,
      unsafe: true,
      passes: 3
    }
  }
})

I see it takes a ‘passes’ option.

It is also worth noting that if you have a mixture of Elm and other JS code, it’ll be dangerous to run the whole thing through uglify like that - unless I’m mistaken those options could potentially result in your JS getting messed up.

You can use dynamic imports to get around this. E.g:

import(/* webpackChunkName: "elm" */ "../elm/Main").then(
  ({ Elm: elm }) => {
    const app = elm.Main.init({ flags: null });
  }
);

That will split your code into two files - one with your elm, one with everything else. you can then have two uglify runs with different options - one over each file:

optimization: {
    minimizer: [
        new UglifyJsPlugin({
            test: /index\.js$/
        }),
        new UglifyJsPlugin({
            test: /elm\.js$/
            uglifyOptions: {
                compress: {
                    pure_funcs: ['F2','F3','F4','F5','F6','F7','F8','F9','A2','A3','A4','A5','A6','A7','A8','A9'],
                    pure_getters: true,
                    keep_fargs: false,
                    unsafe_comps: true,
                    unsafe: true,
                    passes: 3
                }
            }
        })
    ]
}
3 Likes

That was very helpful, I didn’t understand how to use dynamic imports previously.

I just tried it out now and it seems that creating separate chunks and uglifying the code as you suggested actually generated a slightly larger total file size! I am not sure why that is the case, but seems that we will stick with the default settings for now then.

Perhaps this is because the elm component is only a small part of the total js for that page

Without chunking + default uglify
247,787 bytes

With chunking + elm compression settings
226,032 + 22,088 = 248,120 bytes

(all gziped)

I’m working on Elm minification for Webpack.

2 Likes

You can now use elm-minify as part of Webpack with this plugin.

An example can be found here.

Let me know if you have any problems with the package :sunny:

5 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.