Posted by & filed under Content - Highlights and Reviews, Programming & Development.

A guest post by Ben Hockey, a Senior Software Developer at Sitepen, living in Nashville, TN with his wife and 3 kids. At SitePen, he builds Web-based Applications using The Dojo Toolkit.

With RequireJS, a module’s dependencies are indicated using the module IDs of the modules needed. Take a look at Getting Started with RequireJS and AMD for more on RequireJS. You can think of module IDs as a layer of abstraction that removes the need to know specific resource locations. RequireJS resolves module IDs to URLs and then loads the resources found at those URLs. However, that raises the question: how is a module ID resolved to a URL?

Common Config

RequireJS modules are authored using the Asynchronous Module Definition (AMD) format, and one of the motivations for a common format like AMD is to provide interoperability between different loaders. In order to achieve this interoperability, developers of AMD loaders work together on specifying what it is they all agree on.

A part of what the implementers of AMD loaders have agreed upon, is a common way to configure loaders. The Common Config wiki page says it’s in “DRAFT” status, but that’s mostly to indicate there will likely be additions in the future. It’s quite safe to depend on what is already there and if we look at the documentation for RequireJS we’ll see that it supports everything agreed upon and more.

To understand how a module ID is resolved to a URL, we are going to look at these config options:

  • baseUrl
  • paths
  • packages

Before we dig into what these config options mean though, let’s quickly look at how we can configure RequireJS.

Configuring RequireJS

There are a number of ways to configure RequireJS, but the way I find most convenient is by calling the global require function and passing a config object as the first argument:

TIP: You can add a data-main attribute to the script tag that loads require.js to indicate the URL of another script to be injected into the page when require.js is loaded. Leveraging this feature, we could avoid the 2nd script block above by moving the contents into a separate file and loading it automatically with an appropriate value for the data-main attribute.

baseUrl

The baseUrl sets the root URL used when resolving module IDs. Some of the other configuration options can use relative paths, and baseUrl is used as the point of reference they are relative to. The default value is the path of the HTML page hosting your application. However, if you’ve used data-main then the base path of that attribute’s value will be the baseUrl.

By default, RequireJS will resolve module IDs as baseUrl + id + '.js'. So, for a baseUrl of './js' and a module ID of 'app/main' the resolved URL will be './js/app/main.js'. This value is used as the src attribute of a script tag and so the browser’s rules for resolving relative URLs will come into play.

If you’ve given any consideration at all about how to structure the source code of your project, you probably have a directory that contains all your JavaScript code. The URL of that directory would be a good candidate for the value of baseUrl.

As an example, part of a project might look like:

In this example, the www/js directory contains all the JavaScript for this application so the URL for that directory would be a prime candidate for the value of baseUrl.

paths

Sometimes, you don’t always get to control the layout of your code and the default method of resolving module IDs relative to baseUrl would lead to some very long module IDs – e.g. 'some/external/project/lib/utils/arrays'. Maybe you’re also pulling in code from a location not under baseUrl – perhaps an alternative domain, like a CDN – and so resolving those module IDs relative to baseUrl is not a viable option.

In these cases, the paths config can help you. The paths config is an object used by RequireJS to replace module ID prefixes (indicated by the property names in paths) with the corresponding path value. Path values can be absolute (which includes URLs to alternative domains) or relative. Relative paths are resolved relative to baseUrl.

Using this example:

  • module ID 'utils/array' resolves to URL './js/some/external/project/lib/utils/array.js'
  • module ID 'magic/magic' resolves to URL '//magicjs.cdn.com/js/magicjs/magic.js'

packages

The packages config achieves something similar to the paths config but it provides some extra “magic”. The value of packages is an array of package configurations.

Package configurations are objects with the following properties:

  • name: The name of the package. Module IDs with the first segment of their ID matching this value will be resolved using this package configuration.
  • location (optional): Path to use as a replacement for name when converting a module ID to a URL. The default value is the name value of the this package configuration. Relative values are resolved relative to baseUrl.
  • main (optional): The name of the module to be used as the “main module” of this package. This module is the one to use when resolving the module ID that exactly matches the name value of this package configuration. The default value is 'main'.

Items in the packages array could also be strings, in which case, the string indicates the name value of a package configuration and the default values are used for location and main.

With this configuration:

  • The module ID 'app' resolves to the URL './js/app/main.js'.
  • The module ID 'utils/array' resolves to the URL './js/some/external/project/lib/utils/array.js'.
  • The module ID 'magic' resolves to the URL '//magicjs.cdn.com/js/magicjs/magic.js'.

TIP: In software development, something characterized as “magic” typically means it should be avoided because of it’s obscurity. I think that the implicit “main module” translation qualifies as being obscure. If you use the packages config, I’d suggest making it a best practice for your team to avoid the implicit “main module” and make your dependencies more explicit – i.e. use the module ID 'app/main' rather than just 'app'.

Conclusion

Understanding how RequireJS resolves module IDs to URLs should help provide guidance on how to structure your application’s source code in order to help simplify your code. In another post we take a look at the Map Config and show how it is used in resolving dependencies.

Be sure to look at the RequireJS resources that you can find in Safari Books Online.

Not a subscriber? Sign up for a free trial.

Safari Books Online has the content you need

Instant Dependency Management with RequireJS How-to has a good chapter on using RequireJS and creating AMD modules.
The Twitter Flight Edge has a section on module loading with RequireJS.
Backbone.js Cookbook has a section on organizing a project structure with RequireJS.

About the author

benhockey Born and raised in Australia, Ben Hockey lives in Nashville, TN with his wife and 3 kids. As a Senior Software Developer on an amazing team of developers at SitePen, he builds Web-based Applications using The Dojo Toolkit.

Tags: AMD, Asynchronous Module Definition, Javascript, packages, RequireJS,

Comments are closed.