Is it possible to alter javascript with google closure compiler? - javascript

I'm working on a modular component framework and have different needs for the code in my test environment vs production. Each component has its own js file and these are being compiled together for production use, but left separate in my dev environment.
In production I want to trap errors in various stages of initialization so as to not break the call stack. In my development (uncompressed javascript), I do not want to trap the errors, so I can quickly get line numbers, etc to find where the breaks occur. Is there any way to use the closure compiler to manipulate a javascript file (such as removing a flagged line such as a comment opener and closer) that might allow me to do this?
For instance:
proto.instantiateChildFromDescriptor = function(childObj, callback, customDiv){
/*ProductionErrorTrap
try{
/**/
//code for instantiating a module
/*ProductionErrorTrap
}catch(e){
console.log("problem instantiating child in " + this.getName());
console.error(e);
}
/**/
}
replacing /*ProductionErrorTrap with /*ProductionErrorTrap*/ would work nicely (so is removing my stars on the second comment block, but they are there)
I can't do this with a find and replace on the files themselves because it would mess with github. I suppose I could create a new copy of the files, run a find and replace on them and compile *those files, but It would be nice if I could do it all in the closure compiler.
Thanks

I think messing with compiled results, matching comments to emulate ifdefs is pretty unelegant.
You could actually do something like this:
debugTry(function() {
alert('This is code that might cause an error');
},function(e) {
alert('Whoops! Caught error '+e);
});
Then while debugging you do:
function debugTry(test,handle) {
try {
test();
} catch(e) {
handle(e);
}
}
And in production:
function debugTry(test,handle) {
test();
}
Closure Compiler is pretty smart. With the first debugTry the example compiles to:
try{alert("This is code that might cause an error")}
catch(a){alert("Whoops! Caught error "+a)};
With the second debugTry it compiles to:
alert("This is code that might cause an error");
As you see, the unnecessary code is detected and eliminated.
Of course, things would be easier if you didn't conditionally include a try block. If it's just some statements included conditionally, not changing the structure of other code, you can do:
/** #const */ var debug = false;
if(debug) {
alert('Yay for debugging!');
}
The alert will be compiled away.

Use compiler flags for advanced mode.
from:
https://developers.google.com/closure/compiler/docs/js-for-compiler
#define {Type} description
For example:
/** #define {boolean} */
var ENABLE_DEBUG = true;
/** #define {boolean} */
goog.userAgent.ASSUME_IE = false;
Indicates a constant that can be overridden by the compiler at compile-time. With the example above, you can pass the flag --define='ENABLE_DEBUG=false' to the compiler to change the value of ENABLE_DEBUG to false.

Stephen Chung's comment seems to be the best approach. Adding his comments as an answer to this question for anyone else with this problem. I entered the following into my code
proto.updateProperties = function(propsObj){
/** #preserve ProductionErrorTrap try{ /**/
//do stuff here
/** #preserve ProductionErrorTrap }catch(e){console.log("problem updating properties in " + this.getName());console.error(e);}/**/
}
After compressing the javascript I just run a find and replace on the compressed file, remove the line breaks and replace ProductionErrorTrap with ProductionErrorTrap*/ and everything seems to work fine without making a mess of the repository.
Thanks Stephen!

Related

Importing script and calling a method on a variable

I'm new to typescript and I'm trying to use a bit of code I found online to change links to look nice for my site,
<script src="https://wow.zamimg.com/widgets/power.js"></script>
<script>var wowhead_tooltips = {
"colorlinks": true,
"iconizelinks": true,
"renamelinks": true
};
</script>
I included that into my index.html, and it works great, until I load a component, I spent a lot of time looking around and found that I need to call $WowheadPower.refreshLinks();
To get the links to change when a new element is added, I wasn't sure how to declare that variable in typescript so I could tie it to various angular commands I wanted to do, unless I add it in a try catch:
loadScript(){
try{
// update tooltips
if(typeof $WowheadPower == 'undefined'){
$.getScript('//wow.zamimg.com/widgets/power.js');
} else {
$WowheadPower.refreshLinks();
console.log($WowheadPower)
}
} finally {}
}
I get an error that says
Cannot find name '$WowheadPower'
but I saved it anyway, and somehow on my page it works as I want it too.
It works perfect, but I still got the error so I declared it
try{
// update tooltips
var $WowheadPower
if(typeof $WowheadPower == 'undefined'){
$.getScript('//wow.zamimg.com/widgets/power.js');
} else {
$ WowheadPower.refreshLinks();
console.log($WowheadPower)
}
} finally {}
and it broke, I assume because I overwrote the correctly variable that has the right method.
Now I have to leave the error in to get functionality, but the error stops me from compiling when I ng serve. Until I hit save on VScode then it works fine again.
Any thoughts on how to resolve this?
since $WowheadPower is an external variable imported from another file, you can tell typescript that it exists without explicitly declaring it:
declare var $WowheadPower: any
write this at the beginning of your code so you tell TS that this variable exists. Ideally, you would write an interface that correctly defines $WowheadPower's type instead of any there.

