Use RequireJS to include a class defined using ES6 syntax - javascript

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
}
}
});

Related

returning instanced class + babel JS

I'm building a "library" on JS, and it is mandatory to be only one .js file.
I just started to build, and i got this until now:
app-es6/main.js
class Sjs {
create(obj) {
console.log(obj);
}
}
index.html
<script src="app/main.js"></script>
<script>
let sjs = new Sjs();
sjs.create({
type: 'select'
});
</script>
.babelrc
{
"presets": ["es2015"]
}
What i need to do:
1) The class should not be instantiated in html, the instance should come ready from js, so i can just type sjs.create(), like jQuery, moment, etc.
2) If i need to import more JS files, to build somehting more structured, can i "wrap" to a single js, minimized?
Thanks in advice.
1) The class should not be instantiated in html, the instance should come ready from js, so i can just type sjs.create(), like jQuery, moment, etc.
Just put it in the main.js, after the class declaration:
let sjs = new Sjs();
let at global scope creates a global variable (though not a property on the global object [usually accessed as window on browsers]). If you want that property as well, use var instead of let or assign to window.sjs (which will also create a global variable; all properties on the global object are globals, it's just that as of ES2015, not all globals are properties on the global object).
That said, if it's a singleton, there isn't much benefit to using class. It's probably also worth noting that jQuery and MomentJS both expose functions (jQuery/$, moment), not non-callable objects.
2) If i need to import more JS files, to build somehting more structured, can i "wrap" to a single js, minimized?
You're looking for a bundler, like Webpack, Rollup, etc. They have plugins for integrating with Babel, doing minification, ...

Restore original lodash method overwritten by mixin?

We're using the lodash-contrib package, which includes a camelCase method that behaves differently than the original _.camelCase method.
Is there any way for me to restore the pointer to the original method?
In the requirejs config, we have a shim:
lodashContrib: ['lodash']
As soon as lodashContrib has loaded, it's added mixins to lodash. An example of our code:
define([
'lodashContrib'
], function() {
// our code here. At this point, _.camelCase is overridden by contrib
});
Create a file lodashCustom.js or something you can add to your requirejs configuration, and put the following inside it, then wherever you require lodashContrib you can require this instead:
define(['lodash', 'lodashContrib'], function(_, _c) {
_c.camelCase = _.camelCase;
return _c;
});
Assuming your shim implementation doesn't rely on globals, this should hopefully work fine.

Can I use an ES6/2015 module import to set a reference in 'global' scope?

