Search code examples
javascriptrequirejsr.js

RequireJS optimization when using variables in require() calls


I am using RequireJS and the r.js optimizer to build a production-ready app. I noticed that when passing a variable to a require() call, everything works as expected.

var someDependencies = ["dependency-1", "dependency-2", "dependency-3"];
require(someDependencies);

However, when running the optimizer the dependencies aren't read from the variable, and therefore aren't included in the concatenated app script. As I understand it, this is because r.js looks for string literals inside of require() calls. It does not process JavaScript, so a variable means nothing to it. This is even stated in the documentation for r.js.

The optimizer will only combine modules that are specified in arrays of string literals that are passed to top-level require and define calls, or the require('name') string literal calls in a simplified CommonJS wrapping. So, it will not find modules that are loaded via a variable name.

My question is: Is there any way to overcome this so that I can pass variables into require() calls but still include them in the r.js build?


Solution

  • Modify the source before passing it to optimizer programatically. In your build configuration specify:

    onBuildRead: function (moduleName, path, contents) {
        // modify contents here
        return contents;
    }
    

    This is called before optimizer starts processing the source. You can then match dependencies and replace. Simple concept:

    var content = 'var someDependencies = ["dependency-1", "dependency-2", "dependency-3"];\n' +
        'require(someDependencies, function(){\n' +
        '  });';
    
    var varName = /require\((\w+)/.exec(content)[1];
    var regex = new RegExp(varName + '\\s*=\\s*(\\[.*\\])');
    var dependencies = regex.exec(content)[1];
    var modifiedContent = content.replace('require(' + varName, 'require(' + dependencies);
    
    console.log(modifiedContent);
    

    See FIDDLER demo.