RequireJS optimized file returns undefined - javascript

We are trying to minify/obfuscate a js library, so that we can reuse it in other projects. Background info: I have done some js, but i'm new with AMD or requireJS.
Here's a reduced version of the library, retaining only the format:
(function (global, factory) {
if ( typeof exports === 'object' && typeof module !== 'undefined'){
factory(exports);
}
else if (typeof define === 'function' && define.amd){
define(['exports'], factory);
}
else{
global['SimpleMath'] = factory({});
}}(typeof global !== "undefined" ? global : self.window || self.global,
(function (exports) {
'use strict';
exports.square = function square(value){
return value * value;
}
exports['square'] = exports.square;
return exports;})));
Inside the main.html we have
requirejs(['./libs/SimpleMath'], function (smath){
console.log(smath);
console.log(require.s.contexts._.defined);
});
This works and we can see the loaded object print out.
Since we wish to optimize/obfuscate the code, we are trying requirejs' optimizer (single js file):
node ../../r.js -o name=SimpleMath out=SimpleMath.min.js baseUrl=.
And we do get something that looks like a minified file.
However, if now I try to load the minified version with the same method:
requirejs(['./libs/SimpleMath.min'], function (smath){
console.log(smath);
console.log(require.s.contexts._.defined);
});
we get 'undefined' for the print out instead.
I can't tell if the original code was wrong, or we are not using the optimizer correctly, or we totally got the concept wrong. Any help or pointers is much appreciated. Thanks in advance.

Answering myself in case someone might find it useful: The minified file was exporting a named module like this
define('SimpleMath',['exports'],t)..
we had to either
load it as a named module with require.config or,
make it anonymous, which seems to be the recommended option for our case.

Related

sharing variables in javascript ES6 without using an import statement?

I was looking for a javascript implementation of the Astar algorithm that i could steal for my own project, and google immediately led me to bgrins implementation on github:
https://github.com/bgrins/javascript-astar
I copied and pasted his astar.js, and quickly found myself confused on how i was supposed to access the code from my own js module. I took a look at his demo.js file in their repo, and quickly saw that there were no imports. demo.js immediately has access to the astar function as well as the graph object that it takes as a parameter, and i am a little confused by how they managed to accomplish this.
On line 129 of demo.js, they have
this.graph = new Graph(nodes);
without ever using an import statement.
looking closer at the top of astar.js, i see the following code:
(function (definition) {
/* global module, define */
if (typeof module === 'object' && typeof module.exports === 'object') {
module.exports = definition();
} else if (typeof define === 'function' && define.amd) {
define([], definition);
} else {
var exports = definition();
window.astar = exports.astar;
window.Graph = exports.Graph;
}
})(function () {
Clearly setting window.astar and window.Graph are a part of the answer here, but I find this whole code block a little confusing and very abstract. Can anybody help me to understand what they are doing right here?

THREE.js TypeError: 'global' is undefined in Firefox

So I tried to include threejs as a drop-in script into my code. No webpack, no browserify, no requirejs. Just a simple gulp/browsersync serve.
I load an external angular app and extend it. Now I need my own THREEjs Version within the codebase.
It gets loaded - but right in the first line they try to set the variable 'global' which doesn't seem to be defined. What am I missing?
// edit:
I am using a js api from another company. I don't know if they set the 'global' var, but Threejs definitely tries to use the var 'global' although I don't use it in a node setup. but in all examples it just works as a drop-in script.
If I use the minified version the error changes to
TypeError: global is undefined
*three.min.js:2:168
anonymous
https://localhost:9000/scripts/three.min.js:2:168
anonymous
https://localhost:9000/scripts/three.min.js:2:2*
and this originates from the following first lines of the three.js file:
function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.THREE = {}))); }(this, (function (exports) { 'use strict'; ...
//EDIT 2:
I finally maaged to find the error which is causing all this.
if youre using gulp-babel and include scripts with that snippet on top, babel tries to replace THIS with the current context, which is - of course - undefined. and thats why bable literally replaces this with undefined. so: never babel() your final vendor files!
The part of THREE.js that you show in your question is not a problem. If we focus only on the problem you've been having, and eliminate the code for the CommonJS and AMD cases, it boils down to this:
(function (global, factory) {
factory(global.THREE = {});
}(this, (function (exports) { 'use strict';
// ...
})));
This is a common pattern. Note that the first anonymous function is called with this as the first argument. So global is set to the value that this has in the global space. If the code above executes in a top-level execution context, then this will automatically have the value of the global object for the environment in which you run the code. In Node, that object is named global. So open a Node session and type:
global === this
You'll get true. In a browser the global object is named window. Open the console in debugging and type:
window === this
You'll get true. So what the code snippet with the anonymous function does is that it uses this to grab a reference to the global object irrespective of where the code executes. It does not have to check whether window exits or global exist, or self or anything else. Instead, it just passes this to the anonymous function and, this way, automatically gets a reference to the global object. There's nothing wrong with that method. It is super common and generally works.
However, it is possible to prevent the code from working properly. For instance if the code above is wrapped in another function and that function uses "use strict", then this will be undefined, and global will be undefined too. Here's an example:
(function() {
"use strict";
(function(global, factory) {
console.log("XXX", global); // You'll get "XXX undefined" here
}(this, (function(exports) {
'use strict';
})));
}());
Sometimes, build processes or code loading tools add such wrapping code, and then they mess up the original code.
Since "just getting babel to ignore certain files" is by no means trivial when working in gulp, a quick and dirty fix for this error is to swap this with window on the 5th line of three.js:
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.THREE = {})));
}(window, (function (exports) { 'use strict';
Is this what you need? Just add this one-liner at the very head of your code, in the global scope:
if (typeof global === "undefined"){global=window;}
This is going to reference the global object to the window object, which is the object containing all global variables in browser.

Using functions from external JavaScript libraries in Moodle plugins

I am making a Moodle plugin and wanted to use bowser to detect the user's web browser. I referenced the file by putting
$PAGE->requires->js( new moodle_url($CFG->wwwroot.MOODLE_TINYMCE_RECORDRTC_ROOT.'tinymce/js/bowser.js') );
in the plugin's plugintype_pluginname.php file (placeholders of course), and I call the bowser function from the plugin's module.js file.
When I load the plugin (it appears as a button in TinyMCE), the console throws ReferenceError: bowser not defined, so I'm assuming this means Moodle doesn't make the functions in Bowser globally available.
I read many in many places that I need to wrap my code in an AMD, or something to that effect, but after lots of reading it still goes over my head. Is there any way to make bowser's functions available to the main plugin module?
Note: This works for me in Moodle 3.3.2, ymmv.
Put bowser.js into my_plugin_folder/amd/src/.
When using the original bowser.js I got Uncaught TypeError: bowser._detect is not a function. I don't exactly understand why I get this error but here's one way to fix it: Replace the top code block in bowser.js with this one from umdjs/umd.
Your file should now look like this:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
root.returnExports = factory();
}
}(typeof self !== 'undefined' ? self : this, function () {
// module definition here
return bowser
}));
Moodle bundles all JavaScript modules together so that clients don't need to perform a separate HTTP request to get each one. This bundle is called first.js. It contains all modules that aren't lazy-loaded. If you load a Moodle page now it should contain the contents of bowser.js with some values replaced by Moodle.
If you don't want bowser to be loaded on every page, you can just rename it to bowser-lazy.js. Then it should only be loaded when you use it.
You can test if it worked by calling:
require(['plugintype_pluginname/bowser'], function(bowser) {
var ua = bowser._detect(navigator.userAgent);
console.log(ua);
});
Seems like you need to change the require call to use bowser-lazy instead of bowser when you want to use lazy-loading.

