Skip to content

Instantly share code, notes, and snippets.

@guybedford
Last active August 29, 2015 13:56
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/5622c9ed5c9ad4bc0417 to your computer and use it in GitHub Desktop.
Save guybedford/5622c9ed5c9ad4bc0417 to your computer and use it in GitHub Desktop.

Loading AMD from AMD within an ES6 loader

AMD modules define on to the default property of the module object in the module registry:

define(function() {
  return 'hello world';
});

defines the module object in the ES6 loader registry as Module({ default: 'hello world' })

AMD modules need to have their AMD dependencies returned as AMD modules directly:

define(['a', 'b'], function(a, b) {
});

When a is AMD and b is ES6, we need to return the default property accordingly for a, while having b remain an ES6 module.

We can track AMD modules with meta when creating them Module({ default: 'hello world', __defaultOnly: true })

In this way, a above would return 'hello world' into the define function. This allows us to load AMD from AMD.

Loading AMD from ES6

ES6 modules need to have AMD modules treated as ES6 modules, so they have to still get the Module object with a default property.

An ES6 module would load an AMD module using default syntax only:

  import $ from 'jquery';

If doing a named export from AMD, we'd always get empty (since the module is Module({ default: ..., __defaultOnly: true })).

If loadng the module object of an AMD module directly (module x from 'x'), we'd get the module object above as well.

Loading ES6 transpiled into AMD

We transpile ES6 into AMD to the following output:

define(['a', 'b'], function(a, b) {
  // undo the default lowering above
  if (!a || !a.__esModule) a = { default: a }
  if (!b || !b.__esModule) b = { default: b }
  
  return {
    some: 'export',
    __esModule: true
  };
});

Any dependencies of the ES6 module that are AMD will be lowered to become their default properties.

But in ES6, we don't want this (as described in the previous section).

So the undo code above has to check if any imports are not module objects, and turn them back into module objects.

When a is AMD and b is ES6, we end up with the correct two ES6 module access forms we need.

Note that in order for this to work in an ES6 loader, the AMD compatibility layer must clone the module object into a new object that has the __esModule property.

The transpiled module returns a flag on its own output to ensure that it, itself, doesn't get marked as an AMD module, and instead can be a full module object (Module({ some: 'export' })).

This way the interop works comprehensively in both existing AMD environments and can be shimmed for compatibility in ES6 loaders as well.

I think that covers everything...

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