Leading Underscore transpiled wrong with es6 classes - javascript

I am experiencing a really weird behavior and can't even say which package to blame for it.
My setup: RequireJS project with the JSXTransformer and the jsx! plugin
I have an es6 class like this:
define([
'react'
], function(
React
) {
class MyComponent extends React.Component {
myMethod() {
otherObject.someMethod()._privateProp; // Yes, we need this accessing and have no influence on it
}
}
return MyComponent;
});
The transpiled output in the resulting bundle after running r.js is:
define('jsx!project/components/InputOutput',[
'react'
], function(
React
) {
var ____Class8=React.Component;for(var ____Class8____Key in ____Class8){if(____Class8.hasOwnProperty(____Class8____Key)){MyComponent[____Class8____Key]=____Class8[____Class8____Key];}}var ____SuperProtoOf____Class8=____Class8===null?null:____Class8.prototype;MyComponent.prototype=Object.create(____SuperProtoOf____Class8);MyComponent.prototype.constructor=MyComponent;MyComponent.__superConstructor__=____Class8;function MyComponent(){"use strict";if(____Class8!==null){____Class8.apply(this,arguments);}}
MyComponent.prototype.myMethod=function() {"use strict";
otherObject.someMethod().$MyComponent_privateProp;
};
return MyComponent;
});
Note how otherObject.someMethod().$MyComponent_privateProp; is written there. This obviously breaks because it is not a property on instances of MyComponent.

Add /** #preventMunge */ to the top of the file. See this GitHub issue:
Yes, sorry this is a non-standard fb-ism. For now you can work around this and toggle this feature off by putting /** #preventMunge */ at the top of your file -- but that's also a pretty big fb-ism. We should (a) turn this into a transform option (rather than a direct docblock directive) and (b) make it opt-in rather than opt-out (since it's non-standard).
For context: We munge all under-prefixed object properties on a per-module basis partly because our under-prefix convention applies to both objects and classes. Additionally, even if we wanted to lax the objects vs classes distinction, it's impossible to tell (in the general case) if a property is a reference to this since alias variables can occur (i.e. var self = this; self._stuff;).

Related

ng.core.Component(...).Class is not a function

I'm using ES5 flavor of Angular 2. Thus instead of using decorators, I define a template as follow:
var someComponent = ng.core.Component({
// component configuration
}).Class({
// component class
});
It was working fine. Recently I upgraded to Angular 5, and now I have these error for the same code:
ng.core.Pipe(...).Class is not a function
ng.core.Component(...).Class> is not a function
ng.core.Directive(...).Class is not a function
How should I change this in Angular 5?
JavaScript is currently considered second-class (no pun) citizen, while TypeScript is the language of choice, primarily because of the focus on AOT compilation, which is available for TypeScript only.
As explained here,
It is no longer possible to declare classes in this format.
Component({...}).
Class({
constructor: function() {...}
})
This format would only work with JIT and with ES5. This mode doesn’t
allow build tools like Webpack to process and optimize the code, which
results in prohibitively large bundles. We are removing this API
because we are trying to ensure that everyone is on the fast path by
default, and it is not possible to get on the fast path using the ES5
DSL. The replacement is to use TypeScript and #Decorator format.
#Component({...})
class {
constructor() {...}
}
As described in this answer, the alternative to decorators in plain ES5 and higher is the annotation of classes (and therefore, functions) with annotations and parameters static properties.
So
var SomeComponent = ng.core.Component({...})
.Class({
constructor: [
[new ng.core.Inject(Svc)],
function (svc) {...}
],
foo: function () {...}
});
becomes
SomeComponent.annotations = [new ng.core.Component(...)];
SomeComponent.parameters = [
[new ng.core.Inject(Svc)]
];
function SomeComponent(svc) {...}
SomeComponent.prototype.foo = function () {...};
As a replacement for Class functionality, any third-party utility class library can be used to introduce syntactic sugar and inheritance in ES5 as a substitute for functions, e.g. uberproto or inherits.
It it highly advisable to migrate existing Angular JavaScript applications to TypeScript, the latter offers superior performance and much smaller application footprint in AOT compilation mode, which is going to be default mode in future versions.