I have this situation where I am trying to import an existing library, which I'll call troublesome (using Webpack/Babel FWIW) and it has a global reference to jQuery in it which i am trying to resolve using module syntax.
I have successfully imported jquery into the 'local' scope of a module, via:
import jQuery from 'jquery'
so I tried:
import jQuery from 'jquery'
import 'troublesome'
but perhaps not surprisingly, I get something like jQuery is not a function kicked back from troublesome.js
I have tried this as well:
System.import('jquery')
.then(jQuery => {
window.jQuery = jQuery
})
import 'troublesome'
but, it turns out that System.import is part of the, so-called, 'module-loader' spec, which was pulled from the es6/2015 spec, so it isn't provided by Babel. There is a poly-fill, but Webpack wouldn't be able to manage dynamic imports accomplished via calls to System.import anyway.
but... if I call out the script files in index.html like so:
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/troublesome/troublesome.js"></script>
<script src="the-rest-of-my-js.js"></script>
the reference to jQuery is resolved in troublesome.js and things are good,
but I would prefer to avoid the script tag route as webpack doesn't manage those.
Can anyone recommend a decent strategy for dealing with scenarios like this?
update
with some guidance from #TN1ck, I was eventually able to identify one Webpack-centric solution, using the imports-loader
The configuration for this solution looks something like this:
//...
module: {
loaders: [
//...
{
test: require.resolve('troublesome'),
loader: "imports?jQuery=jquery,$=jquery"
}
]
}
Shimming modules is the way to go: http://webpack.github.io/docs/shimming-modules.html
I quote from the page:
plugin ProvidePlugin
This plugin makes a module available as variable in every module. The module is required only if you use the variable.
Example: Make $ and jQuery available in every module without writing require("jquery").
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
To use this with your webpack-config just add this object to an array called plugins in the config:
// the plugins we want to use
var plugins = [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
];
// this is your webpack-config
module.exports = {
entry: ...,
output: ...,
module: ...,
plugins: plugins
}
For es6/2015 I done the following.
import {jsdom} from 'jsdom';
import jQuery from 'jquery';
var window = jsdom(undefined, {}).defaultView;
var $ = jQuery(window);
//window.jQuery = $; //probably not needed but it's there if something wrong
//window.$ = $;//probably not needed but it's there if something wrong
Then you can use it as normal
var text = $('<div />').text('hello world!!!').text();
console.log(text);
Hope this helps.
Importing jQuery into your module does not make it available for 'troublesome'. Instead, you could create a thin wrapper module for 'troublesome' that provides jQuery and any other required "globals".
troublesome-module.js:
// Bring jQuery into scope for troublesome.
import jQuery from 'jquery';
// Import any other 'troublesome'-assumed globals.
// Paste or have build tool interpolate existing troublesome.js code here.
Then in your code you should be able to
import 'troublesome-module';
I've had a similar issue using jspm and dygraphs. The way i solved it was to use dynamic loading like you attempted using System.import but the important part was to chain-load each consecutive "part" using System.import again inside the promise onfulfillment handler (then) after setting the global namespace variable. In my scenario I actually had to have several import steps separated between then handlers.
The reason it didn't work with jspm, and probably why it didn't work for you as well is that the import ... from ... syntax is evaluated before any code, and definitely before System.import which of async.
In your case it could be as simple as:
import jQuery from 'jquery';
window.jQuery = jQuery;
System.import('troublesome').then(troublesome => {
// Do something with it...
});
Also note that the System module loader recommendation has been left out of the final ES6 specification, and a new loader spec is being drafted.
run npm install import-loader.
replace import 'troublesome' with import 'imports?jQuery=jquery,$=jquery!troublesome.
In my opinion, this is the simplest solution to your question. It is similar to the answer you wrote in your question #TN1ck, but without altering your webpack config. For more reading, see: https://github.com/webpack/imports-loader
Shimming is fine and there are various ways of resolving this, but as per my answer here, the simplest is actually just to revert to using require for the loading of the library that requires the global dependency - then just make sure your window. assignment is before that require statement, and they are both after your other imports, and your ordering should remain as intended. The issue is caused by Babel hoisting imports such that they all get executed before any other code.

Leading Underscore transpiled wrong with es6 classes

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;).

Load prototype enhancements with require.js

I am using require.js to load my modules which generally works fine. Nevertheless, I do have two additonal questions:
1) If you have a module that is like a helper class and defines additional methods for existing prototypes (such as String.isNullOrEmpty), how would you include them? You want to avoid using the reference to the module.
2) What needs to be changed to use jQuery, too. I understand that jQuery needs to be required but do I also need to pass on $?
Thanks!
1) If you have a module that is like a helper class and defines
additional methods for existing prototypes (such as
String.isNullOrEmpty), how would you include them? You want to avoid
using the reference to the module.
If you need to extend prototypes then just don't return a value and use it as your last argument to require:
// helpers/string.js
define(function() {
String.prototype.isNullOrEmpty = function() {
//
}
});
// main.js
require(['moduleA', 'helpers/string'], function(moduleA) {
});
2) What needs to be changed to use jQuery, too. I understand that
jQuery needs to be required but do I also need to pass on $?
The only requirement for jQuery is that you configure the path correct
require.config({
paths: {
jquery: 'path/to/jquery'
}
});
require(['jquery', 'moduleB'], function($, moduleB) {
// Use $.whatever
});
In my opinion it's unnecessary to use the version of RequireJS that has jQuery built into it as this was primarily used when jQuery didn't support AMD.
Nowadays it does and keeping it separate allows you to swap another library out easily (think Zepto).
2/ For jquery it's really simple :
require(["jquery", "jquery.alpha", "jquery.beta"], function($) {
//the jquery.alpha.js and jquery.beta.js plugins have been loaded.
$(function() {
$('body').alpha().beta();
});
});
More information on require site : http://requirejs.org/docs/jquery.html#get
1/ in my devs for such extension I did it in a global file without require module code.... and I include it in my app with require... not perfect, but it's work fine
global.js
myglobalvar ="";
(...other global stuff...)
myapp.js
// Filename: app.js
define([
(...)
'metrix.globals'
], function(.....){
myApp = {
(...)

Categories