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?

Oleg Zarevennyi
  • 2,753
  • 1
  • 21
  • 21

3 Answers3

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.
Ken Lin
  • 1,819
  • 21
  • 21
7

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!

Oleg Zarevennyi
  • 2,753
  • 1
  • 21
  • 21
  • 5
    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." – Michael B. Oct 13 '20 at 23:32
  • @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). – Oleg Zarevennyi Oct 20 '20 at 10:04
  • I got an issue with this plugin, I can't preserve a few UserScript comments with it. – FabianoLothor Sep 21 '21 at 22:00
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();
})();
Blaise
  • 13,139
  • 9
  • 69
  • 97