Closure compiler removes more than what I want to remove - javascript

I've followed the advice from this other SO thread to remove console.log() statements from my code.
Unfortunately, now Closure compiler is removing my entire code and not just the console.log() statements.
Might someone explain this? I'm at a loss...
JS file 1
(function(){
/** #const */
LOG = false;
function log() {
return console.log.apply(console, arguments);
}
LOG && log('hello world !');
var foo='bar';
LOG && log("foo"+foo);
})();
JS file 2
(function(){
/** #const */
LOG = false;
function test(){
alert('testing...');
}
var baz='bazzy';
LOG && log("baz"+baz);
})();
Closure compiler step:
$ java -jar compiler-latest/compiler.jar --js j1.js j2.js --js_output_file compiled.js
Result:
(function(){LOG=!1})();(function(){LOG=!1})();

Because the compiler determines that the rest of your code is unreachable.
Both files have a constant LOG set to false and you are not exporting anything (goog.export* or window['...'] = ...).
The code which could get executed has a LOG && in front of it, which means it is not executed.
Therefor nothing can get executed, and thus the compiler removes it all.
Why is function test remoed: nobody calls it, simple as that. Call it in one of your files, and the compiler won't strip it away.
You can (should, actually) define LOG and log only in one file.
For that to work, remove the anonymous function call around your code in every file.
You can tell the compiler to add it back to the compiled code with the commandline option:
--output_wrapper=(function(){ %output% })();
So your two files should look like this:
JS file 1
/** #const */
var LOG = false;
function log() {
return console.log.apply(console, arguments);
}
LOG && log('hello world !');
var foo='bar';
LOG && log("foo"+foo);
JS file 2
function test(){
alert('testing...');
}
var baz='bazzy';
LOG && log("baz"+baz);
// call test, so it a) isnt stripped and b) well, executed :)
test();
Also, you might want to put your global vars and functions into a "namespace" to not pollute the global scope:
// create namespace (which is just a normal object)
var MyNamespace = {};
// create a namespaced function
MyNamespace.test = function() {
alert('test');
}
// call it
MyNamespace.test();

Related

Does Node run all the code inside required modules?

Are node modules run when they are required?
For example: You have a file foo.js that contains some code and some exports.
When I import the file by running the following code
var foo = require(./foo.js);
is all the code inside the file foo.js run and only exported after that?
Much like in a browser's <script>, as soon as you require a module the code is parsed and executed.
However, depending on how the module's code is structured, there may be no function calls.
For example:
// my-module-1.js
// This one only defines a function.
// Nothing happens until you call it.
function doSomething () {
// body
}
module.exports = doSomething;
// my-module-2.js
// This one will actually call the anonymous
// function as soon as you `require` it.
(function () {
// body
})();
Some examples..
'use strict';
var a = 2 * 4; //this is executed when require called
console.log('required'); //so is this..
function doSomething() {}; //this is just parsed
module.exports = doSomething; //this is placed on the exports, but still not executed..
Only in the sense that any other JS code is run when loaded.
e.g. a function definition in the main body of the module will be run and create a function, but that function won't be called until some other code actually calls it.
Before exporting the content that are visible outside of your module, if there is same code that can be execute it it execute but the content that are export like a class will be execute in the code that import it.
For example, if I have this code
console.log("foo.js")
module.exports = {
Person: function(){}
}
the console.log will be execute when you require it.

Debug code removal using Google Closure Compiler

If I run the following code through advanced optimization, I can still see the debug statements in the code.
var log = console.info.bind(console);
(function() {
/** #const */
var DEBUG = false;
log('Brady', createRank({
max: 100,
debug: DEBUG
}));
})();
function createRank(options) {
if (options.debug) {
log('This should be in debug mode only');
}
if(typeof alert == 'function'){
alert(options);
}
return (Math.random() * options.max) | 0;
}
output after Advanced mode compilation
(function() {
var a = console.info.bind(console),
b = {
max: 100,
debug: !1
};
b.debug && a("This should be in debug mode only");
"function" == typeof alert && alert(b);
a("Brady", Math.random() * b.max | 0);
})();
How can we get rid of debug message with advanced mode?
if the DEBUG variable is defined as global, and logging statements are enclosed like
if (DEBUG) {
log('debug message');
}
then it would work but is there a way to make it work if we don't not want it as a global variable, and rather pass the value around to individual modules/functions via parameters.
This is a limitation of the current set of optimizations and when they are run. The optimizations are a trade off between compilation time and optimization and the choices made are not necessarily ideal for every code pattern.
In this particular case, the issue is that "property collapsing" only happens once for global scope, and that is before function inlining occurs ("property collapsing" for objects local to a function occurs during the main optimization loop). For the code in your example to be removed, "collapse properties" would need to run at least once more, or the function local version (which is more conservative) would need to be enhanced to run in global scope.
This is also discussed here: https://github.com/google/closure-compiler/issues/891

