Search code examples
javascriptrequirejsamd

Using r.js to optimize and concatenate files for use as a library


Okay, I've just gotten dropped into a project I have several different modules written in AMD format. I need to get these javascript files that are loosely related into one javascript file, that will then be referenced as yet another AMD module across different projects (probably from a CDN).

The problem I'm facing is when I run r.js against these files and get them into one file, when I pull that combined file into another project it just gets spit out as undefined.

To give an idea of what I'm talking about

words.spelling.js

define(['jquery', 'some.other.class'], function($, foo){
...
}

words.grammar.js

define(['jquery', 'some.other.class'], function($, foo){
...
}

words.translation.js

define(['jquery', 'some.other.class'], function($, foo){
...
}

Run through r.js into words.min.js

Then say I pull it into app.js as

require(['jquery', 'app/main/main', 'words.min'], function($, main, words) {
$(document).ready(function() {
    console.log(words);
}

words just shows up as undefined.

Just concatenating them all doesn't do anything as that just gives me a bunch of define statements one after another.

I tried creating a fake class that has

define(['word.grammar', 'word.translation', 'word.spelling'], function( g, t, s){
    return {
      grammar: g,
      translation: t,
      spelling: s
    };
});

and running that through r.js, but no dice there either. Am I missing something here or am I going to have re-write these in non-AMD format so I can concatenate them together and return one giant object? Keep in mind, words.min.js is going to have to be hosted on a CDN and cached as it'll be shared throughout a number of projects so I need that as a separate file.


Solution

  • One solution would be to use a paths configuration to map these module names to their actual file:

    So in development you use something like this

    require.config({
      paths: {
        'words.spelling': 'libs/words.spelling',
        'words.grammar': 'libs/words.grammar',
        'words.translation': 'libs/words.translation'
      }
    }
    

    You'll want to pass the same paths config from development into the r.js optimizer, so that the module names it puts inside the combined file have just the name, not some extra path info. For example, you want the modules name inside your combined bundle to be: 'words.spelling', not 'some/other/path/words.spelling'

    And then to use the combined version in another application, you do something like this to map all those module names to the same file:

    require.config({
      paths: {
        'words.spelling': 'libs/words.min',
        'words.grammar': 'libs/words.min',
        'words.translation': 'libs/words.min'
      }
    }
    

    Part of the confusion is that this is not the primary use of the r.js optimizer. It seems to be designed for use by the final web site developers, not by the module developers. But as you see above, it's possible to coerce it into that mode.