In my project, i use an external library that it has some private functions on its local scope, it seems like this:
(function(window, undefined) {
var isArray = function() {...}
var forEach = function() {...}
var int = function() {...}
{(this))
The external lib gives me some funcionality, but additionally i will use these functions i mentioned in my project, so, I need to put externalLib private functions on window scope. To avoid that, i'm going to build myLib.js with my code and the externalLib code.
So, i need to put some code from externalLib.js (basically i only need to remove first and last line from the code).
What do you think about the best form to accomplish this task in GruntJS? I hope I explained well
You can preprocess files during grunt.js build using grunt-preprocess module. This requires some additional directives in your code:
// #ifdef DEBUG
(function(window, undefined) {
// #endif
var isArray = function() {...}
var forEach = function() {...}
var int = function() {...}
// #ifdef DEBUG
{(this))
// #endif
Where DEBUG can be any environment variable added either via command line switch or configuration file.
Also you can use #exclude directive that will simply remove code from grunt.js processed files. More on this here - https://github.com/onehealth/preprocess#directive-syntax
Related
I'm studying module pattern and have a question.
I want to use 'settings' anywhere.
script1.js
var MODULE = (function() {
var t = {};
this.settings = { // this == Window
users: ['john', 'emma']
}
return t;
})()
script2.js
MODULE.dom = function() {
var t = this; // this == MODULE
document.querySelector('p').textContent = t.settings.users[0]; // error
function _say() {
return t.settings.users[1] // error
}
return {
say: _say
}
}
MODULE.DOM = MODULE.dom.call(MODULE)
When use this.settings = {...}, 'this' means Window so code doesn't work.
When use t.settings = {...}, 'this' means MODULE so code works but when write MODULE in dev console, settings is exposed in MODULE variable. Is it ok?
I'd greatly appreciate any help, thank you!
When use t.settings = {...}, 'this' means MODULE so code works
That's the right way to do it.
but when write MODULE in dev console, settings is exposed in MODULE variable. Is it ok?
It's mostly OK.
If you're worried about the client being able to type in the variable name and see the code that gets run - there's no way to avoid that. They can just look at the devtools to see what the network requests are, and what is downloaded - or look at the Sources panel.
If you're worried about running into naming collisions with larger scripts, then - sometimes, libraries deliberately assign themselves to the window to allow other parts of the code to access them. Perhaps you'd like your MODULE to be like this. If not, then you should utilize JavaScript modules instead, which allow for scripts to be imported inside other scripts without polluting the global namespace at all or having any possibility of naming collisions. For example, you could do
// script1.js
export const MODULE = {
settings: {
users: ['john', 'emma'];
}
};
// script2.js
import { MODULE } from './script1.js';
// proceed to use MODULE
And you can do import { MODULE } from './script1.js'; from any script file, not just script2.js.
Personally, I consider the IIFE module pattern in JavaScript to be mostly obsolete nowadays. For any reasonable-sized script, better to write code in separate files and then import and export as needed. (A 1000 line file is somewhat hard to debug and refactor. Ten 100 line files are easier to debug and refactor.)
I've created a library in C++ using Embind and Emscripten.
Some hand written JS code is also added to the library using --pre-js
The library works. But I would like to rearrange the code, to this:
var MYLIB = (function(){
// ... Original Code ...
return Module;
})();
So the code would not pollute the global namespace, and the code minifier could do better optimizations.
Are there build in functions for this in emcc ?
The library will only run in webbrowsers, not in nodejs.
What you're looking for are the MODULARIZE and EXPORT_NAME options. Check the documentation in settings.js.
Quoting from that file:
// By default we emit all code in a straightforward way into the output
// .js file. That means that if you load that in a script tag in a web
// page, it will use the global scope. With MODULARIZE set, we will instead emit
//
// var EXPORT_NAME = function(Module) {
// Module = Module || {};
// // .. all the emitted code from emscripten ..
// return Module;
// };
//
// where EXPORT_NAME is from the option of the same name
If I want to span my JavaScript project across multiple source files, but have each file have access to the same private variable, how would one do that?
For example, if I have the following code:
APP = (function () {
var _secret = {},
app = {};
// Application part 01:
app.part01 = (function () { /* function that uses _secret */ }());
// Application part 02:
app.part02 = (function () { /* function that uses _secret */ }());
//
return app;
}());
How do I put app.part01 and app.part02 in seperate files, but still have access to _secret?
I don't want to pass it as an argument. That's just giving the secret away, as app.part01() could be replaced by any other function.
Maybe I am asking the impossible, but your suggestions might lead me in the right way.
I want to work with multiple files, but I don't know how. Copying and pasting everything inside a single function each time before testing is not something I want to do.
How do I put app.part01 and app.part02 in seperate files, but still have access to _secret?
That's impossible indeed. Script files are executed in the global scope, and don't have any special privileges. All variables that they will be able to access are just as accessible to all other scripts.
Copying and pasting everything inside a single function each time before testing is not something I want to do
What you are looking for is an automated build script. You will be able to configure it so that it bundles your files together, and wraps them in an IEFE in whose scope they will be able to share their private state. The most simple example:
#!/bin/sh
echo "APP = (function () {
var _secret = {},
app = {};" > app.js
cat app.part01.js >> app.js
cat app.part02.js >> app.js
echo " return app;
}());" >> app.js
The only way that you can share _secret is attaching it to the application object and then application object to the window object. Here is an example.
// FIRST JS FILE...
var application; // will be attached to window
(function(app) {
app.secret = "blah!"; // will be attached to application
})(application || (application = {}));
// ANOTHER JS FILE
var application;
(function(app) {
app.method1 = function(){ console.log(app.secret); }; // will be attached to application;
})(application || (application = {}));
console.log(application.method1()); // will display 'blah!' on the console
Working example on jsbin
One way I was able to accomplish this was to create a JS file that contained the global object.
// Define a global object to contain all environment and security variables
var envGlobalObj = {
appDatabase: process.env.YCAPPDATABASEURL,
sessionDatabase: process.env.YCSESSIONDATABASEURL,
secretPhrase: process.env.YCSECRETPHRASE,
appEmailAddress: process.env.YCAPPEMAILADDRESS,
appEmailPassword: process.env.YCAPPEMAILPASSWORD
}
module.exports = envGlobalObj
Then in the files I wish to reference this object, I added a require statement.
var envGlobalObj = require("./envGlobalObj.js");
This allowed me to centralize the environment and secrect variables.
I use UglifyJS to minify a concatenated set of files, which works fine but not good enough. The built lib uses namespaces, so classes, functions and constants are stored in a root namespace variable:
(function() {
var root = { api:{}, core:{}, names:{} };
/* util.js file */
root.names.SOME_LONG_NAMED_CONST='Angel';
/* Person.js file */
root.core.Person = function(name) { this.name = name };
/* API.js with the functions we want to expose */
root.api.perform = function(param_for_api) { /* do something */ }
window.lib_name.perform = root.api.perform;
})();
which is minified to the not-so-minimal version
(function(){var a={api:{},core:{},names:{}};a.names.SOME_LONG_NAMED_CONST="Angel",a.core.Person=function(a){this.name=a},a.api.perform=function(){},window.lib_name.perform=a.api.perform})();
I understand uglify probably thinks that root var is a data structure that must be kept as-is and can't be changed. Is there a way to let UglifyJS mangle the nested names in the root namespace?
When you minimize Javascript you can only change names of variables, the api, core and names are not variables but properties of an object. If these were changed by the minimizer, you would potentially get unexpected results. What if in your code you would call
root["api"].perform = function()...
or even something like
function doIt(section, method, argument) {
root[section][method](argument);
}
doIt('api','perform', 101);
All perfectly legal JS, but a minimizer could never figure out what's going on.
I have been trying to use --mangle-props of UglifyJS2 and can tell you: 'it makes a mess'.
As someone pointed out: 'Developer should decide what properties to mangle, not uglifyjs'
I am approaching the problem using this options:
--mangle-props
--mangle-regexp="/_$/"
The regex matches any property with a underscore at the end.
You asked to mangle nested names in the root namespace. So, your code:
(function() {
var root = { api:{}, core:{}, names:{} };
root.names.SOME_LONG_NAMED_CONST_='Angel';
root.core.Person_ = function(name) { this.name = name };
root.api.perform_ = function(param_for_api) { }
window.lib_name.perform = root.api.perform;
})();
Would result in this:
(function() {
var n = {
api: {},
core: {},
names: {}
};
n.names.a = "Angel";
n.core.b = function(n) {
this.name = n;
};
n.api.c = function(n) {};
window.lib_name.perform = n.api.c;
})();
Command:
uglifyjs --beautify --mangle --mangle-props --mangle-regex="/_$/" -- file.js
If you want to mangle first level of root namespace (api, core, names) just put a underscore on them (api_, core_, names_), you are in control ;)
Just a side note: when you are mangling properties usable by other js files, you should mangle all files together with the same command, so the same identifier will be used over all files.
Aside from #JanMisker 's point (which is completely valid), rewriting properties is unsafe because they can be exposed to code outside the scope of the minification.
Although the self executing function has a scope, and if the code is only
(function() {
var root = { api:{}, core:{}, names:{} };
root.names.SOME_LONG_NAMED_CONST='Angel';
alert(root.names.SOME_LONG_NAMED_CONST); // some code that does something
})();
It is true that outside of the function, there is no way to access the root object, so rewriting the property names is safe, and the following code would result in the same:
(function() {
var a = { b:{}, c:{}, d:{} };
a.d.e='Angel';
alert(a.d.e);
})();
But even if you are inside your private scope you can access, and more importantly assign to variables from an outer scope! Imagine this:
(function() {
var root = { api:{}, core:{}, names:{} };
root.api.perform = function(param_for_api) { /* do something */ }
window.lib_name = root.api;
})();
You are not only exposing a function but an object with a function on it. And the function will be visible from any place where window is visible.
So, for example writing the following in the javascript console would yield different results with and without minification:
window.lib_name.perform(asdf);
With minification you would have to write:
window.lib_name.f(asdf);
Or something similar.
Remember that there can always be code outside your minification.
It is not that crucial to have the absolute minimal JS, but if IT IS that crucial for some reason (for example: aliens abducted your stepdaughter, and the only way to have her back is to minify this below 100 characters or so), you can manually replace an undesirably long property name to a shorter one, just be sure that it will not be exposed anywhere, and isn't be accessed through associative array notation (root['api']).
as #Jan-Misker explained in his answer, property name mangling is NOT an good idea because it could potentially break your code.
However, you can workaround it by define the property names as local variables, and modify all .properties to [keys], to make smaller file size:
(function() {
var API = 'api';
var CORE = 'core';
var NAMES = 'names';
var SLNC = 'SOME_LONG_NAMED_CONST';
var root = {};
root[API]={};
root[CORE]={};
root[NAMES]={};
/* util.js file */
root[NAMES][SLNC] ='Angel';
/* Person.js file */
root[CORE].Person = function(name) { this.name = name };
/* API.js with the functions we want to expose */
root[API].perform = function(param_for_api) { /* do something */ }
window.lib_name.perform = root[API].perform;
})();
Because now all the properties became a local variable, uglify js will mangle/shorten the variable names and as consequence you overall file size reduced:
!function(){var a="api",b="core",c="names",d="SOME_LONG_NAMED_CONST",e={};e[a]={},e[b]={},e[c]={},e[c][d]="Angel",e[b].Person=function(a){this.name=a},e[a].perform=function(){},window.lib_name.perform=e[a].perform}();
However, reduced file size doesn't mean you will get shorter downloading time on real server, because usually our http transport is gzipped, most of the repetitions will be compressed by your http server and it does a better job than human.
The latest release of uglify (today) has object property mangling, see v2.4.18. It also supports reserved files for excluding both object properties and variables that you don't want mangled. Check it out.
Use the --mangle-props option and --reserved-file filename1.json filename2.json etc....
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.