Skip to content

Commit c68e780

Browse files
committed
feat(transiler/dart): re-export imported vars
``` import {Foo} from ‘./foo’; var localVar = true; export {Foo, localVar}; ===> import ‘./foo’ show Foo; export ‘./foo’ show Foo; var localVar = true; ``` Closes angular#41
1 parent c515317 commit c68e780

File tree

6 files changed

+163
-26
lines changed

6 files changed

+163
-26
lines changed

tools/transpiler/spec/baz.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export var Baz = 'BAZ';

tools/transpiler/spec/imports_spec.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ import * as exportModule from './export';
99

1010
import {Type} from 'facade/lang';
1111

12+
import {Baz} from './reexport';
13+
1214
export function main() {
1315
describe('imports', function() {
16+
it('should re-export imported vars', function() {
17+
expect(Baz).toBe('BAZ');
18+
});
19+
1420
it('should work', function() {
1521
expect(Foo).toBe('FOO');
1622
expect(Bar).toBe('BAR');

tools/transpiler/spec/reexport.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {Baz} from './baz';
2+
import {Bar1} from './bar';
3+
4+
var localVar = true;
5+
6+
export {Baz, localVar, Bar1};
7+
8+
// Will become:
9+
// export {Baz} from './baz';
10+
// export {Bar1} from './bar';

tools/transpiler/src/codegeneration/DartTransformer.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {InstanceOfTransformer} from './InstanceOfTransformer';
88
import {MultiVarTransformer} from './MultiVarTransformer';
99
import {StrictEqualityTransformer} from './StrictEqualityTransformer';
1010
import {NamedParamsTransformer} from './NamedParamsTransformer';
11+
import {ExportTransformer} from './ExportTransformer';
1112

1213
/**
1314
* Transforms ES6 + annotations to Dart code.
@@ -28,5 +29,6 @@ export class DartTransformer extends MultiTransformer {
2829
append(InstanceOfTransformer);
2930
append(StrictEqualityTransformer);
3031
append(ClassTransformer);
32+
append(ExportTransformer);
3133
}
3234
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import {ParseTreeTransformer} from './ParseTreeTransformer';
2+
import {Map} from 'traceur/src/runtime/polyfills/Map';
3+
import {
4+
ExportDeclaration,
5+
ExportSpecifierSet,
6+
NamedExport,
7+
EmptyStatement
8+
} from 'traceur/src/syntax/trees/ParseTrees';
9+
import {
10+
IMPORT_DECLARATION,
11+
NAMED_EXPORT
12+
} from 'traceur/src/syntax/trees/ParseTreeType';
13+
14+
15+
// Return the index of the first item that is not of IMPORT_DECLARATION type.
16+
function getIndexOfFirstNonImportStatement(items) {
17+
var index = 0;
18+
19+
while (index < items.length && items[index].type === IMPORT_DECLARATION) {
20+
index++;
21+
}
22+
23+
return index;
24+
}
25+
26+
27+
/**
28+
* Transforms re-exports:
29+
* ```
30+
* import {Foo} from './foo';
31+
* import {Bar} from './bar';
32+
* var localVar = true;
33+
*
34+
* export {Foo, Bar, localVar}
35+
*
36+
* ===>
37+
*
38+
* import {Foo} from './foo';
39+
* import {Bar} from './bar';
40+
* var localVar = true;
41+
*
42+
* export {Foo} from './foo';
43+
* export {Bar} from './bar';
44+
* ```
45+
*
46+
* In Dart, all variables defined in the root context of a module that don't start with
47+
* an underscore are exported. Thus we just drop the "export" keyword for the locally defined vars.
48+
* Variables imported from other modules need to be exported with `export './foo' show Foo`.
49+
* This transformer drops the local exports and add the information about module path for the
50+
* imported variables.
51+
*/
52+
export class ExportTransformer extends ParseTreeTransformer {
53+
constructor(idGenerator, reporter) {
54+
this.reporter_ = reporter;
55+
this.importedVars_ = null;
56+
this.collectedExports_ = null;
57+
}
58+
59+
transformModule(tree) {
60+
// New context for each file (module).
61+
this.importedVars_ = new Map();
62+
this.collectedExports_ = [];
63+
64+
tree = super.transformModule(tree);
65+
66+
// In Dart, imports and exports have to be at the top, before any other statement.
67+
// Insert the collected exports before the first non-import statement (after all imports).
68+
var items = tree.scriptItemList;
69+
var index = getIndexOfFirstNonImportStatement(items);
70+
tree.scriptItemList = items.slice(0, index).concat(this.collectedExports_, items.slice(index));
71+
72+
return tree;
73+
}
74+
75+
// For each imported variable, store the module path where it comes from.
76+
// For instance, `import {Foo} from './foo'` will map 'Foo' -> './foo'.
77+
// TODO(vojta): deal with `import * as m from './foo'`.
78+
transformImportDeclaration(tree) {
79+
tree.importClause.specifiers.forEach((specifier) => {
80+
this.importedVars_.set(specifier.binding.binding.identifierToken.value, tree.moduleSpecifier);
81+
});
82+
83+
return tree;
84+
}
85+
86+
transformExportDeclaration(tree) {
87+
if (tree.declaration.type === NAMED_EXPORT && tree.declaration.moduleSpecifier === null) {
88+
// export {...}
89+
tree.declaration.specifierSet.specifiers.forEach((specifier) => {
90+
// Filter out local variables, keep only imported ones.
91+
if (!this.importedVars_.has(specifier.lhs.value)) {
92+
return;
93+
}
94+
95+
// For each specifier, create a new ExportDeclaration and attach the module path (collected
96+
// in `transformImportDeclaration`) to it.
97+
this.collectedExports_.push(new ExportDeclaration(tree.location,
98+
new NamedExport(tree.declaration.location, this.importedVars_.get(specifier.lhs.value),
99+
new ExportSpecifierSet(tree.declaration.specifierSet.location, [specifier])),
100+
tree.annotations));
101+
});
102+
103+
return new EmptyStatement(tree.location);
104+
}
105+
106+
return tree;
107+
}
108+
}

tools/transpiler/src/outputgeneration/DartParseTreeWriter.js

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
import {ParseTreeWriter as JavaScriptParseTreeWriter, ObjectLiteralExpression} from 'traceur/src/outputgeneration/ParseTreeWriter';
2626
import {ImportedBinding, BindingIdentifier} from 'traceur/src/syntax/trees/ParseTrees';
2727
import {IdentifierToken} from 'traceur/src/syntax/IdentifierToken';
28-
import {EXPORT_STAR} from 'traceur/src/syntax/trees/ParseTreeType';
28+
import {EXPORT_STAR, NAMED_EXPORT} from 'traceur/src/syntax/trees/ParseTreeType';
2929

3030
export class DartParseTreeWriter extends JavaScriptParseTreeWriter {
3131
constructor(moduleName, outputPath) {
@@ -34,6 +34,8 @@ export class DartParseTreeWriter extends JavaScriptParseTreeWriter {
3434
this.annotationContextCounter = 0;
3535
}
3636

37+
visitEmptyStatement() {}
38+
3739
// CLASS FIELDS
3840
visitPropertyVariableDeclaration(tree) {
3941
if (tree.isStatic) {
@@ -275,35 +277,43 @@ export class DartParseTreeWriter extends JavaScriptParseTreeWriter {
275277

276278
// EXPORTS
277279
visitExportDeclaration(tree) {
278-
if (tree.declaration.moduleSpecifier) {
279-
if (tree.declaration.specifierSet.type === EXPORT_STAR) {
280-
// export * from './foo'
281-
// ===>
282-
// export './foo';
283-
this.write_('export');
284-
this.writeSpace_();
285-
this.visitModuleSpecifier(tree.declaration.moduleSpecifier);
286-
this.write_(SEMI_COLON);
280+
if (tree.declaration.type === NAMED_EXPORT) {
281+
// export {...}
282+
// export {...} from './foo'
283+
// export * from './foo'
284+
285+
if (tree.declaration.moduleSpecifier) {
286+
if (tree.declaration.specifierSet.type === EXPORT_STAR) {
287+
// export * from './foo'
288+
// ===>
289+
// export './foo';
290+
this.write_('export');
291+
this.writeSpace_();
292+
this.visitModuleSpecifier(tree.declaration.moduleSpecifier);
293+
this.write_(SEMI_COLON);
294+
} else {
295+
// export {Foo, Bar} from './foo'
296+
// ===>
297+
// export './foo' show Foo, Bar;
298+
this.write_('export');
299+
this.writeSpace_();
300+
this.visitModuleSpecifier(tree.declaration.moduleSpecifier);
301+
this.writeSpace_();
302+
this.write_('show');
303+
this.writeSpace_();
304+
this.writeList_(tree.declaration.specifierSet.specifiers, COMMA, false);
305+
this.write_(SEMI_COLON);
306+
}
287307
} else {
288-
// export {Foo, Bar} from './foo'
289-
// ===>
290-
// export './foo' show Foo, Bar;
291-
this.write_('export');
292-
this.writeSpace_();
293-
this.visitModuleSpecifier(tree.declaration.moduleSpecifier);
294-
this.writeSpace_();
295-
this.write_('show');
296-
this.writeSpace_();
297-
this.writeList_(tree.declaration.specifierSet.specifiers, COMMA, false);
298-
this.write_(SEMI_COLON);
308+
// export {...}
309+
// This case is handled in `ExportTransformer`.
310+
throw new Error('Should never happen!');
299311
}
300312
} else {
301-
// Just remove the "export" keyword.
302-
// export var x = true;
313+
// export var x = true
303314
// export class Foo {}
304-
// ===>
305-
// var x = true;
306-
// class Foo {}
315+
// export function bar() {}
316+
// Just remove "export" keyword.
307317
this.writeAnnotations_(tree.annotations);
308318
this.visitAny(tree.declaration);
309319
}

0 commit comments

Comments
 (0)