Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it reasonable to use UMD with no exports, to simply augment a dependency?

I'm creating my first AngularJS module intended for open source distribution. I'd like to package it in a way that's easy for others to consume.

The UMD project provides a pattern for exporting JavaScript modules that are compatible with AMD, CommonJS (or at least Node) and browser globals:

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['b'], factory); // AMD
  } else if (typeof exports === 'object') {
    module.exports = factory(require('b')); // Node
  } else {
    root.returnExports = factory(root.b); // browser global (root is window)
  }
}(this, function (b) {
  // use b in some fashion
  return {}; // return a value to define the module export
}));

However, since AngularJS has its own internal module system, registering a module is done by simply calling a method on the angular object, i.e. angular.module(). Thus, a UMD module wouldn't need to export anything; it would just need to require and act on angular. In terms of the previous example, I think that would look something like this:

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    factory(require(['b'])); // AMD
  } else if (typeof exports === 'object') {
    factory(require('b')); // Node
  } else {
    factory(root.b); // browser global (root is window)
  }
}(this, function (b) {
  // use b in some fashion
}));

Or, specific to my case:

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    factory(require(['angular'])); // AMD
  } else if (typeof exports === 'object') {
    factory(require('angular')); // Node
  } else {
    factory(root.angular); // browser global (root is window)
  }
}(this, function (angular) {
  angular.module( ... );
}));

Is this no big deal, or does it go against the spirit of UMD? I ask because I couldn't find any UMD patterns that don't export anything.

like image 974
Bungle Avatar asked Oct 20 '22 18:10

Bungle


2 Answers

I don't see anything wrong with using this pattern. As you've probably discovered, Angular doesn't play very nice with existing module systems as it uses it's own, but a module doesn't have to export anything. How often does the main module for a project export anything? Usually it just gets dependencies.

If you are looking for an example that does something similar, you could look at the jQuery mousewheel plugin, which uses UMD but depends on jQuery.

Excerpt from jquery.mousewheel.js:

(function (factory) {
    if ( typeof define === 'function' && define.amd ) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS style for Browserify
        module.exports = factory;
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

The one difference is that it return a constructor for the Node/CommonJS export, so that you can pass in the jQuery object you wish to extend.


On a side note, there is one issue with your UMD implementation, in the AMD section.

You have:

factory(require(['b'])); // AMD

Where you would need:

define(['b'], factory); // AMD

Since AMD is asynchronous, you wouldn't be able to synchronously require the angular module.

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['b'], factory); // AMD
  } else if (typeof exports === 'object') {
    factory(require('b')); // Node
  } else {
    factory(root.b); // browser global (root is window)
  }
}(this, function (b) {
  // use b in some fashion
}));
like image 75
Alexander O'Mara Avatar answered Oct 27 '22 16:10

Alexander O'Mara


As an angular plugin, it might be nicer to instead offer a module that exports the builder, so that people stay responsible for "what angular means". For instance, say I am using a customised debug version of angular, but want to use your code: right now, I can't, because you've decided to require angular in for me. That seems like a great idea, but really isn't:

var myangular = require('debug-angular/ext/symbolised');
var yourmodule = require('yourmodule');
yourmodule.register(myangular);

Now I know, as developer, that your code will be using my angular, rather than now having loaded two different versions of angular, with all the hilarious version mismatching bugs that arise from that.

So a much nicer umd factory would be this:

(function (root, factory) {
  if (typeof define !== 'undefined' && define.amd) {
    define(['angular'] , function (angular) {
      factory(angular);
    });
  } else if (typeof module !== 'undefined' && module.exports) {
    module.exports = {
      register: function(angular) {
        factory(angular);
      }
    };
  } else {
    factory(root.angular); // browser global (root is window)
  }
}(this, function (angular) {
  angular.module( ... );
}));
like image 22
Mike 'Pomax' Kamermans Avatar answered Oct 27 '22 17:10

Mike 'Pomax' Kamermans