Using Closure Compiler on ES6 Modules with Properties - javascript

So after a day of testing I'm almost there: using google-closure-compiler-js and gulp.js to minify / compile my ES6 module based "library".
There is just one problem left. How to call the properties of an imported module?
My test case consists of 3 files:
gulpfile.js
const gulp = require("gulp");
const compiler = require("google-closure-compiler-js").gulp();
const sourcemaps = require("gulp-sourcemaps");
gulp.task("default", function() {
// wildcards don't seem to work yet with the js version
// files need to be in reverse order of dependency...
return gulp.src(["src/boilerplate/main.js", "src/app.js"])
.pipe(sourcemaps.init())
.pipe(compiler({
jsOutputFile: "app.min.js",
compilationLevel: "ADVANCED",
warningLevel: "VERBOSE",
createSourceMap: true
}))
.pipe(sourcemaps.write("/"))
.pipe(gulp.dest("./dist"));
});
src/boilerplate/main.js
var mod = {};
export default mod;
var foo = "FOO";
var bar = "BAR";
function foobar(join) {
join = (typeof join === "string") ? join : "";
return foo + join + bar;
}
Object.defineProperty(mod, "foobar", {value: foobar});
src/app.js
import boilerplate from "./boilerplate/main.js";
console.log(boilerplate["foobar"]("+"));
console.log(boilerplate.foobar("+"));
If you log boilerplate here, you see that it has the property "foobar". Symbol intact and all. Great!
Hence app.js' first call – bracket notation ["foobar"] – works perfectly fine. However, the dot notation does not. Calls to an imported module's property get minified by Closure Compiler!
You get your classic "Hi. I'm new to ADVANCED mode" error: TypeError: a.a is not a function.
How do I prevent this? (& let's assume exporting foobar directly isn't viable, for real world's sake.)
Do I need an exports file? How exactly would I do that? My first ventures in that direction did not take me far... is there a trick specific to google-closure-compiler-js?
Is there some kind of hack that allows for dot notation, until Closure Compiler adds a feature that automatically does not rename calls to properties of imports?
And what does this closure-compiler/wiki's article about JS-Modules#type-references advise me to do? Is this the answer I fail to implement?
/** #param {foo.Foo} Foo */
function(Foo) {}

For ADVANCED mode compatibility, use Object.defineProperties to avoid quoted strings.
Object.defineProperties(mod, {
foobar: {value: foobar}
});
Anytime you use quoted strings, you will be required to use brackets (or other techniques) or risk violating the consistent property access rules.

Related

Equivalent of private variable (leading underscore) in es6?

I have this code here:
getData(value, index) {
const {responseMetadata, responseData} = this.getResponseDatum();
return responseData.get(index).get('code').toUpperCase();
}
eslint reports an error:
19:12 "responseMetadata" is defined but never used
In python I can silent this kind of error by renaming the variable to _responseMetadata. Is there a Equivalent in es6?
If you don't need the variable, just don't create it:
const {responseData} = this.getResponseDatum();
A destructuring assignment doesn't need to match all properties of the returned object.
In your case, since you need only one property and don't use it multiple times, there's actually not much reason to use destructuring or a variable at all:
getData(value, index) {
return this.getResponseDatum().responseData.get(index).get('code').toUpperCase();
}
You can turn off a rule for a section of code. See http://eslint.org/docs/user-guide/configuring.html#configuring-rules
/*eslint-disable */
//suppress all warnings between comments
alert('foo');
/*eslint-enable */

ECMA6 Symbols primitive

Currently i started working with JS ECMA6 for my personal web app,
but I am not sure about usage of Symbols
i.e. how can I make use of it?
I believe, they are tokens that serve as unique IDs. But I am not sure about its usage in web app development.
I am currently new to this standard, please provide your suggestions on it.
Symbols enable access control for object state. Symbols allow properties to be keyed by either string (as in ES5) or symbol. Symbols are a new primitive type. Optional description parameter used in debugging - but is not part of identity. Symbols are unique (like gensym), but not private since they are exposed via reflection features like Object.getOwnPropertySymbols.
var MyClass = (function() {
// module scoped symbol
var key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function() {
... this[key] ...
}
};
return MyClass;
})();
var c = new MyClass("hello")
c["key"] === undefined
It can be referred here.
Here are few more links with nicely explained examples.(As already mentioned in comments section.)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
http://www.2ality.com/2014/12/es6-symbols.html

Attaching underscore to Object prototype, bad idea?

