Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UglifyJS - Mangle functions but preserve Function.prototype.name

When using UglifyJS, function names are mangled, unless keep_fnames is set to true. For example, the following Typescript code:

class Test {}
console.log(Test.name);

compiled to JS as:

function Test() {}
console.log(Test.name);

will be uglified to:

function t() {}
console.log(t.name);

and output t instead of test to the console.

Is there a way (other than using keep_fnames option) to preserve the name property after uglification ? (I don't want to use keep_fnames:true because it increases the bundle size quite a lot.

Possible solutions I thought of:

  • Writing a Webpack plugin that hard codes the function name Test.name = 'Test', but this won't work as Function.prototype.name is a read only property;
  • Using Typescript decorators, metadata and the reflection API, but design:type metadata is not emitted for classes, it's only emitted for properties (which I believe is because Function.prototype.name exists, but I guess they missed this edge case ?).
like image 270
user5365075 Avatar asked Oct 05 '17 11:10

user5365075


2 Answers

As it's explained here, Function.prototype.name cannot be relied on in client-side code, because the information on function original name will be destroyed with minification process. Preventing it from being renamed is quick-and-dirty fix.

name is read-only and non-configurable in some browsers, so doing something like

class Test {
  static get name() {
    return 'Test';
  }
}

or

function Test() {}
Object.defineProperty(Test, 'name', { configurable: true, value: 'Test' });

will fix it in most browsers but result in obscure compatibility problems in rest of them (for example, Android 4.x browser).

The proper way to do this is to never rely on name in client-side code for anything but debugging. As for Node.js and Electron, it depends on whether the code needs to be obfuscated.

If string identifier should exist for a class or a function, another static property name can be picked, e.g. id or unsupported yet conventional displayName.

like image 151
Estus Flask Avatar answered Oct 29 '22 07:10

Estus Flask


Is there a way (other than using keep_fnames option) to preserve the name property after uglification...

The only mechanism to keep the correct name involves that name being in the output file, so the short answer is no. If you want to use prototype.name you need to leave that name be.

The alternatives would involve either:

  1. Adding an additional property containing the name, which could introduce errors and would still take up space in your file
  2. Finding a tool that will pre-compile all uses of prototype.name with the string value... I'm not aware that one exists but you never know!
like image 33
Fenton Avatar answered Oct 29 '22 09:10

Fenton