JS function names compression - javascript

I can't seem to find a JS minifier that performs function names compression. For example, given this pseudo-code:
//non pure function
function test() {
//do some computations...
return <whatever>;
}
alert(test());
Would then, for example, be minified to this:
function t(){//do some computations... return <whatever>}alert(t());
So far I have not found any JS tool that does this sort of compression. Does anyone know any?

When you use UglifyJS it will not mangle top-level names, or names available to the global scope.
You can still achieve this if you turn on the -mt or --mangle-toplevel flag in the command line like so:
Here is some code I posted in the demo link you provided:
function longfunctionname(a) {
return function reallylongfunctionname(b) {
return a + b;
};
}
Here is the mangled code without top level mangling turned on:
function longfunctionname(n){return function u(n){return n}}
As you can see the top level function is not mangled, but the inner function is
go here to read more: http://lisperator.net/uglifyjs/

Definitely! You can use a task runner (i.e. Grunt or Gulp) to assist, but the process you are describing is normally defined as "uglifying" or "mangling" JS. Check out gulp-uglify or grunt-contrib-uglify to get started.
EDIT: Per comments on this answer, using a task runner is not necessary, as you can use UglifyJS on its own.

Its good habit to write code that doesn't pollute the global space. By wrapping in an anonymous function the code will minify and will not collide with other global references.
(function (){
function test() {
//do some computations...
return "something";
}
alert(test());
})();
If you run UglifyJs with --mangle the output will look like this
!function(){function t(){return"something"}alert(t())}();
I used https://skalman.github.io/UglifyJS-online/ to get generated UglifyJs code

Related

How to migrate legacy JS app to modules

I have a large (~15k LoC) JS app (namely a NetSuite app) written in old-style all-global way. App consists of 26 files and dependencies between them are totally unclear.
The goal is to gracefully refactor the app to smaller modules. By gracefully I mean not breaking\locking the app for long time, but doing refactoring in smaller chunks, while after completing each chunk app remains usable.
An idea I have here is to concat all the JS files we have now into single-file bundle. After that some code could be extracted into modules. And the legacy code could start importing it. The modules & imports should be transpiled with webpack\whatever, while legacy code remains all-globals style. Finally all this is packed into single JS file and deployed.
My questions are
is there a better approach maybe? This sounds like a typical problem
are there any tools available to support my approach?
I gave webpack a try and I haven't managed to get what I want out of it. The export-loader and resolve-loader are no options because of amount of methods\vars that needs to be imported\exported.
Examples
Now code looks like
function someGlobalFunction() {
...
}
var myVar = 'something';
// and other 15k lines in 26 files like this
What I would ideally like to achieve is
function define(...) { /* function to define a module */ }
function require(moduleName) { /* function to import a module */ }
// block with my refactored out module definitions
define('module1', function () {
// extracted modularised code goes here
});
define('module2', function () {
// extracted modularised code goes here
});
// further down goes legacy code, which can import new modules
var myModule = require('myNewModule');
function myGlobalLegacyFunction() {
// use myModule
}
I'm following an approach similar to that outlined here: https://zirho.github.io/2016/08/13/webpack-to-legacy/
In brief:
Assuming that you can configure webpack to turn something like
export function myFunction(){...}
into a file bundle.js that a browser understands. In webpack's entry point, you can import everything from your module, and assign it to the window object:
// using namespace import to get all exported things from the file
import * as Utils from './utils'
// injecting every function exported from utils.js into global scope(window)
Object.assign(window, Utils).
Then, in your html, make sure to include the webpack output before the existing code:
<script type="text/javascript" src="bundle.js"></script>
<script type="text/javascript" src="legacy.js"></script>
Your IDE should be able to help identify clients of a method as you bring them into a module. As you move a function from legacy.js to myNiceModule.js, check to see if it still has clients that are aware of it globally - if it doesn't, then it doesn't need to be globally available.
No good answer here so far, and it would be great if the person asking the question would come back. I will pose a challenging answer saying that it cannot be done.
All module techniques end up breaking the sequential nature of execution of scripts in the document header.
All dynamically added scripts are loaded in parallel and they do not wait for one another. Since you can be certain that almost all such horrible legacy javascript code is dependent on the sequential execution, where the second script can depend on the first previous one, as soon as you load those dynamically, it can break.
If you use some module approach (either ES7 2018 modules or require.js or you own) you need to execute the code that depends on the loading having occurred in a call-back or Promise/then function block. This destroys the implicit global context, so all these spaghetti coils of global functions and var's we find in legacy javascript code files will not be defined in the global scope any more.
I have determined that only two tricks could allow a smooth transition:
Either some way to pause continuation of a script block until the import Promise is resolved.
const promise = require("dep1.js", "dep2.js", "dep3.js");
await promise;
// legacy stuff follows
or some way to revert the scope of a block inside a function explicitly into the global scope.
with(window) {
function foo() { return 123; }
var bar = 543;
}
But neither wish was granted by the javascript fairy.
In fact, I read that even the await keyword essentially just packs the rest of the statements into function to call when promise is resolved:
async function() {
... aaa makes promise ...
await promise;
... bbb ...
}
is just, I suppose, no different from
async function() {
... aaa makes promise ...
promise.then(r => {
... bbb ...
});
}
So this means, the only way to fix this is by putting legacy javascript statically in the head/script elements, and slowly moving things into modules, but continue to load them statically.
I am tinkering with my own module style:
(function(scope = {}) {
var v1 = ...;
function fn1() { ... }
var v2 = ...;
function fn2() { ... }
return ['v1', 'fn1', 'v2', 'fn2']
.reduce((r, n) => {
r[n] = eval(n);
return r;
}, scope);
})(window)
by calling this "module" function with the window object, the exported items would be put on there just as legacy code would do.
I gleaned a lot of this by using knockout.js and working with the source readable file that has everything together but in such module function calls, ultimately all features are on the "ko" object.
I hate using frameworks and "compilation" so generating the sequence of HTML tags to load them in the correct order by the topologically sorted dependency tree, while I could write myself such a thing quickly, I won't do this, because I do not want to have any "compilation" step, not even my own.
UPDATE: https://stackoverflow.com/a/33670019/7666635 gives the idea that we can just Object.assign(window, module) which is somewhat similar to my trick passing the window object into the "module" function.

How can you capture the result of a HTML script tag in a variable?

I have been reading about AMD and implementations like requirejs. Most of the resources covers the usage and API's.
But, when it comes to implementing this, how can you load a JavaScript file into a variable just like that? For example, you can see functions like this:
define(['jquery'], function($){
});
var jquery = require('./jquery');
From an API consumer's perspective, all I can understand is that the file jquery.jshas magically become $, jQuery etc? How is that achieved?
Any examples would be helpful.
How do AMD loaders work under the hood? is a helpful read.
Edit: I think the eval answers below are nice because it actually is an eval problem in some ways. But I would like to know this from the perspective of an AMD specs implementation.
You don't load a javascript file into a variable, this is instead done by things such as browserify or webpack. Javascript itself can do this, but these modules generate a single file containing all your code. By calling require("file"), you are calling browserify's function to load a file named "file" stored in the code.
An example of this can be if you have a module
function demoModule(){
console.log("It works!");
}
module.exports = demoModule
This makes module.exports now contain the "entire" contents of the file
Browserify/webpack puts that into a function that returns the module.exports of that file
function require(filename) {
switch(filename){
case "demofile":
let module = {exports:{}}; ((module) => {
function demoModule(){
console.log("It works!");
}
module.exports = demoModule
})(module)
return module.exports;
}
};
require("demofile")();
Your file becomes a function that you can call with require("demofile") and it returns anything that was a module.export.
You know how you can say eval(alert("hello!")) and it executes the code?
You can also:
var str = "hello!"
eval('alert("' + str + '");')
So the next step is to have a file that has your actual script in it:
var str = "hello"
alert(str)
then you can use a standard AJAX request to fetch that file into a variable, and you can eval() that variable.
Technically, eval() is considered evil - fraught with dangers - but there are other solutions (for example, injecting a script tag into the document body). I just went with eval() here to make the explanation easier.
Extending what theGleep said.
Something like this:
var str = "if (x == 5) {console.log('z is 42'); z = 42;} else z = 0;";
console.log('z is ', eval(str));
For more read here.
But use eval() very cautiously and be absolutely sure about the pitfalls and drawbacks of eval().
Don't use it unless it is the only option.
Read this answer too.
The way define works under the hood is not by loading an "HTML Script into a variable". Typically a normal script can have multiple variables so it doesn't make sense to capture the value a variable! The eval approaches can probably do something like that if needed. It captures all that is there in the JavaScript source code.
The real key in AMD is that the define API maps a name to a function. Whenever you require with that name, it would just call that function and return the value to you. This return value is expected to be the module. This is a convention that makes everything work!
In other words, AMD uses an interesting convention (design pattern) to ensure that we get a modularization effect in JavaScript code. It is one level of indirection. Instead of the normal style of "write your code and get it executed in the global scope" paradigm, you just write a function that returns a single value that captures all the things you want to expose as a module to the consumer!

The way to use custom method everywhere without requiring the module everywhere

I am using Node.js. I defined a custom method to the String obj like this:
if (!String.prototype.myMethod) {
String.prototype.myMethod= function () {
//do something
return this;
};
}
I found that myMethod maybe used in many different files, so that I have to require the file where this piece of code in. Is there any way that does the many 'requires' ?
Don't do that.
Node is intentionally designed in a module pattern, where each module gets it's own scope to run in and without polluting the global variables. This very intentional and very important.
https://nodejs.org/api/modules.html#modules_the_module_wrapper

goog.inherits present in the output file

I'm trying to use Closure Compiler and Closure Library.
When I use the library everything is ok, I'm including "base.js" in my simulation and it works with all my javascript files.
The problem is present when I "compilate" my application: In the output file I've got a reference to a closure library'sfunction "goog.inherits".
From what I've read, it's not necessary to include "base.js" in production. I'm working on a library, so I don't want to force users to have a reference to the Closure Library.
How can I do?
Here is my code:
NM.ObjectEvent = function( type )
{
goog.base(this);
}
goog.inherits( NM.ObjectEvent, NM.Event );
And the script look like that:
java -jar compiler.jar --compilation_level SIMPLE_OPTIMIZATIONS --js_output_file myLib.js `find ../src/ -name '*.js'`
What you have heard does not apply to SIMPLE_OPTIMIZATIONS. With ADVANCED_OPTIMIZATIONS everything unused in base.js is removed, with SIMPLE_OPTIMIZATIONS only function local optimizations are performed and unused methods are not removed.
Regardless of the mode, if you use goog.inherits it will remain in some form. Something needs to do the work that goog.inherits does to setup the prototype chain.
Like John said, if you have references to goog.base and goog.inherits, you're referencing the library. Fortunately, you can emulate those functions... Something like this should work...
NM.ObjectEvent = function( type )
{
NM.Event.call(this, type);
}
(function(){
var temp = function(){};
temp.prototype = NM.Event.prototype;
NM.ObjectEvent.prototype = new temp();
}();
If you're using goog.base elsewhere (for example, to call superclass methods), then you'll need to do more work, but the above should suffice if you're only using base and inherits where shown in your original post.

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

Categories

Resources