The common wisdom is the just because you can augment native types, doesn't mean you should.
That augmenting a native types prototype is a bad idea always, with the only exception being to polyfill behavior that is within the ECMAScript Spec.
In our project we're using underscore quite a bit and I was wondering, why not use the OOP style it provides directly on our objects?
var _ = require('underscore');
Object.defineProperty( Object.prototype, '_', {
get: function() {
return _(this);
}
});
Which enables us to use underscore methods directly like so, without having to make the decision to require the library everywhere we want to use it, but to simply know it's there and standardize on using underscore functionality when appropriate.
[1, 2, 3]._.contains(3); // true
var funcOnce = function() {
console.log("hello");
}._.once();
funcOnce(); // "hello"
funcOnce(); //
{one: 1, two: 2, three: 3}._.keys(); // ['one', 'two', 'three']
I believe I've limited the scope of the damage by only modifying a single (probably useless _?) property on the native Object.
Can you see how this will come back to bite us in developing our application, assuming there is never a native _ property introduced in Object?
Can you see how this will come back to bite us in developing our application, assuming there is never a native _ property introduced in Object?
Yes. While you have avoided that the property shows up in for in loops, you did forget to provide a setter. And this will definitely hunt you:
// let's start with this
Object.defineProperty( Object.prototype, '_', {
get: function() {
return "sorry";
}
});
// now what do you expect the following statements to do?
> var x = {}; "_" in x;
true // meh, obvious
> var x = {_:"works"}; x._
"works" // this is hijackable in older browsers!
> var x = {}; x._ = "works"; x._
"sorry" // well…
(function(){"use strict"; var x={}; x._ = "works"; return x._; }())
unhandled TypeError: "Invalid assignment in strict mode" // ooops!
> _
"sorry" // ah, window._ inherits from Object.prototype as well
> _ = "works"; _
"sorry" // and prevents even simple global variable assignments
You're using require to get the underscore dependency, which could give you a number of advantages (supposing you use it right), like:
freedom to load the dependency asynchronously, on-demand, and
the ability to use different versions of the dependency in different parts of your code
If you're going to start using underscores on objects, you lose these advantages, the same way you would if you just used underscore as a global variable. And, to what end? So you can replace
_([1, 2, 3]).contains(3)
with
[1, 2, 3]._.contains(3)
?
It may not be a Bad Idea™, but it doesn't seem like a particularly good one, either.

Make Closure Compiler merge several object property declarations as one big literal

I split my code into several files, and then run a script to merge and compile them (with ADVANCED_OPTIMIZATIONS). A big part of the functionality is implemented in a single object's prototype.
So when merged, it could look something like this:
(function(){
/** #constructor */ function MyConstructor() {};
MyConstructor.prototype = {};
MyConstructor.prototype['foo'] = function() { alert('foo'); };
MyConstructor.prototype['bar'] = function() { alert('bar'); };
MyConstructor.prototype['baz'] = function() { alert('baz'); };
window['MyConstructor'] = MyConstructor;
}());
If you put that code into Closure Compiler just like that, here's the output (pretty-printed):
function a() {
}
a.prototype = {};
a.prototype.foo = function() {
alert("foo")
};
a.prototype.bar = function() {
alert("bar")
};
a.prototype.baz = function() {
alert("baz")
};
window.MyConstructor = a;
The question is, is there some way I could tell Closure Compiler that it's ok to merge all of these in a single object literal, and even if there was code in-between (in this example there isn't, but there could be), so that no matter what, it made it all compile into one big object literal?
Here's a couple of solutions, and why they wouldn't work for me:
Solution 1: Simply declare them in one big object literal.
Wouldn't work because I have my code into several files, and I plan to make it so users can remove some of them (if they don't need them) prior to compilation. Object literals have comma-delimiters that would make this a nightmare.
Solution 2: Declare all functionality outside of the object (as private variables in the closure), and attach them into a simplified object literal at the end, which just has references to properties (such as {'foo':foo,'bar':bar,'baz':baz}).
Wouldn't work because, as said, the idea is to create something modular, and removing one file would make the reference break.
I'm open to ideas!
Edit: Some people could think that Closure Compiler can't do this. It can do this and much more, it's just that it has a bad attitude and does things when it feels like it.
Input this into Closure:
(function(){
var MyConstructor = window['MyConstructor'] = function() {};
var myProto = {
'foo': function() { alert('foo'); },
'bar': function() { alert('bar'); }
};
myProto['baz'] = function() { alert('baz'); };
MyConstructor.prototype = myProto;
}());
The result is:
(window.MyConstructor = function() {
}).prototype = {foo:function() {
alert("foo")
}, bar:function() {
alert("bar")
}, baz:function() {
alert("baz")
}};
See? But, this is code is very fragile in that it may compile into something completely different (and not that good) if modified slightly. For example even a variable assignment somewhere in the middle might cause it to output very different results. In other words, this doesn't work (except in this case).
Edit 2: see this jsperf. A big object literal is faster in Chrome (proportional to its size).
Edit 3: Closure Compiler bug report.
There is a workaround I'm doing. It might not apply, so this answer cannot be accepted. Still, this is what works in my case.
My folder structure looked like this:
src
├───components
└───core
And, before compilation, I merged src/intro.js, some files at the src level (in a specific order), then all of the files in core (any order), then all in components (any order), then outro.js.
Now, the folder structure looks like this:
src
├───components
│ ├───modules
│ └───plugs-literal
└───core
├───internal
├───modules
└───plugs-literal
And the compilation order is (note the part with arrows):
src/intro.js
a couple files in src/core, specific order.
All files in src/core/internal
src/core/plugs-literal-intro.js <--
All files in src/core/plugs-literal <--
All files in src/components/plugs-literal <--
src/core/plugs-literal-outro.js <--
All files in src/core/modules
All files in src/components/modules
src/outro.js
The idea is that one file contains the beginning of an object literal, another file has the closing of an object literal, and two folders contain properties. More or less like this:
src/core/plugs-literal-intro.js:
var myObjectLiteral = {
'someSimpleProp': 'foo',
'someOtherSimpleProp': 'bar',
'lastOneWithoutTrailingComma': 'baz'
src/core/plugs-literal/EXAMPLE.js:
,'example': function() { alert('example'); } // comma before, not after.
src/core/plugs-literal-outro.js:
};
If this introduces some unwanted problem, I'll know later. But then, I could assign a different folder to contain prototype properties declared individually.