Referencing global libraries from an ember component

I am using ember-remarkable because I want to add both markdown parsing and syntax highlighting to my project. The only thing it's missing is line numbers, which I want to add by using this library: highlightjs-line-numbers.js
I override ember-remarkable's md-text component with the following:
import MDTextComponent from 'ember-remarkable/components/md-text';
export default MDTextComponent.extend({
tagName: null,
didRender() {
this._super(...arguments);
this.$("code[class*='language-']").each((i, block) => {
this.$(block).addClass('hljs');
hljs.lineNumbersBlock(block);
});
}
});
Everything works! However, my editor complains that hljs is not defined, even though it is available on the page.
How do I avoid this error?
If it is ES6 modules, just import it. Otherwise you can use it as window.hljs.
When you run your js code in browser, window is the root of the global variables. So you can access it as window.hljs.
If you run your code in Fastboot, this should be a better way to access global objects: (window || Fastboot || global).hljs.
On the other hand, instead of using the global object container, you can just prevent linter to give this error. For eslint, see this configuration: Specifying Globals.
You should explicitly import your dependencies, if for no other reason than to communicate to future readers. import hljs from 'highlightjs-line-numbers'

ES6 Module loader to resolve symbolic references

We are trying to create an ES6 loader to use symbolic references to modules instead of well formed URIs or paths in import sentences. For instances, an import like this one:
import { foo } from 'com.bussines.platform.utils';
should be resolve the symbolic reference com.bussines.platform.utils to a real uri and proceed with the regular loading process to get foo.
To achieve this goal we are using the es-module-loader polyfill developed by #guybedford. As, we are in the browser context, we have create a loader delegating to BrowserESModuleLoader:
class CustomBrowserLoader extends RegisterLoader {
constructor(Loader) {
super();
this.loader = new BrowserESModuleLoader();
}
[RegisterLoader.resolve] (key) {
let uri = Resolve (key)
return this.loader[RegisterLoader.resolve] (uri); // (1)
}
[RegisterLoader.instantiate] (uri) {
return this.loader[RegisterLoader.instantiate] (uri); // (2)
}
}
The problem is that, surprisingly, calls at lines (1) and (2) doesn't find the required methods resolve and instantiate. We have tried other approaches based on inheritance but getting similar results.
¿Could anyone give some insights about how to solve the problem? Thanks in advance.
Loaders are separate builds, so I think you would want BrowserESModuleLoader.resolve and BrowserESModuleLoader.instantiate here.
It may be better though to directly fork the Browser ES Module Loader code itself, and adapt it to handle these types of specifiers directly instead.

Use RequireJS to include a class defined using ES6 syntax