Call of anonymous function (vanilla js, pure js, no jquery)

I want to grab browser version and OS from useragent via js - this is a practicing example (cause yes - I know - feature detection is how you do it properly ;).
I stumbled across a little library that does so and now I'm trying to understand whats going on.
See this Codepen: http://codepen.io/anon/pen/gPWZGE?editors=001
obviously by running bowser.version - you get the version extracted from the browsers useragent. However the function bowser itself is an anonymous function - even though I can access elements within this function which has to do with this part of the code
!function (name, definition) {
var module;
var define;
if (typeof module != 'undefined' && module.exports) module.exports = definition();
else if (typeof define == 'function' && define.amd) define(definition);
else this[name] = definition();
}
To be honest I have hardly any idea whats going on there - can someone please explain what those lines are doing?
Thanks a lot
Ok, so step by step ...
first you have an IIFE
!function( name, definition ) {
// some code
}( 'bowser', function(){
// your module code
});
this calls the first function immediately with two parameters: the name of the module (here "bowser") and the actual definition function. In that definition function you create the module: You define any objects, properties and whatever else it needs to work. Important point is: You interface (the view the "outside" has on your module, has to be returned by that defintion function (in your codepen this is line 282: return bowser;).
The outer function then determines, how to publish your module:
First it looks for a CommonJS environment:
typeof module != 'undefined' && module.exports
This would be used, e.g., inside NodeJS. If it found the respective objects, it will export your module as a CommonJS module: module.exports = definition().
If this was not a CommonJS environment, the script looks, if it is an AMD (for example, requireJS):
typeof define == 'function' && define.amd
Again, if that matches, the module is exported as a AMD module: define(definition).
Finally, if this is neither CommonJS nor AMD, the script assumes it is a (vanilla) browser or something similar and just attaches the module to the global namespace:
this[name] = definition();
Note, that each time the definition function is called and the return value is exported as a module. This is why your definition function has to return the module interface.

Javascript Object from External file, program

I am so confused about JavaScript's object system. I know that everything is considered Object in JavaScript but in this code of Esprima, I don't see any statement to declare this project to be accessed with esrpima like the following line: (https://github.com/ariya/esprima/blob/master/esprima.js)
var syntax = esprima.parse(text);
My question is how and where to define something like esprima.parse(text) in Javascript so that it can be exported as external package and be accessed with the object name. I know how to define object like Object = {a: "B"}; but can't find a way to figure this out. Please help me!
(function (root, factory) {
'use strict';
// Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
// Rhino, and plain browser loading.
if (typeof define === 'function' && define.amd) {
define(['exports'], factory);
} else if (typeof exports !== 'undefined') {
factory(exports);
} else {
factory((root.esprima = {}));
}
}(this, function (exports) {
'use strict';
var Token,
TokenName,
...
That's because it doesn't. The variable name, esprima, comes from code that includes esprima. For example:
var esprima = require('esprima');
esprima.parse(text);
You could give the variable any other name:
var foo = require('esprima');
foo.parse(text);
All the the esprima.js file does is define an object which is exported. It does not dictate the name of the variable to which the object is assigned to eventually.
If the script is loaded in a browser, it actually does define esprima explicitly. You can see it in line 55:
factory((root.esprima = {}));
This creates an object and assigns it to root.esprima. But it's also a function call, so the object is passed to factory, which is the function defined in 57, which accept a parameter exports. This is where the code assigns all the properties to.

Categories

Resources