socket.io 1.0.x not compatible with require.js? - node.js

Any time I try to load socket.io 1.0.x after require.js, it produces
Uncaught ReferenceError: io is not defined
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.10/require.min.js"></script>
<script src="https://cdn.socket.io/socket.io-1.0.6.js"></script>
<script type="text/javascript">
var socket = io('http://localhost');
</script>
But if I put socket.io before require.js, it doesn't produce any error:
<script src="https://cdn.socket.io/socket.io-1.0.6.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.10/require.min.js"></script>
<script type="text/javascript">
var socket = io('http://localhost');
</script>
This of course means that socket.io cannot be loaded by require.js this way:
requirejs.config({
paths: {
'socketio' : ['https://cdn.socket.io/socket.io-1.0.6'],
},
shim: {
'socketio': {
exports: 'io'
}
}
});
require(['socketio'], function(io){
console.log(window.io); //undefined
})

Socket.io 0.x
with socket.io 0.x you needed in to export io in the require.js config:
shim: {
'socket.io-client': {
exports: 'io'
}
}
and your js file:
define(['socket.io-client'], function () {
// io is exported here
var socket = io.connect ...
}
Socket.io 1.x
with socket.io 1.x, no need to export io in the require.js config file
and your js file:
define(['socket.io-client'], function (io) {
// io an argument of the function
var socket = io.connect ...
}
But in both cases, you have to use the io object in the define so that it is loaded correctly by require.js when you need it.
Also note that you need socket.io-client instead of socket.io in the browser

RequireJS creates a require and define set of methods... the socket.io js file sees this, and defines itself.
Include socket.io first or Use AMD

I specifically wanted the bower packages (ng-socket), and socket.io-client to cooperate via RequireJS. At first ng-socket errored because io was undefined, even with shimmed deps.
So, I added a startSocketIo.js file:
define([
'socket.io-client'],function(io) {
window.io = io;
});
and added a deps section to the root of my require config file:
deps: [
'./startSocketIo'
],
I see this as analogous to adding via a script tag and no better for performance, but it allowed me to keep using unmodified bower packages and using requirejs for all my js.

Related

How to get a single javascript page using r.js

I am doing my first try using requireJS and it works great !
I now would like to use the optimizer and i meet some issues when running my code in the browser.
I have these JS files:
/public/javascripts/build.js
/public/javascripts/main.js
/public/javascripts/lib/jquery.min.js
/public/javascripts/lib/require.min.js
/public/javascripts/a.js
/public/javascripts/b.js
/public/javascripts/c.js
a.js, b.js and c.js are modules i define for my application using requireJS.
main.js:
require.config({
paths: {
'jQuery': 'lib/jquery.min.js'
},
shim: {
'jQuery': {
exports: '$'
}
}
});
require(['a.js'], function(A){
var Entity = new A();
});
build.js
({
baseUrl: ".",
paths: {
requireLib: "lib/require.min",
jquery: "lib/jquery.min"
},
name: "main",
out: "main-built.js",
include: ["requireLib"]
})
Also i am wondering why do we have to specify the paths of the libraries into the build.js and not the other javascript files.
When i do not use the optimizer and only load the file
<script src="/javascripts/lib/require.min.js" data-main="/javascripts/main"></script>
it works great, but when i run r.js -o ./public/javascripts/build.js and only load
<script src="/javascripts/main-built.js"></script> i get the error Uncaught TypeError: undefined is not a function in the minified code.
How to explain that ?
Here are the logs i get when running r.js
Tracing dependencies for: main
Uglifying file: /public/javascripts/main-built.js
/public/javascripts/main-built.js
----------------
/public/javascripts/lib/require.min.js
/public/javascripts/a.js
/public/javascripts/b.js
/public/javascripts/lib/jquery.min.js
/public/javascripts/c.js
/public/javascripts/main.js
This is definitely wrong:
require(['a.js'], function(A){
var Entity = new A();
});
You should not use extensions in the list of dependencies you give to require or define. Modules should be named without extension. So here 'a', not 'a.js'. Using 'a.js' will cause RequireJS to fail loading what you really want once the optimizer has run. Let's say you have a file named a.js which has:
define(function () {
return function () {};
});
The optimizer will include it into your main-built.js file like this:
define("a", function () {
return function () {};
});
Note how the first parameter to define is now "a". This has been added by r.js. This is the name of the module. When you load main-built.js, a module named "a" is defined. When you use require with "a.js", you are telling RequireJS you want something in a file named a.js so RequireJS will go looking for that and ignore what is in main-built.js.
Also, jQuery 1.8 or over does not need a shim.
I just have added
shim: {
'jQuery': {
exports: '$'
}
}
into the build.js file, and it works perfectly !
Thanks !

AngularJS Protractor and Socket.IO

When setting up an E2E test with my existing angular app, I am having trouble getting starting since I am using socket.io in my application. Protractor can't find the socket.io files that are typically served by the node module since it's not part of my personal code.
I am getting the error:
UnknownError: Error Message => 'Can't find variable: io'
On the angular side:
angular.module('socket.io', []).factory(function($rootScope) {
var socket = io.connect(); // I believe the error is being thrown from here
return {
on: function (eventName, callback) {
socket.on(eventName, function () {...});
},
emit: function (eventName, data, callback) {
socket.emit(eventName, data, function () {...});
}
};
});
I believe that is because it's typically served by the node module:
<script type="text/javascript" src='/socket.io/socket.io.js'></script>
vs my socket.io factory shown above
<script type="text/javascript" src='js/services/socket-service.js'></script>
Is there a way to solve this without having the copy the socket.io.js file in to the client codebase?
Make sure that the file socket.io.js is being included before socket-service.js. You can either do thus manually or use a module loader such as requirejs (http://www.requirejs.org)

require js shim not working for this code

I have the following code.
<script src="js/libs/require.js"></script>
<script>
requirejs.config({
baseUrl:'js/modules/',
paths:{
'bbn':'../libs/backbone',
'underscore':'../libs/underscore'
},
shim:{
'bbn':{
exports:'B',
deps:['underscore']
}
}
})
requirejs(['bbn'], function(B){
console.log(B)
});
</script>
The function parameter B is not pointing to Backbone. Instead its getting logged as undefined.
I followed the following post and arrived to this point:
Loading Highcharts via shim using RequireJS and maintaining jQuery dependency
I see both underscore and backbone JavaScript files getting downloaded in firebug.
Underscore is not AMD compat either, so make sure you shim that too:
requirejs.config({
baseUrl:'js/modules/',
paths:{
'bbn':'../libs/backbone',
'underscore':'../libs/underscore'
},
shim:{
'bbn':{
exports:'Backbone',
deps:['underscore']
},
'underscore': {
exports: '_'
}
}
})
requirejs(['bbn'], function(Backbone){
console.log(Backbone)
});
You will see Underscore being downloaded but because it not defined as a proper module RequireJS just treats it as a normal JS file and doesn't get a return value
The latest version of Underscore (~1.6.0 as of writing this) is AMD-compatible. Do not use it as a shim or you may run into issues.

RequireJS does not run data-main script before loading required modules

My project includes the following files:
./index.html
./js/main.js
./js/vendor/require.js
./js/viewmodel/vm.js
The index.html has the following relevant snippet:
<script data-main="js/main.js" src="js/vendor/require.js"></script>
<script type="text/javascript">
require(['viewmodel/vm', 'ko'],
function(viewmodel, ko) {
ko.applyBindings(viewmodel);
}
);
</script>
The js/main.js file is as follows:
var root = this;
define('jquery', ['http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.3.js'], function () { return root.$; });
define('ko', ['http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.1.0.js'], function (ko) { return ko; });
The js/viewmodel/vm.js file...
define(['jquery', 'ko'],
function($, ko) {
return {
subject: 'world',
greeting: 'hello'
}
}
);
When you open a browser to index.html, then the browser tries to load a file called js/ko.js instead of using the module defined in main.js. It seems like the js file pointed to by the data-main attribute is not guaranteed to run before dependency resolution. This does not seem correct to me since one purpose of the data-main js file is to define require configuration (i.e. path, shim, etc). I am using require v2.1.2.
This works perfectly fine if I copy the contents of my main.js file into the script block in index.html. By "perfectly fine" I mean that it resolved ko to be a module and finds the appropriate CDN link to resolve ko instead of trying to download ./js/ko.js.
to use the data-main attribute for configuring your whole application, it is necessary that it is the single entry point for all your code.
your 2nd script block breaks this requirement by providing a 2nd entry point. since these entry points will resolve independently of each other (and asynchronously), you cannot rely on one to affect the other.
to resolve it, refactor your code in a way that provides a single entry point to your application and do your configuration via this entry point.
That's because requirejs sets the async. Attribute on the script.
The boolean async attribute on script elements allows the external
JavaScript file to run when it's available, without delaying page load
first.
This means that both scripts are loaded and evaluated parallel, so none of the two scripts can access methods or functions from the other one.
If you want to define requirejs variables in one script you mustn't load that script with require js.
For me there are three possibilities how you can solve that problem:
Add the content of main.js to your page (as you mention)
Load the main.js file without requirejs as normal script
Define the require config before loading the scripts (link to requirejs docu )
I had the same problem. The architecture of the site that i was working was components that was loading asynchronous at each part of the page.
Each component has its own html, css, and js code.
So, my solution is to keep a guard function for all the required dependency code, to protect them from running before the main javascript file:
index.html
<head>
<script type="text/javascript">
window.BeforeMainGuard = {
beforeMainLoadedFunctions: [],
hasMainLoaded: false,
guard: function( func ) {
console.assert( typeof func === 'function' );
if( this.hasMainLoaded ) {
func();
}else {
this.beforeMainLoadedFunctions.push( func );
}
},
onMainLoaded: function() {
for( var i = 0; i<this.beforeMainLoadedFunctions.length; ++i ) {
var beforeMainLoadedFunction = this.beforeMainLoadedFunctions[i];
beforeMainLoadedFunction();
}
this.beforeMainLoadedFunctions = null;
this.hasMainLoaded = true;
}
};
</script>
<script data-main="js/main.js" src="js/vendor/require.js"></script>
<script type="text/javascript">
window.BeforeMainGuard.guard( function() {
require(['viewmodel/vm', 'ko'],
function(viewmodel, ko) {
ko.applyBindings(viewmodel);
}
);
});
</script>
</head>
js/main.js
require.config({
// your config
});
require( [ 'AppLogic' ], function( AppLogic ){
AppLogic.Init();
window.BeforeMainGuard.onMainLoaded();
} );

requirejs , multipage , weird order of execution

I am using requirejs for a multipage application. I am having a weird issue with the order of execution of the main files. Here is my setup.
In all of my html pages ( This main has all the common js files that are required by all my pages)
then on each page (say page1.html):
<script data-main="main" src="require.js" />
<script>
require(['require','main'],function(require) {
require(['main-library']);
});
</script>
My main.js looks like:
require.config({
paths:{
'lib':'../lib',
'jquery': '../lib/jquery/jquery-1.7.1.min',
'jquery.iframe-transport' : '../lib/jquery.iframe-transport.min.js'
},
deps:['jquery','jquery-ui'],
shim: {
<other shim files>
},
});
require([<list of all other dependencies>], function() {
});
And the main-library.js looks like:
define('main-library',
['main',
'jquery.iframe-transport',
<other dependencies>
],
function() {
}
);
My expectation:
Was that the "main-library" will not start to load until the "main" and all the dependencies specified in the main are not loaded. I expect this since I require "main-library" in a nested require call on each page. see above.
The issue:
When my page1.html loads, it looks like the main-library.js starts loading even before the main.js has finished loading and main-library fails to get the require.config and hence the paths for the dependencies are not seen by it.
What am I doing wrong ?
Answering my own question. It was infact another require down in that page1.html that was loading the "main-library" again. So I had something like:
<script data-main="main" src="require.js" />
<script>
require(['require','main'],function(require) {
require(['main-library']);
});
</script>
...
... Other html ...
...
<script>
require(['main-library'],function(require) {
//do something.
});
</script>
This second require on main-library was the one that was failing to find the paths. Wrapping it with a require on main worked.

Resources