I have an existing project (which is run fully in the browser, no server-side component) which uses RequireJS to manage dependencies. Previously the way I'd been defining "classes" (including inheritance) was like this:
define([
'jquery',
'classes/SomeParentClass',
], function($, SomeParentClass){
function ThisClass(data){
SomeParentClass.call(this, data);
this.init = function(data){
//Do stuff
}
this.init(data);
}
ThisClass.prototype = Object.create(SomeParentClass.prototype);
ThisClass.prototype.constructor = ThisClass;
return ThisClass;
});
And then when I wanted to include it in some other class I'd use the standard RequireJS define/require syntax:
define(['classes/ThisClass'], function(ThisClass){
var fooInstance = new ThisClass(fooData);
});
I wanted to try my hand at the "class" syntax in ES6, so I set about converting one of my existing classes (I started with a "static" class for simplicity). It currently looks something like this:
define([
'jquery',
], function($){
class SomeClass {
static someStaticFn(){
//Do whatever
}
}
});
Where I'm stymied currently is how to reference SomeClass (with the new ES6 syntax) using RequireJS. Assuming that's even possible.
UPDATE
As others have pointed out below, returning the class is all RequireJS cares about. I could have sworn that I already tried that, but sure enough it worked.
In case anyone else is a dummy like me and ends up here under similar circumstances, I want to also point out that I did have some success with the requirejs-babel plugin, however only after I'd re-written my class to use ES6 export and import statements instead of RequireJS's define. After having done that, anywhere else I was including the class I had to do it like so:
define(['es6!ThisClass'], function(ThisClass){ ...
I ended up choosing to modify a class that was included in every other module, so adding all those "es6!" strings turned out to be quite a drag. Although this article on porting existing projects to ES6 mentions a way of automating the process via RequireJS config.
However, as requirejs-babel transpiles the ES6 code at runtime, I will probably not incorporate that into my project at this time. If I do decide I want to go whole hog with ES6 I'd probably use "regular" Babel or possibly TypeScript
ES6 Class is just a sugarcoat and still with same implementation underneath. You can use it the old way as creating class by function. Also be careful with browser compatibility https://kangax.github.io/compat-table/es6/.
define([
'jquery',
], function($){
return class SomeClass {
static someStaticFn(){
//Do whatever
}
}
});

ES6 modules: Export single class of static methods OR multiple individual methods

I'm using ECMAScript6 modules. What is the correct way to export/import multiple methods from a module from the options below?
Single class of static methods:
//------ myClass.js ------
export default class myClass {
static myMethod1() {
console.log('foo');
}
static myMethod2(args...) {
console.log('bar');
}
}
//------ app.js ------
import myClass from 'myClass';
myClass.myMethod1(); //foo
Multiple exported methods:
//------ myMethods.js ------
export function myMethod1() {
console.log('foo');
}
export function myMethod2() {
console.log('bar');
}
//------ app.js ------
import {myMethod1, myMethod2} from 'myMethods';
myMethod1() //foo;
//OR
import * as myMethods from 'myMethods';
myMethods.myMethod1() //foo;
1) Exporting:
A class of just static methods feels like a bit of a 'code smell' but similarly exporting everything individually does feel a bit verbose. Is it simply developer preference or are there performance implications here?
2) Importing:
'* as' syntax is my preferred method as it allows you to use the dot notation (referencing both the module AND the method) aiding code readability. Does this have performance implications though when I may only be using 1 of the methods?
A class of just static methods feels like a bit of a 'code smell'
Yes indeed. You don't need a class structure here! Just export a normal "module" object:
//------ myMethods.js ------
export default {
myMethod1() {
console.log('foo');
},
myMethod2(args...) {
console.log('bar');
}
};
I do recommend your second approach with multiple exports, though.
exporting everything individually does feel a bit verbose
Well, you don't need any wrapper structure, so I'd say it's less boilerplate. You just have to explicitly tag everything that you want to be exported, which is not a bad thing.
* as syntax is my preferred method as it allows you to use the dot notation (referencing both the module AND the method) aiding code readability.
That's very much personal preference, and does depend on the type of code you are writing. Sometimes conciseness is superior, but the ability to explicitly reference the module can be helpful as well. Notice that namespace imports using * as and default-imported objects are very similar here, though only named exports allow you to directly reference them via import {myMethod1, myMethod2}. So better leave the choice to those that import your module.
Does this have any performance implications?
Not much. Current ES6 implementations are not yet aiming for performance optimisations anyway.
In general, static identifiers are easier to resolve and optimise than property accesses[1], multiple named exports and partial imports could theoretically make JIT faster, and of course smaller files need less time to load if unused exports are removed during bundling. See here for details. There hardly will be noticeable performance differences, you should use what is better maintainable.
[1]: module namespaces (import * as ns) are static as well, even if ns.… looks like a dynamic property access
TLDR; Use multiple exported methods and explicit import.
#Bergi is right about not needing a class with static fields, only an object in your first case. However, this option is discouraged by Axel Rauschmayer:
Note that default-exporting objects is usually an anti-pattern (if you want to export the properties). You lose some ES6 module benefits (tree-shaking and faster access to imports).
Devs at Airbnb recommend named exports and explicit wildcrad import, see this thread: https://github.com/airbnb/javascript/issues/710#issuecomment-297840604

Categories