Skip to content

Instantly share code, notes, and snippets.

@guybedford
Last active November 21, 2017 06:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guybedford/10328654 to your computer and use it in GitHub Desktop.
Save guybedford/10328654 to your computer and use it in GitHub Desktop.
ES6 Module Spec Proposal - Deferred Execution and Instantiate Normalization

Key Issues

1. No Deferred Execution for Dynamic Modules

It can be quite confusing to new users that:

<module name="test">
  define(function() {
    console.log('running');
  });
</module>

will log immediately when the tag loads, while

<module name="test">
  console.log('running');
  export var test = 'module';
</module>

will only execute when loaded by name with a System.import('test'). It is something that is immediately noticeable to users. It also makes for a different execution between ES6 and ES6 transpiled into ES5.

This is caused since the execute function for dynamic modules is run during linking. ES6 modules normally only execute on a System.get call which runs EnsureEvaluated. But there is no trigger in EnsureEvaluated for dynamic modules at all, and so there is no way for them to be informed to execute on a System.get call. It is expected that they always execute fully into a completely defined module object in the link stage process with the current spec, leading to this noticable difference in behaviour.

2. Instantiate Normalization

Currently instantiate doesn't provide normalized names as arguments into the execute function, so we are expecting the user to normalize these names. This results in all dynamic module names being normalized twice, once by the user and once within the loader itself. This is unnecessary, and could also lead to unwanted side affects.

3. Supporting Circular References

Currently this is done with a side table. Which all works fine. But the implementation can be simplified considerably by a combined solution to problems (1) and (2) above.

Proposal

Change the instantiate form to:

{
  deps: ['some', './deps'],
  exports: ['these', 'are', 'the', 'export', 'names'],
  execute: function(exports, normalizeMap) {
    System.get(normalizeMap['some']);
    exports.export = 'here';
    System.get(normalizeMap['./deps']);
    exports.names = 'another';
    // ...
  }
}
  • This allows the full module to be constructed at Linking time, based on a shell module with getters to an underlying mutable JavaScript object. This solves (1), and is the only way I know of to solve (1).
  • In the process we solve (3), making a side-table no longer necessary for the correct handling of circular references. The System.get call is enough to drive module execution for dynamic modules.
  • We also solve (2) in the process, because we are returning a normalization map so there is no need to renormalize.
  • The fact is, that all CommonJS and AMD modules define only into a default property anyway, so exports: ['default'] is what would be used for AMD and CommonJS support.
  • For ES6 compilation, the proposal is to use System.register, which acts as a proxy to this instantiate function:
  System.register(moduleName, deps, exports, execute);

The implementation of System.register is then trivial since it is simply a cache for the instantiation function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment