I have 3 JS libraries that I use that are in their own separate files. They are commented with the code minified in each individual file
file 1: http://jsfiddle.net/NGMVa/
file 2: http://jsfiddle.net/AzEME/
file 3: http://jsfiddle.net/qVkhn/
Now individually they work fine in my app, don't throw any errors. But I wanted to load less files so I combined them into one file like this: http://jsfiddle.net/Gxswy/
However, in Chrome it throws an error on the last line of the file:
Uncaught TypeError: undefined is not a function
and it then it doesn't work anymore. I didn't change anything in the code before I combined them, I just copy and pasted the content of the first 3 files into the new one, and it doesn't work anymore. Don't understand why combining them into one file seems to break functionality
Was hoping someone would have an idea what's going here?
Make sure to add the semicolons at the end of each file or concatenate and then minify and minifier should take care of that.
Try this code: https://gist.github.com/elclanrs/4728677
Reason
In my case it was because I specified dependencies in requirejs's shim, for a library (perfect-scrollbar) that already supports AMD loading. The result is that the whole of its code in define is bypassed after grunt-contrib-requirejs did its job.
Basically, to concat requirejs files together, grunt-contrib-requirejs would
// change this:
define(['d3'],function(d3){...});
// into this:
define('d3', ['d3'],function(d3){...});
Whereas inside perfect-scrollbar, there's already
(function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory); // <-------------------------- this part
// after minification would become:
// define('PerfectScrollbar', ['jquery'], factory);
} else if (typeof exports === 'object') {
factory(require('jquery')); // Node/CommonJS
} else {
factory(jQuery); // Browser globals
}
}(function ($) {
// perfect-scrollbar code here...
});
Which conflicted with what I specified in shim:
shim: {
"PerfectScrollbar": ['jquery', 'jquery.mousewheel']
}
Therefore when requirejs got to the part where PerfectScrollbar was really defined, it hopped over it, assuming that it has already done the job.
Solution
Don't specify dependencies in shim for libraries that already have AMD support.
Question
But what if I need to specify dependencies? I need jquery.mousewheel on top of the jquery it already specified in its code.
Answer
Require that file with proper require, and get its own dependencies right:
define(['perfect-scrollbar', 'jquery.mousewheel'], function(){...});
Either when the library you need has AMD support,
// inside jquery.mousewheel:
require(['jquery'], factory);
Or it doesn't
shim: {
"IDontSupportAMD": ['jquery']
}
Related
It seems that recent versions of Backbone.js and Underscore.js support AMD.
So am I correct in assuming that it is no longer needed to "shim" these libraries in the require.js configuration?
Yes, you are correct, no shim needed. And it's easy to test, here's the simplest setup:
requirejs.config({
/**
* Paths to lib dependencies.
*
* Use non-minified files where possible as they will be minified (and
* optimized via uglify) on release build (r.js)
*/
paths: {
"jquery": "libs/jquery/dist/jquery",
"underscore": "libs/underscore/underscore",
"backbone": "libs/backbone/backbone",
},
deps: ["app"] // starts the app
});
And to make sure it works and it's not the global Underscore that's used:
// I'm using Underscore as to avoid conflicting with the global _
// but you could use _ as the name for the local variable as well.
define(['backbone', 'underscore'], function(Backbone, Underscore) {
console.log("Backbone:", Backbone.VERSION)
console.log("Local Underscore:", Underscore.VERSION);
console.log("Global Underscore:", _.VERSION, _ === Underscore);
});
For Backbone, it's clear in the source that it supports AMD by default:
// Set up Backbone appropriately for the environment. Start with AMD.
if (typeof define === 'function' && define.amd) {
define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
// Export global even in AMD case in case this script is loaded with
// others that may still expect a global Backbone.
root.Backbone = factory(root, exports, _, $);
});
As for Underscore, it is registering itself at the end:
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
// practice for AMD registration is to be anonymous, underscore registers
// as a named module because, like jQuery, it is a base library that is
// popular enough to be bundled in a third party lib, but not be part of
// an AMD load request. Those cases could generate an error when an
// anonymous define() is called outside of a loader request.
if (typeof define === 'function' && define.amd) {
define('underscore', [], function() {
return _;
});
}
Same thing with jQuery:
// Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work.
// Note that for maximum portability, libraries that are not jQuery should
// declare themselves as anonymous modules, and avoid setting a global if an
// AMD loader is present. jQuery is a special case. For more information, see
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
if ( typeof define === "function" && define.amd ) {
define( "jquery", [], function() {
return jQuery;
} );
}
As #ggozad mentioned:
Well, if underscore is already loaded and available, you do not need
the shim at all. Backbone will happily load. If not, it's probably
because underscore is not actually loaded.
It sounds however wrong to be only partially using require.js, you
might as well AMD-load them all.
I guess that explain why you don't need to load it using require.js
I am trying to integrate requirejs framework to my app.
Is possible to create a virtual module (which doesn't exists as a physically file), where i could group all the jquery-validation plugins together?
For example, i need to load 4 dependencies everytime i want to use jquery-validate.
Instead of requesting them, each time, i create a jquery-val "virtual module", which should request all the dependencies automatically.
However, trying to load "jquery-val" actually tries to load the file from disk (which i don't have).
What should be the best practice in solving this issue?
// config
requirejs.config({
baseUrl: '/Content',
paths: {
'jquery': 'frameworks/jquery-3.1.1.min',
"jquery-validate": "frameworks/jquery.validate.min",
"jquery-validate-unobtrusive": "frameworks/jquery.validate.unobtrusive.min",
"jquery-unobtrusive-ajax": "frameworks/jquery.unobtrusive-ajax.min"
},
shim: {
"jquery-val": ["jquery", "jquery-validate", "jquery-validate-unobtrusive", "jquery-unobtrusive-ajax"]
}
});
// Solution 1: working, but ugly
define(["jquery", "jquery-validate-unobtrusive", "jquery-unobtrusive-ajax"], function ($) {
// My Module
});
// Solution 2: not working
define(["jquery-val"], function () {
// My Module
});
// Solution 3: create jquery-val.js file, which loads the dependencies automatically
// jquery-val.js
(function (global) {
define(["jquery", "jquery-validate-unobtrusive", "jquery-unobtrusive-ajax"], function ($) {
});
}(this));
take some time and read:
http://requirejs.org/docs/api.html#modulenotes
One module per file.: Only one module should be defined per JavaScript file, given the nature of the module name-to-file-path lookup algorithm. You shoud only use the optimization tool to group multiple modules into optimized files.
Optimization Tool
To answer your question:
It is good practice to define one module per file, so you don't need to define explicit a name for the module AND do the need for inserting it somewhere to be available before the other modules are loaded.
So you could require just the file: require("../services/myGroupModule") and this file would hold your module and requireJS would take care of the loading dependencies (and later the optimizations for concatenating into one file!). Here the module name is the file name.
You could nevertheless do the following and loading it as a file module or like you tried to define it beforehand and give the module a name:
//Explicitly defines the "foo/title" module:
define("myGroupModule",
["dependency1", "dependency2"],
function(dependency1, dependency2) {
return function myGroupModule {
return {
doSomething: function () { console.log("hey"); }
}
}
}
);
Maybe you should also give a look at some new module loaders:
WebPack 2: https://webpack.js.org/
SystemJS: https://github.com/systemjs/systemjs
I am attempting to get started with RequireJS, and am running into an annoying issue. . .
require.config({
baseUrl: 'app_content/scripts',
paths: {
// the left side is the module ID,
// the right side is the path to
// the jQuery file, relative to baseUrl.
// Also, the path should NOT include
// the '.js' file extension. This example
// is using jQuery 1.9.0 located at
// js/lib/jquery-1.9.0.js, relative to
// the HTML page.
'jQuery': 'lib/jQuery/jQuery-2.0.3'
}
});
This, using uppercase Q, does not work, But if I change it to jquery, it does. Also, this is true in my required areas...
<script type="text/javascript">
require(["jQuery"], function ($) {
console.log($);
});
</script>
This returns undefined, but if I change everything to straight up jquery, it works.
Is this expected behavior, and is there anything I can do about it?
Yes, jQuery defines itself as 'jquery', all lowercase. That's normal.
If you open the source to jQuery you'll find:
// Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work.
if ( typeof define === "function" && define.amd ) {
define( "jquery", [], function () { return jQuery; } );
}
So you have to refer to it as "jquery" everywhere in RequireJS calls. The issue here is that the define that jQuery uses is a "named define" which is something we normally do not use when creating modules. The RequireJS optimizer adds these names for us when we run it.
At any rate, when a "named define" is used the module name is set to the name given to define rather than by file names (as is otherwise the case when we don't use a named define).
It is possible to rename "jquery" to "jQuery", like this:
require.config({
baseUrl: "./js",
paths: {
"jquery": "jquery-1.10.2"
}
});
define("jQuery", ["jquery"], function ($) {
return $;
});
require(["jQuery"], function ($) {
console.log($);
console.log($("body")[0]);
});
I'm making use of the version of define that takes a name as the first parameter. Full example here.
We can use AMD compatible versions of BackboneJS and UnderscoreJS
I had a look in the AMD compatible library for both (https://github.com/amdjs/) and below is the relevant code to make them AMD compatible.
BackboneJS (AMD);
else if (typeof define === 'function' && define.amd) {
// AMD
define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
// Export global even in AMD case in case this script is loaded with
// others that may still expect a global Backbone.
root.Backbone = factory(root, exports, _, $);
});
UnderscoreJS (AMD);
// AMD define happens at the end for compatibility with AMD loaders
// that don't enforce next-turn semantics on modules.
if (typeof define === 'function' && define.amd) {
define('underscore', function() {
return _;
});
}
Now to use these AMD compatible libraries in our code, we say;
requirejs.config({
enforceDefine: true,
paths: {
"jquery": "libs/jquery-1.8.3",
"underscore": "libs/underscore-amd",
"backbone": "libs/backbone-amd"
}
});
Now I read that the name for the backbone module can be anything, but the name for underscore must be “underscore” and for underscore, the capitalization is important.
Why is the difference based on how the library is defined?
The define function can be called with or without a name for the module being defined. This call does not have the module name. It starts with an array of dependencies:
define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
RequireJS will assign a module name on the basis of the base name of the file in which the define appears or on the basis of the name you give in the paths configuration. You could put paths: { platypus: '... path to file' } and your module would be named platypus.
This call has a module name:
define('underscore', function() {
It names the module as underscore. (The first argument is not an array, so RequireJS interprets it as a module name.) And when the name is set with define, it does not change, ever. So when you put it as a dependency it has to be called underscore. (You could use map in your RequireJS config to remap the name but ultimately the module name is fixed to underscore.) If you tried the platypus example above with this module, RequireJS would generate an error because it would find a module named underscore but none named platypus.
Specifying module names in define calls without an explicit and substantial reason to do so is bad practice. The documentation for RequireJS recommends not doing it:
These [i.e. module names] are normally generated by the optimization tool. You can explicitly name modules yourself, but it makes the modules less portable -- if you move the file to another directory you will need to change the name. It is normally best to avoid coding in a name for the module and just let the optimization tool burn in the module names. The optimization tool needs to add the names so that more than one module can be bundled in a file, to allow for faster loading in the browser.
Currently, I use a few defines via the Google Closure Compiler along the lines of IS_CJS and IS_BROWSER, and just have different files that get built (browser.myproject.js, cjs.myproject.js, etc).
Is this the standard way of doing things? If not, what is it and what are the advantages?
I've been using the following preamble in all my projects, for libraries that are loaded by both browser and server code:
if (define === undefined) {
var define = function(f) {
require.paths.unshift('.');
f(require, exports, module);
};
}
define(function(require, exports, module) {
...
// main library here
...
// use require to import dependencies
var v = require(something);
...
// use exports to return library functions
exports.<stuff> = { some stuff };
...
});
This works to load the library with a require(<library>) call running on my node server, as well as with a require(<library>) call with RequireJS. On the browser, nested require calls are pre-fetched by RequireJS prior to library execution, on Node these dependencies are loaded synchronously. Since I'm not using my libraries as stand-alone scripts (via a script tag in the html), and only as dependencies for scripts loaded via the script tag, this works well for me.
However, looking at stand-alone libraries, it looks like the following preamble seems to be the most flexible. (cut and paste from the Q promise library
(function (definition, undefined) {
// This file will function properly as a <script> tag, or a module
// using CommonJS and NodeJS or RequireJS module formats. In
// Common/Node/RequireJS, the module exports the Q API and when
// executed as a simple <script>, it creates a Q global instead.
// The use of "undefined" in the arguments is a
// micro-optmization for compression systems, permitting
// every occurrence of the "undefined" variable to be
// replaced with a single-character.
// RequireJS
if (typeof define === "function") {
define(function (require, exports, module) {
definition(require, exports, module);
});
// CommonJS
} else if (typeof exports === "object") {
definition(require, exports, module);
// <script>
} else {
Q = definition(undefined, {}, {});
}
})(function (serverSideRequire, exports, module, undefined) {
...
main library here
...
/*
* In module systems that support ``module.exports`` assignment or exports
* return, allow the ``ref`` function to be used as the ``Q`` constructor
* exported by the "q" module.
*/
for (var name in exports)
ref[name] = exports[name];
module.exports = ref;
return ref;
});
While wordy, it's impressively flexible, and simply works, no matter what your execution environment is.
You can use uRequire that converts modules written in either AMD or CommonJS to either AMD, CommonJS or UMD through a template system.
Optionally uRequire builds your whole bundle as a combinedFile.js that runs in ALL environments (nodejs, AMD or module-less browser < script/>) thats using rjs optimizer and almond under the hood.
uRequire saves you from having to maintain any boilerplate in each module - just write plain AMD or CommonJS modules (as .js, .coffee, .coco, .ls etc) without gimmicks.
Plus you can declaratively add standard functionality such as exporting a module to global such as window.myModule along with a noConflict() method, or have runtimeInfo (eg __isNode, __isAMD) selectively or replace/remove/inject a dependency while building, automatically minify, manipulate module code and much more.
All of these configuration options can be turned on and off per bundle OR per module, and you can have different build profiles (development, test, production etc) that derive(inherit) from each other.
It works great with grunt through grunt-urequire or standalone and it has a great watch option that rebuilds ONLY changed files.
Have you tried this: https://github.com/medikoo/modules-webmake#modules-webmake ?
It's the approach I'm taking, and it works really well. No boilerplate in a code and you can run same modules on both server and client side