amdjs / amdjs-api

Houses the Asynchronous Module Definition API
https://groups.google.com/group/amd-implement
4.31k stars 499 forks source link

Defer resolution errors to execution time in CJS #21

Closed jugglinmike closed 10 years ago

jugglinmike commented 10 years ago

Script loaders should execute the factory function for CommonJS-style modules regardless of whether all dependencies were successfully resolved. Any errors in resolution should instead be thrown by the local require function. This would more closely mimic the behavior of CJS environments and enable module authors to handle unsatisfied dependencies. Authors could then specify "optional" dependencies (which currently cannot be expressed with AMD):

define(function(require) {
  var Backbone = {};
  var _ = require('underscore');

  try {
    Backbone.$ = require('jquery');
  } catch(err) {}

  // etc.
});
unscriptable commented 10 years ago

I love this idea, Mike! This would narrow the functionality gap between node and AMD loaders.

This level of functionality has never been concretely specified in AMD. The spec is fairly loose about when factories run, when errors are detected or thrown, etc. Therefore, I suspect your proposal won't have universal acceptance. However, I'm going to flag it as a proposed feature in rave (and perhaps curl.js).

jrburke commented 10 years ago

This is not how ES modules will work. All dependencies are statically declared, anything dynamic is loaded via an async mechanism like System.import (similar to AMD require([])), or the use of a loader plugin.

Also, the developer will most likely appreciate an early error if they forgot a dependency, as that is the more likely trigger for this kind of error.

The more basic problem in the example is that Backbone is a monolithic library that really should be a set of smaller modules in a package. Then, only the view modules that need jQuery would ask for it, and those modules would not be loaded in a node environment.

For me, this try/catch thing is just an abuse of one of the weakest parts of the CommonJS/Node format, expecting any require(stringvalue) to synchronously resolve, even if that argument is a variable. That part of their module system is just not compatible with network-loaded code, and we should not try to encourage it. Browserified code cannot handle those require(variableName) cases either. It would have been best if that part of their module system was never introduced, and that part is explicitly not in ES modules.

unscriptable commented 10 years ago

@jrburke is right: you would have to dynamically import (async) or override a loader hook to make this work. The closest analogs to these in AMD are the async require() and loader plugins.

We should all be driving towards ES6, imho.

jugglinmike commented 10 years ago

This makes a lot of sense. Thanks for the detailed explanation!