WebPack+TerserPlugin: mangle ignores properties and class names – poor quality of the mangled code
Asked Answered
S

3

13

The resulting code is minified but almost not mangled. This is how it looks like in the Google Chrome (beautified): Example of result did not mangled code 1/2. Example of result did not mangled code 1/2.

All properties names, lots of variables have their original names. Even with Terser's mangle options specified explicitly:

  • mangle: true,
  • sourceMap: false,
  • keep_fnames: false,
  • toplevel: true,

This is WebPack config:

const TerserPlugin = require('terser-webpack-plugin');
const path = require('path');

module.exports = {
    entry: './src/scripts/index.ts',
    devtool: 'inline-source-map',
    module: {
        rules: [        
            {
                test: /\.tsx?$/,                
                use: {
                    loader: 'ts-loader',
                    options: { configFile: 'tsconfig-build.json' }
                },
                exclude: /node_modules/,                
            },
        ],        
    },
    resolve: {
        extensions: [ '.tsx', '.ts', '.js' ],
    },
    plugins: [ ],

    // PROBLEMS HERE:
    optimization: {        
        minimize: true,        
        minimizer: [new TerserPlugin({
            sourceMap: false,            
            extractComments: false, // To avoid separate file with licenses.
            terserOptions: {
                mangle: true,
                sourceMap: false,       
                //keep_classnames: false,
                keep_fnames: false,
                toplevel: true,                                
            },                     
        })],
    },

    output: {    
        path: path.resolve(__dirname, resultDirPath),
        filename: 'main.js',
        publicPath: './',
    }   

}

Did I miss something in the config?

Samaritan answered 13/11, 2019 at 10:31 Comment(0)
E
10

I believe in your original config you need to add mangle.properties to get your ES6 class methods and data members to be mangled. To avoid mangling external libraries, I "uniquely" name all my methods and data members to be mangled using a prefix strategy matching the regex below.

            new TerserPlugin({
                terserOptions: {
                    mangle: {
                        properties: {
                            regex: /(^P1|^p1|^_p1)[A-Z]\w*/
                        }
                    }
                }
            })
        "terser-webpack-plugin": "^2.2.1",

The niggly bits of this approach:

  • My naming currently doesn't match the naming in the external libraries I am using. There's no guarantee of this in future library releases.
  • It makes my original src a bit uglier.
Elevated answered 7/12, 2019 at 20:9 Comment(0)
S
8

Unfortunately, there is no easy solution, and this code is already the best of what you can get with Terser.


However, I found a perfect solution: "JavaScript obfuscator" – https://github.com/javascript-obfuscator/javascript-obfuscator#readme

And its WebPack plugin: "javascript-obfuscator plugin for Webpack" – https://github.com/javascript-obfuscator/webpack-obfuscator

The resulting JS file after beautifying in Google Chrome will look like this: An example of perfectly obfuscated JS code with "JavaScript obfuscator".

By the way, in my case it is only ~35% larger.


So, summarize, the all you need, just to:

  1. Install "javascript-obfuscator plugin for Webpack": "npm install --save-dev webpack-obfuscator"
  2. And add plugin to the WebPack:
const JavaScriptObfuscator = require('webpack-obfuscator');

// ...

// webpack plugins array
plugins: [
    new JavaScriptObfuscator ({
      rotateUnicodeArray: true
  }, ['excluded_bundle_name.js'])
],

That's all!

Samaritan answered 15/11, 2019 at 13:46 Comment(3)
Just as info, this answers the question, but if you intend to use it for minification - this does not make your bundle smaller! From obfuscator docs: "... the obfuscated code is 15-80% slower (depends on options) and the files are significantly larger."Noctule
@MichaelB. I mostly agree with you, but Terser minification is too delicate without options to make it more strict. The process of minification includes the changes of the names to the shortest possible form – this already makes code looks mangled (obfuscated). The problem is highly visible in comparison to "Uglify JS" ( "--compress" arg. only).Samaritan
I got an issue with this plugin, I can't preserve a few UserScript comments with it.Reexamine
R
5

mangle: { properties: true } is safe if you're not doing introspection or other magic that is untypical in TypeScript:

class MyClassName {
    constructor(public arg: string) {
        console.log(arg);
    }

    getContext() {
        console.log("I'm not mangled because I might be a HTMLCanvasElement.getContext");
    }

    someCustomMethodName() {
        console.log("What's my name?");
    }
}

const x = new Foo("Hello world");
x.getContext();
x.someCustomMethodName();

will be turned into

(() => {
    const e = new class {
        constructor(e) {
            this.o = e, console.log(e);
        }

        getContext() {
            console.log("I'm not mangled because I might be a HTMLCanvasElement.getContext");
        }

        t() {
            console.log("What's my name?");
        }
    }("Hello world");
    e.getContext(), e.t();
})();
Roentgenogram answered 14/4, 2021 at 19:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.