Variable Mangling For Better Minification

Variable Mangling For Better Minification
Photo by Markus Spiske / Unsplash

Mangling, in the context of javascript transpilations, refers to the process of changing the name of identifiers, so that instead of storing Foo.bar we can have a.b. It sounds good at first but once you look it a bit further, problems become obvious: it can and probably will break your code when you have dependencies outside of your compilation process.

For example, imagine this simple application, where PIXI is an external dependency:

import * as PIXI from "pixi.js";
let app = new PIXI.Application();

The result of this with mangle/mangling enabled can be something like this:

const t=PIXI;
let a=t.t();

Did you see the problem? If we allow the minifiers to mangle everything, it will in fact mangle everything, and that's not good. t.t() is clearly not going to work if PIXI is outside of our mangling process.

Ok, that was the problem. We can't just mangle anything outside of our compiling process, otherwise, it will break. So how do we fix it? The solution I found is not ideal, but it enabled a more controlled mangling. The idea is only to mangle identifiers that we flag as safe. You can decide if you want to whitelist or blacklist a list of identifiers or patterns. Personally, I find this simple regex combining: /^_|_$/, this means that now you will have to write code like this:

class TestClass () {
  private _privVar: number;
  public pubVar_: number;
  myFunc_() {}
}

Basically, we flag each member of a class by prefixing or suffixing an underscore _. This can be achieved with Webpack 5 by defining mangle.properties:

new TerserPlugin({
  terserOptions: {
    toplevel: true,
    mangle: {
      toplevel: true,
      properties: {
        regex: /^_|_$/,
      },
    }
  }
});

Another option would be that instead of defining what should mangled one could define what shouldn't be. For that, I think google's closure compiler with the ADVANCED option is really good.

npx google-closure-compiler \
  --js dist/app.bundle.js \
  --js_output_file dist/app.bundle.closure.js \
  --compilation_level ADVANCED \
  --externs extern.js

But, here you need to define the --externs, that's the definition of anything that's outside of your project, with types and everything. And is not that easy to write or maintain, nor you will find an active project that can generate the externs automatically. If you ask me, adding the _ sounds is way easier.