Javascript app as an AMD module with external dependencies - javascript

I am writing a Javascript application that is supposed to be hosted on various sites. The application itself uses jQuery and jQuery UI. I am aware of jQuery.noConflict(true) trick and using it currently so not to pollute the global space. My aim is to always keep jQuery and any of its plugins local to my application so as not to conflict with anything on the hosting site. It should be possible for the hosting site to load its own jQuery (maybe different version) and add some plugins.
Now, I would like to structure the app as an AMD module. It is OK with me to require from the hosting site to use an AMD loader (require.js, curl, etc.) but I wouldn't like to require any particular one - just anything conforming to AMD API (yep, I know it's not 100% ready yet).
Is it at all possible? Can I do it in an interoperable way, so that the hosting site can use e.g. either requirejs, curl or Dojo AMD loader?
For example, with this:
define(['jquery'], function($) {
var myLocaljQuery = $.noConflict(true);
... my app module implementation here ...
});
how can I specify the path for jQuery that my application wants to use? As far as I understand, I cannot do anything like this:
require.config({
paths: {
'jquery' : 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min'
}
});
define(['require'], function(require) {
require(['jquery'], function($) {
var myLocaljQuery = $.noConflict(true);
... my app module implementation here ...
});
});
because the first require is a global object and this is specific to requirejs.

After doing some more research, I think it is not feasible at the moment. At least for one thing - the module ids are global within AMD context, so I cannot simply use "jquery" or "jquery-ui" module IDs in my application and be sure that there won't be any conflict when someone hosts my application and wants to use jQuery (potentially other version) through AMD too. See for example here:
curl.js does not prevent conflicts with third-party scripts that may load other versions of jQuery as an AMD module.
As far as I understand, AMD is aimed at modularizing stuff within your own app. It doesn't have much support for integration with third-party apps, at least not when you want to do some encapsulation to avoid conflicts.

Related

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.

What is the best way to distribute reusable JavaScript modules with dependencies?

There are many ways to format JavaScript modules: AMD, CommonJS, UMD, ES6, global script. I've seen projects that structure their source code in whatever way they want and run a build process to generate a dist directory containing code in all the above formats. This has the advantage that the user of the code can just pick whichever format is most applicable to his environment.
This method works fine as long as the module has no dependencies on other modules. In the case where the modules must import other modules, there are implied complications. For example RequireJS uses a config file that looks like:
requirejs.config({
paths: {
'jquery': 'js/lib/jquery',
'ember': 'js/lib/ember',
'handlebars': 'js/lib/handlebars',
'underscore': 'js/lib/underscore'
}
});
Other loaders have equivalent mechanisms for mapping import paths.
If jQuery is a dependency, should the module import it from the path 'jquery'? What if the system in which it is being incorporated stores jQuery at the path 'libs/jquery'? In this case, is it the responsibility of the author of the system incorporating jQuery to provide aliases in the configuration of the import path?
This questioning strongly suggests that a truly reusable module must provide code formatted in all module formats as well as document clearly upon what libraries (and versions thereof) it depends and document what import paths at which those libraries are assumed to exist.
For example I could author a fancy jQuery plugin that I distribute in AMD, CommonJS, ES6, and global variations. I would document that this plugin depends on jQuery version 2.0 imported through the path 'jquery_on_a_path_that_confuses_you'. The would-be user of this plugin must copy the plugin into his project and then configure his module loader or build tool to export jQuery at the path 'jquery_on_a_path_that_confuses_you'.
As far as I can tell:
There is no standard for what to use for import paths.
There is no standard way to express the dependency, version, and import path requirements to the user of a piece of code.
There is no standard remedy to deal with clashing import paths or load multiple versions of a library.
Does there exist any plan to deal with this strange arrangement? To me it seems a little crazy to have module systems that don't know how to name their modules. Am I wrong?
You may want to check jspm.io + SystemJS which is a relatively new package manager and universal module loader which is increasing in popularity.
Please find below some presentations and article on the subject I found useful:
https://www.youtube.com/watch?v=MXzQP38mdnE,
https://vimeo.com/65042246,
https://www.youtube.com/watch?v=szJjsduHBQQ,
http://javascriptplayground.com/blog/2014/11/js-modules-jspm-systemjs/
Late with the answer, but if you're after writing plain JS code (without jQuery or other frameworks), I've found that there's the deploader.js repo, which you can use to wrap any kind of JS into modules and do dependency loading.
May worth checking out.

How let users to access requirejs modules outside main?

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.

Require.js 3rd Party Library Dependencies

What is the benefit of defining 3rd party libraries (JQuery/Underscore/Backbone) as modules and using those as dependencies?
require(["jquery", "underscore"], function($, _) {
// Use $ and _ in here
});
Underscore for example creates a global '_' variable, that I could just as easily use within the function above, assuming underscore is included prior to that function.
To be used by Require, Underscore requires the code to be modified to return a value, or a shim defined. Why bother, when I can just include it via a script tag?
I get that it provides a certain level of indirection and allows me to map other dependencies to those same variables, and have it scoped locally to that function. However, I don't see this ever being useful for these types of 3rd party libraries that form the core of the application.
After building some apps with Backbone/requireJs I can see no disadvantage in building a backbone app with requireJs, where the main dependencies are simple loaded via an old school script tag.
You have to load this requirements anyway in the first place, its used by the most of your modules and you will probably never exchange it with another framework. So there is no afford for the boilerplate code in every module.
You don't have to change the library to use it in AMD loader.
require.config({paths:{underscore:'//some.cdn/path/to/underscore.js'}})
require(["jquery", "underscore"], function($ /*, note that we don't override _ here */) {
// Use AMD $ and global _ in here
});

How to manage requireJS based packages for Node.js applications

Since coming across RequireJS I have started to adopt it wholeheartedly as it seems a great way of organising dependencies etc.
For my current project I have created a 'package' of requireJS-organised modules, which will provide the needed database API, to many node.js applications.
But I have come across a stumbling block ... how can I allow third party applications to use my package, without needing to faff with requireJS?
My directory structure for my applications and API is currently as follows:
api_package/node_modules
api_package/controllers/*
api_package/views/*
api_package/helpers/*
api_package/models/*
api_package/main.js
application_1/node_modules
application_1/app.js
application_2/node_modules
application_2/app.js
I need my applications to be self-contained - so they can be easily deployed - so my current work around is to copy api_package/* into the 'node_modules' directory of application_1 & 2 and setting the their requirejs config to the following:
(function() {
var requirejs;
requirejs = require('requirejs').config({
baseUrl: __dirname,
nodeRequire: require,
packages: [
{
name: 'api_package',
location: './node_modules/api_package'
}
]
});
This feels a little dirty and wrong!
Is there a better way? Am I missing some packaging feature for requireJS?
Is it even possible to hide the implementation details of my api_package (the detail being that I am using requireJS) and allow applications to use it as they would any other module:
require('api_package')
You can use the amdefine package, which allows you to code to the AMD API and have the module work in node programs without requiring those other programs to use AMD.
In addition to the documentation on the amdefine github page, this is also documented on the RequireJS website.

Categories