An object disappears after packing a JavaScript library

I have created a JavaScript library and packed it with these options selected : Shrink Variables and Base62 Encoded at this url: http://dean.edwards.name/packer/. In this library I have declared an object ax, but when I use the packed version in my web page I get an error saying Uncaught ReferenceError: ax is not defined.
The original code of this library looks like below.
var ax = {
scaleUp:function(win) {
//code omitted
},
downGrade:function(win) {
//code omitted
}
}
In my web page in which I am using this library, I have code like below. This code works, if instead of packing, I minify it using Microsoft's Minifier or just use the original JavaScript library without minification or packing.
var result = ax.downGrade(w);
Question :
Why is the variable ax not accessible with packed version? Do I need to add something else when using the packed version?
UPDATE 1:
I could not get the packed file to work but packing my code through another compression utility at following url worked in my case: http://jsutility.pjoneil.net/. It provided an equally good compression.
I am still not sure why the utility at original url failed to produce a working version of my library, even though my original code works without any errors on any web page.
Check your console for errors before trying to call ax. Explicitly place semi-colons where they belong.Example at the end of the definition for ax you should put a semi-colon, even though in standard code it's good as is. Remove the explicit var declarations. When I did these things:
ax = {
scaleUp:function(win) {
alert("up");
},
downGrade:function(win) {
alert("down");
}
};
result = ax.downGrade();
Ran without issue in jsFiddle and console: http://jsfiddle.net/7kdnw65n/. I suspect it has to do with how the algorithm "shrinks" the variables. The resulting pack was:
eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('0={5:1(2){3("6")},4:1(2){3("7")}};8=0.4();',9,9,'ax|function|a|alert|downGrade|scaleUp|up|down|result'.split('|'),0,{}))

Should I keep console statements in JavaScript application?

I am using console.log() and console.dir() statements in 3 places in my 780 lines of code JS script. But all of them are useful for debugging and discovering problems that might appear when using the application.
I have a function that prints internal application's state i.e current value of variables:
printData: function () {
var props = {
operation: this.operation,
operand: this.operand,
operandStr: this.operandStr,
memory: this.memory,
result: this.result,
digitsField: this.digitsField,
dgField: this.dgField,
operationField: this.operationField,
opField: this.opField
};
console.dir(props);
}
I also have a list of immutable "contants" which are hidden with closure but I can print them with accessor method called list(); in console.
Something like this:
list: function () {
var index = 0,
newStr = "",
constant = '';
for (constant in constants) {
if (constants.hasOwnProperty(constant)) {
index = constant.indexOf('_');
newStr = constant.substr(index + 1);
console.log(newStr + ": " + constants[constant]);
}
}
}
The third place where I use console in debugging purposes is in my init(); function, where I print exception error if it happens.
init: function (config) {
try {
this.memoryLabelField = global.getElementById(MEMORY_LABEL);
this.digitsField = global.getElementById(DIGITS_FIELD);
this.digitsField.value = '0';
this.operationField = global.getElementById(OPERATION_FIELD);
this.operationField.value = '';
return this;
} catch (error) {
console.log(error.message);
return error.message;
}
}
As my question states, should I keep these console statements in production code?
But they come very useful for later maintenance of the code.
Let me know your thoughts.
Since these are not persistent logs you won't get much benefit out of them. Also it runs on every individuals machine since everyone has their own copy of the program. If you really need it, it would be better to have a variable that can toggle this feature. Espcially if you are targetting to debug a whole lot of pre-determined stuffs.
Client Side issues needs to be debugged slightly different from Server Side. Everyone has their own copy of the program. Browser-JS is run on client side you open the browser and all code that you wrote is with you in a full blown repl and debugging is easy compared to Server side where most likely you wouldn't have access to that system. It is so flexible that you could just override it when you are checking something in production right from your own browser without affecting anyone. Just override it with those console statements and debug the issue.
Its a good idea to have logs in Server side programming. It gives a lot of useful information and is persistent.
As said above, debugging code should not be present on a production environment.
However, if you plan to keep it anyway, keep in mind that all browsers do not support it.
So you may check its availability and provide a fallback:
if (typeof window.console === 'undefined')
{
window.console = {};
}
if (typeof window.console.log === 'undefined')
{
window.console.log = function() { };
}
According to mozilla developer network
This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.
Leaving console.log() on production sites can cause issues since it is not supported by older browsers. It is most likely to throw exceptions in that case.
It's historically considered a bad-practice by Javascript programmers. At the start of the eons, some browsers didn't have support for it (IE8 or less for example). Other thing to take in mind is that you are making your code larger to download.