How do you add verbose logging code to functions without affecting performance?

Performance is important for a certain class I'm writing.
I thought about calling a function like so:
debug('This is a debug message, only visible when debugging is on');
And the contents would be like
function debug(message) {
if (DEBUG) console.log(message);
}
So I wonder: is this enough for V8 to flag this as "dead code" if the DEBUG variable never changes?
Edit: I'm more worried about the performance in Node than on the browser, so removing the code while minifying would be insufficient.
Edit2: I made a JSPerf benchmark out of the proposed solutions, and they are very surprising: http://jsperf.com/verbose-debug-loggin-conditionals-functions-and-no-ops/3
I use comments that when a file is minified get removed, such as:
function name(arg) {
// <debug>
if (!arg) {
throw new Error("arg must be defined");
}
// </debug>
... code goes here
}
For example: https://github.com/PANmedia/raptor-editor/blob/master/src/raptor-widget.js#L29-L33
An my (custom) build script to do the aforementioned https://github.com/PANmedia/raptor-build/blob/master/build/raptor-builder.js#L305-L313
There's a couple of solutions available(aside Petah's...):
Use UglifyJS2 conditional compilation:
You can use the --define (-d) switch in order to declare global
variables that UglifyJS will assume to be constants (unless defined in
scope). For example if you pass --define DEBUG=false then, coupled
with dead code removal UglifyJS will discard the following from the
output:
if (DEBUG) {
console.log("debug stuff");
}
UglifyJS will warn about the condition being always false and about
dropping unreachable code; for now there is no option to turn off only
this specific warning, you can pass warnings=false to turn off all
warnings.
Another way of doing that is to declare your globals as constants in a
separate file and include it into the build. For example you can have
a build/defines.js file with the following:
const DEBUG = false;
const PRODUCTION = true;
// etc.
and build your code like this:
uglifyjs build/defines.js js/foo.js js/bar.js... -c UglifyJS will
notice the constants and, since they cannot be altered, it will
evaluate references to them to the value itself and drop unreachable
code as usual. The possible downside of this approach is that the
build will contain the const declarations.
Use a wrapper function.
For example you have this method:
exports.complicatedMethod = function (arg1, arg2, arg3) {
stuff...
};
You add logging to it by wrapping it in a logger function:
function logger(fn) {
if (!DEBUG) {
return fn;
}
return function () {
console.log(fn.name, arguments); // You can also use `fn.toString()` to get the argument names.
fn.apply(this, arguments);
};
}
exports.complicatedMethod = logger(function (arg1, arg2, arg3) {
stuff...
});
This way the only performance hit would be at startup time. You can also use AOP method with the above wrapper function:
exports.complicatedMethod = function (arg1, arg2, arg3) {
stuff...
};
if (DEBUG) {
for (var name in exports) {
exports[name] = logger(exports[name]);
}
}
And you can pass information to the logger by adding properties to the function:
exports.complicatedMethod.description = 'This function only shows how tired i was when I was writing it and nothing else!';
You can have a look at this question where someone created code that creates a logger for functions in an object recursively. Also check this answer of mine.
Use C Pre Processor.
You can just do something like this:
#if DEBUG
console.log("trace message");
#endif
or something like this
#if DEBUG
#define DEBUG_LOG(x) console.log(x);
#else
#define DEBUG_LOG(x) //console.log(x);
#endif
Then you can do this in your code
DEBUG_LOG('put a random message her to confuse sys admins!')
Or you use it's npm warapper: laudanumscript
Create a sweetjs macro.
I haven't been able to find conditional compilation with sweetjs, but I'm sure it wouldn't be too hard to implement it. The end syntax would be(or should be!) similar to cpp.
You can use a logger library that supports:
Logging level
Late binding functions
Example: https://github.com/duongchienthang/js-logger

Removing debug code from inside a function using Closure Compiler simple optimisations

