How to include SignalR with gulp build process? - javascript

When I run my Durandal app with main.js (not minified), it loads signalr.core and signalr.hubs correctly, however, after building with gulp, it fails to load signalr.hubs.
Here is my RequireJS config:
requirejs.config({
paths: {
'text': '../Scripts/text',
'durandal': '../Scripts/durandal',
'plugins': '../Scripts/durandal/plugins',
'transitions': '../Scripts/durandal/transitions',
'knockout': '../Scripts/knockout-3.2.0',
'knockout.validation': '../Scripts/knockout.validation',
'bootstrap': '../Scripts/bootstrap',
'jquery': '../Scripts/jquery-2.1.3',
'jquery.utilities': '../Scripts/jquery.utilities',
'toastr': '../Scripts/toastr',
'offline': '../Scripts/offline',
'signalr.core': '../Scripts/jquery.signalR-2.2.0.min',
"signalr.hubs": '../signalr/hubs?'
},
shim: {
'jquery.utilities': {
deps: ['jquery']
},
'bootstrap': {
deps: ['jquery'],
exports: 'jQuery'
},
'knockout.validation': {
deps: ['knockout']
},
'signalr.core': {
deps: ['jquery'],
exports: '$.connection'
},
'signalr.hubs': {
deps: ['signalr.core'],
}
}
});
define('jquery', function () { return jQuery; });
define('knockout', ko);
define('moment', moment);
define(['durandal/system', 'durandal/app', 'durandal/viewLocator', 'durandal/composition', 'global/session', 'knockout', 'knockout.validation', 'signalr.core', 'signalr.hubs'], function (system, app, viewLocator, composition, session) {
});
Here is my gulpfile:
var gulp = require('gulp');
var durandal = require('gulp-durandal');
gulp.task('durandal', function () {
durandal({
baseDir: 'app', //same as default, so not really required.
main: 'main.js', //same as default, so not really required.
output: 'main-built.js', //same as default, so not really required.
almond: true,
minify: true
})
.pipe(gulp.dest('app'));
});
I don't fully understand the minification process, but I do know that SignalR is not AMD compliant so that might be the issue. Also, the hub endpoint produces dynamic JavaScript so it would make sense that it can't be included in the build.
What am I missing to get signalr.hubs to play nicely with main-built.js? Should I load SignalR separately from RequireJS?

I guess there are ways to make the dynamic endpoint work, but as you already thought that's a bit of a different beast to treat. I would suggest you either use the proxyless approach (which removes the need of the dynamic endpoint, but you'll have to tweak your calls and event handlers a bit), or you add a step to your build process to serialize the dynamic endpoint through the signar.exe utility, as explained here.

Related

RequireJS Not Executing all Files

I've designed this application to have a main.js file, which is executed on all pages. Where applicable, I am creating JavaScript files per page. The per-page JS is executing fine, but I notice main.js often only loads on a hard refresh which included with a per-page javascript file. I'll illustrate the setup below:
<script src="/admin/js/require.config.js"></script>
<script src="/admin/js/lib/require.js" data-main="./app/user/form"></script>
require.config.js
var require = {
baseUrl: '/admin/js',
paths: {
main: 'app/main',
bootstrap: 'lib/bootstrap',
"datatables.net": 'lib/jquery.datatables',
datepicker: 'lib/bootstrap-datetimepicker',
dropzone: 'lib/dropzone',
moment: 'lib/moment',
notify: 'lib/bootstrap-notify',
paper: 'lib/paper-dashboard',
jquery: 'lib/jquery',
select: 'lib/bootstrap-select',
sortable: 'lib/Sortable.min',
swal: 'lib/sweetalert2',
switchTags: 'lib/bootstrap-switch-tags',
wizard: 'lib/jquery.bootstrap.wizard.min',
validate: 'lib/jquery.validate.min',
yummySearch: '/yummy/js/yummy-search',
zxcvbn: 'lib/zxcvbn'
},
shim:{
bootstrap:{
deps: ['jquery']
},
validate: {
deps: ['jquery']
},
wizard:{
deps: ['bootstrap']
},
select:{
deps: ['bootstrap']
},
datepicker:{
deps: ['bootstrap','moment']
},
switchTags:{
deps: ['bootstrap']
},
paper: {
deps: ['bootstrap','switchTags']
},
notify: {
deps: ['jquery','bootstrap']
},
"datatables.net": {
deps: ['jquery']
},
main: {
deps: ['paper','notify','moment','datepicker','swal']
}
},
deps: ['main']
};
main.js
require(['jquery','swal','yummySearch','notify'], function($, swal) {
console.log('does not always execute');
});
app/user/form.js
define(['jquery','swal','validate','datatables.net'], function($,swal) {
console.log('will always run when loaded via data-main');
})
I am open to redoing this architecture if there are better ways, but would like to at least figure out this problem I am experiencing.
As far as I know, deps property in the requirejs config is an array of dependencies to load. Useful when require is defined as a config object before require.js is loaded, and you want to specify dependencies to load as soon as require() is defined. Saying that, it only happens once, exactly when the require.js is loaded and is the reason your main.js only execute once.
Note that using deps is just like doing a require([]) call to any other module, but done as soon as require.js is loaded and the loader has processed the configuration.
If you want your main.js to run on every page, then you will need to manually required
app/user/form.js
define(['main','jquery','.... other deps'], function(main, $, others) {
console.log('main will always run');
})

