Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export syntax for ES6 modules #1215

Closed
sheetalkamat opened this issue Nov 19, 2014 · 5 comments
Closed

Export syntax for ES6 modules #1215

sheetalkamat opened this issue Nov 19, 2014 · 5 comments
Labels
ES6 Relates to the ES6 Spec

Comments

@sheetalkamat
Copy link
Member

As per ES6 grammar:

ExportDeclaration :
export * FromClause ;
export ExportClause FromClause ;
export ExportClause ;
export VariableStatement
export Declaration
export default HoistableDeclaration[Default]
export default [lookahead ≠ function ] AssignmentExpression[In] ;


ExportClause[NoReference] :
{ }
{ ExportsList }
{ ExportsList , }

ExportsList :
ExportSpecifier
ExportsList , ExportSpecifier

ExportSpecifier :
IdentifierName
IdentifierName as IdentifierName

FromClause :
from ModuleSpecifier

ModuleSpecifier :
StringLiteral

Meanings of export syntax that we currently do not support (doesn't list generators since we do not support them as yet)

export default function name() { } 
// function name() { } 
// exports.default = name;
export default function () { } 
// function _temp() { } 
// exports.default = _temp;
export default = expression; 
// var _tmp = expression;
// exports.default = _tmp;
export { }; 
// Make this external module
export { } from StringLiteral; 
// import __tmp = require(StringLiteral)
export { a }; 
//exports.a = a;
export { a } from StringLiteral; 
// import __tmp = require(StringLiteral)
// exports.a = _tmp.a
export { id1 as a}; 
//exports.a = id1;
export { id1 as a } from StringLiteral; 
// import __tmp = require(StringLiteral)
// exports.a = _tmp.id1
export * from StringLiteral
// import _tmp = require(StringLIteral)
// foreach(a in _tmp.exports) 
// exports.a = _tmp.exports[a] 

Few things to consider:

  1. Resolution of export name is as follows,
  • if the name is directly exported clause (without referring to another module) - the resolution is returned for this name
  • if the name is exported using names eg. export {a} from stringLiteral - that resolution is returned
  • it is tried to be resolved from export entries of name (except default) from export * from StringLiteral
  • Should we be reporting errors for the name from export * from StringLiteral that is shadowed by the local exports/indirect named exports/existing * exports. According to ES6 it is ok, but should we still report error for shadowing for unintentional cases?
  1. The export names are always resolved in order of local exports, indirect exports and then star exports, But typescript currently uses declaration order to determine duplicate error. Should we change this order for duplicate naming or is it ok?
@sheetalkamat sheetalkamat added the ES6 Relates to the ES6 Spec label Nov 19, 2014
@sheetalkamat
Copy link
Member Author

Note that syntax of for export {a } from StringLiteral is dynamic so we would need to emit import statement at the reference location and the bindings at that point

@rbuckton
Copy link
Member

The export binding here has the same issue as the import binding in #1203. As the original export is mutable, export bindings re-exported from an external module would lose this trait.

a.js

export var a = 1;
export function changeA() { a = 2; }

b.js

export * from 'a'
export { a as renamed } from 'a';

c.js

import { a, changeA, renamed } from 'b';
console.log(a); // 1
console.log(renamed); // 1
changeA();
console.log(a); // 2
console.log(renamed); // 2

The only way to guarantee the bindings match with the ES6 implementation is to implement them as getters, which wouldn't be supported in ES3:

b.ts

export * from 'a';
// import _tmp = require('a');
// Object.defineProperties(exports, {
//   a: { ..., get: function() { return _tmp.a; } },
//   changeA: { ..., get: function() { return _tmp.changeA; } }
// });

export { a as renamed } from 'a';
// import _tmp = require('a');
// Object.defineProperty(exports, 'renamed', { ..., get: function() { return _tmp.a; } }

@rbuckton
Copy link
Member

The ... is where I elided the following: enumerable: true, configurable: true, though these could be ignored as enumerability and configurability really wouldn't matter

@jonaskello
Copy link

@rbuckton I am trying to re-export a variable binding like in your example above and am looking for some clarification on how this works. As I understand it, this should is support according to the ES6 standard. However typescript does not support this as it copies the re-exports rather than forwards them. Is this correct? If so is there a work-around for re-exporting live bindings or is this just not possible when using typescript? (Want to know if I should keep digging and try to make it work or just give up).

Btw, for me is is OK to use ES5 only, so if there is some way to enable your example using getters to forward the bindings that would be useful.

UPDATE: Also asked on stackoverflow. Since I have target=es5 I think the suggestion to generate getters should be OK. Is there an open issue to implement that or should I open one?

@mhegazy
Copy link
Contributor

mhegazy commented Jan 5, 2016

@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
ES6 Relates to the ES6 Spec
Projects
None yet
Development

No branches or pull requests

4 participants