How to declare string constants in JavaScript? [duplicate]

This question already has answers here:
Are there constants in JavaScript?
(33 answers)
Closed 6 years ago.
I want to declare string constants in JavaScript.
Is there is a way to do that?
Many browsers' implementations (and Node) have constants, used with const.
const SOME_VALUE = "Your string";
This const means that you can't reassign it to any other value.
Check the compatibility notes to see if your targeted browsers are supported.
Alternatively, you could also modify the first example, using defineProperty() or its friends and make the writable property false. This will mean the variable's contents can not be changed, like a constant.
Are you using JQuery? Do you want to use the constants in multiple javascript files? Then read on. (This is my answer for a related JQuery question)
There is a handy jQuery method called 'getScript'. Make sure you use the same relative path that you would if accessing the file from your html/jsp/etc files (i.e. the path is NOT relative to where you place the getScript method, but instead relative to your domain path). For example, for an app at localhost:8080/myDomain:
$(document).ready(function() {
$.getScript('/myDomain/myScriptsDir/constants.js');
...
then, if you have this in a file called constants.js:
var jsEnum = { //not really an enum, just an object that serves a similar purpose
FOO : "foofoo",
BAR : "barbar",
}
You can now print out 'foofoo' with
jsEnum.FOO
There's no constants in JavaScript, but to declare a literal all you have to do is:
var myString = "Hello World";
I'm not sure what you mean by store them in a resource file; that's not a JavaScript concept.
Of course, this wasn't an option when the OP submitted the question, but ECMAScript 6 now also allows for constants by way of the "const" keyword:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
You can see ECMAScript 6 adoption here.
Standard freeze function of built-in Object can be used to freeze an object containing constants.
var obj = {
constant_1 : 'value_1'
};
Object.freeze(obj);
obj.constant_1 = 'value_2'; //Silently does nothing
obj.constant_2 = 'value_3'; //Silently does nothing
In strict mode, setting values on immutable object throws TypeError. For more details, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
Well, you can do it like so:
(function() {
var localByaka;
Object.defineProperty(window, 'Byaka', {
get: function() {
return localByaka;
},
set: function(val) {
localByaka = window.Byaka || val;
}
});
}());
window.Byaka = "foo"; //set constant
window.Byaka = "bar"; // try resetting it for shits and giggles
window.Byaka; // will allways return foo!
If you do this as above in global scope this will be a true constant, because you cannot overwrite the window object.
I've created a library to create constants and immutable objects in javascript. Its still version 0.2 but it does the trick nicely. http://beckafly.github.io/insulatejs
Starting ECMAScript 2015 (a.k.a ES6), you can use const
const constantString = 'Hello';
But not all browsers/servers support this yet. In order to support this, use a polyfill library like Babel.
So many ways to skin this cat. You can do this in a closure. This code will give you a read-only , namespaced way to have constants. Just declare them in the Public area.
//Namespaced Constants
var MyAppName;
//MyAppName Namespace
(function (MyAppName) {
//MyAppName.Constants Namespace
(function (Constants) {
//Private
function createConstant(name, val) {
Object.defineProperty(MyAppName.Constants, name, {
value: val,
writable: false
});
}
//Public
Constants.FOO = createConstant("FOO", 1);
Constants.FOO2 = createConstant("FOO2", 1);
MyAppName.Constants = Constants;
})(MyAppName.Constants || (MyAppName.Constants = {}));
})(MyAppName || (MyAppName = {}));
Usage:
console.log(MyAppName.Constants.FOO); //prints 1
MyAppName.Constants.FOO = 2;
console.log(MyAppName.Constants.FOO); //does not change - still prints 1
You can use freeze method of Object to create a constant. For example:
var configObj ={timeOut :36000};
Object.freeze(configObj);
In this way you can not alter the configObj.
Use global namespace or global object like Constants.
var Constants = {};
And using defineObject write function which will add all properties to that object and assign value to it.
function createConstant (prop, value) {
Object.defineProperty(Constants , prop, {
value: value,
writable: false
});
};
Just declare variable outside of scope of any js function. Such variables will be global.

Categories