How let users to access requirejs modules outside main? - javascript

I'm implementing an AMD module oriented js framework that will be used on third party sites.
With this line, framework users will configure necessary modules
<script data-main="/main.js" src="/require.js"></script>
The problem is that data-main reference is loaded asynchronously so any js logic depending on modules loaded by main.js would fail unless I can be sure that it finished loading.
I'm pretty new to requirejs so not sure what's the good practices to create a framework that will be used by other people.
How could resolve this very simple problem?
EDIT
An example to explain my point
main.js
requirejs.config({
paths: {
jquery: '//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min',
}
});
main js reference + extra code
<script data-main="/main.js" src="/require.js"></script>
<script>
require(['jquery'], function($) {
// since main.js is not loaded yet, it will assume that there is a jquery.js
// file on the same folder that this file
console.log('myModule was also loaded and can use jQuery');
});
</script>

If you want to depend on other libraries and are specifically targeting being in a Require pipeline, all you need to do is to declare some dependencies with
define(
'myModule', // module name
['foo'], // module dependencies
function(foo){ // module construction
var myModule = {};
.... code to set up the module ....
return myModule;
});
and Require will take care of things. This will register your module with Require and won't attempt to build your module until all of your dependencies are available. This functionality is discussed here.
Update with example
Require JS is designed to work both with and without a prebuilt configuration. The paths property of the Require config object only provides Require with information on how to attempt to find libraries which have not yet been registered. However, the registration and then dependency resolution is handled by Require regardless of how/where the module was registered. Please see this JSFiddle for a working example of how you can register and use dependencies.
Update 2 regarding config
Since RequireJS loads everything asynchronously, you are correct, your code example will not work. However, you're making an incorrect assumption about how it is "supposed" to work. You have an incorrect example of what your library's clients' Require configuration will look like. If someone else is building an application using RequireJS and they want to use your library, they should declare the path to your library in their require.config:
require.config({
paths: {
// this tells require how to load jQuery (a library maintained neither by you nor your clients).
'jquery': '//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min',
// this tells require how to load myModule (the library you are building for your clients).
'myModule': '//example.com/js/my-module.min',
// this tells require how to load foo (a library built and hosted by your clients).
'foo': 'scripts/foo'
}
});
On the other hand, if your clients can't update their Require config to include your library in the declarations, then you're out of luck. All you can do is take all of your dependencies and bundle them up in your distribution file and then declare no dependencies:
define(
'myModule',
[], // can't declare any dependencies
function() {
// these dependencies are inside of the definition function to keep the global namespace clean
// since we need jQuery, we have to inline it:
var jQuery = ....
// same goes for d3.js
var d3 = ....
// now we can set up the module itself
var myModule = {};
.... code to set up the module ....
return myModule;
}
);
Obviously, this option means that you can't use the libraries which are being used by your clients. This means your library will be a lot heavier and include effectively duplicate code and data.
Hope that helps you understand how Require works and how other people will use your library.

I've finally used this approach
<script src="http://mydomain/js/require.js"></script>
<script>
requirejs.config({
baseUrl: 'http://mydomain/js'
});
require(['main'],function(){
// here users can do anything they want as all required libraries are loaded
});
</script>
main.js is loaded with a require instruction instead of using data-main attribute from script tag , this provides a callback where users can put their code.

Related

Mark script as already defined in require js

