How do we best help devs deal with AMD issues?

Today a coworker combined the jQuery plugins scrollTo and localScroll in a single file, loaded it with a SCRIPT tag, but neither ended up defined in $.fn. This is because they have rolled in optional AMD support. If they detect AMD's define() they use it to define themselves anonymously, but do not require themselves automatically of course, so effectively the SCRIPT load is a noop (and worse, no errors).

There are a few options to get them working

  1. put the plugins in separate files as views in /js/jquery/scrollTo.js then load them via require(['jquery/scrollTo'], ...)
  2. temporarily hide the global define var and replace it after, so they just fallback to modifying the global $.

The first case also requires loading them in the right order*, but since they don't use dependencies internally you have to hack this:

require(["jquery/scrollTo"], function () {
    require(["jquery/localScroll", "jquery"], function (localScroll, $) {
        $.localScroll();
    });
});

Notice also that the localScroll isn't anything useful. The plugins just modify jQuery like they would normally.

I guess I'm just nervous about an all AMD environment, particularly how hard it can be to debug problems. Before I had the code in front of me (trying to help remotely) it took me a several minutes to realize what was going on.

* Technically I think order doesn't matter, but the author assumes they're loaded in order.

  • I guess this could be filed under FUD, but hopefully blogging these issues can help others.

  • Don't tell me about debugging AMD issues... see https://community.elgg.org/discussion/view/1827358/how-to-correctly-use-elgg-register-js-elgg-define-js-on-elgg-18-and-19. I also had problems getting scrollTo to work but finally got it working in some way. But with the buzz.js I've not managed it and given up (it also tries to detect an AMD environment but for whatever reason it never worked if not loaded the old way like on Elgg 1.8 plus setting a priority - which wasn't necessary on Elgg 1.8).

  • TL;DR: I don't think this is our problem to deal with.

    The main issue here is that their implementations assume that if you are using AMD at all then you will NOT be relying on the traditional method (synchronous script) ever. This is a bad assumption and a bug in their AMD support, IMO. Furthermore, this results is possibly synchronous or asynchronous behavior, depending on the environment. This is just bad API design because it causes bugs like the one your coworker experienced.

    Alternative to patching the original project: just use AMD (+ shim config if necessary) in Elgg 1.9+ always. Stop loading script tags manually!

    If order matters, then jquery.scrollTo should be registered as a dependency of jquery.localScroll. If they haven't done this in their AMD define, then this is another bug in localScroll.

    Side comment: jQuery plugins should use dot as a separator, not slash. jquery/* is reserved for jQuery's source, which we don't want plugins trampling on. jquery.scrollTo is a more correct AMD module name for the plugin, IMO.

     

  • The main issue here is that their implementations assume that if you are using AMD at all then you will NOT be relying on the traditional method (synchronous script) ever.

    I've now seen this in almost every modern library my coworker has chosen. Sniffing for define.amd and using it if found seems to be the ad hoc standard way of supporting both environments. Whether it's a bad assumption or not, that's where we are.

    It would be generally much less of a problem if RequireJS's error messages were more helpful.

    BTW, hiding define via statements before and after code will not work, because it disables it in other threads. You basically must wrap the library in an anon function:

    !function (){ var define;
    
    // lib here
    
    }();
Feedback and Planning

Feedback and Planning

Discussions about the past, present, and future of Elgg and this community site.