I'm looking for a way to strip out debug code from functions so I can add test hooks to closures. I've read
Google Closure Compiler advanced: remove code blocks at compile time and tested out removing debug code with the following:
/** #define {boolean} */
var DEBUG = true;
if (DEBUG) {
console.log('remove me');
}
Simple optimisation with --define='DEBUG=false' reduces this to var DEBUG=!1;. The same applies for this:
/** #const */
var DEBUG = false;
if (DEBUG) {
console.log('remove me');
}
Where I run into trouble is using this convention inside a function:
/** #const */
var DEBUG = false;
function logMe() {
if (DEBUG) {
console.log('remove me');
}
}
This reduces to the following:
var DEBUG=!1;function logMe(){DEBUG&&console.log("remove me")};
I would expect it to reduce further to:
var DEBUG=!1;function logMe(){};
Is there a reason this is not working as expected? I'm really just looking for a clean way to strip debug code and am not ready to take the plunge into advanced optimizations.
Update
Per #John's answer, I implemented my own compiler and have found that the following configuration will remove if (DEBUG) {} from inside and outside the code for the case of a #define:
CompilerOptions options = new CompilerOptions();
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
//options.setInlineConstantVars(true);
options.setInlineVariables(CompilerOptions.Reach.ALL);
options.setDefineToBooleanLiteral("DEBUG", false);
This works well enough for a single file with the following limitations:
This requires var DEBUG to be defined in each file, which is bad practice.
When combining multiple files, you can only have a single var DEBUG or the compiler can't optimize around it. This could be avoided by compiling each file individually and merging them.
Because the value is defined at the beginning of the file, there's no flexibility to receive the value beforehand.
I've toyed with the idea of removing all var DEBUG definitions from the files and injecting it into the source or extern before execution, but I've run into two issues:
Defining it in extern appears to do nothing.
Undefined DEBUG in the uncompiled code throws a reference error in the browser.
The ideal option would be to test window.DEBUG, which does not throw a reference error. Unfortunately, while injecting /** #const */ var window = {}; /** #const */ window.DEBUG = false; works at the top level, reducing if (window.DEBUG) {}, the optimization is actually reverted if placed in a function.
Unless another compiler option works the only option that would really make sense is to go with window.DEBUG and before compilation inject /** #const */ var DEBUG = false; and to a global replace of /\bwindow.DEBUG\b/ with DEBUG. Is there a better way?
Use #define annotation:
#define {boolean}
DEBUG = true;
And compile with option
--define="DEBUG=false"
A custom build of the compiler would allow you to do this. You basically want to "inline constant variables":
options.setInlineConstantVars(true);
You could add it here, in applySafeCompilationOptions:
http://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/javascript/jscomp/CompilationLevel.java?r=706
Or you could use the Java API and add the option (without modifying the compiler's code). Michael Bolin given an example of how to do this here:
http://blog.bolinfest.com/2009/11/calling-closure-compiler-from-java.html
This is an old answer, but I found a way that's not mentioned here.
(function(){
var DEBUG = true;
if (DEBUG) {
if (something === "wrong") {
console.warn("Stop! Hammer time!");
}
else if (something === "as expected") {
console.log("All good :-)");
}
}
foo();
})();
With ADVANCED_OPTIMIZATIONS this compiles to this:
"wrong" === something ?
console.warn("Stop! Hammer time!") :
"as expected" === something && console.log("All good :-)");
foo();
In our build script we can rewrite the DEBUG line to set it to false, which would then yield this output.
foo();
The reason this happens is Closure will remove unreachable code. By creating the closure, and defining a local variable, Closure can see that we can't do something like window.DEBUG === true, so the code is guaranteed to never be run.
Your DEBUG variable is currently global. GCC will not remove or rename global variables in simple optimization mode, so they'll remain available to any code in other scripts that might possibly want to access them. Try enclosing your code into anonymous function.
The way i solved the problem of "removing debug functions from closure compiled javascript using SIMPLE_OPTIMIZATION" was by combining a similar method as #John proposes as well as using some of #Brian Nichols update. I could only get the compiler to remove the lines by placing this is the global scope of my main js file and doing a custom compile (using multiple .js files this still removed them)
/** #const
* #type {boolean}
*/
var DEBUG = false;
//and used this format for my debug function
DEBUG && myLog('foo');
and then compiling the closure-compiler java with ant to include this option options.setInlineVariables(CompilerOptions.Reach.ALL);
under the applySafeCompilationOptions function in the CompilationLevel.java file as #john suggests. This worked for me and didnt break my codebase as ADVANCED did...
Remove var DEBUG = true; from your code and convert all your conditions that check if (DEBUG) to if (goog.DEBUG). Modify your compiler option to read --define goog.DEBUG=false. The goog variable is built into the Closure Library API to provide options and flags for the compiler.

Turn off console.log inside the scope of a function

I am writing code in modules, and each module can be debugged individually. I want to turn on or off console.log messages on a module level, to focus on just the module I'm working on.
I could have a _debug_ variable for each module set to true or false, and write log messages like
if(_debug_) {
console.log('The is a debug message.');
}
I find this approach a little cumbersome. Can I do better?
You can hijack the console object upon entering the module scope if you want to disable debug statements. Here's a simple way to generate a substitute "console" object:
function noopConsole()
{
var konsol = {};
function noop(){}
for (var k in window.console)
{
konsol[k] = noop;
}
return konsol;
}
Then, at the beginning of the module:
var console = _debug_ ? window.console || noopConsole();
That's it.
You can redefine console in modules in which you want to deactivate logging.
Module without logging:
var console = {log: function(){}}
This assumes that each module is in its own scope.

Categories

Resources