Skip to content

Updating existing libraries

Piotr Kowalski edited this page Mar 20, 2017 · 8 revisions

Here are some things to consider when updating an existing web library to optionally register as an AMD module by calling define().

Providing a global

By using string names to identify dependencies, AMD avoids polluting the global space with variables used by libraries.

This allows creating small, self-contained libraries that can be easily wrapped in a function, or scale up to allow loading multiple versions of a dependency in a page, and it avoids the need for a library to implement a .noConflict() option.

So it is best to avoid creating a global variable for your library if define() is available. However, there can be situations where a global still may be created if define is available.

In the case of jQuery, it is used as a foundation for other third party libraries that all assume a global is available. Those third party libraries can be loaded outside of an AMD loader -- they can be part of page templates that are not accessible to the main page script logic that uses an AMD loader.

In that case, if jQuery were to not register a global when calling define, the third party code could break.

This is an extremely specialized case though, and most code should be fine by skipping the global variable creation if define() is available.

If you find it causes issues by skipping the global variable creation, you can add it back as necessary, but it is worth asking the amd-implement list if there are alternate ways to solve the problem.

Register as an anonymous module

A named module is when a string name is passed as the first argument to define:

    define('mylib', function () {});

Normally you should not register a named module, but instead register as an anonymous module:

    define(function () {});

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 is a notable exception that does register as a named module: jQuery.

It does so because it is commonly used as a dependency by other libraries, and so it is common for other code to use 'jquery' as the agreed-upon name for these dependencies. This allows those other dependencies to share the same module instance.

jQuery is also the defacto implementation of the module interface implied by that name.

In addition, it is common for it to be used by other third party javascript libraries. Those third party libraries may be loaded outside of an AMD script loader that is used on the page to load the rest of the page logic.

If jQuery is not loaded by an AMD script loader and there is an anonymous define call, that could lead to an error in loading, since the anonymous define call cannot be tied to a name that the loader was expecting.

So, to ease into AMD loading on the web, jQuery registers as a named module, particularly since it will likely be referred to by that ID. As AMD loading becomes even more common on the web, and AMD loaders can reliably discard anonymous define()'d modules they did not load, at some point it may switch to an anonymous module call.

For your code, please always start with an anonymous define() call, and then if there is a problem in practice, a named call can be considered. However, most code should be just fine with an anonymous call.

Code suggestions

The umdjs/umd project has some code suggestions for different scenarios, but these forms are suggested for most web based libraries:

if ( typeof define === "function" && define.amd ) {
	define( "jquery", [], function() {
		return jQuery;
	});
}

var
	// Map over jQuery in case of overwrite
	_jQuery = window.jQuery,

	// Map over the $ in case of overwrite
	_$ = window.$;

jQuery.noConflict = function( deep ) {
	if ( window.$ === jQuery ) {
		window.$ = _$;
	}

	if ( deep && window.jQuery === jQuery ) {
		window.jQuery = _jQuery;
	}

	return jQuery;
}