There is --mangle-props with --reserved and --reserved-file, which can exclude some names from mangling, but there is missing a --mangle-props with --mangle-only-these-names and --mangle-only-these-names-file.
The logic could be the same as in reserved and these should be exclusive (either --reserved or --mangle-only-these-names).
The reason for this is that --mangle-props is very likely to break the code as it mangles nearly everything except those named in --reserved or --reserved-file. In nearly all cases (I assume) it would be better to select what to mangle, because the developer KNOWS what is safe to mangle, but UglifyJS cannot KNOW this for sure, it can only make assumptions.
Currently I have no idea how to achieve this using current implementation.
I think that I found a way to invert the functionality of --reserved and --reserved-file. In my case I wanted to mangle 215 props of total 805 props.
I first tried the default --reserve-domprops, but it mangled my data-attributes and many of the attributes that were defined in a non-predictable way (in a way that UglifyJS cannot know what to do), and the app was broken. I tried then to find all those props that causes the breakage, but it got too complicated and I didn't succeed to find all of those props.
I realized that UglifyJS cannot KNOW for sure what is safe to mangle, but I do KNOW this, so I have to find a way to tell this to UglifyJS. The desired functionality proved to be exactly the INVERSE of --reserved.
I made a new copy of uglifyJS to another folder and modified can_mangle() and mangle() functions in node_modules/uglify-js/lib/propmangle.js.
Original:
function can_mangle(name) {
if (reserved.indexOf(name) >= 0) return false;
if (/^[0-9.]+$/.test(name)) return false;
return true;
}
function mangle(name) {
var mangled = cache.props.get(name);
if (!mangled) {
do {
mangled = base54(++cache.cname);
} while (!can_mangle(mangled));
cache.props.set(name, mangled);
}
return mangled;
}
Inverse:
function can_mangle(name) {
if (reserved.indexOf(name) >= 0) return true;
return false;
}
function mangle(name) {
var mangled = cache.props.get(name);
if (!mangled) {
mangled = name;
if(can_mangle(mangled)) mangled = base54(++cache.cname);
cache.props.set(name, mangled);
}
return mangled;
}
Finally the app minified successfully, when I called it:
test/node_modules/uglify-js/bin/uglifyjs unminified_app.js --mangle toplevel --mangle-props --reserved-file mangle-only-these.js -o minified_app.js
The call is fully insane because --reserved-file points to a file that contains the props to be mangled, as it normally points to a file that contains the props NOT to be mangled. But because I know this, who cares. It would, of course, be perfect if this like reversed functionality would be built in into the UglifyJS.
:+1: for integrating this
+1
I have been using --mangle-regex="/_$/" to workaround the lack of this functionality and renaming the props I want to mangle from myVar.myProp to myVar.myProp_
@ffabreti: What is an example command of this (something like "uglifyjs unminified_app.js --something")?
@timo22345 if you want to literally declare all prop names (as would be done in the exclude list), I reckon you should be able just use a long regex:
uglifyjs --compress --mangle --mangle-props --mangle-regex="/myPropertyA|myPropertyB|myPropertyC|myPropertyD|myPropertyE|anotherProp|soManyProps/"
Sure, it's a bit crazy, but you could have hundreds of properties in there. And if your build is scripted, you could read these props in from a JSON file:
new RegExp(myPropsArray.join('|'))
It is interesting that some DOM properties aren't mangled by default, without specifying --reserve-domprops, e.g. offsetTop or addEventListener. Others like cookie are mangled.
The same is true for some JQuery functions, e.g. hide is kept,show is mangled.
Can someone explain why this is happening?
Can you explain how to reproduce those cases?
-> echo 'var x = {offsetTop: 5}; alert(x.offsetTop);' | ./bin/uglifyjs --mangle-props
var x={a:5};alert(x.a);
With this sample
function Test() {
function mousedownHandler(ev) {
document.cookie = "color=" + color;
canvas.focus();
if (state == 0x1f || state == 0x41) {
var x = ev.pageX - canvas.offsetLeft + offsetX;
var y = ev.pageY - canvas.offsetTop + offsetY;
}
}
this.show = function(showButtons) {
showButtons ? btnOk.show() : btnOk.hide();
showButtons ? btnCancel.show() : btnCancel.hide();
}
}
the result is:
function Test() {
function mousedownHandler(ev) {
document.a = "color=" + color;
canvas.focus();
if (state == 31 || state == 65) {
var x = ev.pageX - canvas.offsetLeft + offsetX;
var y = ev.pageY - canvas.offsetTop + offsetY;
}
}
this.b = function(showButtons) {
showButtons ? btnOk.b() : btnOk.hide();
showButtons ? btnCancel.b() : btnCancel.hide();
};
}
My way of having --mangle-only-these-names is using --mangle-regex :
Example (bash script):
UGLIFYJS=$PROJECT_HOME/node_modules/uglify-js/bin/uglifyjs
SOURCE=$PROJECT_HOME/platforms/android/assets/www/dist_js/app.js
DESTINATION=$PROJECT_HOME/platforms/android/assets/www/dist_js/app.mangled.js
# REGEXPs for mangling props via uglifyJS
# double-check that all properties must be a full match and partial matches may cause you trouble, ex: getPos will also match getPosition
# ----------------------------
RE_LOCALSTORE="forEachStorageKey|clearKeysLike|removeStorage|getStorage|setStorage"
RE_NOTIFIER="subscribe\$|emit\$"
$UGLIFYJS --beautify \
--mangle-props \
--mangle-regex="/$RE_LOCALSTORE|$RE_NOTIFIER/" \
-- \
$SOURCE > $DESTINATION
While debugging the case I saw that property expressions are only mangled when part of an assignment (my interpretation of the code):
...
else if (node instanceof AST_Dot) {
if (this.parent() instanceof AST_Assign) {
add(node.property);
}
}
...
That's why only document.cookie and this.show are mangled, all other props' parents are either call or binary.
"show" is a name clash which would be hard to detect and should be avoided, that was my fault.
Commenting out the check for the assignment results in
function Test() {
function mousedownHandler(ev) {
document.c = "color=" + color;
canvas.d();
if (state == 31 || state == 65) {
var x = ev.e - canvas.f + offsetX;
var y = ev.g - canvas.h + offsetY;
}
}
this.i = function(showButtons) {
!showButtons ? btnOk.j() : btnOk.i();
showButtons ? btnCancel.i() : btnCancel.j();
};
}
which looks better for me
Most helpful comment
:+1: for integrating this