How to prevent JS minification from removing names of named-function-expressions? - javascript

I have this function on an object that I need to trace REALLY BADLY, along with the parent caller of the invocation and arguments passed to the caller.
This well works until minified:
var foo = {
FunctionToBeLogged: function GiveMeAName() {
console.log('> %s called from %s - args: %o',
arguments.callee.name,
arguments.callee.caller.name,
arguments.callee.caller.arguments);
}
}
var bar = {
A: function A(something) {
foo.FunctionToBeLogged('nothing', 12, true);
},
B: function B(whatever, doesntMatter) {
foo.FunctionToBeLogged('nothing', 12, true);
}
}
bar.A(1.2, 'Fred', { }); // > GiveMeAName called from A - args: [1.2, "Fred", Object]
bar.B('Barney', 42, false); // > GiveMeAName called from B - args: ["Barney", 42, false]
Minification gets rid of the these names and my output becomes:
bar.A(1.2, 'Fred', { }); // > called from - args: [1.2, "Fred", Object]
bar.B('Barney', 42, false); // > called from - args: ["Barney", 42, false]
I really don't want to go and create function declarations and assignments because I have TONS of them (I inherited this code with 7,564... and I can easily run some regex sub to name the function expressions.)
What can I do to prevent the minifier to get rid of my these function names?

To achieve this you can pass specific names to not be mangled, for example in UglifyJS:
To avoid that, you can use --reserved-file to pass a filename that should contain the names to be excluded from mangling
In that file you would have a list of names you do not want to be changed like so:
{
"vars": [ "define", "require", ... ],
"props": [ "length", "prototype", ... ]
}
Uglify Mangle options docs...

Related

How to get TypeScript to allow optional parameters?

