Printing Dependency Tree in RequireJS When Module Load Error Occurs

Have you ever tried to figure out how to resolve a circular dependency in RequireJS?  Yeah – its not fun.. and RequireJS does not make it easier either – the Error output to the console provides no clues as to what module was being loaded at the time that the failure happen.

I have encountered this a few times, over the years, while working with RequireJS and in the last week or so, as I embarked on what now seems like a very long path to refactor a set of libraries, I have been reminded again of just how painful this can be. One thing that I wish I had in this situation is a printout of the dependency tree resolution at the time that the module load error occurred.

And that is exactly what I did by altering RequireJS’s code that calls Error callback.  At around line 756 of RequireJS, is the code that handles module errors. After setting a breakpoint and inspecting what data is available at that point, I noticed that the Module has a property called map that keeps track of the dependency tree. So, I replaced this:

if (errback) {
 //Register for errors on this module.
 this.on('error', errback);
} else if (this.events.error) {
 //If no errback already, but there are error listeners
 //on this module, set up an errback to pass to the deps.
 errback = bind(this, function (err) {
 this.emit('error', err);
 });
}

With this:

// HEY!!! REQUIREJS - TELL ME WHO DID IT??????
var printDepTree = function(parentMap, printOut, indent){
    if (!parentMap) {
        console.log(printOut);
        return;
    }
    if (!printOut) {
        printOut = "";
    }
    if (!indent) {
        indent = "  ";
    }
    printOut += parentMap.name + "\n" + indent;

    indent += "  ";

    printDepTree(parentMap.parentMap, printOut, indent);
};

if (errback) {
    var realErrBack = errback;
    var _this = this;
    errback = function(err){
        printDepTree(_this.map, "ERROR: " + err.message + "\n");
        return realErrBack.apply(this, arguments);
    }
    //Register for errors on this module.
    this.on('error', errback);
} else if (this.events.error) {
    //If no errback already, but there are error listeners
    //on this module, set up an errback to pass to the deps.
    errback = bind(this, function (err) {
        printDepTree(this.map, "ERROR: " + err.message + "\n");
        this.emit('error', err);
    });
}

The outcome is that the browser console now prints something like this:

ERROR: Cannot read property 'BaseCell' of undefined
widgetLib/cell/SelectCell
  widgetLib/field/Select
    widgetLib/common/Groups
      widgetLib/common/index
        widgetLib/common/handlers
          User/Profile

 

I wish RequireJS had a “dev mode” where more helpful information could be printed out, but until that happens, this snippet above will have to do.  This also reminds me of yet another benefit of working with ES6 modules, which seem to do a much better job at resolving these types of issues without requiring careful grouping of your library code or imposing restrictions on how to you can use your modules.

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s