Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mangle nested classes and variables with UglifyJS

I use UglifyJS to minify a concatenated set of files, which works fine but not good enough. The built lib uses namespaces, so classes, functions and constants are stored in a root namespace variable:

(function() {
  var root = { api:{}, core:{}, names:{} };

  /* util.js file */
  root.names.SOME_LONG_NAMED_CONST='Angel';

  /* Person.js file */
  root.core.Person = function(name) { this.name = name };

  /* API.js with the functions we want to expose */
  root.api.perform = function(param_for_api) { /* do something */ }

  window.lib_name.perform = root.api.perform;

})();

which is minified to the not-so-minimal version

(function(){var a={api:{},core:{},names:{}};a.names.SOME_LONG_NAMED_CONST="Angel",a.core.Person=function(a){this.name=a},a.api.perform=function(){},window.lib_name.perform=a.api.perform})();

I understand uglify probably thinks that root var is a data structure that must be kept as-is and can't be changed. Is there a way to let UglifyJS mangle the nested names in the root namespace?

like image 945
whadar Avatar asked May 02 '13 09:05

whadar


2 Answers

When you minimize Javascript you can only change names of variables, the api, core and names are not variables but properties of an object. If these were changed by the minimizer, you would potentially get unexpected results. What if in your code you would call

root["api"].perform = function()...

or even something like

function doIt(section, method, argument) {
    root[section][method](argument);
}
doIt('api','perform', 101);

All perfectly legal JS, but a minimizer could never figure out what's going on.

like image 187
Jan Misker Avatar answered Nov 10 '22 15:11

Jan Misker


I have been trying to use --mangle-props of UglifyJS2 and can tell you: 'it makes a mess'.

As someone pointed out: 'Developer should decide what properties to mangle, not uglifyjs'

I am approaching the problem using this options:

--mangle-props
--mangle-regexp="/_$/"

The regex matches any property with a underscore at the end.

You asked to mangle nested names in the root namespace. So, your code:

(function() {
  var root = { api:{}, core:{}, names:{} };

  root.names.SOME_LONG_NAMED_CONST_='Angel';

  root.core.Person_ = function(name) { this.name = name };

  root.api.perform_ = function(param_for_api) {  }

  window.lib_name.perform = root.api.perform;
})();

Would result in this:

(function() {
    var n = {
        api: {},
        core: {},
        names: {}
    };
    n.names.a = "Angel";
    n.core.b = function(n) {
        this.name = n;
    };
    n.api.c = function(n) {};
    window.lib_name.perform = n.api.c;
})();

Command: uglifyjs --beautify --mangle --mangle-props --mangle-regex="/_$/" -- file.js

If you want to mangle first level of root namespace (api, core, names) just put a underscore on them (api_, core_, names_), you are in control ;)

Just a side note: when you are mangling properties usable by other js files, you should mangle all files together with the same command, so the same identifier will be used over all files.

like image 25
Fernando Fabreti Avatar answered Nov 10 '22 17:11

Fernando Fabreti