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

re-export module and override single export function #1031

Closed
chauthai opened this issue Jan 12, 2016 · 8 comments
Closed

re-export module and override single export function #1031

chauthai opened this issue Jan 12, 2016 · 8 comments

Comments

@chauthai
Copy link

I have module-a, module-b and module-c. module-b exports all of module-a and only changes one export. After some research and questioning on Stack Overflow the following solution was proposed (http://stackoverflow.com/questions/34745110/es2015-re-export-module-and-override-single-export-function-of-the-re-exported-m)

module-a
export let a = "A";
export let b = "B";
export default "C";
module-b
import * as moduleA from './module-a';

const moduleB = Object.assign({}, moduleA);
moduleB.b = 'Overridden B!';

export default moduleB;
module-c (test suite)
import { a, b } from './module-a';
console.assert(a == 'A', 1);
console.assert(b == 'B', 2);

import defaultB from './module-b';
console.assert(defaultB.a == 'A', 3); 
console.assert(defaultB.b == 'Overridden B!', 4);


import * as moduleA from './module-a' ;
console.log('moduleA');
console.log(moduleA);
console.assert(moduleA.a == 'A', 5);
console.assert(moduleA.b == 'B', 6);

import * as moduleB from './module-b';
console.log('moduleB');
console.log(moduleB);
console.assert(moduleB.default.a == 'A', 7); 
console.assert(moduleB.default.b == 'Overridden B!', 8); 

import defaultA from './module-a';
console.log(defaultA);
console.assert(defaultA == 'C', 11);

The down side of this approach is a forced use of default exports with objects.

My ideal solution would be to use the new export * from 'module-a' expression.

module-b
import * as moduleA from './module-a';
export let b = `Overridden {moduleA.b}!`;
export * from 'module-a';
module-c (modified test suite)
import { a, b } from './module-a';
console.assert(a == 'A', 1);
console.assert(b == 'B', 2);

import { a as ca, b as db } from './module-b';
console.assert(ca == 'A', 3); 
console.assert(db == 'Overridden B!', 4); 


import * as moduleA from './module-a' ;
console.log('moduleA');
console.log(moduleA);
console.assert(moduleA.a == 'A', 5);
console.assert(moduleA.b == 'B', 6);

import * as moduleB from './module-b';
console.log('moduleB');
console.log(moduleB);
console.assert(moduleB.a == 'A', 7); 
console.assert(moduleB.b == 'Overridden B!', 8); 

import defaultA from './module-a';
console.log(defaultA);
console.assert(defaultA == 'C', 11);

import defaultB from './module-b';
console.log(defaultB);
console.assert(defaultB == 'C', 12); 

The reasoning behind this is the first come first serve principle. The first explicit export b gets a higher priority than the second implicit export of b in export * from 'module-a'.
But this solution doesn't work because of the implicit export of b in module-a and explicit export of b in module-b. (http://www.ecma-international.org/ecma-262/6.0/#sec-module-semantics-static-semantics-early-errors: 'It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries.')
The advantage of this solution is you don't need a default export object.

Is there any other possibility to this problem besides submitting a proposal to change the ECMAScript spec or using export default with an object?

@guybedford
Copy link
Member

That spec point is referring to things like:

export let a = 'sadf';
export function a() {
}

Which is a syntax error.

The algorithm in http://www.ecma-international.org/ecma-262/6.0/#sec-getexportednames is designed so that local exports DO take priority.

That is, your second code example should work, but just needs to be written:

export * from './module-a';
export let b = 'overridden';

Note though that export * is not supposed to export default exports, so that can be a catch. The Traceur transpiler implementation catches this, while the Babel one does not.

@peterjuras
Copy link

I noticed that there is a difference in behavior depending on how I write the export. If I write the module like this:

export * from './A'; // has a and b

function b() {
   console.log("I'm b from project");
}

export { b };

Then b is overwritten. which is nice.

However, when I write it like this:

export * from './A';

export function b() {
   console.log("I'm b from project");
}

Function b is not overwritten. Shouldn't these two export statements behave in the same way?

I found the reason for this in the system.js source code, it is simply a matter of which function gets assigned to the export object first (thereby later being overwritten by the other one).

The bigger question here is: Is overwriting exports like in the first example a feature or a bug? We'd like to know if we can depend on it in the future, because it would be nice to be able to override exported functions.

@guybedford
Copy link
Member

@peterjuras it's a feature, and as mentioned above it is not consistent in Babel's transpilation output. Traceur has what I believe to be the correct implementation of the module syntax edge cases though.

@guybedford
Copy link
Member

(I'd suggest posting a Babel issue if this is something you would like to be prioritised, although unfortunately I personally have limited resources to work on Babel's implementation, but can advise)

@chauthai
Copy link
Author

@guybedford thanks for the clarification. I will file a Babel issue.

@chauthai
Copy link
Author

I think this Babel issue is related: https://phabricator.babeljs.io/T2438

@guybedford
Copy link
Member

Yes, note that it is module output format specific though... I believe that conversation is wrt the ComonJS output.

@chauthai
Copy link
Author

posted a new issue: https://phabricator.babeljs.io/T6967

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants