diff --git a/packages/compiler/test/aot/compiler_spec.ts b/packages/compiler/test/aot/compiler_spec.ts index a515126e517..5b4be6665a2 100644 --- a/packages/compiler/test/aot/compiler_spec.ts +++ b/packages/compiler/test/aot/compiler_spec.ts @@ -821,6 +821,7 @@ const LIBRARY: MockDirectory = { 'public-api.ts': ` export * from './src/bolder.component'; export * from './src/bolder.module'; + export {BolderModule as ReExportedModule} from './src/bolder.module'; `, src: { 'bolder.component.ts': ` diff --git a/tools/@angular/tsc-wrapped/src/bundler.ts b/tools/@angular/tsc-wrapped/src/bundler.ts index b599ddf5602..a7f370a424c 100644 --- a/tools/@angular/tsc-wrapped/src/bundler.ts +++ b/tools/@angular/tsc-wrapped/src/bundler.ts @@ -259,14 +259,40 @@ export class MetadataBundler { exportedSymbols.forEach(symbol => this.convertSymbol(symbol)); + const symbolsMap = new Map(); Array.from(this.symbolMap.values()).forEach(symbol => { if (symbol.referenced && !symbol.reexport) { let name = symbol.name; + const declaredName = symbol.declaration !.name; if (symbol.isPrivate && !symbol.privateName) { name = newPrivateName(); symbol.privateName = name; } - result[name] = symbol.value; + if (symbolsMap.has(declaredName)) { + const names = symbolsMap.get(declaredName); + names !.push(name); + } else { + symbolsMap.set(declaredName, [name]); + } + result[name] = symbol.value !; + } + }); + + // check for duplicated entries + symbolsMap.forEach((names: string[], declaredName: string) => { + if (names.length > 1) { + // prefer the export that uses the declared name (if any) + let reference = names.indexOf(declaredName); + if (reference === -1) { + reference = 0; + } + + // keep one entry and replace the others by references + names.forEach((name: string, i: number) => { + if (i !== reference) { + result[name] = {__symbolic: 'reference', name: declaredName}; + } + }); } }); diff --git a/tools/@angular/tsc-wrapped/test/bundler_spec.ts b/tools/@angular/tsc-wrapped/test/bundler_spec.ts index 54ba8dca60c..45ff178b633 100644 --- a/tools/@angular/tsc-wrapped/test/bundler_spec.ts +++ b/tools/@angular/tsc-wrapped/test/bundler_spec.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {MetadataBundler, MetadataBundlerHost} from '../src/bundler'; import {MetadataCollector} from '../src/collector'; -import {ModuleMetadata} from '../src/schema'; +import {ClassMetadata, MetadataGlobalReferenceExpression, ModuleMetadata} from '../src/schema'; import {Directory, open} from './typescript.mocks'; @@ -192,6 +192,35 @@ describe('metadata bundler', () => { ]); expect(result.metadata.origins['E']).toBeUndefined(); }); + + it('should be able to de-duplicate symbols of re-exported modules', () => { + const host = new MockStringBundlerHost('/', { + 'public-api.ts': ` + export {A as A2, A, B as B1, B as B2} from './src/core'; + `, + 'src': { + 'core.ts': ` + export class A {} + export class B {} + `, + } + }); + + const bundler = new MetadataBundler('/public-api', undefined, host); + const result = bundler.getMetadataBundle(); + const {A, A2, B1, B2} = result.metadata.metadata as{ + A: ClassMetadata, + A2: MetadataGlobalReferenceExpression, + B1: ClassMetadata, + B2: MetadataGlobalReferenceExpression + }; + expect(A.__symbolic).toEqual('class'); + expect(A2.__symbolic).toEqual('reference'); + expect(A2.name).toEqual('A'); + expect(B1.__symbolic).toEqual('class'); + expect(B2.__symbolic).toEqual('reference'); + expect(B2.name).toEqual('B'); + }); }); export class MockStringBundlerHost implements MetadataBundlerHost { @@ -242,4 +271,4 @@ export const SIMPLE_LIBRARY = { } } } -}; \ No newline at end of file +};