RequireJS + Optimizer Include modules defined on the main file - requirejs

I'm using Optimizer for the first time and I am running in some issues or questions.
I'm trying to optimize a main file and it puts, like I've expected, the jQuery, Backbone and Require modules ( and uses then across the whole navigation). But let's say I have a jQuery Plugin that I use on several views. I've tried to add it in the main file using the "include" option on the build.js file. It adds it ( e.g jQuery Slides ) but as I have a view with define("jquery-slides") ( again, an example ) the browser loads the file of the plugin again. Even if it is on the main built file.
Is this suppose to happen? Can I fix this?
Thanks.
Here is some code. Hope it helps =)
build.js
{
baseUrl: "javascripts/",
appDir: "..",
dir: "dist",
name: "main-site",
include: ['libs/requirejs/require', jquery-slides'],
insertRequire: ['main-site'],
paths: {
"main-site": 'main-site',
'jquery': 'libs/jquery/jquery',
'jquery-slides': 'libs/jquery/plugins/slides.min.jquery'
}
}
main-site.js
require.config({
baseUrl: "/javascripts/",
paths: {
'jquery': 'libs/jquery/jquery',
'underscore': 'libs/underscore/underscore',
'bootstrap': 'libs/bootstrap/bootstrap.min',
'datepicker': 'libs/bootstrap/plugins/bootstrap-datepicker',
'backbone': 'libs/backbone/backbone.max',
'backbone-paginator': 'libs/backbone/plugins/backbone.paginator',
'backbone-validation': 'libs/backbone/plugins/backbone.validation',
'text': 'libs/requirejs/text',
'templates': '/templates/site',
'views': 'views/site',
'jquery-cookie': 'libs/jquery/plugins/jquery.cookie',
'jquery-raty': 'libs/jquery/plugins/jquery.raty.min',
'jquery-slides': 'libs/jquery/plugins/slides.min.jquery'
},
shim: {
'backbone-paginator': ['backbone'],
'bootstrap': ['jquery'],
'datepicker': ['bootstrap'],
'jquery-cookies': ['jquery'],
'jquery-raty': ['jquery'],
'jquery-slides': ['jquery'],
'backbone-validation': ['backbone']
}
});
require([
'app-site'
], function(App) {
$(function(){
App.initialize();
});
});

Instead of using include I recommend you to declare the modules you want to build. In this way requirejs will package the module and all its dependencies in the optimized bundle.
{
baseUrl: "javascripts/",
appDir: "..",
dir: "dist",
paths: {
"main-site": 'main-site',
'jquery': 'libs/jquery/jquery',
'jquery-slides': 'libs/jquery/plugins/slides.min.jquery'
},
modules : [
{
name : 'main-site',
}
]
}
Further considerations:
If you have jquery-slides included as a dependency in any of your modules define(['jquery-slides'], function() {... } you don't need to use the include directive since all the dependencies of that module will be included in the optimized file
See the documentation of the modules property in this link
https://github.com/jrburke/r.js/blob/master/build/example.build.js#L330
Use the property mainConfigFile to avoid duplications https://github.com/jrburke/r.js/blob/master/build/example.build.js#L35
Good luck and I hope this helps you

Related

Bootstrap library with requirejs

I am trying to use bootstrap with require js. So far jquery, underscore and boostrap are loading fine, but I am having issues with one library not loading: bootstrap-tagsinput. How can I debug requirejs and see whether this library is loading or not?
Here is my common.js
requirejs.config({
shim: {
'jquery': {
exports: '$'
},
'underscore': {
exports: '_'
},
'bootstrap': {
deps: [ "jquery" ]
},
'bootstrap-tagsinput': {
deps: [ "bootstrap" ]
}
},
baseUrl: "/",
paths: {
'jquery': [
'//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min',
'jquery/jquery.min'
],
'underscore': [
'//underscorejs.org/underscore-min',
'underscore/underscore-min'
],
'bootstrap': [
'//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min',
'bootstrap/dist/js/bootstrap.min'
],
'bootstrap-tagsinput': 'bootstrap-tagsinput/dist/bootstrap-tagsinput.min'
}
});
Require.js is invented to handle dependencies between modules, so it is quite clear that you specify those dependencies in the modules where you use it like you said in your own answer already. The function given as the second parameter to define() gets handles to those modules in the order they are given.
As $ is normally used for jQuery library, it is not a good idea to use $ for something else, because this would create a lot of confusion. But normally you need jQuery in a module as well, so your define() call will most probably look like
define['jquery', 'bootstrap-tagsinput'], function($) {
...
});
In this case $ is bound to jQuery, not to bootstrap-tagsinput. It is instead called via the jQuery mechanisms, so you don't need a second parameter in the function.
I figured out that 'bootstrap-tagsinput' needs to be defined where it's being used. For example, in a page that makes use of this library:
define(['bootstrap-tagsinput'], function() {
// js for the page here
});
And in order to auto-load modules that should always be available (eg. bootstrap or jquery) we can do the same.
define(['bootstrap'], function() {
// main.js contents
});

RequireJS baseUrl and multiple optimized files

I've separated out my 3rd party libraries from my app code and grouped them all together into a vendor.js file for requirejs to pull in. In my build.js file, I'm using the modules syntax to optimize my main application, excluding the vendor scripts, and to optimize the vendor.js file. The only issue I'm having is when my compiled main module requests vendor, it's getting the baseUrl from the config file and so doesn't load the optimized vendor.js file. My build.js file looks like this:
({
baseUrl: "js",
dir: "build",
mainConfigFile: "js/main.js",
removeCombined: true,
findNestedDependencies: true,
skipDirOptimize: true,
inlineText: true,
useStrict: true,
wrap: true,
keepBuildDir: false,
optimize: "uglify2",
modules: [
{
name: "vendor"
},
{
name: "main",
exclude: ["vendor"]
}
]
})
And my main.js file looks like this:
requirejs.config({
baseUrl: "js",
paths: {
jquery: 'vendor/jquery/jquery-2.1.3.min',
bootstrap: 'vendor/bootstrap/bootstrap.min',
handlebars: 'vendor/handlebars/handlebars-v2.0.0',
backbone: 'vendor/backbone/backbone-min',
underscore: 'vendor/lodash/lodash.underscore',
marionette: 'vendor/marionette/backbone.marionette.min',
models: 'common/models',
collections: 'common/collections'
}
});
define(['module', 'vendor'], function(module) {
var configPath = "config/config." + module.config().env;
require([configPath, 'app', 'jquery'], function(config, Application, $) {
$(function() {
// Kick off the app
Application.start(config);
});
});
});
All development is done in the js folder, and my build.js file is outside that folder. The optimized files end up in build, a sibling to js, but when I include my main file like this:
<script data-main="build/main" src="js/vendor/require/require.max.js"></script>
It ends up loading js/vendor.js for that define() call. What am I missing here? How can I tell the optimized main file to load build/vendor.js instead, yet allow the unoptimized version to still load js/vendor.js?
Ok, I figured this out. It was simple, really, just a case of too much configuration. When you load your script using data-main, the baseUrl is set relative to that file. So, if I specified js/main, the baseUrl would be js. But, since I explicitly specified baseUrl in the config block of main.js, that gets overridden, both in development and production. By removing baseUrl: "js" from main.js, everything works as expected. The development build loads everything relative to js and the production build loads everything (vendor.js) relative to build when I change data-main to build/main. Hope this helps somebody else someday.
requirejs.config({
paths: {
jquery: 'vendor/jquery/jquery-2.1.3.min',
...
}
});
// 'vendor' is loaded relative to whatever directory main.js is in
define(['module', 'vendor'], function(module) {
...
});

RequireJS - Performance difference with define vs require

I'm trying to understand if there are any potential downsides to the way I am using requirejs. I am fan of using the commonjs syntax, a typical module will look like:
define(function(require) {
"use strict";
var Backbone = require('backbone');
var Templates = require('templates');
var User = require('accounts/models').User;
...
I then compile my application down to a single JS file. My build config looks like so:
name: 'main',
mainConfigFile: '<%= build_dir %>/<%= main_app %>',
out: '<%= build_dir %>/app.min.js',
optimize: 'none',
include: ['main'],
insertRequire: ['main'],
almond:true,
cjsTranslate: true,
findNestedDependencies: true,
preserveLicenseComments: false
My question is, does using this commonjs format pose any performance or optimisation issues that I would avoid if using a define array instead? As I understand it, the cjsTranslate argument converts it to a define call anyway, but I wasn't sure if there was something I was missing? Is it purely a preference / code readability thing?
For reference, my config file (main.js):
require.config({
paths: {
// Libraries
jquery: '../../vendor/jquery/jquery',
underscore: '../../vendor/underscore/underscore',
backbone: '../../vendor/backbone/backbone',
handlebars: '../../vendor/handlebars.js/dist/handlebars',
modernizr: '../../vendor/modernizr/modernizr',
templates: '../templates'
},
shim: {
"underscore":{
exports: "_"
},
"backbone": {
deps: ["underscore", "jquery", "modernizr", "moment"],
exports: "Backbone"
},
"handlebars": {
exports: "Handlebars"
}
}
});
Is it purely a preference / code readability thing?
Yes absolutely. Once you build, R.js convert Commonjs to plain AMD.
The only performance hit would be when using unbuilded project. But for development it is fast enough (I see no clear difference myself).

whats the correct way to define custom builds of vendor libraries in bower_components?

I am using the yeoman webapp generator with requirejs and I have installed canjs using bower.
canjs has a dir structure like the following
app/bower_components/canjs/amd/can.js
app/bower_components/canjs/amd/can/control.js
app/bower_components/canjs/amd/can/control/route.js
etc..
Inside the can.js file is the following.
define(["can/util/library", "can/control/route", "can/model", "can/view/ejs", "can/route"], function(can) {
return can;
});
All of the dependancy files (control.js, route.js) have their dependancies listed inside define() functions.
What I want to do is customise the canjs build and replace "can/view/ejs" with "can/view/mustache". I can get it to work by changing the reference to ejs within the can.js file but that means I'm editing a vendor file inside of bower_components dir.
I have tried to create a mycan.js build within my scripts dir which looks the same as the can.js file (except for the mustache dependency change) in bower_components and then I change the config to look like this.
require.config({
paths: {
jquery: '../bower_components/jquery/jquery',
can: '../bower_components/canjs/amd/can',
etc..
Then I require the mycan module in any of my files that need it.
This will work properly if I comment out the code inside bower_components/canjs/amd/can.js but if I don't comment the file out, it will require both builds (including the can/view/ejs file I didn't want).
In the require docs http://requirejs.org/docs/api.html under usage 1.1, it has an example of
• www/
• index.html
• js/
• app/
• sub.js
• lib/
• jquery.js
• canvas.js
• app.js
and in app.js:
requirejs.config({
//By default load any module IDs from js/lib
baseUrl: 'js/lib',
//except, if the module ID starts with "app",
//load it from the js/app directory. paths
//config is relative to the baseUrl, and
//never includes a ".js" extension since
//the paths config could be for a directory.
paths: {
app: '../app'
}
});
// Start the main app logic.
requirejs(['jquery', 'canvas', 'app/sub'],
function ($, canvas, sub) {
//jQuery, canvas and the app/sub module are all
//loaded and can be used here now.
});
Here they are using a path which is a directory, not a file. The sub module is getting found because it matches app/sub with the app in the paths config.
If I define my own version of can within the main.js file which contains the require.config then it seems to work but then when I go to build the app, it says
tim#machine:~/server/javascript/yoman:ruby-1.9.3: (master)$ grunt
Running "jshint:all" (jshint) task
Linting app/scripts/main.js ...ERROR
[L54:C1] W117: 'define' is not defined.
define('can', [
Warning: Task "jshint:all" failed. Use --force to continue.
Aborted due to warnings.
Elapsed time
default 567ms
jshint:all 124ms
Total 691ms
Whats the correct way for me to make a custom build of vendor libraries within bower_components?
Here is my main.js. This version works but fails when linting.
require.config({
paths: {
jquery: '../bower_components/jquery/jquery',
bootstrapAffix: '../bower_components/sass-bootstrap/js/affix',
bootstrapAlert: '../bower_components/sass-bootstrap/js/alert',
bootstrapButton: '../bower_components/sass-bootstrap/js/button',
bootstrapCarousel: '../bower_components/sass-bootstrap/js/carousel',
bootstrapCollapse: '../bower_components/sass-bootstrap/js/collapse',
bootstrapDropdown: '../bower_components/sass-bootstrap/js/dropdown',
bootstrapPopover: '../bower_components/sass-bootstrap/js/popover',
bootstrapScrollspy: '../bower_components/sass-bootstrap/js/scrollspy',
bootstrapTab: '../bower_components/sass-bootstrap/js/tab',
bootstrapTooltip: '../bower_components/sass-bootstrap/js/tooltip',
bootstrapTransition: '../bower_components/sass-bootstrap/js/transition',
can: '../bower_components/canjs/amd/can'
},
shim: {
bootstrapAffix: {
deps: ['jquery']
},
bootstrapAlert: {
deps: ['jquery']
},
bootstrapButton: {
deps: ['jquery']
},
bootstrapCarousel: {
deps: ['jquery']
},
bootstrapCollapse: {
deps: ['jquery']
},
bootstrapDropdown: {
deps: ['jquery']
},
bootstrapPopover: {
deps: ['jquery']
},
bootstrapScrollspy: {
deps: ['jquery']
},
bootstrapTab: {
deps: ['jquery']
},
bootstrapTooltip: {
deps: ['jquery']
},
bootstrapTransition: {
deps: ['jquery']
}
}
});
define('can', [
'can/util/library',
'can/control/route',
'can/construct/proxy',
'can/model',
'can/view/mustache',
'can/route'
], function(can) {
'use strict';
return can;
});
require(['app', 'jquery'], function (app, $) {
'use strict';
// use app here
console.log(app);
console.log('Running jQuery %s', $().jquery);
});
JSHint is complaining because require is in an external file. All require's functions are defined before your script loads, but because they're not inside the script JSHint thinks they're custom code which you forgot to define. This is an easy fix; add a predef config so that define, require are already passed to JSHint before it starts linting your files.
jshint: {
options: {
// all of your other options...
predef: ['define', 'require']
},
files : ['app/scripts/main.js']
},

Configure grunt watch to run Jasmine tests against an app using requirejs

In an attempt to level up my general coding skills... and to learn something new.
I've started attempting to wire up a front end only solution consisting of
Durandal
Jasmine - [added via npm]
Grunt Watch to monitor & run my tests as my code files change - [added via npm]
Feel free to correct me, as this is all based on my experimentation in the last 2 days. Most of this is new to me. My goal is to have something similar as to what angular has with karma.
Now I am aware that that the Durandal project (comes with a custom spec runner, as found in the github solution)
My setup:
gruntfile.js
module.exports = function(grunt) {
var appPath = 'App/viewmodels/*.js';
var testPath = 'Tests/**/*.js';
grunt.initConfig({
jasmine: {
pivotal: {
src: appPath,
options: {
specs: testPath,
template: require('grunt-template-jasmine-requirejs'),
templateOptions: {
requireConfigFile: 'SpecRunner.js'
}
}
}
},
jshint: {
all: [testPath, appPath],
options: {
curly: true
}
},
watch: {
files: [testPath, appPath],
tasks: ['jshint','jasmine']
}
});
grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['jshint','jasmine']);
};
SpecRunner.js
require.config({
paths: {
jquery: 'Scripts/jquery-1.9.1',
knockout: 'Scripts/knockout-2.3.0'
},
shim: {
knockout: {
exports: "ko"
}
}
});
When I run grunt, I get a Illegal path or script error: ['plugins/http']
(I've sorted out the ko issue in the screenshot)
Question:
How would i go about setting up my gruntfile to require any dependencies. I'm quite new to require, and I'm not sure how to configure it to make my tests aware of where to find things like 3rd party libraries and other custom js files for that matter
SpecRunner require.config is missing Durandal specific path information. If you set the baseUrl to 'App' then the paths below matches the HTML samples or StarterKit layout. If your layout is different you'd have to adjust this accordingly.
requirejs.config({
paths: {
'text': '../lib/require/text',
'durandal':'../lib/durandal/js',
'plugins' : '../lib/durandal/js/plugins',
'transitions' : '../lib/durandal/js/transitions',
'knockout': '../lib/knockout/knockout-2.3.0',
'bootstrap': '../lib/bootstrap/js/bootstrap',
'jquery': '../lib/jquery/jquery-1.9.1'
}
});

Resources