The below code works as intended in JavaScript, but when compiling it to TypeScript I get the error
t.ts:11:34 - error TS2554: Expected 1 arguments, but got 6.
11 console.log(where_undefined(obj, 0, 'category1', 'nested', 'b', 2))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
t.ts:14:34 - error TS2554: Expected 1 arguments, but got 6.
14 console.log(where_undefined(obj, 0, 'category1', 'nested', 'b', 4))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Question
How can I get this function to work in TypeScript?
var obj = [{
"category1": {
nested: {
a: 'string',
b: [69, 13, 15]
}
},
"category2": "2",
}];
console.log(where_undefined(obj, 0, 'category1', 'nested', 'b', 2))
// print 15
console.log(where_undefined(obj, 0, 'category1', 'nested', 'b', 4))
// print undefined at key 4
function where_undefined(obj) {
var args = Array.prototype.slice.call(arguments)
args.shift();
while (args.length) {
var arg = args.shift();
if (obj[arg] === undefined) {
console.log("undefined at key " + arg);
break;
} else {
obj = obj[arg];
}
}
return obj;
}
First, like #A_A has described, we should be using rest parameters:
function where_undefined(obj: any, ...args: PropertyKey[]) {
We also need to give them types. I have used any for obj here, because if we had used unknown, we'd get many more unnecessary errors in the function body. PropertyKey is a built-in type that represents all possible types for keys (string | number | symbol).
Since we're using rest parameters, we don't need this bit:
var args = Array.prototype.slice.call(arguments)
args.shift();
All this is doing is getting the same result as args in our new code.
The next change is here:
const arg = args.shift()!;
We use ! to assert that this cannot be undefined (since undefined cannot be used as a key). If we did not use an assertion here, we'd get errors when we try to use arg as a key:
obj[arg] // ! Type 'undefined' cannot be used as an index type.
However, now when you give it an object, it always returns any, no matter what the type of the object was, so we'll go back and change any to use a generic:
function where_undefined<T>(obj: T, ...args: PropertyKey[]): T {
But with this change, we get even more errors in the body. We can "ignore" these by changing this to an external signature:
function where_undefined<T>(obj: T, ...args: PropertyKey[]): T {
function where_undefined(obj: any, ...args: PropertyKey[]) {
The final result can be seen in this playground.

How can I get variable data in the object prototype?

I have an array of objects, and I want to create a method which returns the data inside the method. How do I do this? Thanks
// for example, I have this data
var data = [{ number: 1, name: "Bob" }, { number: 2, name: "Jim" }];
Object.prototype.number = () => {
// how do I access the object data inside this prototype
// i am trying to return something like this, but it doesnt work:
// return this.number;
};
// i want this to return the number 1
data[0].number();
^
| I am trying to access this data in the prototype function and return the number
I know this can be done with data[0].number, but I am trying to do it with an object prototype method.
You need to:
Use different property names for the property holding the method and the property holding the data
Not use an arrow function (which has lexical this)
// for example, I have this data
var data = [{
xnumber: 1,
name: "Bob"
}, {
xnumber: 2,
name: "Jim"
}];
Object.prototype.number = function() {
return this.xnumber;
};
// i want this to return the number 1
console.log(data[0].number());
Note that editing the prototype of built-in objects is considered dangerous.

Complex objects in javascript

I'm fiddling around with a library called bcoin for node. Running the following code:
chain.on('block', function(block) {
console.log('Connected block to blockchain:');
block.txs.forEach(function(t) {
t.inputs.forEach(function(i) {
console.log(typeof i, i);
console.log(JSON.stringify(i));
});
});
});
This is the response I'm getting:
Connected block to blockchain:
object { type: 'coinbase',
subtype: null,
address: null,
script: <Script: 486604799 676>,
witness: <Witness: >,
redeem: null,
sequence: 4294967295,
prevout: <Outpoint: 0000000000000000000000000000000000000000000000000000000000000000/4294967295>,
coin: null }
{"prevout":{"hash":"0000000000000000000000000000000000000000000000000000000000000000","index":4294967295},"script":"04ffff001d02a402","witness":"00","sequence":4294967295,"address":null}
Notice that even though the attribute type for example, is shown when we print i, that attribute does not exist when we JSON.stringify the object. If I tried to console.log(i.type) I'd get undefined.
How is that possible? And what is a good way of debugging what's going on with an object?
JSON.stringify will only includes enumerable properties that are not functions.
So if you define a property and set as non-enumerable, it will not be a part of JSON string.
var obj = {
a: 'test'
};
// Non-enumerable property
Object.defineProperty(obj, 'type', {
enumerable: false,
value: 'Test'
});
// Get property
Object.defineProperty(obj, 'type2', {
get: function(){
return 'Test 2'
}
});
console.log(JSON.stringify(obj), obj);
console.log(obj.type, obj.type2)

AngularJS: Way to define a large $watchCollection?

This post is not the same as: Is there a tidy way to define a large watch collection for AngularJS?
My code is (service.js):
var MyJSON = {
array: [
{k:'v'},
{k:'v'},
{k:'v'}
],
info: {
k: 'v',
k2: 'v2'
},
last: 1398680914943 // Date.now()
}
$rootScope.$watchCollection(function () { return MyJSON; }, function (n, o) {
console.log('Change');
});
Detects changes when I work on the root object "MyJSON". Like this:
MyJSON.last = 123456789; // console: Change
But if I am doing something like this:
MyJSON.info.k = 'vDummie';
or:
MyJSON.array.push({k:'something'});
"$watchCollection" does not work.
$watchCollection watches only the 1st level properties; use $watch(..., ..., true) to do "deep" watching. Note: there are 3 arguments, the first two are the same as your code, the third is true for deep watch.

How to determine if string contains array, object array or function definition

I have a solution now, but it's not the best I think.
function parseEval(value){
var result = undefined;
try {
result = eval(value);
} catch (e) { }
return result;
}
So if the value is undefined or contains uninterpretable value the function return undefined.
If contains an existing function name than returns function object
if contains "[1,2,3]" then return int array
if contains "[{ label: "Choice1", value: "value1" },{ label: "Choice2", value: "value2" }]" then return an array of objects
I'm open for any solution because the eval has lot of disadvantages. (performance, security, flexibility, maintainability)
If this is an internal function that will never be passed any user-supplied data, this might be the best way to go about things. Otherwise, you would probably be better off using JSON.parse to parse data and look up functions and other non-JSON data in a whitelist:
var someObject = {
aFunction: function() {},
anInt: 42
};
function parse(value) {
var result;
try {
return JSON.parse(value);
} catch(e) {
return someObject[value];
}
}
[{ label: "Choice1", value: "value1" },{ label: "Choice2", value: "value2" }]
isn't json, but assuming that it should be, because you mentioned that in the question,
console.log(typeof JSON.parse(string))
Should work nicely for anything but functions.

Categories

Resources