From c056b8c7f15b47c3624e309c2ab2e9b8d3ce153f Mon Sep 17 00:00:00 2001 From: Olivier Combe Date: Thu, 7 Sep 2017 14:43:29 +0200 Subject: [PATCH] fix(tsc-wrapped): deduplicate metadata for re-exported modules --- packages/compiler/test/aot/compiler_spec.ts | 1 + packages/tsc-wrapped/src/bundler.ts | 26 ++++++++++++++++ packages/tsc-wrapped/test/bundler_spec.ts | 33 +++++++++++++++++++-- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/packages/compiler/test/aot/compiler_spec.ts b/packages/compiler/test/aot/compiler_spec.ts index ef08c70c94..2fc38bb195 100644 --- a/packages/compiler/test/aot/compiler_spec.ts +++ b/packages/compiler/test/aot/compiler_spec.ts @@ -863,6 +863,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/packages/tsc-wrapped/src/bundler.ts b/packages/tsc-wrapped/src/bundler.ts index 92a48744e6..fd9939e734 100644 --- a/packages/tsc-wrapped/src/bundler.ts +++ b/packages/tsc-wrapped/src/bundler.ts @@ -261,17 +261,43 @@ 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; } + 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}; + } + }); + } + }); + return result; } diff --git a/packages/tsc-wrapped/test/bundler_spec.ts b/packages/tsc-wrapped/test/bundler_spec.ts index 347b3c58b2..7ecbc4abea 100644 --- a/packages/tsc-wrapped/test/bundler_spec.ts +++ b/packages/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 +};