I am trying to load the Modernizr feature detects dynamically with requireJS.
As Modernizr has built in AMD support this shouldn't be a problem.
My requireJS configuration contains the paths to the Modernizr source directory and to the feature detects directory:
requirejs.config({
paths: {
"modernizr" : "components/modernizr/src",
"feature-detects": "components/modernizr/feature-detects"
}
});
Lets assume one of my views would require the svg test.
My view definition might look like this
define(["feature-detects/svg"], function() { .. });
Unfortunately the svg.js can't find Modernizr.js because all feature detects and Modernizr source files load Modernizr without specifying any directory: define(['Modernizr'], ...
Which results in a very ugly require.config
requirejs.config({
paths: {
"Modernizr": "components/modernizr/src/Modernizr",
"addTest": "components/modernizr/src/addTest",
"ModernizrProto": "components/modernizr/src/ModernizrProto",
"setClasses": "components/modernizr/src/setClasses",
"hasOwnProp": "components/modernizr/src/hasOwnProp",
"tests": "components/modernizr/src/tests",
"is": "components/modernizr/src/is",
"docElement": "components/modernizr/src/docElement",
"feature-detects": "components/modernizr/feature-detects"
}
});
Is there a cleaner way to tell requireJS to search in components/modernizr/src/ whenever it couldn't find the file?
Update
I created an example github project which includes the basic setup and a running demonstration.
Modernizr's AMD structure is (currently) just for its own internal build process. We've discussed exposing this so that it can be used as you've tried, but haven't agreed on a convenient way to do this yet. A Modernizr plugin for RequireJS could be one option.
Are you using Bower? If so, it's worth noting Modernizr isn't suitable for use with Bower yet.
The recommended way to tie Modernizr into your build process at the moment is using grunt-modernizr, which will automatically find references to Modernizr detects in your code (or you can explicitly define them), then you can just use the resulting Modernizr build like any other AMD dependency whenever you need it:
define(['path/to/built/modernizr.js'], function (Modernizr) {
if (Modernizr.svg) {
...
}
});
My suggestion would be to have a shim
Config
paths: {
'Modernizr': 'PATH TO MODERNIZR'
},
shim: {
'Modernizr': {
exports: 'Modernizr'
}
}
=================
You can use define in your script
define(['Modernizr'],function(Modernizr) {
'use strict';
console.log(Modernizr)
// This should log the Modernizr function
//For Example
if(Modernizr.boxshadow){
// Do something here
}
});
If I understand your question correctly, wouldn't you just define your feature like so:
define([
"modernizr",
"feature-detects/svg"
], function(Modernizr) {
Modernizr.addTest();
});
This way modernizr will be loaded before your feature detection code runs.
Related
So, I am using RequireJS and React, trying to load a third-party component, which has been installed with:
npm install react-autocomplete
The structure is here: https://github.com/rackt/react-autocomplete/tree/master/lib
Now, I have a main.js file, initiated when requireJS is loaded, that looks like this:
require.config({
paths: {
"react" : "react/react",
"jsx-transformer" : "react/JSXTransformer",
"react-autocomplete" : "node_modules/react-autocomplete/lib/main"
}
});
require(["react"], function(react) {
console.log("React loaded OK.");
});
require(["jsx-transformer"], function(jsx) {
console.log("JSX transformer loaded OK.");
});
require(['react-autocomplete'], function (Autocomplete) {
console.log("React autocomplete component loaded OK.");
var Combobox = Autocomplete.Combobox;
var ComboboxOption = Autocomplete.Option;
console.log("Autocomplete initiated OK");
});
Now, it all loads OK, but the third require statement throws a "module is not defined", for the main.js file in the third-party component, which looks like this:
module.exports = {
Combobox: require('./combobox'),
Option: require('./option')
};
I've been reading about that this has to do with me trying to require a CommonJS-style module, but I can't figure out how to fix it on my own, as I'm new to this.
Does anyone have a clear example on how I could get around this?
RequireJS cannot load CommonJS modules as-is. However, there is a minimal modification you can make to them to load them. You have to wrap them in a define call like this:
define(function (require, exports, module) {
module.exports = {
Combobox: require('./combobox'),
Option: require('./option')
};
});
If you have a bunch of modules you need to convert, or if you are using a third-party library written in the CommonJS pattern and want to convert it as part of a build process, you can use r.js to perform the conversion for you.
The problem is that requireJS doesn't support CommonJS modules only AMD. So if the third party library doesn't support AMD then you'll have to jump through some hoops to get it to work.
If you have the option I would suggest using browserify or webpack for module loading since those are the tools that the majority of the react community have chosen and practically all third-party react components are published on npm as CommonJS modules.
I'm attempting to use LeafletJS with requireJS and some LeafletJS plugins. I know that if you are using files that aren't AMD modules, you need to shim them in the config. Does this mean I will have to shim each individual plugin with the LeafletJS dependency? Ex:
require.config({
paths: {
'leaflet': 'vendor/leaflet/leafletjs',
'leafletplugin1': 'vendor/leaflet/leafletplugin1',
'leafletplugin2': 'vendor/leaflet/leafletplugin2'
},
shim: {
'leafletplugin1': {
deps: 'leaflet'
},
'leafletplugin2': {
deps: 'leaflet'
}
}
});
The problem is that I plan to have many libraries with many plugins, and this config will get extremely long since I have to not only shim each individual plugin but also provide a path for each. Is there a simpler way to do this? It would be nice if I could even define a require.config inside of a module for use only with that module, that way I could keep my project better organized and less cluttered.
Also, there is no other way to simply require(['leafletplugin1'], function(){}); without shimming it, correct?
EDIT: Just to clarify, this doesn't have to be a LeafletJS-specific question/answer. This is just the current example I'm working with.
Option 1: depending on your build process and..
Number of plugs
Total size of plugins
would be concat all of the 'leaf' plugins into a single file.
Option 2: Turn all the plugins into proper modules.
This would mean having a ~/leaf directory and a ~/leafModule directory and having the build process "wrap" each plugin into a proper module.
In a similar situation, I did option 2. Was fairly easy and made the rest of my code far easier to work on.
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.
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.
I am experimenting a little bit with RequireJS 2.0.1. My goal is to load correctly jQuery, Underscore and Backbone. From the original RequireJS doc I discovered that the author J. Burke added (to this new release) a new config option called shim.
Then I wrote this stuff down here:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Testing time</title>
<script data-main="scripts/main" src="scripts/require.js"></script>
</head>
<body>
<h1>Testing time</h1>
</body>
</html>
scripts/main.js
requirejs.config({
shim: {
'libs/jquery': {
exports: '$'
},
'libs/underscore': {
exports: '_'
},
'libs/backbone': {
deps: ['libs/underscore', 'libs/jquery'],
exports: 'Backbone'
}
}
});
define(
['libs/jquery', 'libs/underscore', 'libs/backbone'],
function (jQueryLocal, underscoreLocal, backboneLocal) {
console.log('local', jQueryLocal);
console.log('local', underscoreLocal);
console.log('local', backboneLocal);
console.log('global', $);
console.log('global', _);
console.log('global', Backbone);
}
);
Everything seems to work quite fine, but I have the feeling that I'm missing something, I know that there are AMDed version of jQuery and Underscore but if the setup is so simple I don't understand why I should use them.
So, is this setup right or I'm missing something?
You only need to use "shim" config if the library does not already call define() to declare a module. jQuery does this already, so you can remove that from the shim config. The above code will work as is, but the exports shim config for jQuery will be ignored since jQuery will call define() before the shim work is done.
The downsides with the shim vs having the script call define() to define a module:
It is less portable/reliable: every developer needs to do the shim config, and keep track of library changes. If the library author does it inline in the library, everyone gets the benefits more efficiently. The code templates at umdjs/umd can help with that code change.
Less optimal code loading: shim config works by loading shim deps before loading the actual library. So it is a bit more sequential loading than parallel. It is faster if all scripts can be loaded in parallel, which is possible when define() is used. If you do a build/optimization for final deployment and combine all the scripts into one script, then this is not really a downside.
What you are doing is correct, but jQuery does not need to be in the shim config because it exports an AMD (Asynchronous Module Definition) module. Underscore removed its support for AMD / Require.js quickly after adding it, see: Why underscore.js removed support for AMD
Shim is intended as a convenience for using libraries that do not export an AMD module. If the library you are using does support AMD, or has 2 versions (one that supports AMD, and one that is a global variable) you should use the AMD version. You should use the AMD version for the same reasons you would use AMD in the first place and also because the library may include require.js (or Almond ) in its source and would be adding unnecessary file size to your project.
Can you actually avoid "shimming" jquery in the original example (where the path to jquery is set to 'libs/jquery'), since jquery adds the name "jquery" in their amd module definition?
define( "jquery", [], function () { return jQuery; } );
My experience is that you need to put jquery.js in the baseurl directory to get the jquery amd module defined as expected, or "shim" it like in the original example.