Why I have to use 'include' again in grunt-requirejs to optimize into a single js file?

I'm trying to use grunt-requirejs to optimize my requirejs project into a single production js file. However, I'm not sure if my Gruntfile.js is correct because I have to specify which model to include during the optimization again, which seems wrong to me as I specify mainConfigFile already. I thought it's going to read everything from my requirejs file. I asked this because I have a lot of modules and I don't want to DRY.
Here's my Gruntfile.js
requirejs : {
compile : {
options: {
almond: true,
baseUrl : 'src/Web/Scripts',
mainConfigFile : 'src/Web/Scripts/main-new.js',
out: 'src/Web/Scripts/out.js',
optimize: 'uglify2',
include: ['main-new',
'app/app.js',
'app/viewmodels/home/home.mobile.js',
'zepto',
'hammer',
'zepto-hammer',
'zepto-dragswipe',
'knockout',
'knockout-validation',
'knockout-postbox',
'knockout-mapping',
'knockout-notification',
'q',
'underscore'
]
}
}
},
And this is my main-new.js file
requirejs.config({
baseUrl: '/m/Scripts',
paths: {
'jquery': 'vendors/jquery/jquery-1.10.2',
'jquery-cookie': 'vendors/jquery/jquery.cookie-1.4.1',
'zepto': 'vendors/zepto/zepto',
'hammer': 'vendors/hammer/hammer',
'zepto-hammer': 'vendors/zepto/zepto.hammer',
'zepto-dragswipe': 'vendors/zepto/zepto.dragswipe',
'knockout': 'vendors/knockout/knockout-3.1.0',
'knockout-validation': 'vendors/knockout/knockout.validation',
'knockout-postbox': 'vendors/knockout/knockout-postbox',
'knockout-mapping': 'vendors/knockout/knockout.mapping',
'knockout-notification': 'vendors/knockout/knockout-notification-1.1.0',
'viewmodels': 'app/viewmodels',
'service': 'app/services',
'config': 'app/config',
'helpers': 'app/helpers',
'q': 'vendors/q/q-0.9.6',
'underscore': 'vendors/underscore/underscore-min'
},
shim: {
'knockout-validation': ['knockout'],
'knockout-mapping': ['knockout'],
'knockout-postbox': ['knockout'],
'knockout-notification': ['knockout'],
'jquery-cookie': ['jquery'],
'hammer': ['zepto'],
'zepto-hammer': ['zepto', 'hammer'],
'zepto-dragswipe': ['zepto', 'hammer'],
'zepto': {
exports: 'Zepto'
},
'underscore': {
exports: '_'
}
}
});
In include section you should put modules that is required dynamically like:
require(['path/' + moduleName], function() { ... })
Because r.js can't predict at build time what you will be requiring in runtime.
Everything else should be resolved by dependency arrays in your modules.