Enable/Disable debug code dynamically

I'm writing a decent sized JavaScript animation library, that I would like to include debugging code in. I could easily do a check :
if(myLib.debugger){
console.warn('warning message');
}
However if this runs a couple thousand times a second, it would eventually cause performance issues. Add in a few more checks throughout the code and the effect will be even more noticeable.
What I'm wondering is if it would be possible to check onload if the debugger should be enabled, and if so... turn something like this:
//debugger if(!this.name) console.warn('No name provided');
into:
if(!this.name) console.warn('No name provided');
Leaving the code commented if its not enabled, and uncommenting it if it is, thus removing any possible performance issues. Could this be done somehow with a regular expression on the entire script if loaded in through ajax? I'm trying to avoid the need for 2 versions of the same code, lib.dbug.js and a lib.js.
Cross browser compatibility is not of great importance for this (I'm really only worried about new browsers), I see it as nice to have item. If its possible however, it would be a great thing to have.
Any insight would be greatly appreciated.
The simplest way to do this would be to check if the debugger should be disabled and if so, replace it with a mock object that does nothing at the very start of your script:
if (!myLib.debugger) {
window.console = (function () {
var newConsole = {};
var key;
for (key in window.console) {
if (typeof window.console[key] === 'function') {
newConsole[key] = function () {};
}
}
return newConsole;
}());
}
The overhead of this approach should be negligible.
If this is a JavaScript library... then I'd expect as a 3rd party developer that I could download/use 2 versions. The production version (no debug, AND minimized). If I wanted to debug, I would point to the debug version of the library instead.
e.g.
<script src="foo-lib-min.js"></script>
<!-- swap to this for debugging <script src="foo-lib-full.js"></script>-->

Namespacing Hierarchy, Minify and Cross-Browser Compatibility

I've been using the following model for Namespacing my newest Scripts. So far, it has some distinct advantages that, while I'm sure could be replicated in other ways, really help to in my coding process. Unfortunately, I've come across a significant disadvantage... When using some JS compression utilities, they mangle the code badly enough that I must avoid many advantageous options. Luckily, the code I save with this model helps mitigate the "damages".
I'm still curious to know if there is a more viable solution as the min.js only fail consistently in Chrome/IE. I know the below is a little too abstract for some. Are there any experts that might point me in the right direction. I've used YUI, Packer and JSMin. JSMin works reliably, but is not nearly as efficient...
/* Global Namspace */
(function (T) {"use strict";
/* Top.Sub1 */
(function(S1) {
// ... Some methods (public/private)
/* Top.Sub1.Mini */
(function(M) {
// ... Some methods (public/private)
}(S1.Mini = S1.Mini || function(o){}));
}
(T.Sub1 = T.Sub1 || function(o){}));
/* Top.Sub2 */
(function(S2) {
// ... Some methods (public/private)
/* Top.Sub2.Mini1 */
(function(M1) {
// ... Some methods (public/private)
}(S2.Mini1 = S2.Mini1 || function(o) {}));
/* Top.Sub2.Mini2 */
(function(M2) {
// ... Some methods (public/private)
}(S2.Mini2 = S2.Mini2 || function(o) {}));
} (T.Sub2 = T.Sub2 || function(o) {}));
} (window.Namespace = window.Namespace || function(o){}));
UPDATE: The most common error I am faced with is "unexpected token" of various sorts.. sometimes a ')' and sometimes a '}'. Every once in a while, it is a '('. I haven't yet addressed gzip as I want this out of the way.
UPDATE 2: Have checked/removed ns with a Tidied-jsHint passing file and still does not minify correctly. It definitely has to do with this model... Does anyone have a clear answer as to why? If not, further recommendations are welcome. P.S. the Github has been updated with Tidied-jsHint passing code.
I'd say read this article about what needs to be done and what needs to be avoided for good minification – http://alistapart.com/article/javascript-minification-part-II
And then choose a proper modules framework like AMD or commonjs.
UPD. My main advice will be to use a linter on your code like http://jshint.com and adhere to a coding style like http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml It mostly comes with explanations of why something will break in certain sitautions. It's also going to make your code more approachable for open source contributors.
After testing nearly every option on every minifier I could get my hands on, the code minifies quite fine. (With and without the tidy, etc...) The issue comes when any of the minifiers try to replace or obfuscate symbols. In particular, it does not handle this for loop well:
for (i = 0; i < l; i++) {
_.points[i] = new S.Point(_, pts[i]);
}
Removal of the loop allows for the optimization to occur correctly.

Categories

Resources