Skip to content

CommonJS and AMD interop with single exports #1388

@guybedford

Description

@guybedford

As mentioned in #1366 (comment), the interop scenario for ES6 compiled into CommonJS becomes nicer when we can allow things like:

import { readFile } from 'fs';

as well as:

import fs from 'fs';

At the moment the first example is not possible in Traceur. We could enable this with something like:

var $__0 = $traceurRuntime.processDependency(require('fs'));
module.exports = $traceurRuntime.System.newModule({});

Where the function $traceurRuntime.processDependency could be written:

function processDependency(originalModule) {
  // if it is an ES6 module already just return
  if (Reflect.isModule(originalModule))
    return originalModule;

  // if we have a cached __esModule, return it
  if (originalModule.__esModule)
    return originalModule.__esModule);

  // non-extensible objects don't participate in this feature
  // due to caching restrictions (see next part)
  if (!Object.isExtensible(originalModule))
    return { 'default': originalModule };

  // we create a module object and cache it as originalModule.__esModule
  var module = {};
  Object.keys(originalModule).forEach((name) => {
    module[name] = originalModule[name];
  });
  module.default = originalModule;
  originalModule.__esModule = System.newModule(module);
  return originalModule.__esModule;
}

So the questions with this form are:

  1. What is the performance cost of this approach?
  2. Do we want to enforce a Traceur runtime dependency for all CommonJS and AMD compilation, including giving up potential ES3 compilation support for ES6 syntax only?

Note Reflect.isModule consideration is completely orthogonal to this question. I've included it in the implementation to show it can be useful, but it can be done without as well. Here the question is simply how badly we want import { readFile } from 'fs' in ES6 compiled to CommonJS running on Node, and if we can tolerate these hoops to get there.

My personal opinion is that this is unnecessary complexity and incredibly ugly, making modules more confusing and difficult to understand than they already are - that predictability and simplicity in interop should be favoured over what may be deemed as nice to have. But my personal opinion is largely irrelevant as what matters is what users need here. I just want to see these interop scenarios working consistently in a way we can agree on.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions