Always require certain dependencies in RequireJS - javascript

I'm working on a Backbone project and I'm loading jQuery, Underscore and Backbone with RequireJS.
I find myself typing this pattern over and over again in all the modules:
define(['jquery', 'underscore', 'backbone'], function($, _, Backbone) { ...
Is there a way or workaround to make these 3 libraries available to all modules without explicitly requiring them so I can concentrate on requiring extra things?
I though about loading this dependencies stack within script tags and use RequireJS for my modules and extra dependencies, but I'd lose the JamJS compile feature by having to concatenate jquery, underscore and backbone myself.
EDIT:
See Backbone Boilerplate: They are using JamJS too but they don't require backbone,underscore,jquery on each file. Somehow it's available to all of them.
They require the config.js file within the markup with RequireJS. This exports require.config stuff and then delegates to main.js. Within main, then they have magically access to Backbone!
What happened in the middle?

I have a sandbox as mentioned in the comments. Here is a sample in coffee-script:
define [
"core"
"jquery"
"extensions/backbone"
"underscore"
], (core, $, backbone, underscore) ->
util:
underscore: underscore
mvc:
Model: backbone.Model
Collection: backbone.Collection
View: backbone.View
Events: backbone.Events
Router: backbone.Router
This allows me to do
define ["sandbox"], (sandbox) -> class View extends sandbox.mvc.View
This is similar to the sandbox used in the AuraJS project and an implementation of the facade pattern which has the benefit that...
It lets you expose the parts of a JavaScript library that are safe to
use instead of exposing the entire API. This is particularly useful
when working in teams.
Also, in the future if I want to extend all views from a BaseView class instead of directly from Backbone.View, I just have to change the reference in my sandbox.

Hello you can make globally accessible using the SHIM Config
Example taken from there:
shim: {
'backbone': {
//These script dependencies should be loaded before loading
//backbone.js
deps: ['underscore', 'jquery'],
//Once loaded, use the global 'Backbone' as the
//module value.
exports: 'Backbone'
},

Related

Dynamic requirejs configuration extending

I'm using requirejs for multipage project. Each page is an App. All of the apps have some common dependencies, i.e. jquery, backbone, underscore etc.
I want to move all this dependencies to the one single file.
That's how the js folder structure looks like:
js
base-app-require-configuration.coffee
app
homepeage
init.coffee
build.js
application.coffee
app1
init.coffee
build.js
application.coffee
app2
init.coffee
build.js
application.coffee
Homepage application example:
js/base-app-require-configuration.coffee
define ->
requirejs.config
urlArgs: "bust=#{ new Date().getTime() }"
# yep, tricky paths here
paths:
jquery: '../../jquery.min'
underscore: '../../underscore-min'
backbone: '../../backbone.min'
js/app/homepage/init.coffee
define [
'../../base-app-require-configuration'
], (
baseRequireConfig
) ->
requirejs.config
paths:
'jquery.alphanum': '../../jquery.alphanum'
shim:
'jquery.alphanum':
deps: ['jquery']
require [
'jquery'
'application'
], (
$
Application
) ->
$ -> new Application
js/app/homepage/build.js
({
mainConfigFile: ['../../base-app-require-configuration.js', 'init.js'],
wrapShim: 'true',
baseUrl: './',
name: 'init',
findNestedDependencies: true,
out: 'init.js'
})
My data-name is init.js
The thing works pretty well for multiple apps with the common dependencies moved to one sigle file - base-app-require-configuration.coffee, except one thing: the only way to compress/optimize this using r.js is to set the flag findNestedDependencies to true, because otherwise r.js won't see requirejs.config calls nested into define/require.
My questions are:
Is using findNestedDependencies a good practice?
Is there a prettier way to organize my dependencies without repeating?
If there is such a way - will it be compatible with r.js?
Let me share this solution with you.
I'm also looking for the similar solution with requirejs (how to organize the multipage project without repetitions of a long configuration, with a "shim" feature), and I have found the following one (I would be glad if this snippet can help you):
Inside HTML:
...
<script src="js/lib/require.js"></script>
<script>
//Load common code that includes config, then load the app
//logic for this page. Do the requirejs calls here instead of
//a separate file so after a build there are only 2 HTTP
//requests instead of three.
requirejs(['./js/common'], function (common) {
//js/common sets the baseUrl to be js/ so
//can just ask for 'app/main1' here instead
//of 'js/app/main1'
requirejs(['app/main1']);
});
</script>
...
where "common.js" contains the common configuration of requirejs for your project. This sample is from: https://github.com/requirejs/example-multipage-shim/blob/master/www/page1.html.
The full code of a sample project is here: https://github.com/requirejs/example-multipage-shim. The sample "build.js" file also providen, I see there is no necessity in "findNestedDependencies" in this case.
Sure, there is a bit more code inside HTML, but I think this is not significant downside.
Is using findNestedDependencies a good practice?
Not sure. the only thing i know, is that this option can slow down the bundling process quite a lot:
Is r.js dependency tracing significantly slower since v2.1.16? Or is it just me?
Is there a prettier way to organize my dependencies without repeating?
If there is such a way - will it be compatible with r.js?
this is a great article out organising backbone modules using r.js:
https://cdnjs.com/libraries/backbone.js/tutorials/organizing-backbone-using-modules

Integrating dated JavaScript libraries with updated TypeScript

My team and I are building on a large web application project that uses older versions of popular JavaScript libraries (jQuery, Backbone, Underscore, etc.). We're currently trying to phase in the use of TypeScript for new solutions as well as for replacing existing JavaScript code. Because our project is heavily dependent on specific JS library versions (e.g. jQuery 1.8.3), updating these libraries to the newest versions is slightly more complicated than just downloading and dropping in the most recent version.
This creates a problem for us in regard to phasing in TypeScript; we need declarations of dated JS library versions that are compatible with the latest TypeScript standards. Although DefinitelyTyped does a great job at providing declaration files for the
newest versions of JS libraries specific to the latest version of TypeScript, it doesn't provide declaration files for older JS library versions that are compatible with the latest TypeScript standards.
It seems that as TypeScript grows, all of the older library declarations (e.g. jQuery-1.8.3.d.ts) are left in the dust because those libraries are simultaneously evolving. This is completely understandable as I don't expect there to be much of an effort in the community to continuously update dated library declaration files so that they are compatible with the new TypeScript standards.
So my question is: what is the best way to go about phasing in new TypeScript code that relies heavily on older JS libraries?
Below is an example of a problem that I'm running into. I'm essentially trying to create a Backbone application using TypeScript.
// Model.ts
import Backbone = require("backbone");
export class NewModel extends Backbone.Model {
...
}
And because we're using AMD with require.js we get the something like the following:
// Model.js
...
define(['require', 'exports', 'backbone'], function(require, exports, Backbone) {
...
}
This is great and it's exactly the output that we want because our require.config file defines all of the libraries that we need:
// Config.ts
require.config({
baseUrl: './',
paths: {
'jquery': '../jquery-1.8.3.min',
'underscore': '../underscore/underscore.min',
'backbone': '../backbone/backbone',
'text': '../require/text'
},
shim: {
jquery: {
exports: '$'
},
underscore: {
exports: '_'
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
However, TypeScript won't let us compile this code unless we have a backbone.d.ts that describes the structure of our backbone.js library and provides an ambient declaration of the "backbone" module.
// backbone.d.ts
declare module Backbone {
...
export class Model extends ModelBase {
...
}
...
}
declare module 'backbone' {
export = Backbone;
}
// Model.ts
/// <reference path="path/to/backbone.d.ts" />
import Backbone = require("backbone")
export class NewModel extends Backbone.Model {
...
}
If we don't have these declarations then we have no ambient declaration of the Backbone module and thus can't extend Backbone.Model in TypeScript because Backbone.Model is not defined as a class anywhere. This is a bit of an exaggerated sample problem because the Model class type exists in all 'backbone.d.ts' files regardless of the version. However, there are more subtle changes to JS libraries that can cause your code to break if you don't have an accurate .d.ts file to represent it.
That being said, you can also get away with using a more recent version of the .d.ts file (which we have been doing) as long as you only use the parts that actually exist the associated JS library and as long as you use them directly. Otherwise, if you use a function that doesn't exist or if you pass a function too many parameters, then you will only see that error when the function is actually invoked. TypeScript won't pick up on it because its information is based solely on that of the .d.ts file.
We could generate the define requirements using the amd dependency work-around with something like:
// Model.ts
/// <amd-dependency path="backbone" />
var Backbone = require("backbone");
export class NewModel extends Backbone.Model {
...
}
Which would generate the following:
// Model.js
...
define(['require', 'exports', 'backbone'], function(require, exports) {
var Backbone = require('backbone');
...
}
This gives us the Backbone reference that we want but TypeScript still complains because it has no knowledge of the Backbone variable that we're declaring. In other words, it doesn't provide the structure that the .d.ts file provides.
Most of our TypeScript solutions are actually working as they should despite our lack of correlation between .js files and their associated .d.ts files. My concern is: in the future will this discrepancy cause major problems in our application?
Right now, the best solutions I can think of are to:
Get the .d.ts versions closest to our JS library versions and update them so that they are compatible with the latest TypeScript standards.
Use the most recent .d.ts files and update them accordingly as we run into problems.
I'm not particularly thrilled about either solution, which is why I'm reaching out for another opinion. If anyone is still with me at this point (sorry for dragging this on), then I would appreciate your proposed solution.
This can be a rough problem, and something our team certainly dealt with in the early days of TypeScript. We ended up starting with versions of the d.ts files on definitely typed, and adapting them to our needs. For a lot of the libraries, we found the typing wasn't using new language features, wasn't complete, or in some cases was flat out wrong. We've done our best to contribute back where it makes sense.
Given the two approaches you're considering, I'd go with #1. Keeping d.ts files up to date with the latest TypeScript standard was more of an issue when the language was evolving at a super fast pace. While I still expect a lot of new features to come in v2, v1 is fairly stable. During the run up to 1.0, there were changes in the language on what seemed like a monthly basis. I don't think we need to deal with that level of churn now :)

How to prevent browserify-shim from requiring all shims?

I am using browserify and browserify-shim in a project, run through gulp using gulp-browserify.
gulp.src(['./resources/js/main.js'])
.pipe(browserify({
shim: {
angular: {
path: './node_modules/angular/angular.js',
exports: 'angular'
},
'angular-animate': {
path: './node_modules/angular-animate/angular-animate.js',
exports: 'ngAnimate',
depends: {
angular: 'angular',
jQuery: 'jQuery'
}
},
[...]
}
}))
.pipe(concat('app.js'))
.pipe(gulp.dest('./web/js'));
This setup works fine and, for most parts, as intended. However, Browserify will always include all shimmed libraries in the build, even if none of them is called by require().
The documentation seems to be non-existant on this topic. Is there a way to prevent this? It seems very counter-intuitive to me - the build should only contain what I actually require.
(Update: I installed angular and other libs using napa/npm)
When you shim with Browserify, it's making those libraries (and specifically the objects you tell it to "export") global. The convention is to still use require() for those libraries, however, doing so is just best practice so that if the library were to convert to module.exports down the road, you won't have to replace the global references in your code. Plus, it's nicer to list all of the files you require at the top in good node form. :)
So to answer your question, by shimming those libraries, you've told browserify to include them as global variables so they can be used anywhere, so they'll be included in the build automatically, regardless of whether you require() them.
If you want to include some and not others based on something like gulp.env, you could try building the options object separately and passing it into the browserify function.

Loading jQuery plugins using RequireJS -- jQuery undefined intermittently even after specifying deps?

I am attempting to load jQuery.jstree through RequireJS. You can see the exact source of the plugin here: https://gist.github.com/MeoMix/7882144
As I understand it, jQuery.jstree has three dependecies: jQuery, jQuery UI, and jQuery.cookie.
I begin with by loading my RequireJS shim config and then call an initial 'require' to kick things off:
require.config({
// Set the base URL to the Scripts directory of CSWeb
baseUrl: '/csweb/Scripts/',
shim: {
'jquery-ui': ['jquery'],
'jquery.jstree': ['jquery', 'jquery-ui', 'jquery.cookie'],
'jquery.cookie': ['jquery']
}
});
Here, I define my base URL relative to the root of my JavaScript files. The file jquery.js is located at the baseUrl. I also define dependencies for both plugins. Note: I have tried playing around with more explicit shim declarations including defining exports. I noticed no difference in effect.
After defining my config, I call:
require([
'jquery',
'jquery-ui',
'jquery.cookie',
'jstree/jquery.jstree'
], function () {
'use strict';
});
Doing so yields an error intermittently. Clearly an async-loading issue. The error reads:
Uncaught ReferenceError: jQuery is not defined jquery.jstree.js:978
Line 978 of jquery.jstree is simply where jQuery is passed into the closure to begin initialization of the plugin:
// 978 jquery.jstree.js: })(jQuery);
What am I not understanding here? I don't seem to experience this issue with most of my plugins. Is there something especially crappy about how this jstree plugin was written such that it is giving RequireJS fits? Or am I not understanding a core mechanic of RequireJS shim configuration?
UPDATE 1: It appears that it is something to do with the fact that I load jquery.jstree from a path. If I load another, empty file (jstree/jquery.test) -- I am able to replace the issue. However, if I then move test up a directory such that it is level with the other plugins -- it all loads fine.
The whole path you give to the shim configuration has to match the whole path of the module you require.
You name the shim jquery.jstree but you require it as jstree/jquery.jstree, so RequireJS does not use the shim and thus does not know that the plugin depends on jquery, etc. and so you get intermittent errors.
For things like plugins that I might want to use globally, I prefer to give them a well-known name that I can use throughout my application without worrying about paths. So in your case, I'd fix the problem by adding this to my config:
paths: {
"jquery.jstree": "jstree/jquery.jstree"
}
and I would then require it jquery.jstree.

Using RequireJS to load a module and all its plugins

Is there a way to do
require ['jquery'], ($) ->
...
instead of
require ['jquery', 'jquery-cookies', 'jquery-scroll', 'jquery-foo', jquery'bar'], ($) ->
...
each time?
One option would be to just define a new module that is jquery with your plugins.
// new module in something like jqueryBundle.js
define(['jquery', 'jquery-cookies', 'jquery-scroll', 'jquery-foo', 'jquery-bar'], function($) {
return $;
});
Then you could just require jqueryBundle instead.
Edit: Per comment suggestion, I neglected to mention that my answer expected the jquery plugins to be either amd modules (by author or wrapped by you) or already configured via shim configuration: http://requirejs.org/docs/api.html#config-shim
Many jquery plugins are not AMD modules, but are usually great candidates for simple shim configuration. For a small sample see my answer to another question: Using require.js with Twitter Boostrap API and backbone

Categories

Resources