ModuleImport

Ron Buckton rbuckton at chronicles.org
Fri Jun 20 15:21:19 PDT 2014


> -----Original Message-----

> From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of

> Sébastien Cevey

> Sent: Friday, June 20, 2014 3:46 AM

> To: Axel Rauschmayer

> Cc: es-discuss list

> Subject: Re: ModuleImport

>

> On 20 June 2014 11:39, Axel Rauschmayer <axel at rauschma.de> wrote:

> > > The `*' symbol universally represents a glob of "everything", but

> > > when used to import from a module that has multiple exports, you

> > > won't get everything, you will get either the single default export

> > > (if there is

> > > one) or nothing.

> >

> >

> > What gives you that impression? Quoting David’s original email:

> >

> > ```js

> > import * as fs from "fs"; // importing the named exports as an object

> > import Dict from "dict";  // importing a default export, same as ever

> > ```



With all due respect, why is it that we cannot change the specification to allow `import name from "module"` for both the default export (for single export modules) and the Module object (for multi-named export modules). The same question goes for using `import { name as "name" } from "module"` for both. As specified, a default export is equivalent to a Module object with a "default" property, and as a result requires special handling with respect to how it is bound to the _ImportBinding_ in `import name from "module"`. Wouldn't it make sense to simplify the syntax and expand the static and runtime semantics for imports? Are we sure that the current semantics are the right approach that we should shoehorn the syntax into?



Is it imperative for module authors to be able to provide both a default export *and* named exports within the same module? From most of the comments in this thread, it seems that expected module use falls into two categories:  Single-export modules and Multi-export modules. Is there a use-case in the wild (via ES6 module transpilers) where a single module today uses both a default export as well as named exports?



With respect to Node libraries, I often see one of three approaches to exporting from a module:



_Named Exports_:

```

exports.foo = 1;

// or

module.exports.foo = 1;

```



_Single Export Object_:

```

module.exports = {

  foo: 1,

  bar: function() {}
}

```



_Single Export Function_:

```

module.exports = function() { }

```



In Node, if you wanted to have a default export that is a function, but also have additional exports you would most likely add them as data properties on the function:

```

module.exports = function() {}

module.exports.foo = 1;

```



Given that, why not simplify the syntax and semantics to just the following three forms:

```

import "module"; // imports the module but does not perform binding

import name from "module"; // imports the module (either the default export or a module object with the named exports, see below)

import { name1, name2 as "otherName" } from "module"; // imports members of the module.

```



Simplifying this requires the following (approximate) changes in semantics:



* Either (A) a module cannot have *both* a default export and named exports, _or_..

* (B) A modules named exports become attached properties of the default export if provided.

    * If (B), it becomes an runtime error to add a default export after a named export, and a runtime error to add a named export if the default export is not an Object.

* The ImportBinding (`name` above) becomes bound to a [[Value]] property of an (not part of the current spec) Import exotic object.

* When the Module exotic object is loaded, if it has a property named "default", that becomes the value of the [[Value]] property of the Import exotic object.

* If the Module exotic object does not have a property named "default", the Module itself becomes the value of the [[Value]] property of the Import exotic object.

* NamedImports now points to bindings to the [[Value]] property of the Import exotic object. If you want both a default export and named exports, attach the named exports as properties of the default export.



With the above changes, whether you're using a default export or named exports becomes transparent to the developer.  If the developer _really_ wants the module object, they could fall back to:

```

import "module";

var name = System.get("module"); // Returns the Module object without the transformations applied from above.

```



The above is a rough approximation of the semantics changes. If anyone finds merit to this proposal, I'll find some time this weekend to write out exactly what kind of changes there would need to be in the static and runtime semantics in the current spec. The overall goal is to keep the import syntax simple and expand the static and runtime semantics to support that simplicity. This includes continuing to support the ability to handle cyclic dependencies. Engine authors will need to write the code for the import semantics once, while the development community will use the import syntax over and over for some time to come. It seems like a simpler syntax should win out over possibly changing the semantics.



Best regards,

Ron









p.s. Here are some examples of various inputs and outputs based on this proposal:



# Exports



[named-exports.js]

```

export var version = "1.0";

export function println(text) { console.log(text); }

```



[single-export.js]

```

function hello (text) { alert("Hello " + text + "!"); }

hello.goodbye = function (text) { alert("Goodbye " + text + "!"); }

export default hello;

```



[multi-export-with-default.js]

```

// if (A) above, this is an early error

// if (B) above, version becomes Person.version

export default class Person {

  jump() { return "How high?"; }
}

export var version = "2.0";

```



[single-export-of-object.js]

```

export default {

  version: "1.0",

  assert(test) { if (!test) throw new Error("failed!"); }

}

```



# Imports



[import-named-imports.js]

```

import namedExports from "named-exports"; // namedExports = binding to Import.[[Value]] which results in a Module object with properties "version" and "println";

import { version, println } from "named-exports"; // version = binding to Import.[[Value]].version, println = binding to Import.[[Value]].println

// or...

import namedExports, { version, println } from "named-exports"; // both of the above

```



[import-single-export.js]

```

import hello from "single-export"; // hello = binding to Import.[[Value]] which results in the "hello" function

import { goodbye } from "single-export"; // goodbye = binding to Import.[[Value]].goodbye

// or...

import hello, { goodbye } from "single-export"; // both of the above

```



[import-multi-export-with-default.js]

```

// assumes (B) above

import Person from "multi-export-with-default"; // Person = binding to Import.[[Value]] which results in the "Person" class

import { version } from "multi-export-with-default"; // version = binding to Import.[[Value]].version

// or...

import Person, { version } from "multi-export-with-default"; // both of the above

```



[import-single-export-of-object.js]

```

import test from "single-export-of-object"; // test = binding to Import.[[Value]] which results in an Object with properties "version" and "assert"

import { version, assert } from "single-export-of-object"; // version = binding to Import.[[Value]].version, assert = binding to Import.[[Value]].assert

// or...

import test, { version, assert } from "single-export-of-object"; // both of the above.

```




-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140620/baba4e45/attachment-0001.html>


More information about the es-discuss mailing list