I had to move some scripts to synchronious load through bundle, and I want to set those scripts as already defined, so require.js would not ask server for them in next calls.
Let me explain:
I had some scripts that were required everywhere, e.g. i18n, and jquery. So I have hundreeds if calls all around the project such as
require(['jquery', 'i18n', 'commonjs', ...
I bundled 'jquery', 'i18n', 'commonjs' into one script core.js which now is inserted in layout <script src="/core.js"></script>
All functions from jquer, i18n now can be accesses globally, without need of requiring them. I want to specifically say to reuire.js that those scripts are already loaded, something wich bundles should do.
I've read article about using bundles and tryied to put
bundle in my config file
bundles: {
'core': ['jquery', 'i18n', 'commonjs']
}
but it doesnt work, there lots of mistakes fallen and as I understood th only way to use bundle is to use r.js which optimizes js and folders.
Actually, all I want is to set some scripts as already loaded for require.js. Is ther a dirty way to do it?
There's no configuration option to mark a script as already defined. What you can do is to call define yourself with a module name and an appropriate return value. For instance, if you load jQuery with a script element before you start loading any module through RequireJS, you can do:
define("jquery", [], function () {
return $;
});
The code above makes it so that whenever any module requires the module jquery, they get the value of $. I normally place such modules like the one just before my call to require.config. It's just a convenient place for them.

Using shim config with almond

I am trying to shim certain modules for usage with almond like so:
<script>
requirejs.config({
shim: {
'jQuery': { exports: 'jQuery' },
//etc.
</script>
as certain scripts will already be included. However, this code:
require(['jQuery', function($) {
});
results in "undefined missing jQuery". If I shim jQuery like this:
define('jQuery', function() {
return jQuery;
});
it works.
I am not building my JS at all, just dropping almond.js into an existing web software so I can develop my new components with AMD. I would like to shim existing globals for my new modules.
I am guessing shims are only resolved on build and that the build does exactly what I am doing above, is that correct?
The name for jQuery is hard-coded to "jquery". If you deviate from this you'll run into trouble. But that's not your only problem.
Using shim is not the same as calling define with a module name. When you use shim like you do in your question you tell the loader that there exist a module with the name jQuery and that once that module is loaded, RequireJS should return as a module value the value of the variable jQuery. The emphasized text is important: the loader will fetch and load a module named jQuery.
The define you show in your question would usually be placed together with the call to require.config, either just before it or just after it. This declares a module named jQuery. Because the module is already there, when the loader needs to get this module, there is nothing to fetch. This is an important difference when it comes to Almond.
Almond has restrictions, one of them is:
optimize all the modules into one file -- no dynamic code loading.
(Emphasis added.) Using the terms I've used in this answer this means "no fetching". When you use your define call, you are fine. When you use the shim, then unless you optimized your modules into one file, the loader has to try fetching the module. Almond cannot do that.

Preserving jQuery dependency for highcharts with requireJS optimizer

I'm testing out requireJS and am trying to make a simple project using highcharts. I've started with the requireJS multipage example project as a starting point.
My dir structure looks the same as the base structure, with highstock.js added in the lib directory.
page1.html: page 1 of the app.
page2.html: page 2 of the app.
js
app: the directory to store app-specific modules.
lib: the directory to hold third party modules, like jQuery.
common.js: contains the requirejs config, and it will be the build
target for the set of common modules.
page1.js: used for the data-main for page1.html. Loads the common
module, then loads app/main1, the main module for page 1.
page2.js: used for the data-main for page2.html. Loads the common
module, then loads app/main2, the main module for page 2.
common.js holds the configuration and I've added a shim for highstock there:
requirejs.config({
baseUrl: 'js/lib',
paths: {
app: '../app'
},
shim: {
"highstock": {
"exports": "Highcharts",
"deps": [ "jquery"]
},
} // end Shim Configuration
} );
I also am using the base build file, with the addition of a line to set common.js as the config file and another to disable minifying.
optimize: "none",
mainConfigFile: '../www/js/common.js',
In apps/main1.js I've added a var HighCharts= require('highstock'); and I then try to use it.
When I run this in the normal build everything works fine. All the dependencies hold and everything loads.
When I attempt to optimize my build, highcharts doesn't receive the jQuery dependency. I think I see why its happening, but I'm not sure how to fix it.
My build creates 3 files, common.js, page1.js, and page2.js.
The relevant parts of the build output:
js/lib/../common.js
----------------
js/lib/../common.js
js/lib/jquery.js
...
js/lib/../page1.js
----------------
js/lib/../page1.js
js/lib/highstock.js
js/app/main1.js
...
My page then references the built page1. When it attempts to load the highstock module it errors out since jQuery has not yet been loaded/isn't accessible.
When I see the built page1 I can see why.
require(['./common'], function (common) {
require(['app/main1']); //highcharts is in main1 in the non-optimized version
});
define("../page1", function(){});
//a few more defines
(function () { // start highcharts module definition HERE
So instead of being defined in the callback after common (including jQuery) has been loaded, its loaded after making the request, but before the callback executes.
My question is, why is this happening there instead of inside the callback (which is where it is loaded in the non-optimized version). I've tried multiple options in the build.js file and config file and I seem to be missing some key concept or small error.
Sorry for the super long question but I felt all the info was necessary. If more info is needed I can post it, or get rid of something superfluous.
Please take look at very simple example which use require js http://jsfiddle.net/wAM3h/
require({
paths: {
jquery: "//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min",
hchart: [
"http://code.highcharts.com/highcharts",
"http://code.highcharts.com/highcharts-more",
"http://code.highcharts.com/modules/exporting"
]
}
},
['jquery', 'hchart'], function($, hc) {
window.chart = new Highcharts.Chart(options);
});
Not sure you're still involved with the project or not:
I see that you've not defined the path to the highcharts library in the code above. I could not see it even in the repo you mentioned.
And, again, highcharts prevents re-declaration of this namespace, so you must use a different name
- Hence, you must use a different name while shim-ming it
Note: Libraries like highcharts can be safely used in an amd module without using a shim (unless you need explicit access to the object exported by it).
So, your Config File should look like this:
requirejs.config({
baseUrl: 'js/lib',
paths: {
app: '../app',
'highstock-custom-name': 'path/to/highcharts.js'
},
shim: {
"highstock-custom-name": {
... //as is, although not necessary
}
}
});

Mismatched anonymous define() module

I'm getting this error when I browse my webapp for the first time (usually in a browser with disabled cache).
Error: Mismatched anonymous define() module: function (require) {
HTML:
<html>
.
.
.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script> var require = { urlArgs: "v=0.4.1.32" }; </script>
<script data-main="assets/js/main" src="assets/js/libs/require.js"></script>
<script src="assets/js/ace/ace.js?v=0.4.1.32"></script>
</body>
</html>
JS:
$(function () {
define(function (require) {
// do something
});
});
Anyone know exactly what this error means and why its happening?
source file, a short discussion about it in the github issues page
Like AlienWebguy said, per the docs, require.js can blow up if
You have an anonymous define ("modules that call define() with no string ID") in its own script tag (I assume actually they mean anywhere in global scope)
You have modules that have conflicting names
You use loader plugins or anonymous modules but don't use require.js's optimizer to bundle them
I had this problem while including bundles built with browserify alongside require.js modules. The solution was to either:
A. load the non-require.js standalone bundles in script tags before require.js is loaded, or
B. load them using require.js (instead of a script tag)
In getting started with require.js I ran into the issue and as a beginner the docs may as well been written in greek.
The issue I ran into was that most of the beginner examples use "anonymous defines" when you should be using a "string id".
anonymous defines
define(function() {
return { helloWorld: function() { console.log('hello world!') } };
})
define(function() {
return { helloWorld2: function() { console.log('hello world again!') } };
})
define with string id
define('moduleOne',function() {
return { helloWorld: function() { console.log('hello world!') } };
})
define('moduleTwo', function() {
return { helloWorld2: function() { console.log('hello world again!') } };
})
When you use define with a string id then you will avoid this error when you try to use the modules like so:
require([ "moduleOne", "moduleTwo" ], function(moduleOne, moduleTwo) {
moduleOne.helloWorld();
moduleTwo.helloWorld2();
});
I had this error because I included the requirejs file along with other librairies included directly in a script tag. Those librairies (like lodash) used a define function that was conflicting with require's define. The requirejs file was loading asynchronously so I suspect that the require's define was defined after the other libraries define, hence the conflict.
To get rid of the error, include all your other js files by using requirejs.
Per the docs:
If you manually code a script tag in HTML to load a script with an
anonymous define() call, this error can occur.
Also seen if you
manually code a script tag in HTML to load a script that has a few
named modules, but then try to load an anonymous module that ends up
having the same name as one of the named modules in the script loaded
by the manually coded script tag.
Finally, if you use the loader
plugins or anonymous modules (modules that call define() with no
string ID) but do not use the RequireJS optimizer to combine files
together, this error can occur. The optimizer knows how to name
anonymous modules correctly so that they can be combined with other
modules in an optimized file.
To avoid the error:
Be sure to load all scripts that call define() via the RequireJS API.
Do not manually code script tags in HTML to load scripts that have
define() calls in them.
If you manually code an HTML script tag, be
sure it only includes named modules, and that an anonymous module that
will have the same name as one of the modules in that file is not
loaded.
If the problem is the use of loader plugins or anonymous
modules but the RequireJS optimizer is not used for file bundling, use
the RequireJS optimizer.
The existing answers explain the problem well but if including your script files using or before requireJS is not an easy option due to legacy code a slightly hacky workaround is to remove require from the window scope before your script tag and then reinstate it afterwords. In our project this is wrapped behind a server-side function call but effectively the browser sees the following:
<script>
window.__define = window.define;
window.__require = window.require;
window.define = undefined;
window.require = undefined;
</script>
<script src="your-script-file.js"></script>
<script>
window.define = window.__define;
window.require = window.__require;
window.__define = undefined;
window.__require = undefined;
</script>
Not the neatest but seems to work and has saved a lot of refractoring.
Be aware that some browser extensions can add code to the pages.
In my case I had an "Emmet in all textareas" plugin that messed up with my requireJs.
Make sure that no extra code is beign added to your document by inspecting it in the browser.
Or you can use this approach.
Add require.js in your code base
then load your script through that code
<script data-main="js/app.js" src="js/require.js"></script>
What it will do it will load your script after loading require.js.
I was also seeing the same error on browser console for a project based out of require.js. As stated under MISMATCHED ANONYMOUS DEFINE() MODULES at https://requirejs.org/docs/errors.html, this error has multiple causes, the interesting one in my case being: If the problem is the use of loader plugins or anonymous modules but the RequireJS optimizer is not used for file bundling, use the RequireJS optimizer. As it turns out, Google Closure compiler was getting used to merge/minify the Javascript code during build. Solution was to remove the Google closure compiler, and instead use require.js's optimizer (r.js) to merge the js files.

In RequireJS - Cannot alias jQuery name in path

I'm a RequireJS noob. When I use "require.config" and include a path to jQuery with a name different than jQuery, results are not as expected.
Here's a very simple example to help explain my issue.
Structure of files
root
├── Index.htm
└── scripts
├── libs
│   ├── jquery-1.7.1.js
│   └── require.js
├── main.js
└── someModule.js
index.htm
<html>
<head>
<title>BackboneJS Modular app with RequireJS</title>
<script data-main="scripts/main" src="scripts/libs/require.js"></script>
</head>
<body>
<h3>BackboneJS is awesome</h3>
</body>
</html>
Here the script tag references require in scripts/libs. When require gets ran the JavaScript file called main.js in the scripts directory should be executed.
main.js
require.config({
"paths": {
"mod1": "someModule"
}
});
require(["mod1"], function (sm) {
console.log(sm.someValue);
});
In my experience the "mod1" can be anything as long as it's referenced the same in the require.config path and in the require method.
someModule.js
define([], function () {
console.log();
return { someValue: "abcd" };
});
Just for completeness I included someModule.js
The perceived inconstancy occurs when I include JQuery.
In the following main.js I added jQuery to the config and the require method.
Main.js
require.config({
"paths": {
"jquery": "libs/jquery-1.7.1"
,"mod1": "someModule"
}
});
require(["mod1", "jquery"], function (sm, $) {
console.log(sm.someValue);
console.log($);
});
With the additional of jQuery everything seems to still works. The "console.log($)" writes the jQuery function.
Now the kicker. In the following code I change "jquery" to "jqueryA" in both the paths and require
require.config({
"paths": {
"jqueryA": "libs/jquery-1.7.1"
,"mod1": "someModule"
}
});
require(["mod1", "jqueryA"], function (sm, $) {
console.log(sm.someValue);
console.log($);
});
Now "console.log($)" writes null.
Should this be expected? Is there a reason why the name must be jquery, but for mod1 it can be anything?
I can work-around this without a problem, but this issue seems odd. I know I can use the combined RequireJS and jQuery file, but when jQuery has an update I don't want to be dependent on RequireJS to include the new jQuery.
In jQuery 1.7 they decided to support AMD loading. To do this, it defines a module named 'jquery' which passes back a reference to the jQuery object. When you define your path to jquery with another name (eg 'jqueryA'), things aren't exactly breaking as you think they are.
The jquery script always defines itself as a module named 'jquery', which is registered with require for your app. When you named your path shortcut 'jquery' and 'jquery' was loaded as a dependency, require was actually referencing the 'jquery' module defined by jquery-1.7.1.js, which does pass back the correct reference. When you name your module shortcut jqueryA, you are now referencing an undefined reference, because the jquery script itself does not pass back a reference, except via the module named 'jquery'. It's silly, I know.
The jquery script defines the module as 'jquery' and expects that you will simply reference it as 'jquery'. If you want to reference it as another name (and as a bonus, keep it from conflicting with other loaded jquery libraries), use this method:
Use requirejs and jquery, without clobbering global jquery?
Here's my workaround, based on the implementation I read of Require.JS 2.1.0:
define.amd.jQuery = false;
require.config({
...
shim: {
"jQuery-1.8.2": {
exports: "jQuery"
}
}
...
});
I believe I found the answer to my issue.
Optionally call AMD define() to register module
https://github.com/documentcloud/underscore/pull/338#issuecomment-3253751
Here's a quote from the previous link. Even though it pertains to underscore, I believe it relates to JQuery also.
all AMD loaders allow mapping a module ID to a partial
path, usually the configuration is called 'paths', so to do what you
want:
requirejs.config({
paths:
underscore: 'js/libs/underscore-1.2.3.min'
} }); require(['underscore'], function () {}); Since underscore is used by other higher-level modules, like backbone, a common dependency
name needs to be used to communicate a common dependency on
underscore, and it makes sense to call that dependency 'underscore'.
The paths config gives a way to do the mapping to a specific URL you
want to use for that dependency.
Here's a rant that does a very good job of describing the issues with AMD and named modules.
AMD modules with named defines. So much pain for what gain?
http://dvdotsenko.blogspot.com/2011/12/amd-modules-with-named-defines-so-much.html
Quote from the link above
If the only way to consume the module properly is to force the
end-developer to hard-code its name again in a config file, at the
consumption point, (in that respect only) why waste time, effort and
hard-code the name in the module in the first place (let alone cause
grief to those devs who DO need to load the module under different
name / from alternate sources)?
In this post James Burk recommends not using name module.
https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
Normally you should not register a named module, but instead register
as an anonymous module:
This allows users of your code to rename
your library to a name suitable for their project layout. It also
allows them to map your module to a dependency name that is used by
other libraries. For instance, Zepto.js can be mapped to fulfill the
module duty for the 'jquery' module ID.
There are some notable exceptions that do register as named modules:
•jQuery •underscore
Exception suck. Exceptions makes it difficult for noobs.

Categories