Uglify JS - remove only console.log - javascript

I have a question regarding the Uglify JS module, in the usecase of webpack plugin.
My codebase have several console.log statements around the code for debugging purposes localy, it will only be printede out in development mode, but they are still taking up space in the codebase and i dont what them in the production build but, i would like to keep my console.error and console.warn still.
I know the Uglify JS have key drop_console flag but that kills every console output there is and i still want the warnings and errors in my console.
The reason for this question is i have a custom error handler than ships errors to Sentry, and I want to read the error in the console when it happens, so i use the error and warning console out put.

From docs:
drop_console (default: false) -- Pass true to discard calls to console.* functions. If you wish to drop a specific function call such as console.info and/or retain side effects from function arguments after dropping the function call then use pure_funcs instead.
...
pure_funcs (default: null) -- [...] You can pass pure_funcs: [ 'Math.floor' ] to let it know that this function won't produce any side effect, in which case the whole statement would get discarded.
So the option you're looking for is pure_funcs: [ 'console.log' ]

Related

Chrome automatically formats Error stacks, how does this work?

The content of (new Error('foo')).stack looks something like this:
Error: foo
at Object.notifier (http://localhost:6969/js/main.js:12705:37)
at trackHookChanges (http://localhost:6969/js/main.js:1813:27)
at UseState (http://localhost:6969/js/main.js:1982:13)
at K._.data (http://localhost:6969/js/main.js:70174:6005)
at K (http://localhost:6969/js/main.js:70174:6380)
at Z (http://localhost:6969/js/main.js:70174:9187)
However, when I console.log it, it looks like:
Error: foo
at Object.notifier (wdyr.ts:10)
at trackHookChanges (whyDidYouRender.js:1306)
at UseState (whyDidYouRender.js:1475)
at K._.data (index.esm.js:1)
at K (index.esm.js:1)
at Z (index.esm.js:1)
Is Chrome devtools is using sourcemaps to automatically change the string being logged? Is it there an easy way to access the source file names in my code? I want to ignore errors originating from certain NPM module.
Unfortunately (but luckly for devs) yes, Chrome uses sourcemaps to format errors in the console and there is (still) no way to access the same function it uses or output it produces. Even if it was possible, it would work only on a specific browser/platform.
TLDR
Emulate the browser sourcemap resolution with StackTraceJS or filter your errors by their prototype or any of their properties (like Error.message for example)
Discussion
JS Errors stacktrace are a mess, and so unreliable:
they are very dependant on the running environment, if you run code on Chrome you could end up with a different stack with respect of running it on Firefox, IE or node (even if in the latest times they are reaching to a "stacktrace agreement" between environments).
The maximum length of the error stacktrace is (almost always) 10 lines, so if your function (hook) was called before that time you will never catch it.
internal or delayed callbacks can erase/change/augment the stacktrace of a function in certain environments (it can be very hard, sometimes impossible, to catch the full stacktrace of a callback called inside a setTimeout for example)
Partial solution (StackTraceJS)
If you can afford to make a http request for a sourcemap, you can exploit the same mechanism that Chrome (or any other browser) uses to parse and map the error stacktrace files into the original files and filter those you don't like. The downside of this operation is that your code must be completely reworked with promise-like chain (because of the http request).
Luckly there is already a library which makes this process much much easier: StackTraceJS, you can give it a try.
This would be its usage (from library docs):
var error = new Error('BOOM!');
StackTrace.fromError(error).then(callback).catch(errback)
/*
==> Promise([
{functionName: 'fn', fileName: 'file.js', lineNumber: 32, columnNumber: 1},
{functionName: 'fn2', fileName: 'file.js', lineNumber: 543, columnNumber: 32},
{functionName: 'fn3', fileName: 'file.js', lineNumber: 8, columnNumber: 1}
], Error)
*/
Side Note
As you stated in the question comment, you are using React, and the usual working pipeline is using wepack or other js bundler to output a single JS file from all the dependencies. During developing you could encounter no problems to find out the file from the errors stack, but in production you could omit sourcemap informations from the bundle or either have some internal/uglified filenames which are not linked with the original file. This means that the behaviour of your code could change between dev/prod configuration depending on your building pipeline.
Theoretical solution
The (proto-OOP) theory states to use prototype to discriminate between Error types in order to filter unwanted behaviours.
So first of all you should use a custom class to define the errors thrown by your application/library (see Custom Error - MDN section). By doing so you must throw or extend only your CustomError(s) in your code.
Secondly you should filter the error by its type/properties and not by its source file, so (if you can) check what classes of Error the 3rd party function can throw.
In this way it's easy to isolate only those 3rd party errors, you can do just a simple check of inheritance within the try/catch block:
try { /* dangerous code */ }
catch (ex) {
if (ex instanceof MyError) { /* handle your errors */ }
else if (ex instanceof The3rdPartyCustomError) { /* handle specific 3rd party CustomError */ }
else if (ex.__proto__ instanceof Error) { /* handle generic 3rd party CustomErrors */ }
else { /* handle native errors (or bad practice 3rd party errors) */ }
}
But all of this theory can be difficult to implement, especially because 3rd party libraries very rarely implement their CustomError classes, so you will end up to handle only native errors and your defined classes.
Give it a try and check what kind of errors can throw your 3rd parties libs.
Maybe the simpler solution is to filter the erros by Error.message or any other properties which could work better than expected in your domain case.

How to fix 'thrown exception caught locally' when using instanceof to determine internal errors type

I have a block of code that loads a set of plugins. For each plugin, a list of functions is called inside of a try catch block to determine if the plugin has failed or not and act accordingly. At the same time, one of the multiple functions of the plugin can return a specific error that mean that the plugin has not failed but is not valid to execute the next functions with the data.
Let's see an example (the code is Typescript, but I am going to make it as language agnostic as possible):
for each plugin:
try:
plugin.function1(data)
plugin.function2(data)
plugin.function3(data)
try:
plugin.function4(data)
catch error:
if error instanceof PluginNotValidForThisKindOfDataError:
continue
else:
throw error
plugin.function5(data)
plugin.function6(data)
catch error:
log(plugin has failed)
(I hope the code is clear enough. I'll update it if required)
As can be seen, I execute function4 and I parse the possible errors because one of them (there are multiple) is "tolerable" and just means that it is not valid for function5 and function6 with that specific set of data. However, I still have to throw the other errors because they are not good. At the end, I catch the global set of errors to determine if the plugin has crashed or not.
What I get in my IDE, JetBrains (WebStorm specifically) is a thrown exception caught locally warning. But I am not able to reimagine how to redo that block to act differently. I am not using throws as flow control but just passing the error.
I admit that the main problem is that in Javascript I can not do catch PluginNotValidForThisKindOfDataError, which would be the optimal situation (hope its added some day). But, with the tools I have, how can I refactor this?
Thank you very much.
Added to both language-agnostic and javascript because of the specific Javascript try-catch method
I see three options:
Ideally, the plugin wouldn't throw an error for a non-error condition like that. Instead, have it return a value that tells the code in your question whether it should run functions 5 and 6.
If you want to keep it the way it is, though, you can either:
Ignore the warning (and probably there's a way to disable it for one line), or
Don't re-throw; instead, do the same thing you're doing in the outer catch (log(plugin has failed)) followed by continue. Provided that's just a single function call or similar, the duplication isn't ideal, but isn't horrible either. If there's any complexity to that step, isolate it into a function (perhaps even a local function) and call that from both places.

Intern - window is undefined

I have a loop of tests running in intern-geezer, with about twenty out of a hundred very similar tests running successfully. Then suddenly:
FATAL ERROR
ReferenceError: window is not defined
and the loop stops. There are no explicit calls to window or document in my code. It's pure JS. I'm using intern-geezer, 2.2.2. The line numbers referenced in the error stack make absolutely no sense. They're way off.
I've read the suggestion to switch from the command:
./node_modules/.bin/intern-client config=tests/intern
to:
./node_modules/.bin/intern-runner config=tests/intern
but I don't want to connect to a server or open a browser (there's a separate, strange loading error occurring there which seems specific to geezer). I want to get this going at the command line. Grateful for any help, I'm totally new to Intern.
window is a browser object, so it's not going to be available in Node. If you want to run tests exclusively in the node client (intern-client), you'll need to make sure you're not using (and none of your dependencies are using) any code that references browser objects like document, window, navigator, etc.
If the stack trace line numbers are off, it's probably the code coverage instrumentation. You can disable that by setting the excludeInstrumentation property in your Intern config to /./.
You probably set 'Html' as reporter in your intern configuration file :
intern.reporters = [
{ id: 'Html' },
{ id: 'JUnit', filename: 'test-reports/quality/functional/junit.xml' },
...,
];

Node.js console.log vs console.info

What is the benefit of using console.log vs console.info?
Or any of the other console commands for that matter?
console.info("info");
console.error("error");
console.warn("warn");
vs
console.log("log");
I thought it might change the color of the output or concatenate some sort of label, but they seem to all do the same thing. And according to the documentation here:
https://nodejs.org/api/console.html#console_console_info_data
they seem to all do the same as console.log
According to the documentation that you linked to, console.error and console.warn outputs to stderr. The others output to stdout.
If you are doing piping or redirection from node.js the difference is important.
There is a lot of JavaScript written to run in both the browser and Node.js. Having node implement the full console allows for greater code cross-compatibility.
In most browsers, not only do these log in different colors, but you can also filter to see specific messages.
console.info("info");
console.error("error");
console.warn("warn");
console.log("log");
While console.log and console.info might seem the same, with only mere coloring differences (in most browsers), you can take advantage of having these differently named functions.
For example, you can configure a linter like eslint to produce a warning message whenever console.log is used but no warnings for console.info. Now you can use console.log for temporary development/debug logging and console.info for information that end users might need. The linter warnings will remind or even force you to removed the temporary console.log calls before commits code or publishing release builds.
console.log() is shorter than console.info()
They're the same thing, and that's the only advantage.
According to the docs it's pretty clear.
console.info([data], [...])#
Same as console.log.
console.error([data], [...])#
Same as console.log but prints to stderr.
console.warn([data], [...])#
Same as console.error.
This means there is no benefit or downside. info == log, and warn == error. Unless you want to print to stderr, info and or log will work.
Visually, No difference actually among console.log , console.info , console.warn , as well as console.error regarding the server side(terminal).
However, there are lightweight modules that add blue, orange and red colors for console.info , console.warn , as well as console.error respectively . By that, console API behaves like client-side.
npm i console-info console-warn console-error --save-dev;
One more detail in addition to the accepted answer: In Chrome and FireFox, console.info log lines are prefixed with a little i icon while console.log lines are not. warn and error lines are prefixed with a little triangle and x, respectively.
stdin
A readable stream for reading input from the user.
stdout
A writable stream, either synchronously or asynchronously.
stderr
A blocking synchronous writable stream intended for error messages.
The stdout or non-blocking functions are: console.log, console.info, util.puts, util.print and Stderr.
The blocking functons are: console.warn, console.error, util.debug and process.stdin (a readable stream for getting user input).
It has been established that log and info are basically the same thing, but I'm not sure that completely answers the question:
What is the benefit of using console.log vs console.info?
One benefit, apart from what has already been mentioned, is you can use each for a different purpose. For example, you might use console.log just for quick debugging and spitting things out to the console, while you might use console.info for permanent messages you want to output to the console in your code, such as information on the current app status. Then when you have a situation where a random object is printed in your console and you realize you've left a log statement in there somewhere by accident, you can do a global search for 'console.log' and delete every instance and have confidence you didn't delete anything important that you meant to leave in there.
I have seen where console.log is for temporarily logging of state information for debugging.
console.info is a more permanent thing - such as saying what port something is running on, and something you wouldn't cut out after you are done debugging.
This makes it easy to clean up your code for committing it. You can even have your linter have a rule to prevent console.log from being committed.
There is a difference for React devs. It comes from an issue in the react devtool extension and at least affects Create-React-App users, not sure if it's all web pack.
Issue is mentioned here:
react devtools console.log() from react_devtools_backend.js:4049
but the jist is:
console.log will always report its source as
react_devtools_backend.js:4049
wheres console.info will have the actual filename and line number you're logging from.
The different logging levels let you manage the noise level in your console: In both the Firefox (I'm using 78 right now) and Chrome (84) devtools, the js console lets you choose what "debug level" of output you want to see. FF lets you toggle the visibility of console.error, .warn, .log, .info, and .debug messages by clicking individual buttons for each (that show you how many were suppressed, when "off"), whereas Chrome has a dropdown with checkmarks next to the items (.info and .log are controlled by the "Info" one, and .debug by "Verbose"). The Chrome dropdown label ("All levels" or whatever you set) turns red if output was suppressed.

Difference between console.log enabled verification

I have seen jquery-ui-1.8.11.js verify console.log functionality by doing
if (this.debug)
console.log()
I have also seen people define an anonymous function that is a no-op for browsers with no console logging like IE7.
if(typeof console === "undefined") {
console = { log: function() { } };
}
Is there a technical difference or are both functionally equivalent?
In the first example you've given, this.debug will be a reference to a debug variable in the jQueryUI code. This debug variable will have been set elsewhere, possibly by checking whether console is defined, but also possibly with other settings.
In any case, the first example is specific to the application; if you want to do a generic check to see whether the console is available, you'll need to use the second technique. You don't have to define an empty console object as per the example, but doing it that way does mean that you won't have to do the if() condition every time you want to call console.log().
Having said all of that, I would counsel you strongly to avoid putting any code into production which contains calls to the console. The console should only be used for debugging purposes while you are working on the code. It should not be necessary in final release, and doing so can be a sign that either your code is unstable or you're unconfident of it, neither of which is a good sign if you're releasing the code for live use.
(libraries such as jQueryUI are an exception to this rule, as they need to provide functionality for developers to do debugging while writing code using their library)
Both of those do something else. The first code suppresses logging messages unless a flag is set. The people who develop jQuery UI need logging when working on it, and turn it on. But people using the library don't want to have their console cluttered by log messages from libraries, so it's off by default. It lets you turn off logging even when the browser supports it – that regular users on IE7 don't get script errors is a (possibly intended) side effect.
The second code defines a dummy console.log(), so you can call the method without checking if it exists everywhere. It will not suppress logging on browsers where it's supported.
The second of the two is standalone, not relying on jQuery. In my opinion, that makes it better.

Categories

Resources