Modules feedback from March 2013 meeting

Sam Tobin-Hochstadt samth at ccs.neu.edu
Mon Mar 25 12:56:59 PDT 2013


On Mon, Mar 25, 2013 at 1:31 PM, James Burke <jrburke at gmail.com> wrote:
> I expect the module champions to be busy, so I am not expecting a
> response. This is just some feedback to consider or discard at their
> discretion. I'll wait for the next public update on modules to see
> where things end up. In general, sounds promising.

I'm only going to respond to part of this right now, since there's a lot here.

> ### Single Anonymous Export ###

[snip]

> Syntax is hard though, so I will not be surprised if this falls down.

I'm going to leave off responding to syntax discussions at the moment ...

> let { encrypt } from "crypto";

... other than to point out that this is a form of `import`, just with
different checks performed on the bindings.

> With that capability, it may be possible to go without `import` at
> all, at least at this stage of ES (macros later may require it). The
> one case where I think `import` may help are cycles, but if the cycle
> parts are placed in separate modules with a single export, it may
> still work out. Using the assumption of single anonymous export and
> the "odd even" example from the doku wiki:

Therefore, these changes aren't really simplifying things.

> ### Loader pipeline hooks ###
>
> I know more needs to be specified for this, so this feedback may be too early.
>
> The examples look like they assign to the hooks:
>
> System.translate = function () {}
>
> Are these additive assignments? If more than one thing wants to
> translate, is this more like addEventListener?

These are assignments just like you'd expect -- assigning overwrites
the previous value.

New hook implementations will most likely want to either save the
existing value and defer to it, or use a more complex library that
implements a dispatch/plugin/etc system.

> I recommend allowing something like AMD loader plugins, since they
> allow participating in the pipeline without needing to be loaded first
> before any other modules. It also allows the caller to decide what
> hooks/transforms should be done, instead of some global handler
> sneaking in and making the decision.
>
> So, if a dependency is "text at some.html" (AMD loader plugins use !,
> "text!some.html", pick whatever you all think works as a separator):
>
> * load the text module.
> * Grab the export value. If the export value has a property (or an
> explicit export property) that matches one of the pipeline names,
> normalize, resolve, fetch, translate, etc…, use those when trying to
> process "some.html".

This is certainly implementable in the current system, and Dave
presented an example of how it works on slide 18 of his presentation.

The system doesn't build AMD-style plugins into the core of the module
proposal, however. They're neither fully-general (you could configure
based on something other than a prefix) nor used everywhere in
existing JS, and we don't want to prematurely standardize on one
system.

> The main feedback here is to not expect to load all pipeline hooks up
> front. This would break dependency encapsulation. It seems fine to
> also allow those hooks to be specified up front before module loading
> begins, but that should not be the only mechanism.

You can certainly load whatever code you want in loader hooks, subject
to synchrony restrictions.

> ### Declarative loader configuration
>
> Related to System.ondemand: I suggest considering the declarative
> config worked out by AMD loaders:
> https://github.com/amdjs/amdjs-api/wiki/Common-Config
>
> I would probably use "alias" instead of "map" now, and I will likely
> add a "layers" config that is similar to what "ondemand" is: given
> this layer module ID, here are the fully qualified, **exact match**
> module IDs in that file.
>
> "shim" config has proved to be very useful when bridging the gap with
> legacy code.

Fortunately, it looks like the "shim" config is very similar to what
the `link` hook supports.

> ### Nested modules ###
>
> This was explicitly stated as a non-goal, but it is something that
> shows up in the AMD module world (library builds with almond wrapped
> with a UMD style boilerplate for use by the outside world):
>
> module "publicThing" {
>   module "j" {}
>   module "k" {}
>
>   export …. //something visible outside publicThing
> }
>
> The idea being that internally "publicThing" uses some modules, but
> they are only for its internal use, not for use outside of
> "publicThing".

You could express this as:

 module "publicThing/j" {}
 module "publicThing/k" {}

 module "publicThing" {

   export …. //something visible outside publicThing
 }

This doesn't enforce the privacy of the other modules, but if privacy
is required, then you can use internal bindings in "publicThing" that
aren't exported.

> It would be nice to allow a chain of ID lookup tables, favoring a
> local lookup table/closer tables and working up the chain to find a
> match. If no match, a module is fetched/loaded and placed in the top
> level table.
>
> If something like the declarative "map" config from AMD is supported
> in the loader, it would allow resolving possible top level ID/version
> conflicts.

I don't really understand what you're suggesting here.

> ### Legacy opt-in use case ###
>
> I did not see this explicitly mentioned in the use cases, but I
> believe it would help the bootstrapping phase for ES modules if the
> default loader also parses for `System.get` and `System.set` usage in
> addition to the `import` or `from` new syntax. This would allow JS
> libraries that need to operate in pre-ES6 worlds to still stick with
> ES3-5 syntax but then be usable in a project that can just use ES6:
>
> //Some base library that needs to be in ES5 syntax:
> var dep1, dep2
> if (typeof System !== 'undefined' && System.get) {
>     //ES6 module loader. The loader will fetch and process
>     //these dependencies before executing this file
>     dep1 = System.get('dep1');
>     dep2 = System.get('dep2');
> } else {
>     //browser globals case, assume the scripts have already loaded
>     dep1 = global.dep1;
>     dep2 = global.dep2;
> }

In this setting, you could just run the code exactly as you wrote it,
without changing the default loader at all, and it would work provided
that the dependencies were, in fact, already loaded, just the way it's
assumed in the browser globals case.  I imagine that lots of libraries
will work exactly like this, the same way jQuery plugins expect jQuery
to already be loaded today.

Adding a build step that performs this analysis explicitly seems like
a much better idea than building an ad-hoc analysis that changes the
semantics of these methods.

Sam


More information about the es-discuss mailing list