The jquery.fileupload plugin causes require.js to randomly fail imports

I'm using require.js and have a page with an from that used jquery.fileupload. After introducing the plugin I now see some files fail to be imported before the define call back is executed. This causes random errors where the libraries can't find their dependencies. It's as though require.js is moving on before all the dependencies can be resolved.
I've followed these instructions:
https://github.com/blueimp/jQuery-File-Upload/wiki/How-to-use-jQuery-File-Upload-with-RequireJS
But beyond that it's a very vanilla install. I'm using the minified versions of libraries where possible. Any insight is welcome.
here's the main.js:
(function () {
'use strict';
require.config({
baseUrl: '/js',
waitSeconds: 800,
paths: {
jquery: ['//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min',
'lib/jquery/jquery-2.0.3.min'],
'jquery.fileupload': 'lib/jquery.fileupload/jquery.fileupload',
'jquery.fileupload-ui': 'lib/jquery.fileupload/jquery.fileupload-ui',
'jquery.fileupload-image': 'lib/jquery.fileupload/jquery.fileupload-image',
'jquery.fileupload-validate': 'lib/jquery.fileupload/jquery.fileupload-validate',
'jquery.fileupload-video': 'lib/jquery.fileupload/jquery.fileupload-video',
'jquery.fileupload-audio': 'lib/jquery.fileupload/jquery.fileupload-audio',
'jquery.fileupload-process': 'lib/jquery.fileupload/jquery.fileupload-process',
'jquery.ui.widget': 'lib/jquery.ui/jquery.ui.widget',
'jquery.iframe-transport': 'lib/jquery.iframe-transport/jquery.iframe-transport',
'load-image': 'lib/load-image/load-image.min',
'load-image-meta': 'lib/load-image/load-image-meta',
'load-image-exif': 'lib/load-image/load-image-exif',
'load-image-ios': 'lib/load-image/load-image-ios',
'canvas-to-blob': 'lib/canvas-to-blob/canvas-to-blob.min',
tmpl: 'lib/tmpl/tmpl.min',
bootstrap: 'lib/bootstrap/bootstrap',
bootstrapTab: 'lib/bootstrap/bootstrap-tab',
EventEmitter: 'lib/event_emitter/EventEmitter',
linkedin: ['//platform.linkedin.com/in.js?async=true',
'http://platform.linkedin.com/in.js?async=true'],
skinny: 'lib/skinny/skinny',
selectize: 'lib/selectize/selectize.min',
sifter: 'lib/sifter/sifter',
microplugin: 'lib/microplugin/microplugin.min'
},
shim: {
bootstrap: {
deps: ['jquery'],
},
bootstrapTab: {
deps: ['jquery', 'bootstrap'],
},
linkedin: {
exports: 'IN'
},
selectize: {
deps: ['jquery', 'sifter', 'microplugin']
},
'jquery.iframe-transport': {
deps: ['jquery']
}
}
});
require(['app'], function (App) {
App.initialize();
});
}());
And the from code:
define([], function () {
'use strict';
return function () {
require(['jquery', 'tmpl', 'load-image', 'canvas-to-blob',
'jquery.iframe-transport', 'jquery.fileupload-ui'], function ($) {
$('#product').fileupload({
url: '/products/create'
});
});
};
});
The module gets called after the page has been loaded.
It's also worth noting that all files are downloaded successfully. No 404's, etc.
It turns out there is a flaw in the minified version of load-image.js that breaks how the dependencies load. I don't have exact proof as to why, it could be the smaller size causes a race condition, or it could be something weird in that particular file. What I do know is the minified version causes the random errors and the normal version does not (this is off master so I suppose I was taking a risk).
I raised a flag here
EDIT: it turns out the minified version of the plugin includes all the extensions which explains the odd dependency behavior.
The Answer from matt is the best solution in this case. Thanks a million, it save us a lot of time.
In requirejs.config, you have to add the load-image dependecies separatly - file by file.
For example:
require.config({
'jquery.ui.widget' : 'lib/jQuery-File-Upload-9.9.2/js/vendor/jquery.ui.widget',
'jquery.fileupload':'lib/jQuery-File-Upload-9.9.2/js/jquery.fileupload',
'jquery.fileupload-ui': 'lib/jQuery-File-Upload-9.9.2/js/jquery.fileupload-ui',
'jquery.fileupload-image': 'lib/jQuery-File-Upload-9.9.2/js/jquery.fileupload-image',
'jquery.fileupload-validate':'lib/jQuery-File-Upload-9.9.2/js/jquery.fileupload-validate',
'jquery.fileupload-audio':'lib/jQuery-File-Upload-9.9.2/js/jquery.fileupload-audio',
'jquery.fileupload-video':'lib/jQuery-File-Upload-9.9.2/js/jquery.fileupload-video',
'jquery.fileupload-process': 'lib/jQuery-File-Upload-9.9.2/js/jquery.fileupload-process',
'jquery.fileupload-jquery-ui': 'lib/jQuery-File-Upload-9.9.2/js/jquery.fileupload-jquery-ui',
'jquery.iframe-transport': 'lib/jQuery-File-Upload-9.9.2/js/jquery.iframe-transport',
'load-image':'lib/load-image-1.10.0',
'load-image-meta':'lib/load-image-meta-1.10.0',
'load-image-ios':'lib/load-image-ios-1.10.0',
'load-image-exif':'lib/load-image-exif-1.10.0',
'canvas-to-blob':'lib/canvas-to-blob-2.0.5',
'tmpl':'lib/tmpl.2.4.1'
}
});
call in html site:
requirejs(['jquery',
'jquery.ui.widget',
'tmpl',
'load-image',
'jquery.iframe-transport',
'jquery.fileupload-ui'], function () {
$('#fileupload').fileupload({
url: 'photo-upload.html'
});
}
);
One possibility modify the shim:
shim: {
bootstrap: {
deps: ['jquery'],
},
bootstrapTab: {
deps: ['jquery', 'bootstrap'],
},
linkedin: {
exports: 'IN'
},
selectize: {
deps: ['jquery', 'sifter', 'microplugin']
},
'jquery.iframe-transport': {
deps: ['jquery']
},
'jquery.fileupload-ui':{
deps: ['jquery']
}
Another option downgrade jquery to 1.X (this is because the sample page is using jquery 1.X)

Getting blank page using RequireJS and Grunt

I'm going a bit crazy here. I'm trying to use Grunt to go through a large RequireJS-based project and combine and minify the results during the deployment process. Here is my grunt process (using grunt-contrib-requirejs):
requirejs: {
compile: {
options: {
baseUrl: "public/js",
mainConfigFile: "public/js/config.js",
name: 'config',
out: "public/js/optimized/script.min.js",
preserveLicenseComments: false
}
}
}
Initially, I was taking the outputted script and placing it in the HTML -- but this lead to the 'define is undefined' error that means that RequireJS wasn't evoked. So instead, I'm putting in the HTML like this:
<script data-main="js/optimized/script.min" src="js/vendor/require.js"></script>
However, now I'm only getting a blank page.
The closest thing I can find out there that sounds like this is here, but's not being super helpful right now. For reference, I was using this as a starting point of my project -- however, when I run it, everything seems to be working for them but I can't find the differences.
Here is my config.js file:
require.config({
//Define the base url where our javascript files live
baseUrl: "js",
//Increase the timeout time so if the server is insanely slow the client won't burst
waitSeconds: 200,
//Set up paths to our libraries and plugins
paths: {
'jquery': 'vendor/jquery-2.0.3.min',
'underscore': 'vendor/underscore.min',
'backbone': 'vendor/backbone.min',
'marionette': 'vendor/backbone.marionette',
'mustache': 'vendor/mustache.min',
'bootstrap': 'vendor/bootstrap.min',
'bootbox': 'vendor/bootbox.min',
'jquery-ui': 'vendor/jquery-ui-1.10.2',
'app-ajax': '../conf/app-ajax',
'common': 'common',
'moment': 'vendor/moment.min'
},
//Set up shims for non-AMD style libaries
shim: {
'underscore': {
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'marionette': {
deps: ['backbone', 'underscore', 'jquery'],
exports: 'Marionette'
},
'mustache': {
exports: 'mustache'
},
'bootstrap': {
deps: ['jquery']
},
'bootbox': {
deps: ['jquery', 'bootstrap'],
exports: 'bootbox'
},
'jquery-ui': {
deps: ['jquery']
},
'jquery-layout': {
deps: ['jquery', 'jquery-ui']
}
}
});
//Initalize the App right after setting up the configuration
define([
'jquery',
'backbone',
'marionette',
'common',
'mustache',
'bootbox',
'controllers/GlobalController',
'routers/GlobalRouter'
],
function ($, Backbone, Marionette, Common, Mustache, bootbox) {
//Declare ECMAScript5 Strict Mode first and foremost
'use strict';
//Define the App Namespace before anything else
var App = Common.app_namespace || {};
//Create the Marionette Application
App.Application = new Marionette.Application();
//Add wrapper region, so we can easily swap all of our views in the controller in and out of this constant
App.Application.addRegions({
wrapper: '#wrapper'
});
// Set up Initalizer (this will run as soon as the app is started)
App.Application.addInitializer(function () {
//Reach into Marionette and switch out templating system to Mustache
Backbone.Marionette.TemplateCache.prototype.compileTemplate = function (rawTemplate) {
return Mustache.compile(rawTemplate);
};
var globalController = new App.Controllers.GlobalController();
var globalRouter = new App.Routers.GlobalRouter({
controller: globalController
});
//Start Backbone History
Backbone.history.start();
});
//Start Application
App.Application.start();
}
);
Okay, so this is the crazy simple fix:
In the module that's declared after the require.config, use 'require' instead of 'define' when declaring the module.
If you use 'define', it added 'config' as a dependency of that module, which broke the whole thing. Craziness!

How to optimize requireJS application setup?

I have a working application using requirejs, which I'm trying to optimize. I'm launching the application in 2 steps:
First I start with a setup file setup.js, which I'm calling from the page source code:
<script data-main="../js/setup" src="../js/l/require/require.js"></script>
This file contains my require setup
var IS_LOCAL = /(:\/\/localhost|file:\/\/)/.test(document.location.href);
requirejs.config({
waitSeconds: (IS_LOCAL ? 2 : 45),
baseUrl: "../js",
paths: {
intro: 'intro',
overrides: 'overrides',
jquery: 'l/jquery/jquery-1.8.2.min',
mobile: 'l/jquery-mobile/jquery-mobile-1.3pre.min',
multiview: 'p/multiview/multiview.min',
respond: 'p/respond/respond.min',
i18next: 'p/i18next.min.js'
},
shim: {
'overrides': { deps: ['jquery'] },
'mobile': { deps: ['jquery'] },
'multiview': { deps: ['jquery', 'mobile'] },
'respond': { deps: ['jquery'] },
'i18next': { deps: ['jquery'] }
}
});
// launch application - works, but I'm not happy with it
requirejs('overrides','jquery','mobile','multiview','respond','i18next','intro'],
function (overrides, $, mobile, multiview, respond, i18next, Intro) {
'use strict';
Intro.start(overrides, $, mobile, multiview, respond, i18next);
}
);
This "triggers" my application controller intro.js, which looks like this:
define([], function () {
'use strict';
var start = function () {
require(['overrides','jquery','mobile','multiview','respond','i18next'],
function () {
// stuff
}
); // end require
} // end start
return {
"start": start
};
});
I'm still figuring out my way with requireJS, so while the above initializes correctly, I'm not sure if this is the best way to handle. Specifically I'm wondering:
Questions:
1) In my setup.js file, I'm triggering Intro.start() with plenty of parameters. Do I need the parameters there or should I put them inside intro.js when I'm doing my second require call?
2) Do I need the first require at all, because I'm requiring all files and then trigger a "file" (intro), which requires everything again?
3) How do I make objects available in intro.js? Say I need the i18next object inside intro.js to do some manual translations. Do I export in the shim and pass along through my two requires?
Thanks for some insights!

Categories